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 -©2015-2019 by 2.7 +©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です">⇒ 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">⇒ <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 "$@"