s4

changeset 815:66d9b07edcc2

First complete merge with featureWorld branch
author HIROSE Yuuji <yuuji@gentei.org>
date Tue, 16 Jun 2020 13:11:18 +0900
parents 70e056d2443a 04034092338d
children de988b0a7afa
files
diffstat 9 files changed, 627 insertions(+), 39 deletions(-) [+]
line diff
     1.1 --- a/examples/common/default/default.css	Tue Jun 16 13:10:57 2020 +0900
     1.2 +++ b/examples/common/default/default.css	Tue Jun 16 13:11:18 2020 +0900
     1.3 @@ -16,6 +16,7 @@
     1.4      box-shadow: #242 2px 3px 5px;
     1.5      text-shadow: #fff 0px 0px 10px;
     1.6  }
     1.7 +div.topmenu ul li.worldname {background: #eeeeff;}
     1.8  div.topmenu ul li:hover {background: #8fa;}
     1.9  div.topmenu ul a {text-decoration: none;}
    1.10  
    1.11 @@ -331,6 +332,20 @@
    1.12  input[type="reset"] {margin-left: 4em;}
    1.13  
    1.14  /*
    1.15 + * World List
    1.16 + */
    1.17 +li.casmenu div {display: none; position: relative; width: 200%;
    1.18 +		min-width: 80%; margin-right: 0;}
    1.19 +li.casmenu div table {
    1.20 +    background: white; position: absolute; top: 0em; border: 3px solid navy;
    1.21 +    max-width: 100%;
    1.22 +}
    1.23 +li.casmenu div table td {text-align: left; padding: 0.5ex 1em;}
    1.24 +li.casmenu:hover div, li.casmenu:active div,
    1.25 +li.casmenu div:hover, li.casmenu div:active
    1.26 +{display: block;}
    1.27 +
    1.28 +/*
    1.29   * PR Web
    1.30   */
    1.31  body.pr {font-size: 200%;}
     2.1 --- a/examples/common/default/footer.m4.html	Tue Jun 16 13:10:57 2020 +0900
     2.2 +++ b/examples/common/default/footer.m4.html	Tue Jun 16 13:11:18 2020 +0900
     2.3 @@ -1,6 +1,6 @@
     2.4  <p class="copyright">Driven by
     2.5  <a href="http://www.gentei.org/~yuuji/software/s4/">s4</a>
     2.6 -&copy;2015-2019 by 
     2.7 +&copy;2015-2020 by
     2.8  <a href="http://www.gentei.org/~yuuji/">yuuji</a>
     2.9  </body>
    2.10  </html>
     3.1 --- a/examples/common/default/html.m4.html	Tue Jun 16 13:10:57 2020 +0900
     3.2 +++ b/examples/common/default/html.m4.html	Tue Jun 16 13:11:18 2020 +0900
     3.3 @@ -7,7 +7,7 @@
     3.4  `<meta name="theme-color" content="_S4COLOR_">',
     3.5  `<meta name="theme-color" content="#8ea">')
     3.6  <meta name="viewport" content="width=device-width,initial-scale=0.5">
     3.7 -<title>_TITLE_|ifdef(`_S4NAME_',`_S4NAME_',s4)</title>
     3.8 +<title>_TITLE_|ifdef(`_S4WORLD_',`{_S4WORLD_}')ifdef(`_S4NAME_',`_S4NAME_',s4)</title>
     3.9  <link rel="stylesheet" type="text/css" href="templ/default/default.css">
    3.10  <script type="text/javascript" src="s4-main.js" charset="utf-8"></script>
    3.11  ifdef(`_S4CSS_',
    3.12 @@ -18,17 +18,19 @@
    3.13  <body class="_BODYCLASS_">
    3.14  <div class="topmenu">
    3.15  <ul>
    3.16 - <li><a href="?home" accesskey="1" title="Shortcut: 1
    3.17 -Home">ホーム</a></li>
    3.18 + <li class="casmenu"><a href="?home" accesskey="1" title="Shortcut: 1
    3.19 +Home">ホーム</a>ifdef(`_S4WORLDS_',`_S4WORLDS_')</li>
    3.20   <!-- <li><a href="?blog">話題作成</a></li> -->
    3.21   <li><a href="?mems" accesskey="2" title="Shortcut: 2
    3.22  Members">参加者一覧</a></li>
    3.23 - <li><a href="?grps" accesskey="3" title="Shortcut: 3
    3.24 -Groups">グループ一覧</a></li>
    3.25 - <li><a href="?invite" accesskey="4" title="Shortcut: 4
    3.26 -Invite">招待</a></li>
    3.27 + <li class="casmenu"><a href="?grps" accesskey="3" title="Shortcut: 3
    3.28 +Groups">グループ一覧</a>ifdef(`_S4WORLDGRPS_',`_S4WORLDGRPS_')</li>
    3.29 +ifdef(`_S4WORLDNAME_',`',
    3.30 +` <li><a href="?invite" accesskey="4" title="Shortcut: 4
    3.31 +Invite">招待</a></li>')dnl
    3.32   <li><a href="?login" accesskey="5" title="Shortcut: 5
    3.33  Sign out">再ログイン</a></li>
    3.34   <!-- <li><a href="?userconf">userconf</a></li> -->
    3.35 +ifdef(`_S4WORLDNAME_',`<li class="worldname">@_S4WORLDNAME_</li>',`')dnl
    3.36  </ul>
    3.37  </div>
     4.1 --- a/s4-blog.sh	Tue Jun 16 13:10:57 2020 +0900
     4.2 +++ b/s4-blog.sh	Tue Jun 16 13:11:18 2020 +0900
     4.3 @@ -127,7 +127,7 @@
     4.4    td=`getcachedir "article/$2"`
     4.5    [ -d "$td" ] || mkdir -p $td
     4.6    tbl=${1%%[!A-Z0-9a-z_]*} rowid=${2%%[!A-Z0-9a-z_]*}
     4.7 -  err blow_showentry: rowid=$rowid, '$2'=$2 user=$user
     4.8 +  err blog_showentry: rowid=$rowid, '$2'=$2 user=$user
     4.9    ts=${tbl}_s tm=${tbl}_m
    4.10    at=article as=article_s am=article_m
    4.11    serial=$(($(date +%s)-1420038000))s$$
    4.12 @@ -532,7 +532,7 @@
    4.13  	echo "</td></tr>"
    4.14        } > "$cachefile.$$"	######## New ROW Creation Ends here ########
    4.15        # Care about race condition
    4.16 -      if [ -s $cachefile -a $cachefile -nt $cachestamp ]; then
    4.17 +      if [ -z "$hte" -a -s $cachefile -a $cachefile -nt $cachestamp ]; then
    4.18  	# If other process have created cache, give up to serve our file
    4.19  	rm -f $cachefile.$$
    4.20        else
     5.1 --- a/s4-funcs.sh	Tue Jun 16 13:10:57 2020 +0900
     5.2 +++ b/s4-funcs.sh	Tue Jun 16 13:11:18 2020 +0900
     5.3 @@ -4,20 +4,39 @@
     5.4  
     5.5  [ -f s4-config.sh ] && . ./s4-config.sh
     5.6  
     5.7 +test -n "$HTTP_HOST" && isCGI=true || isCGI=false
     5.8 +if $isCGI; then
     5.9 +  case "$SCRIPT_NAME" in
    5.10 +    *-world-*)
    5.11 +      S4WORLD=${SCRIPT_NAME#*world-}
    5.12 +      S4WORLD=${S4WORLD%.*}
    5.13 +      echo S4WORLD=$S4WORLD >&2
    5.14 +      worldconf=s4-config-${S4WORLD}.sh
    5.15 +      ;;
    5.16 +    *)
    5.17 +      worldconf=s4-config.sh
    5.18 +      ;;
    5.19 +  esac
    5.20 +  echo worldconf=$worldconf >&2
    5.21 +  [ -n "$worldconf" -a -e "$worldconf" ] && . ./$worldconf
    5.22 +  echo DB=$DB >&2
    5.23 +fi
    5.24  myname=`basename ${SCRIPT_NAME:-$0}`
    5.25  mydir=`dirname ${SCRIPT_FILENAME:-$0}`
    5.26 +cgiext=${CGIEXT:-.cgi}
    5.27  myargs="$@"
    5.28 -test -n "$HTTP_HOST" && isCGI=true
    5.29  PATH=/usr/local/sqlite3/bin:/usr/local/vim7/bin:/usr/iekei/ImageMagick/bin:/usr/local/ImageMagick/bin:$PATH
    5.30  tmpdir=${TMPDIR:-tmp}
    5.31  dbdir=${DBDIR:-db}
    5.32 +logdir=${LOGDIR:-tmp}
    5.33  tmpfiles=""
    5.34 +querylog=${QUERYLOG:-$logdir/query.log}
    5.35 +searchlog=${SEARCHLOG:-$logdir/search.log}
    5.36  db=${DB:-$dbdir/cgi.sq3}
    5.37 -querylog=${QUERYLOG:-$tmpdir/query.log}
    5.38 -searchlog=${SEARCHLOG:-$tmpdir/search.log}
    5.39 +sessdb=${SESSDB:-$dbdir/sess.sq3}
    5.40 +userupdateflag=$dbdir/userupdate
    5.41 +sesstb=tmp.sess
    5.42  workdb=$dbdir/tmpdata.sq3
    5.43 -sessdb=$dbdir/sess.sq3
    5.44 -sesstb=tmp.sess
    5.45  listentlimit=${LISTENTLIMIT:-30}
    5.46  listartlimit=${LISTARTLIMIT:-50}
    5.47  admin=${ADMIN:-hostmaster@example.org}
    5.48 @@ -59,16 +78,36 @@
    5.49  session=$main_session
    5.50  
    5.51  tconfs=""
    5.52 -imgcached=cache/img.`date +%Y/%m`
    5.53 +imgcached=cache/${S4WORLD:+$S4WORLD/}img.`date +%Y/%m`
    5.54  conftbl=_tblconf
    5.55  nl="
    5.56  "
    5.57  likeesc=`printf '\037'`		# ESCAPE char of LIKE operator
    5.58  iconcachekey="profimgcache_S"
    5.59 +
    5.60 +# Start debug logging
    5.61 +logtag="${S4WORLD:+{$S4WORLD\}}"
    5.62 +exec 3>> $logdir/debug.out
    5.63 +err() {
    5.64 +  echo "[`date +%F-%T%z`]$logtag $@" 1>&3
    5.65 +}
    5.66  case "$HTTP_USER_AGENT" in
    5.67    *i[Pp]hone*|*[Aa]ndroid*)	touchpanel=1 ;;
    5.68    *)				touchpanel="" ;;
    5.69  esac
    5.70 +
    5.71 +# If S4MASTERDB is set, behave in another world
    5.72 +### if [ -n "$S4MASTERDB" -a -s "$S4MASTERDB" ]; then
    5.73 +# If S4WORLDLIST is set, this s4 have world!
    5.74 +if [ -n "$S4WORLDLIST" ]; then
    5.75 +  . ./s4-world.sh 2>> $logdir/debug.out
    5.76 +  # Variables set in s4-world.sh
    5.77 +  #	$S4WORLDS, $S4WROLDNAME, $S4WORLDGRPS
    5.78 +  # Files created in s4-world.sh
    5.79 +  #	$worldlistfile, $worldoptionfile, $worldnamefile, $worldgrpfile
    5.80 +fi
    5.81 +
    5.82 +
    5.83  [ -f ./s4-cgi.sh ] && . ./s4-cgi.sh
    5.84  
    5.85  : <<EOF
    5.86 @@ -335,10 +374,10 @@
    5.87  EOF
    5.88  
    5.89  logstart() {
    5.90 -  echo "`date '+%F %T'`:[${user:-NULL}] <<<" >> ${1:-$querylog}
    5.91 +  echo "`date '+%F %T'`:[${user:-NULL}]$logtag <<<" >> ${1:-$querylog}
    5.92  }
    5.93  logend() {
    5.94 -  echo ">>>" >> ${1:-$querylog}
    5.95 +  echo ">>>$logtag" >> ${1:-$querylog}
    5.96  }
    5.97  sqlog() {
    5.98    logstart
    5.99 @@ -372,12 +411,11 @@
   5.100    #tail -f $sqi | sq $db &	# "tail -f" is too heavy. DO NOT USE!!
   5.101    sq  $db < $sqi &
   5.102    sq3pid="`jobs -p` $!"
   5.103 -  if [ -n "$isCGI" ]; then
   5.104 -     exec 2>> $tmpdir/error.out
   5.105 +  if $isCGI; then
   5.106 +     exec 2>> $logdir/error.out
   5.107    fi
   5.108 -  exec 3>> $tmpdir/debug.out
   5.109    exec 5> $sqi # Turning $sqi access through fd5 for continuous open state
   5.110 -  chmod o-r $tmpdir/error.out $tmpdir/debug.out
   5.111 +  chmod o-r $logdir/error.out $logdir/debug.out
   5.112    rm $sqi
   5.113    # Attach supplemental DB
   5.114    cat >&5 <<-EOF
   5.115 @@ -424,7 +462,11 @@
   5.116  }
   5.117  _m4() {
   5.118    #S4NAME=f,f,f
   5.119 -  m4 ${S4NAME:+"-D_S4NAME_=${S4NAME}"} ${S4CSS:+-D_S4CSS_="$S4CSS"} "$@"
   5.120 +  m4 ${S4NAME:+"-D_S4NAME_=${S4NAME}"} ${S4CSS:+-D_S4CSS_="$S4CSS"} \
   5.121 +     ${S4WORLD:+-D_S4WORLD_="$S4WORLD"} \
   5.122 +     ${S4WORLDNAME:+-D_S4WORLDNAME_="$S4WORLDNAME"} \
   5.123 +     ${S4WORLDGRPS:+-D_S4WORLDGRPS_="$S4WORLDGRPS"} \
   5.124 +     ${S4WORLDS:+-D_S4WORLDS_="$S4WORLDS"} "$@"
   5.125  }
   5.126  ismember() {
   5.127    # $1=user, $2=group
   5.128 @@ -1216,6 +1258,7 @@
   5.129    for kv in `echo $HTTP_COOKIE|sed 's/[;, ]/ /g'`; do
   5.130      k="${kv%%=*}"
   5.131      v="`echo ${kv#*=}|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
   5.132 +    ## err "GetCookie: $k=[$v]"
   5.133      case "$k" in
   5.134        user)	_user="$v" ;;
   5.135        skey)	_skey="$v" ;;
   5.136 @@ -1235,7 +1278,7 @@
   5.137    # smail rcpts subj (file)
   5.138    # $SMAIL_TO  <- Recipient value of To: header
   5.139    # $MAIL_FROM <- From: header value
   5.140 -  from=`echo "${MAIL_FROM:-$admin}"|nkf -jM|tr -d '\n'`
   5.141 +  from=`echo "${MAIL_FROM:-$admin}"|nkf -jM|tr : /|tr -d '\n'`
   5.142    rcpt=`echo $1|tr ' ' '\n'|sort -u|tr '\n' ' '` # uniq and strip newlines
   5.143    rcptheader=`echo $1|tr ' ' '\n'|sort -u|sed '2,$s/^/To: /g'`
   5.144    subj=`echo $2|nkf -jM|tr -d '\n'`
   5.145 @@ -1345,7 +1388,7 @@
   5.146    newpswd=`genrandom` # newsalt=`genrandom 5`
   5.147    #encpswd=`mycrypt "$newpswd" "$newsalt"`
   5.148    encpswd=`echo $newpswd|mypwhash`
   5.149 -  dbsetbyid user $user pswd "$encpswd"
   5.150 +  dbsetbyid user $user pswd "$encpswd" && touch $userupdateflag
   5.151    # Avoid $user substitution with m4, because $url comes from user input.
   5.152    _m4 -D_PSWD_="$newpswd" -D_URL_="$url" -D_ADMIN_="$admin" \
   5.153       $msgdir/mail-newaccount.m4 \
   5.154 @@ -1420,9 +1463,6 @@
   5.155  trap cleanup INT HUP EXIT TERM PIPE
   5.156  # trap cleanup INT HUP
   5.157  
   5.158 -err() {
   5.159 -  echo "[`date +%F-%T%z`] $@" 1>&3
   5.160 -}
   5.161  
   5.162  cgiinit() {
   5.163    tmpd=`tmpd=$tmpdir mktempd`
   5.164 @@ -1827,10 +1867,38 @@
   5.165    # GF_ACTION="?grp+$1" edittable "$formdir/grp.def" "grp" "$rowid" #2015-0804
   5.166    GF_STAGE="groupupdate" edittable "$formdir/grp.def" "grp" "$rowid"
   5.167    if [ -z "$STOPCLONEMSG" ]; then
   5.168 -    html div 'class="fold"' <<-EOF
   5.169 -	`cgi_checkbox clone yes id="clone"`<label
   5.170 -	 for="clone">同一メンバーで別グループを作成する</label>
   5.171 -	<div>
   5.172 +    ## Setup migration menu
   5.173 +    height="10em"		## Ugly!!
   5.174 +    if [ -n "$S4WORLDLIST" ]; then
   5.175 +      v=`fgrep -v "value=\"$worldconf\"" $worldoptionfile`
   5.176 +      err v=$v
   5.177 +      if [ -n "$v" ]; then
   5.178 +	migrate=$(cat<<-EOF
   5.179 +	`cgi_radio grpaction migrate id="migrate"`<label
   5.180 +	 for="migrate">別Worldへ移住</label>
   5.181 +	<div style="height: $height;">
   5.182 +	<form action="?migrategrp">
   5.183 +	<p>移住先:<select name="migrateto">$nl$v$nl</select></p>
   5.184 +	<p>グループや掲示板のURLが変わります。
   5.185 +	外部からリンクしている場合は飛べなくなります。
   5.186 +	すでにリンクされた掲示板を多数含む場合は既存グループを温存し、
   5.187 +	「グループのクローン」で
   5.188 +	メンバーを引き継いだ上でそのクローンを移住するのがお勧めです。</p>
   5.189 +	<p><label>`cgi_checkbox emichk yes`確認</label></p>
   5.190 +	`cgi_hidden stage migrategrp`
   5.191 +	`cgi_hidden rowid $rowid`
   5.192 +	`cgi_submit OK`
   5.193 +	`cgi_reset Reset`
   5.194 +	</form>
   5.195 +	</div>
   5.196 +	EOF
   5.197 +	       )
   5.198 +      fi
   5.199 +    fi
   5.200 +    html div 'class="foldtabs"' <<-EOF
   5.201 +	`cgi_radio grpaction clone id="clone"`<label
   5.202 +	 for="clone">グループのクローン作成</label>
   5.203 +	<div style="height: $height;">
   5.204  	 <p>構成メンバーが同じ新規グループを作成します。</p>
   5.205  	 <table>
   5.206  	  <tr><td><a href="?groupclone+$rowid">
   5.207 @@ -1843,9 +1911,55 @@
   5.208  	 <p>ボタンを押すと即作成します。不要な場合はグループ編集で
   5.209  	 削除してください。</p>
   5.210  	</div>
   5.211 +	$migrate
   5.212 +	 `cgi_radio grpaction close id="x"`<label for="x" accesskey="x">×</label>
   5.213 +	<div style="height: $height; background: transparent;"></div>
   5.214  	EOF
   5.215    fi
   5.216  }
   5.217 +migrategrp() {
   5.218 +  rowid=`getpar rowid`
   5.219 +  rowid=${rowid%%[!0-9]*}
   5.220 +  grp=`getgroupbyid $rowid`
   5.221 +  if ! isgrpowner "$user" "$grp"; then
   5.222 +    echo "<p><a href=\"?grp+$rowid\">`echo "$grp"|htmlescape`</a></p>"
   5.223 +    return
   5.224 +  fi
   5.225 +  if [ x`getpar emichk` != x"yes" ]; then
   5.226 +    echo "移住確認未チェックなので中止します。" | html p
   5.227 +    grp "$rowid"
   5.228 +    return
   5.229 +  fi
   5.230 +  destconf=`getpar migrateto`
   5.231 +  err destconf=$destconf
   5.232 +  if [ ! -e $destconf ]; then
   5.233 +    echo "移住先Worldが認識できないので中止します($destconf)。" | html p
   5.234 +    grp "$rowid"
   5.235 +    return
   5.236 +  fi
   5.237 +  if [ -n "$worldconf" ]; then
   5.238 +    srcconf=$worldconf
   5.239 +  else
   5.240 +    srcconf=s4-config.sh
   5.241 +  fi
   5.242 +  _m4 -D_TITLE_="移住操作" -D_BODYCLASS_="" $layout/html.m4.html
   5.243 +  echo "移住操作" | html h1
   5.244 +  echo '<pre>'
   5.245 +  set -- "$srcconf" "$destconf" "$rowid"
   5.246 +  err  ./s4-migrate.sh "$srcconf" "$destconf" "$rowid"
   5.247 +  . ./s4-migrate.sh		# Dot(.) sourcing might not pass arguments
   5.248 +  rc=$?
   5.249 +  echo "</pre>"
   5.250 +  if [ $rc -eq 0 ]; then
   5.251 +    echo "World [$world] への移住完了。" | html p
   5.252 +    echo "<p><a href=\"$dsturl?grp+$destrowid\">移住先</a></p>"
   5.253 +    clean-orphaned
   5.254 +  else
   5.255 +    echo "移住失敗" | html p
   5.256 +    echo "移動先に重複がないか確認して下さい。" | html p
   5.257 +  fi
   5.258 +  return
   5.259 +}
   5.260  mems() {
   5.261    _m4 -D_TITLE_="参加者一覧" -D_BODYCLASS_=listmember $layout/html.m4.html
   5.262    kwd=`getpar kwd`
   5.263 @@ -2067,8 +2181,14 @@
   5.264    cond="gname in (select gname from grp_mem where user='$uname')"
   5.265    search_form_args=""
   5.266    if [ x"$user" = x"$uname" ]; then
   5.267 -    usermenu="<a href=\"?userconf\" accesskey=\"e\"
   5.268 -	 title=\"Shortcut: E${nl}Edit Profile\">プロフィールの編集</a> /
   5.269 +    if [ -z "$S4MASTERDB" ]; then
   5.270 +      usermenu="<a href=\"?userconf\" accesskey=\"e\"
   5.271 +	 title=\"Shortcut: E${nl}Edit Profile\">プロフィールの編集</a> / "
   5.272 +    elif [ -n "$S4MASTERURL" ]; then
   5.273 +      usermenu="<a href=\"$S4MASTERURL\" accesskey=\"e\"
   5.274 +	 title=\"Shortcut: E${nl}Main Site\">Base World</a> / "
   5.275 +    fi
   5.276 +    usermenu="$usermenu
   5.277  	<a href=\"?blog\" accesskey=\"n\" title=\"Shortcut: N${nl}New blog\">新規話題の作成</a>"
   5.278      # Display folders
   5.279      sql="select count(id) from article_m where id
   5.280 @@ -2091,7 +2211,8 @@
   5.281  
   5.282    tf=$tmpd/title.$$ pf=$tmpd/profile.$$ bf=$tmpd/blogs.$$ sf=$tmpd/search.$$
   5.283    search_form "$search_form_args"	> $sf
   5.284 -  printf "%s さん" "$gecos"|htmlescape	> $tf
   5.285 +  printf "%s さん%s" "$gecos" "${S4WORLDNAME:+@$S4WORLDNAME}" \
   5.286 +    | htmlescape	> $tf
   5.287    { echo "<div class=\"noprofimg\">"
   5.288      viewtable $formdir/user.def user $1
   5.289      echo "</div>"
   5.290 @@ -2390,7 +2511,7 @@
   5.291    # Note that mtime is stored only in grp_s.
   5.292  ## err LE:sql.1="$sql"
   5.293    total=`query "with x as ($sql) select count(*) from x;"`
   5.294 -  echo "${entity} 一覧" | html h2
   5.295 +  echo "${entity} 一覧" "${S4WORLDNAME:+@$S4WORLDNAME}" | html h2
   5.296    echo '<div class="listentry">'		# List-entry div
   5.297    # Show owner/member filter button
   5.298    METHOD=GET
   5.299 @@ -3654,6 +3775,26 @@
   5.300    [ "$ddd" ] && err "----- `gdate +%FT%T.%3N` ------------666666"
   5.301  }
   5.302  
   5.303 +clean-orphaned() {
   5.304 +  # This shoud be done by foreign_key rules, but some db lack them
   5.305 +  query<<-EOF
   5.306 +	-- Find blogs that have no parent
   5.307 +	WITH orphanedblog AS (
   5.308 +	  SELECT blog.id,val FROM blog JOIN blog_s bs
   5.309 +	         ON blog.id=bs.id AND key='owner'
   5.310 +              WHERE val NOT IN (SELECT gname FROM grp)
   5.311 +                AND val NOT IN (SELECT name FROM user)
   5.312 +	) -- Remove them
   5.313 +	DELETE FROM blog WHERE id IN (SELECT id FROM orphanedblog);
   5.314 +
   5.315 +	-- Find articles that have no parent blog
   5.316 +	WITH orphanedarticle AS (
   5.317 +	  SELECT id FROM article
   5.318 +	  WHERE  blogid NOT IN (SELECT id FROM blog)
   5.319 +	) -- Remove them
   5.320 +	DELETE FROM article WHERE id IN (SELECT id FROM orphanedarticle);
   5.321 +	EOF
   5.322 +}
   5.323  par2table() (
   5.324    # copy current parameters of par into destination table
   5.325    # $1=definition-file
   5.326 @@ -3708,6 +3849,9 @@
   5.327      if [ x"$rm" = x"yes" ]; then
   5.328        if [ x"$rm$cfm" = x"yesyes" ]; then
   5.329  	query "delete from $tbl where rowid=$rowid;"
   5.330 +	if [ x"$tbl" = x"grp" -o x"$tbl" = x"blog" ]; then
   5.331 +	  clean-orphaned
   5.332 +	fi
   5.333  	return 4
   5.334        else
   5.335  	echo "消去確認のチェックがないので消さなかったの..." | html p
   5.336 @@ -3960,8 +4104,10 @@
   5.337  	.read $transaction
   5.338  	RELEASE SAVEPOINT pa2table_insert;
   5.339  	EOF
   5.340 -  return $?
   5.341 -  ##err donee
   5.342 +  rc=$?
   5.343 +  [ $rc -eq 0 -a x"$tbl" = x"user" ] && touch $userupdateflag
   5.344 +  ## err "Table:$tbl update done "
   5.345 +  return $rc
   5.346  )
   5.347  genform() {
   5.348    # $1 = form definition file
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/s4-migrate.sh	Tue Jun 16 13:11:18 2020 +0900
     6.3 @@ -0,0 +1,201 @@
     6.4 +#!/bin/sh
     6.5 +# s4 - migration module
     6.6 +# (C)2020 by HIROSE, Yuuji
     6.7 +
     6.8 +srcdb=`unset DB; [ -f $1 ] && . ./$1 && echo ${DB:-db/cgi.sq3}`
     6.9 +dst=`unset DB; [ -f $2 ] && . ./$2 && echo "${DB:-db/cgi.sq3}|$URL"`
    6.10 +dstdb=${dst%\|*}
    6.11 +dsturl=${dst##*\|}
    6.12 +type htmlescape >/dev/null 2>&1 || . `dirname $1`/s4-funcs.sh
    6.13 +case "$2" in
    6.14 +  s4-config.sh) world=Base ;;
    6.15 +  *)		world=${2##*-config-}; world=${world%.*} ;;
    6.16 +esac
    6.17 +htmlworld=`echo "$world"|htmlescape`
    6.18 +
    6.19 +err "--- Migration Started with \$1=$1 \$2=$2 at `date` ---"
    6.20 +err srcdb=$srcdb dstdb=$dstdb
    6.21 +err URL=$URL
    6.22 +err dstURL=$dsturl
    6.23 +
    6.24 +shift 2
    6.25 +
    6.26 +query "ATTACH DATABASE \"$dstdb\" AS dst;" || abort "Cannot attach db #{dstdb}"
    6.27 +
    6.28 +failure=0
    6.29 +for grid; do
    6.30 +  grid=$((0 + $grid))
    6.31 +  gnamesql="(SELECT gname FROM main.grp WHERE rowid=$grid)"
    6.32 +  grp=`query "SELECT gname FROM main.grp WHERE rowid=$grid;"`
    6.33 +  htmlgrp=`echo "$grp"|htmlescape`
    6.34 +  qgrp=`sqlquote "$grp"`
    6.35 +  if [ -n "`query \"SELECT gname FROM dst.grp WHERE gname=$qgrp;\"`" ]; then
    6.36 +    echo "[$htmlgrp]グループがWorld[$htmlworld]にあるので中止します。"|html p
    6.37 +    failure=$((failure+1))
    6.38 +    continue
    6.39 +  fi
    6.40 +  echo "Copying $grid..."
    6.41 +  query "BEGIN;"
    6.42 +  query "REPLACE INTO dst.grp SELECT * FROM main.grp WHERE rowid=$grid;"
    6.43 +  destrowid=`query "SELECT last_insert_rowid();"`
    6.44 +  for tbl in grp_s grp_m grp_mem grp_mem_s grp_mem_m \
    6.45 +		   grp_adm grp_adm_s grp_adm_m; do
    6.46 +    query "REPLACE INTO dst.$tbl SELECT * FROM main.$tbl
    6.47 +	   WHERE gname=$gnamesql;"
    6.48 +  done
    6.49 +  blogs=`query "SELECT group_concat(\"'\"||id||\"'\", ',')
    6.50 +  	        FROM main.blog_s WHERE key='owner' AND val=$gnamesql;"`
    6.51 +  echo blogs=$blogs
    6.52 +  for tbl in blog blog_s blog_m; do
    6.53 +    query <<-EOF
    6.54 +	REPLACE INTO dst.$tbl
    6.55 +	  SELECT * FROM main.$tbl
    6.56 +	  WHERE id IN ($blogs);
    6.57 +	EOF
    6.58 +  done
    6.59 +  for tbl in article article_s article_m; do
    6.60 +    query <<-EOF
    6.61 +	REPLACE INTO dst.$tbl
    6.62 +	  SELECT * FROM main.$tbl
    6.63 +	  WHERE id IN (SELECT id FROM main.article WHERE blogid IN ($blogs))
    6.64 +	  ORDER BY rowid;
    6.65 +	EOF
    6.66 +  done
    6.67 +  ## Check the equality of two DBs
    6.68 +  echo "grid=$grid grp=$grp qgrp=$qgrp" | htmlescape
    6.69 +  # grp
    6.70 +  d1=$(query <<-EOF
    6.71 +	SELECT * FROM main.grp
    6.72 +	       	 NATURAL LEFT JOIN main.grp_s
    6.73 +		 NATURAL LEFT JOIN main.grp_m
    6.74 +	WHERE gname=$qgrp
    6.75 +	EXCEPT
    6.76 +	SELECT * FROM dst.grp
    6.77 +	       	 NATURAL LEFT JOIN dst.grp_s
    6.78 +		 NATURAL LEFT JOIN dst.grp_m
    6.79 +	WHERE gname=$qgrp;
    6.80 +	EOF
    6.81 +	)
    6.82 +  err DONE 
    6.83 +  err d1="$d1"
    6.84 +  # blog
    6.85 +  d2=$(query <<-EOF
    6.86 +	SELECT * FROM main.blog
    6.87 +	       	 NATURAL LEFT JOIN main.blog_s
    6.88 +		 NATURAL LEFT JOIN main.blog_m
    6.89 +	WHERE id IN (SELECT id FROM main.blog_s
    6.90 +	      	     WHERE key='owner' AND val=$qgrp)
    6.91 +	EXCEPT
    6.92 +	SELECT * FROM dst.blog
    6.93 +	       	 NATURAL LEFT JOIN dst.blog_s
    6.94 +		 NATURAL LEFT JOIN dst.blog_m
    6.95 +	WHERE id IN (SELECT id FROM dst.blog_s
    6.96 +	      	     WHERE key='owner' AND val=$qgrp);
    6.97 +	EOF
    6.98 +    )
    6.99 +  err d2="$d2"
   6.100 +  # article
   6.101 +  d3=$(query <<-EOF
   6.102 +	SELECT * FROM main.article
   6.103 +	       	 NATURAL LEFT JOIN main.article_s
   6.104 +	       	 NATURAL LEFT JOIN main.article_m
   6.105 +	WHERE blogid IN ($blogs)
   6.106 +	EXCEPT
   6.107 +	SELECT * FROM dst.article
   6.108 +	       	 NATURAL LEFT JOIN dst.article_s
   6.109 +	       	 NATURAL LEFT JOIN dst.article_m
   6.110 +	WHERE blogid IN ($blogs);
   6.111 +	EOF
   6.112 +       )
   6.113 +  err d3="$d3"
   6.114 +  if [ -z "$d1$d2$d3" ]; then
   6.115 +    echo "Copying done, rewriting article links..."
   6.116 +    echo "Old URL: $URL"
   6.117 +    echo "New URL: $dsturl"
   6.118 +    query <<-EOF
   6.119 +	UPDATE dst.article_s
   6.120 +	SET val=replace(val,
   6.121 +			'${URL}?grp+$grid',
   6.122 +			'${dsturl}?grp+$destrowid')
   6.123 +	WHERE key='text' AND val LIKE '%${URL}%';
   6.124 +	EOF
   6.125 +    # Create blog-rowid conversion table
   6.126 +    sedfile=$tmpd/arttrans.sed
   6.127 +    # sedfile=tmp/arttrans.sed
   6.128 +    query <<-EOF > $sedfile
   6.129 +	WITH arttrans AS (
   6.130 +	  SELECT s.rowid srcrid, d.rowid dstrid
   6.131 +	  FROM main.article s JOIN dst.article d ON s.id=d.id
   6.132 +	  WHERE s.id in (SELECT id
   6.133 +	  	     	 FROM article WHERE blogid IN ($blogs))
   6.134 +	) SELECT printf("/^>/s/\#%s($|[^0-9])/\#%s\1/g", srcrid, dstrid)
   6.135 +	  FROM arttrans;
   6.136 +	EOF
   6.137 +    query <<-EOF > $tmpd/repl.art.rowid
   6.138 +	SELECT rowid FROM dst.article_s
   6.139 +	WHERE key='text' AND val GLOB '>*#[1-9]*'
   6.140 +	  AND id IN (SELECT id FROM article WHERE blogid IN ($blogs));
   6.141 +	EOF
   6.142 +    sql=$tmpd/update.sql
   6.143 +    for arid in `cat $tmpd/repl.art.rowid`; do
   6.144 +      newval=`query "SELECT hex(val) FROM dst.article_s WHERE rowid=$arid;" \
   6.145 +              | unhexize | sed -Ef "$sedfile" | hexize`
   6.146 +      echo "UPDATE dst.article_s SET val=X'$newval' WHERE rowid=$arid;"
   6.147 +    done >$sql
   6.148 +    # Rewrite blog-links in the group
   6.149 +    #  Create sed script
   6.150 +    sedfile2=${sedfile}2
   6.151 +    query <<-EOF > $sedfile2
   6.152 +	WITH blogtrans AS (
   6.153 +	  SELECT s.rowid srcrid, d.rowid dstrid
   6.154 +	  FROM main.blog s JOIN dst.blog d ON s.id=d.id
   6.155 +	  WHERE s.id IN ($blogs)
   6.156 +	) SELECT printf('s/(\?replyblog)\+%s($|[^0-9])/\1+%s\2/g',
   6.157 +		         srcrid, dstrid)
   6.158 +	  FROM blogtrans;
   6.159 +	EOF
   6.160 +    bloglinks=$tmpd/bloglinks.rowid
   6.161 +    query <<-EOF > $bloglinks
   6.162 +	SELECT rowid FROM dst.article_s
   6.163 +	WHERE key='text' AND val LIKE '%?replyblog+%'
   6.164 +	  AND id IN (SELECT id FROM article WHERE blogid IN ($blogs));
   6.165 +	EOF
   6.166 +    for arid in `cat $bloglinks`; do
   6.167 +      newval=`query "SELECT hex(replace(val, '$URL', '$dsturl')) 
   6.168 +      		     FROM dst.article_s WHERE rowid=$arid;" \
   6.169 +              | unhexize | sed -Ef "$sedfile2" | hexize`
   6.170 +      echo "UPDATE dst.article_s SET val=X'$newval' WHERE rowid=$arid;" >>$sql
   6.171 +    done
   6.172 +    if [ -z "`query \".read $sql\"`" ]; then
   6.173 +      query <<-EOF
   6.174 +	DELETE FROM main.article WHERE blogid IN ($blogs);
   6.175 +	DELETE FROM main.blog WHERE id IN ($blogs);
   6.176 +	DELETE FROM main.grp WHERE rowid=$grid;
   6.177 +	EOF
   6.178 +      s=`query "SELECT * FROM main.grp WHERE rowid=$grid;"`
   6.179 +      if [ -z "$s" ]; then
   6.180 +	echo "Success!!"
   6.181 +	query "END;"
   6.182 +	clean-orphaned
   6.183 +	echo "Done."
   6.184 +      else
   6.185 +	echo Removal failed
   6.186 +	echo "現行グループ消去ができませんでした。"
   6.187 +	echo "書き込みの多いグループの場合は空いている時間帯に試して下さい。"
   6.188 +	query "ROLLBACK;"
   6.189 +	failure=-2
   6.190 +      fi
   6.191 +    else
   6.192 +      failure=-1
   6.193 +      echo "Replacing failed."
   6.194 +      query "ROLLBACK;"
   6.195 +    fi
   6.196 +  else
   6.197 +    failure=$((failure + 1))
   6.198 +    echo "Fail!"
   6.199 +    query "ROLLBACK;"
   6.200 +  fi
   6.201 +done
   6.202 +
   6.203 +err "Migration ended at `date` with failure=$failure"
   6.204 +return $failure
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/s4-newworld.sh	Tue Jun 16 13:11:18 2020 +0900
     7.3 @@ -0,0 +1,86 @@
     7.4 +#!/bin/ksh
     7.5 +#!/bin/sh
     7.6 +# Create New Wolrd in s4
     7.7 +# Arguments:
     7.8 +#   $1 = World Display Name in UTF-8
     7.9 +#   $2 = World ShortName in alpha-numeric
    7.10 +#   $3 = World Description in UTF-8
    7.11 +. `dirname $0`/s4-config.sh
    7.12 +if ! type htmlescape >/dev/null 2>&1; then
    7.13 +  . `dirname $0`/s4-funcs.sh ### > /dev/null 2>&1
    7.14 +  trap 'exit 1' INT QUIT
    7.15 +fi
    7.16 +dispname=$1
    7.17 +shortname=$2
    7.18 +desc=$3
    7.19 +
    7.20 +readvar() {	# $1=varname $2=PromptString
    7.21 +  echo -n "${2:-$1=:} "
    7.22 +  read $1
    7.23 +}
    7.24 +
    7.25 +echo db=$db URL=$URL isCGI=$isCGI
    7.26 +if ! $isCGI; then
    7.27 +  while true; do
    7.28 +    dispname=`echo $dispname | tr -d ': \t\n"' | fold -w 28 | head -1`
    7.29 +    test -n "$dispname" && break
    7.30 +    readvar dispname "分かりやすいWorld名14字以内"
    7.31 +  done
    7.32 +  while true; do
    7.33 +    shortname=`echo $shortname | tr -c -d '_0-9A-Za-z.-' | colrm 11`
    7.34 +    test -n "$shortname" && break
    7.35 +    readvar shortname "英数字のみ10字以内のWorldシンボル(URLの一部)"
    7.36 +  done
    7.37 +  while true; do
    7.38 +    desc=`echo $desc | tr -d ': \t\n"'`
    7.39 +    test -n "$desc" && break
    7.40 +    readvar desc "概要(どのような基準でこのWorldを使うかなど)"
    7.41 +  done
    7.42 +fi
    7.43 +echo "wl=$S4WORLDLIST"
    7.44 +echo "$dispname:$shortname:$desc"
    7.45 +newworld=$(
    7.46 +  { echo "$S4WORLDLIST" | tr ' ' '\n' \
    7.47 +      | awk -F: "\$2 != \"$shortname\"{print}"
    7.48 +    echo "$dispname:$shortname:$desc"
    7.49 +  } | tr '\n' ' ' | tr -d '"'
    7.50 +	)
    7.51 +if [ -z "$newworld" ]; then
    7.52 +  exit
    7.53 +fi
    7.54 +
    7.55 +# Create config
    7.56 +bgcolor=$(
    7.57 +  od -An -tu1 -N3 < /dev/urandom \
    7.58 +    | { read r g b
    7.59 +	r=$((192+r/4)); g=$((192+g/4)); b=$((192+b/4))
    7.60 +	printf "#%x%x%x" $r $g $b
    7.61 +  })
    7.62 +cat<<-EOF > s4-config-$shortname.sh
    7.63 +	S4MASTERURL=\$URL
    7.64 +	URL=`dirname ${URL}.`/s4-world-$shortname$cgiext
    7.65 +	S4COLOR="$bgcolor"			# Change this!
    7.66 +	DB=$dbdir/$shortname.sq3
    7.67 +	SESSDB=$dbdir/sess.sq3
    7.68 +	S4MASTERDB=$db
    7.69 +	S4CSS=$shortname.css
    7.70 +	TMPDIR=$tmpdir/$shortname
    7.71 +EOF
    7.72 +# Create CSS
    7.73 +cat<<-EOF > $shortname.css
    7.74 +	body {background: $bgcolor;}
    7.75 +	body.moderated {background: $bgcolor; border: 3px gold solid;}
    7.76 +EOF
    7.77 +mkdir -m 1775 $tmpdir/$shortname
    7.78 +# Update s4-config.sh
    7.79 +cat<<-EOF | ed s4-config.sh
    7.80 +	g/^S4WORLDLIST=/d
    7.81 +	\$a
    7.82 +	S4WORLDLIST="`echo $newworld`"
    7.83 +	.
    7.84 +	wq
    7.85 +EOF
    7.86 +DB=db/$shortname.sq3 `dirname $0`/s4-init.sh
    7.87 +(S4MASTERDB=$db; db=db/$shortname.sq3; . ./s4-world.sh)
    7.88 +(cd `dirname $0`; ln -s s4$cgiext s4-world-$shortname$cgiext)
    7.89 +echo $newworld added
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/s4-world.sh	Tue Jun 16 13:11:18 2020 +0900
     8.3 @@ -0,0 +1,138 @@
     8.4 +#!/bin/sh
     8.5 +
     8.6 +# Setup variables for running time
     8.7 +if $isCGI; then
     8.8 +  case "$S4WORLDLIST" in
     8.9 +    *:*:*)
    8.10 +      worldlistfile=cache/worldlist
    8.11 +      worldgrpfile=cache/worldgrps
    8.12 +      worldoptionfile=cache/worldoption
    8.13 +      worldnamefile=cache/worldname
    8.14 +      if [ ! -e $worldlistfile -o $worldlistfile -ot s4-config.sh \
    8.15 +	     -o ! -e $worldoptionfile -o $worldoptionfile -ot s4-config.sh \
    8.16 +	     -o s4-world.sh -nt $worldoptionfile ]
    8.17 +      then
    8.18 +	echo S4MASTERURL=$S4MASTERURL >> tmp/debug.out
    8.19 +	cat <<-EOF > $worldlistfile
    8.20 +	<div><table>
    8.21 +	 <tr><th>World List</th></tr>
    8.22 +	 <tr><td title="Base World$nl拠点となるWorldです">&rArr;
    8.23 +	  <a href="${S4MASTERURL:-$URL}">Base</a></td></tr>
    8.24 +	EOF
    8.25 +	true > $worldoptionfile
    8.26 +	for i in $S4WORLDLIST; do
    8.27 +	  echo $i | {
    8.28 +	    IFS=: read name short d
    8.29 +	    cgi="s4-world-$short$cgiext"
    8.30 +	    conf="s4-config-$short.sh"
    8.31 +	    cat<<-EOF >>$worldlistfile
    8.32 +	    <tr><td title="$d">&rArr; <a href="$cgi">$name</a></td></tr>
    8.33 +		EOF
    8.34 +	    cat<<-EOF >>$worldoptionfile
    8.35 +	    <option title="$d" value="$conf">$name</option>
    8.36 +		EOF
    8.37 +	    echo "$name" > $worldnamefile.$short
    8.38 +	  }
    8.39 +	done
    8.40 +	if [ -s "$worldoptionfile" ]; then
    8.41 +	  echo "<option value=\"s4-config.sh\">Base</option>" >> $worldoptionfile
    8.42 +	  echo "</table>$nl</div>" >> $worldlistfile
    8.43 +	  sed 's/href="\([^>]*\)"/href="\1?grps"/' $worldlistfile \
    8.44 +	      > $worldgrpfile
    8.45 +	else
    8.46 +	  true > $worldoptionfile; true > $worldlistfile	# Remove contents
    8.47 +	  rm -f ${worldnamefile}.*
    8.48 +	fi
    8.49 +      fi
    8.50 +      if [ -s "$worldlistfile" ]; then
    8.51 +	S4WORLDS="▼spaste(\`$worldlistfile')"
    8.52 +	S4WORLDNAME=${S4WORLD:+`cat $worldnamefile.$S4WORLD`}
    8.53 +	S4WORLDGRPS="▼spaste(\`$worldgrpfile')"
    8.54 +      fi
    8.55 +      ;;
    8.56 +  esac
    8.57 +fi
    8.58 +
    8.59 +err "db=$db mas=$S4MASTERDB sessdb=$sessdb"
    8.60 +# If in parent world, no need to do rest of jobs
    8.61 +if [ -z "$S4MASTERDB" -o ! -s "$S4MASTERDB" ]; then
    8.62 +  return
    8.63 +fi
    8.64 +# Confim child
    8.65 +if [ "$db" -ef "$S4MASTERDB" ]; then
    8.66 +  return		# Points to the same file
    8.67 +fi
    8.68 +
    8.69 +# Now Another world is ACTIVE
    8.70 +# sessdb=`dirname $S4MASTERDB`/sess.sq3
    8.71 +## skey="skey-`basename $mydir`"
    8.72 +syncflag=${db%.*}.synctime
    8.73 +runflag=${db%.*}.run
    8.74 +userupdateflag=`dirname $S4MASTERDB`/`basename $userupdateflag`
    8.75 +test ! -e "$userupdateflag"		&& return
    8.76 +test "$syncflag" -nt "$userupdateflag"	&& return
    8.77 +if [ -s "$runflag" ]; then
    8.78 +  limit=`cat $runflag|tr -c -d 0-9`
    8.79 +  if [ -n "$limit" -a "$limit" -gt `date +%s` ]; then
    8.80 +    err "World $S4WORLD account sync withholded by process $$"
    8.81 +    return		# Running sync by other process not leaching timeout
    8.82 +  fi
    8.83 +fi
    8.84 +echo $((`date +%s` + 10)) > $runflag	# Setting running flag by timeout 10s
    8.85 +
    8.86 +#  for sub.sq3
    8.87 +# 
    8.88 +#  main: user: 'taro', 'hanako', 'shige'
    8.89 +#  sub:  user: 'taro', 'hanako', 'shige'
    8.90 +#  sub2: user_s: ('taro', 't'), ('hanako', 'h'), ('shige', 's')
    8.91 +#  then update
    8.92 +# 
    8.93 +
    8.94 +## sqlite3 -cmd '.timer 1' -cmd '.echo 1' $db <<EOF
    8.95 +err "`gdate +%S.%3N` Starting account synchronization[$$]"
    8.96 +## cat > tmp/sql <<EOF
    8.97 +num=$(sqlite3 -bail -cmd 'PRAGMA FOREIGN_KEYS=on' $db <<EOF
    8.98 +.timeout 1000
    8.99 +ATTACH DATABASE "$S4MASTERDB" AS m;
   8.100 +CREATE TABLE IF NOT EXISTS user(name, primary key(name));
   8.101 +BEGIN;
   8.102 +DElETE FROM main.user WHERE rowid NOT IN (SELECT rowid FROM m.user);
   8.103 +INSERT INTO main.user(rowid, name)
   8.104 +       SELECT rowid, name FROM m.user
   8.105 +       WHERE m.user.rowid NOT IN (SELECT rowid FROM user);
   8.106 +UPDATE user SET name = (SELECT name FROM m.user WHERE main.user.rowid=m.user.rowid);
   8.107 +DELETE FROM main.user_s WHERE rowid NOT IN (SELECT rowid FROM m.user_s);
   8.108 +REPLACE INTO main.user_s(rowid, name, key, type, val, bin)
   8.109 +	SELECT rowid,* FROM m.user_s;
   8.110 +DELETE FROM main.user_m WHERE rowid NOT IN (SELECT rowid FROM m.user_m);
   8.111 +REPLACE INTO main.user_m(rowid, name, key, type, val, bin)
   8.112 +	SELECT rowid,* FROM m.user_m
   8.113 +	WHERE key NOT LIKE '%cache%';
   8.114 +END;
   8.115 +
   8.116 +/* Compare user tables */
   8.117 +WITH master AS (
   8.118 +  SELECT p.rowid,* FROM m.user p
   8.119 +  	 NATURAL LEFT JOIN m.user_s
   8.120 +	 NATURAL LEFT JOIN m.user_m
   8.121 +), thisworld AS (
   8.122 +  SELECT p.rowid,* FROM user p
   8.123 +  	 NATURAL LEFT JOIN user_s
   8.124 + 	 NATURAL LEFT JOIN user_m
   8.125 +), m_a AS (
   8.126 +  SELECT * FROM master EXCEPT SELECT * FROM thisworld
   8.127 +), a_m AS (
   8.128 +  SELECT * FROM thisworld EXCEPT SELECT * FROM master
   8.129 +) SELECT (SELECT count(*) FROM m_a) + (SELECT count(*) FROM a_m);
   8.130 +DETACH DATABASE m;
   8.131 +EOF
   8.132 +)
   8.133 +### num=$(sqlite3 -bail -cmd 'PRAGMA FOREIGN_KEYS=on' $db < tmp/sql )
   8.134 +if [ -n "$num" -a $num -eq 0 ]; then
   8.135 +  err "`gdate +%S.%3N` Account synchronization[$$] done in difference $num"
   8.136 +  echo "`date '+%F %T'`: Sync done by process $$" >> $syncflag
   8.137 +else
   8.138 +  err "Account synch[$$] failed or bailed with num=[$num]"
   8.139 +fi
   8.140 +test -e "$runflag" && rm -f "$runflag"
   8.141 +return $num
     9.1 --- a/scripts/s4-sns.case	Tue Jun 16 13:10:57 2020 +0900
     9.2 +++ b/scripts/s4-sns.case	Tue Jun 16 13:11:18 2020 +0900
     9.3 @@ -19,7 +19,7 @@
     9.4      showattc "$@"
     9.5      exit 0			# Do not output further chunks
     9.6      ;;
     9.7 -  invite|groupman|userconf|groupconf|mems|grps|grp|groupupdate|groupclone|grpaction|joingrpadmit|commission|editheading|editart|showattc|send2mem|mvart)
     9.8 +  invite|groupman|userconf|groupconf|mems|grps|grp|groupupdate|groupclone|grpaction|joingrpadmit|commission|editheading|editart|showattc|send2mem|mvart|migrategrp)
     9.9      contenttype; echo
    9.10      [ -n "$1" ] && shift
    9.11      $stage "$@"