s4

view s4-funcs.sh @ 1032:ae88ca565d08

Add wrapper for GNU m4 and md5sum
author HIROSE Yuuji <yuuji@gentei.org>
date Thu, 28 Dec 2023 11:27:06 +0900
parents 9d62cba7344f
children b8b1b8356b94
line source
1 #!/bin/sh
2 # Here's global variable table. Do not use this names.
3 # $HGid$
5 [ -f s4-config.sh ] && . ./s4-config.sh
7 test -n "$HTTP_HOST" && isCGI=true || isCGI=false
8 if $isCGI; then
9 case "$SCRIPT_NAME" in
10 *-world-*)
11 S4WORLD=${SCRIPT_NAME#*world-}
12 S4WORLD=${S4WORLD%.*}
13 echo S4WORLD=$S4WORLD >&2
14 worldconf=s4-config-${S4WORLD}.sh
15 ;;
16 *)
17 worldconf=s4-config.sh
18 ;;
19 esac
20 echo worldconf=$worldconf >&2
21 [ -n "$worldconf" -a -e "$worldconf" ] && . ./$worldconf
22 echo DB=$DB >&2
23 fi
24 myname=`basename ${SCRIPT_NAME:-$0}`
25 mydir=`dirname ${SCRIPT_FILENAME:-$0}`
26 cgiext=${CGIEXT:-.cgi}
27 myargs="$@"
28 PATH=/usr/local/sqlite3/bin:/usr/local/vim7/bin:/usr/iekei/ImageMagick/bin:/usr/local/ImageMagick/bin:$PATH
29 tmpdir=${TMPDIR:-tmp}
30 dbdir=${DBDIR:-db}
31 logdir=${LOGDIR:-tmp}
32 tmpfiles=""
33 querylog=${QUERYLOG:-$logdir/query.log}
34 searchlog=${SEARCHLOG:-$logdir/search.log}
35 defaultdb=$dbdir/cgi.sq3
36 db=${S4INITDB:-${DB:-$defaultdb}}
37 sessdb=${SESSDB:-$dbdir/sess.sq3}
38 userupdateflag=$dbdir/userupdate
39 sesstb=tmp.sess
40 workdb=$dbdir/tmpdata.sq3
41 listentlimit=${LISTENTLIMIT:-30}
42 listartlimit=${LISTARTLIMIT:-50}
43 admin=${ADMIN:-hostmaster@example.org}
44 noreply=${NOREPLY:-noreply@example.org}
45 noreply_from="${S4NAME:-s4} message notification <$noreply>"
46 invite_policy=${INVITE_POLICY:-"このコミュニティに関りのあるあなたの信頼できる人を招きます。"}
47 templ=${TEMPL:-templ}
48 layout=${LAYOUT:-$templ/default}
49 formdir=${FORMDIR:-$templ/form}
50 imgdir=${IMGDIR:-img}
51 url=${URL:-"${REQUEST_SCHEME:-http${HTTPS:+s}}://$HTTP_HOST$REQUEST_URI"}
52 urlbase=${url%%\?*}
53 msgdir=$templ/msg
54 timeout="+2 days"
55 memoplimitdays="7"
56 dumpcollen=22
57 #thumbxy=120x120
58 thumbxy=96x96
59 iconxy_S=80x80
60 iconxy_M=400x400
61 maximagexy=1600x1600
62 ### maximagexy=400x400
63 filesize_max=${FILESIZE_MAX:-$((5*1024*1024))}
64 filesize_max_MB="$((filesize_max/1024/1024))MB"
65 file_accept='accept="image/*,text/*,audio/*,application/vnd.oasis.*,application/pdf,application/x-*,application/sqlite*,application/csv"'
66 file_accept='accept=".jpg,.jpeg,.gif,.png,.tiff,.pdf,.odt,.ods,.odp,.odg,.mp3,.mp4,.m4a,.m4v,.mkv,.obj,.avi,.ogg,.mov,.webm,.gpx,.json,.geojson,.umap,.kml,.kmz,.html,.css,.js,.java,.el,.go,.cc,.rb,.rs,.py,.pl,lua,.awk,.sh,.c,.h,.log,.txt,.tex,.sty,.zip,.xcf,.bz2,.gz,.xz,.7z,.csv,.dat,.db,.sq3,.blend,.gltf,.glb"'
67 file_accept_egrep='^(text/|message/|image/|audio/|video/|application/(vnd.(oasis|sqlite)|pdf|epub|xml|.*zip|[xz]-|json|csv|javascript|octet-stream)|model/)'
68 file_accept_help="
69 添付可能ファイル: テキスト、画像、音声、動画、ODF、PDF、
70 圧縮ファイル、データベースファイル
71 (いずれも ${filesize_max_MB} 以内)
72 "
73 file_warn="$file_accept_help
74 [編集]リンクから修正してください。"
75 hidden_mode="('quiz', 'enquete')"
76 blogreadflagrowid=0
77 blogcutoffflagrowid=-1
78 nonewgroupworld=${NONEWGROUPWORLD:-*archive*}
79 whatsnewdays=${WHATS_NEW_DAYS:-14}
80 main_session=`date +%F-$$`
81 session=$main_session
82 mathjax=${MATHJAX:-'<script>MathJax = {
83 tex: {tags: "ams",
84 macros: {warn: ["\\fcolorbox{red}{white}{#1}", 1],
85 Warn: ["{\\Large \\warn{#1}}", 1]}}};</script>
86 <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script><script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>'}
88 tconfs=""
89 imgcached=cache/${S4WORLD:+$S4WORLD/}img.`date +%Y/%m`
90 conftbl=_tblconf
91 nl="
92 "
93 likeesc=`printf '\037'` # ESCAPE char of LIKE operator
94 iconcachekey="profimgcache_S"
95 asdelim=":::" # delimiter of dumptable td-class specifier
97 # Start debug logging
98 logtag="($$)${S4WORLD:+{$S4WORLD\}}"
99 exec 3>> $logdir/debug.out
100 err() {
101 # echo "[`date +%F-%T%z`]$logtag $@" 1>&3
102 # Avoid backslash escape sequences
103 cat<<EOF 1>&3
104 [`date +%F-%T%z`]$logtag $@
105 EOF
106 }
107 case "$HTTP_USER_AGENT" in
108 *i[Pp]hone*|*[Aa]ndroid*) touchpanel=1 ;;
109 *) touchpanel="" ;;
110 esac
112 # If S4MASTERDB is set, behave in another world
113 ### if [ -n "$S4MASTERDB" -a -s "$S4MASTERDB" ]; then
114 # If S4WORLDLIST is set, this s4 have world!
115 if [ -n "$S4WORLDLIST" ]; then
116 . ./s4-world.sh 2>> $logdir/debug.out
117 # Variables set in s4-world.sh
118 # $S4WORLDS, $S4WROLDNAME, $S4WORLDGRPS
119 # Files created in s4-world.sh
120 # $worldlistfile, $worldoptionfile, $worldnamefile, $worldgrpfile
121 fi
124 [ -f ./s4-cgi.sh ] && . ./s4-cgi.sh
126 : <<EOF
128 !! 検索等でblogテーブル参照時は sql4readableblogs() で定義される
129 !! readableblogs テーブルを使うこと
130 資料配布、グループ管理・ML、ファイル交換、クリッカー、アンケート
131 レポート提出管理
132 ひとつのarticleをheadingにして新規ツリーを作成、あるといいかも。
134 [2016]
135 7/12 根本への反省
136 * cgi自身の $1, $2 での切り替えでなく、CGI変数での受け渡しにすべき。
137 arg1/arg2/arg3 的に $1 に / 区切りでつけた方がよかったかな。
139 [以下2015]
140 8/4 ○グループに承認加入モードを追加
141 ○グループに参加していない場合は grpaction できない
142 Web
143 締切設定
145 8/2 ○s4.cgi生成系 → index.cgi生成
146 ○自分の提出物リスト
148 7/19 ○設置
149 ○一斉送信
150 ○getparfilename の tmpd の扱い
151 ○やっぱりs4にしようかな
152 7/18 ○書込著者からホームへのリンク
153 7/17 ○個人blogに「レポート提出用」がついたときの挙動
154 ○添付ファイル回収
155 ○imgcacheは別ディレクトリにしないと + .htaccess
156 7/15 ○レポート提出モードの表示を付ける
157 管理者権限での削除? → まだいいか
159 7/13 ○前回アクセス基準の新着数は欲しいなあ
160 ○レポート提出はどうしよう
161 → ○blogにモードを追加:
162 ○レポート提出モード
163 添付ファイル (誰が見たかログ)
164 クリッカーは別立てメニューにしないと(管理者がON/OFF)
165 ○添付ファイルの読み出し権(6/22から) ← モードで対処
168 7/9 ○管理者の追加
169 △グループメンバの操作 → 要不要を吟味
170 ○グループ情報編集の行先はそのグループがいい?
171 ○新規グループの作成はどこから入るか
172 △グループホームとユーザホームを揃える
174 7/8 ○グループ一覧をユーザ一覧と揃える。
176 7/6の次 ○グループのconf編集の入口
177 ○グループ検索
179 6/22の次 ○ホーム画面、○招待状、親記事追跡、○編集ボタン、削除ボタン、
182 6/7の次 ○blogを作ってみる || userconfig || _mのまとめ編集(削除)
183 6/7の次の次 ○userconfigの画面だけ作ってみる。
185 ○ 5/28の次 edittableに「削除」ボタンを足す
186 ○6/1 par2tableを triplex 対応に
187 select "yuuji@gentei.org",var,"text",NULL,val from par where var in (select col from _tblconf where tbl="/user" and keytype in ('p', 's'));
188 →とすると 一気に
190 ## form.def を考えなおそう:
191 ## userのように必須カラムを決まった位置に付ける?
192 ## 必須カラム、owner(foreign key passwd(name)), update datetime
193 ## ユーザ管理とグループ管理はデフォルトで持たせてしまえ
195 ## 縦持ちデータの入力/編集を供給する関数 single + multi
196 ## 持てるテーブル構造はシステム標準5種 + ユーザ定義2種類
197 ## 1. passwd
198 ## 2. grp
199 ## 3. grp_mem
200 ## 4. topic 記事のIDとなる
201 ## 5. topic_cont 特定IDの記事の内容物
202 ## 6. list 繰り返し登場あり
203 ## 7. hash 繰り返し登場なし
205 ## ● listの定義:
206 ## create table list(id unique, parentID, type, value);
207 ## ● hashの定義:
208 ## create table hash(parentID, type, value, primary key(parentID, type));
210 ## グループ属性: community, friend
211 ## ○ blob使えるのかな。streamで行けるのか? xxdで行けた。ありがたい。
212 ## form-defとtableは1対1対応でいいか
213 ## csv2sq3 で .csv.sq3 の Makefile
215 ## 書き込みオブジェクトとは何か?
216 ## topic : id, belongto, title, owner, mode
217 ## type := root | comment
218 ## topic_cont : id, topicid(F), ppath, contenttype, filename, content,
219 ## unique(id, filename)
220 ## type := body(single) | attachment(multi)
222 ## group := name(P), tag, gecos, owner(F), mode
223 ## tag := personal | friend | ... any string
224 ## group_member := gname(F), type, name(F), UNIQUE(gname, type, name)
225 ## type := "u" | "g"
226 ## できたー!
227 ## with recursive allmem as (select * from grp_mem where gname='bar' union all select grp_mem.* from grp_mem,allmem where allmem.name=grp_mem.gname) select * from allmem where type='u';
228
229 ↓以下に変更
230 with recursive allmem as
231 (select gname,val from grp_m where gname='foo'
232 union all select grp_m.gname,grp_m.val from
233 grp_m,allmem where allmem.val=grp_m.gname)
234 select val from allmem where val in (select name from user);
237 with recursive allmem as
238 (select gname,val from grp_m where gname='foo'
239 union all select grp_m.gname,grp_m.val from grp_m,allmem
240 where allmem.val=grp_m.gname)
241 select a.*, coalesce(b.val,a.val) from allmem a left join grp_mem_s b
242 on a.gname=b.gname and a.val=b.user and b.key='email'
243 where a.val in (select name from user);
246 ## triggerもできた。
247 ## 5/22から:グループ作成画面
248 ## 埋め込み画像 data:CONTENT-TYPE;base64,.....
250 ## 考え得るノードタイプ
251 ## 日報 - 個人所属かグループ所属か
252 ## 課題提出 - 個人所属かグループ所属か
253 ## グループ管理
254 ## 個人情報管理
255 ##
257 ## 例: group:sip - topic:1:sip:Aperture:yuuji:rw
258 ## - topic:2:sip:ISO:yuuji:rw
259 ## topic_cont 1:1:/:body:text...Aperture
260 ## 2:1:/1:body:text..Aperture
261 ## 3:1:/1:attachment:binary..Aperture
262 ## 4:1:/2:body:text..Aperture
263 ## 5:1:/2:attachment:binary..Aperture
264 ## 6:2:/:body:text..ISO
265 ## 7:2:/6:body:text..ISO
266 ## 8:2:/6:attachment:binary..
268 ## ログテーブル
269 ## time, who, action, tbl, id idなんか取れるかな
273 ■表設計
274 * 3つの表に分散管理
275 id格納表 + hash表 + list表
276 * *_s *_m
280 user, user_map, user_col
282 ■抽象エントリタイプ
283 * user
284 idとして機能 → table中の owner に自動挿入(?)
285 * group
286 権限判定に利用
287 * serial
288 自動idとして機能
289 * password
290 入力 type=passwordで入力
291 変更 oldpasswd, password×2 で確認後修正
292 * session
293 password認証後のセッションキーとして機能
294 * text
295 入力 type=text
296 * textarea
297 入力 textarea
298 * image|document
299 入力 type=fileで入力し、mime-typeを確認
300 * owner
301 入力時の $user で、外部キー制約が付く
302 * gowner
303 グループとしての所有者で、外部キー制約が付く
304 * timestamp
305 datetime()
306 * parent
307 木構造の場合の親の位置
308 * path
309 木構造の場合の自分の位置
311 格納タイプ
312 * list
313 表 parentID, key, val でUNIQUE(parentID, key, val)
314 * hash
315 表 parentID, key, val でUNIQUE(parentID, key)
317 オブジェクトタイプ
318 * entry
319 id, title, owner
320 * textpart
321 id, parentID, text
322 * binarypart
323 id, parentID, contenttype, filename, content
324 * content
325 hash(textpart), list(binarypart)
326 * topic
327 id, hash(content), list(reply)
328 * reply
329 id, parentID, content
330 * blog
331 list(entry)
332 blog = [topic, list(reply)]
335 blog = [ {"title" => "hoge", "owner" => "yuuji", "date" => "2015-04-27",
336 "text" => "hogehoge ..",
337 "reply" => [ {"serial" => 1,
338 "author" => "taro",
339 "date" => "2015-04-28",
340 "parent" => "/",
341 "path" => "/1",
342 "text" => "blah, blah, ....",
343 "image" => ["a.jpg", "b.jpg"] },
344 {"serial" => 2,
345 "author" => "hanako",
346 "date" => "2015-04-29",
347 "parent" => "/",
348 "path" => "/2",
349 "text" => "blah, blah, ....",
350 "image" => [] }]},
351 {"title" => "buha", ...} ]
354 user:=
355 ユーザ名(英数字):name:p:text:length="20" maxlength="40"
356 パスワード:pswd:s:password:length="20" maxlength="40"
357 説明(日本語OK):gecos:s:text:length="20" maxlength="40"
358 セッションキー:skey:s:session
359 メイルアドレス:email:m:text:length="20" maxlength="40"
360 住所:address:m:textarea:maxlength="400"
361 プロフィール画像:profimg:m:image:maxlength="400K"
362 履歴書:profpdf:m:document:maxlength="4M"
364 変換表
365 /user/email=m
367 blog:=
368 シリアル:id:p:serial
369 タイトル:title:s:text:
370 所有者:owner:s:owner:
371 時刻:ctime:s:stamp:
372 リード文:heading:s:textarea:
373 リプライ:reply:m:*article:
375 article:=
376 シリアル:id:p:serial
377 筆者:author:s:owner
378 時刻:ctime:s:stamp:
379 参照元:parent:s:parent:
380 パス:path:s:path:
381 本文:text:s:textarea:
382 画像:image:m:image:
384 履歴書:profpdf:m:document:maxlength="4M"
387 EOF
389 logstart() {
390 echo "`date '+%F %T'`:[${user:-NULL}]$logtag <<<" >> ${1:-$querylog}
391 }
392 logend() {
393 echo ">>>$logtag" >> ${1:-$querylog}
394 }
395 sqlog() {
396 logstart
397 if [ -z "$1" ]; then
398 cat >> $querylog
399 else
400 echo "$*" >> $querylog
401 fi
402 logend
403 }
404 sq() {
405 # ./args.rb -cmd ".timeout 3000" "$@"
406 logstart
407 if [ -z "$1" ]; then
408 tee -a $querylog|sqlite3 -cmd 'PRAGMA foreign_keys=ON' -cmd ".timeout 3000"
409 else
410 echo "$@" >> $querylog
411 sqlite3 -cmd 'PRAGMA foreign_keys=ON' -cmd ".timeout 3000" "$@"
412 ###sqlite3 -bail -cmd 'PRAGMA foreign_keys=ON' -cmd ".timeout 3000" "$@"
413 fi
414 logend
415 }
416 dbsetup() {
417 pipedir=$tmpdir/pipedir
418 [ -d $pipedir ] || mkdir -p -m 1777 $pipedir
419 [ -d $dbdir ] || mkdir -m 1775 $dbdir
420 suf=`date +%s`
421 sqi=$pipedir/sqi-$suf.$$
422 sqo=$pipedir/sqo-$suf.$$
423 mkfifo $sqi $sqo
424 #tail -f $sqi | sq $db & # "tail -f" is too heavy. DO NOT USE!!
425 sq $db < $sqi &
426 sq3pid="`jobs -p` $!"
427 if $isCGI; then
428 exec 2>> $logdir/error.out
429 chmod o-r $logdir/error.out
430 fi
431 exec 5> $sqi # Turning $sqi access through fd5 for continuous open state
432 chmod o-r $logdir/debug.out
433 rm $sqi
434 # Attach supplemental DB
435 cat >&5 <<-EOF
436 .output /dev/null
437 ATTACH DATABASE "$sessdb" AS tmp;
438 CREATE TABLE IF NOT EXISTS $sesstb(user, skey, expire, UNIQUE(user, skey));
439 DELETE FROM $sesstb WHERE expire < datetime('now', 'localtime');
440 DELETE FROM $sesstb WHERE expire is NULL or expire = '';
441 .output stdout
442 EOF
443 }
444 cleanup2() { # Dirty workaround for produced zombie processes
445 if [ -n "$HTTP_USER_AGENT" ]; then # When called from httpd
446 pkill -9 -u `id -u` -P 1
447 fi
448 chmod o-r $querylog $db $sessdb # make sure
449 }
450 cleanup() {
451 trap '' INT HUP EXIT TERM PIPE
452 echo .quit >&5
453 kill $sq3pid
454 kill $sq3pid
455 rm -f $sqo $sqi
456 rm -rf $tmpfiles
457 cleanup2
458 }
459 # We want to use piped function to put querylog, but we use
460 # simple redirection for the sake of speed.
461 query() {
462 # echo ".once $sqo" >&5
463 echo ".output $sqo" >&5
464 logstart
465 if [ -z "$1" ]; then
466 tee -a $querylog
467 else
468 printf '%s\n' "$@" >> $querylog
469 printf '%s\n' "$@"
470 fi >&5
471 echo ".output stdout" >&5
472 cat $sqo
473 rc=$?
474 logend
475 return $rc
476 }
477 _m4() {
478 #S4NAME=f,f,f
479 if m4 --version | grep -q GNU; then
480 if type om4; then # https://github.com/ibara/m4
481 m4() {
482 om4 "$@"
483 }
484 fi
485 fi >/dev/null 2>&1
486 m4 ${S4NAME:+"-D_S4NAME_=${S4NAME}"} ${S4CSS:+-D_S4CSS_="$S4CSS"} \
487 ${S4WORLD:+-D_S4WORLD_="$S4WORLD"} \
488 ${S4WORLDNAME:+-D_S4WORLDNAME_="$S4WORLDNAME"} \
489 ${S4WORLDGRPS:+-D_S4WORLDGRPS_="$S4WORLDGRPS"} \
490 ${S4WORLDS:+-D_S4WORLDS_="$S4WORLDS"} "$@"
491 }
492 if ! type gdate && date --version | grep -q GNU; then
493 gdate() date "$@"
494 fi >/dev/null 2>&1
495 if ! type md5 && type md5sum && md5sum --version | grep -q GNU; then
496 md5() {
497 if [ -z "$1" ]; then
498 md5sum | cut -d' ' -f 1
499 else
500 for f; do
501 printf "MD5 (%s) = %s\n" "$f" "$(md5 < $f)"
502 done
503 fi
504 }
505 fi >/dev/null 2>&1
506 ismember() {
507 # $1=user, $2=group
508 #err ismem: "select user from grp_mem where gname=$(sqlquote $2) and user='$1';"
509 test -n "`query \"select user from grp_mem where gname=$(sqlquote \"$2\") and user='$1';\"`"
510 }
511 isuser() { # Check if $1 is a valid user
512 test -n "`query \"select name from user where name='$1';\"`"
513 }
514 isgroup() { # Check if $1 is a valid group
515 err isgroup: "select gname from grp where gname=$(sqlquote $1);"
516 test -n "`query \"select gname from grp where gname=$(sqlquote \"$1\");\"`"
517 }
518 isgrpowner() (
519 # $1=user, $2=group
520 gn=`sqlquote "$2"`
521 sql="select user from grp_adm where gname=$gn and user='$1';"
522 err isgrpowner: $sql
523 test -n "`query \"$sql\"`"
524 )
525 isgrpownerbygid() (
526 # $1=user, $2=group-rowid
527 sql="select user from grp_adm where gname=(select gname from grp where rowid=$2) and user='$1';"
528 err isgrpownerbygid: $sql
529 test -n "`query $sql`"
530 )
531 getgroupadminmails() {
532 # $1=group
533 for i in $(getgroupadmins "$1"); do
534 email4group "$1" "$i" ;
535 done
536 }
537 getgroupadmins() { # $1=group
538 # This function is called in a backquote, so needn't to be subshellized
539 qgrp=`sqlquote "$1"`
540 query "select user from grp_adm where gname=$qgrp;"
541 }
542 getgroupattr() { # $1=group $2=attr
543 # This function is called in a backquote, so needn't to be subshellized
544 getvalbyid grp $2 \
545 $(query "select rowid from grp where gname=`sqlquote \"$1\"`;")
546 }
547 getgroupbyid() {
548 # $1=id|gname
549 sql="select coalesce((select gname from grp where gname=$(sqlquote \"$1\")),
550 (select gname from grp where rowid=$(sqlquote $1)));"
551 # err ggbyid: `echo $sql`
552 query $sql
553 }
554 isfilereadable() { # $1=user $2=tbl $3=rowid
555 # Return true if user($1) can read attachment files in tbl($2):rowid($3)
556 [ -z "$1" -o -z "$2" -o -z "$3" ] && return 1 # invalid argument
558 # Return true when anonymous mode
559 [ "$anonymousmode" ] && return 0
560 # case `getvalbyid blog mode $2` in
561 # normal|*open*|"") return 0 ;;
562 # *closed*)
563 # owner=`getvalbyid blog owner $2`
564 # if isgrp $owner; then
565 # isgrpowner $1 $owner && return 0 || return 1
566 # elif isuser $owner; then
567 # [ x"$1" = x"$owner" ] && return 0 || return 1
568 # fi
569 # esac
570 # ↑ 要はこういう処理を↓で一気にやっている
571 case "$2" in
572 grp_*)
573 sql="SELECT 'owner'
574 FROM grp_adm
575 WHERE gname=(SELECT gname FROM $2 WHERE rowid=$3)
576 AND
577 user = '$user';"
578 ;;
579 *)
580 sql="with getblog as (
581 select key,val from blog_s where id=(
582 select blogid from article where id in
583 (select id from $2 where rowid=$3))),
584 getowner as (select val from getblog where key='owner'),
585 getauthor as (select author from article where id=(select id from $2 where rowid=$3)),
586 isgrp as (SELECT val from getowner WHERE val IN (select gname from grp)),
587 isgrpadm as (select user from grp_adm where
588 gname=(select val from getowner) and
589 user='$1'),
590 getmode as (select val from getblog where key='mode')
591 select case
592 when (select author from article where
593 id=(select id from $2 where rowid=$3))='$1'
594 then 'author'
595 when (select val from getmode) in ('report-open', 'normal')
596 then 'open'
597 when (select val from getmode) in ('quiz', 'enquete')
598 then CASE
599 WHEN (SELECT val FROM isgrp) IS NULL
600 THEN
601 CASE WHEN (SELECT val from getowner)
602 IN ('$user', (SELECT author FROM getauthor))
603 THEN 'owner-or-user-article-is-readable'
604 ELSE ''
605 END
606 WHEN (select user from isgrpadm) IS NOT NULL
607 THEN 'i-am-admin'
608 ELSE (SELECT author from getauthor WHERE author IN (SELECT user FROM grp_adm WHERE gname=(SELECT val FROM getowner)))
609 END
610 when (select val from getmode) is null
611 then 'open'
612 when (select val from getowner) in (select gname from grp)
613 then (SELECT user FROM isgrpadm)
614 when (select author from article where
615 id=(select id from $2 where rowid=$3))='$1'
616 then 'user+author'
617 else '' end;"
618 ;;
619 esac
620 ## err isfilereadable: sql="`echo $sql`"
621 # caseのネストで内側のcaseがスカラーtrueを返しても外側はtrue扱いにならない
622 # result=`query "$sql"`
623 # err FileAccessibility=$result
624 [ -n "`query $sql`" ] || return 2
625 }
626 linkhome() {
627 # $1=UserOrGroupRowid
628 echo -n "<a href=\"$myname?"
629 if isuser $1; then
630 err "select 'home+'||rowid from user where name='$1';"
631 query "select 'home+'||rowid from user where name='$1';"
632 name=`gecos $1|htmlescape`
633 else
634 _grid=`numericalize "$1"`
635 echo -n "grp+$1"
636 name=`query "SELECT gname FROM grp WHERE rowid=$_grid;"|htmlescape`
637 fi
638 echo "\">$name</a>"
639 }
640 hreflink() {
641 # s4 specific notation:
642 # ^href=URL
643 # ^iframe=URL
644 # ^video=URL
645 # [[#NUM]] - Jump to article ID NUM
646 # [[#Keyword] - Jump to keywrod search for "Keyword"
647 # OSM umap Wikistyle Notation:
648 # [[URL]] - Simle Link
649 # [[URL|Word]] - Link with anchor word
650 # {{URL}} - <img src="URL">
651 # {{URL|width}} - <img src="URL" width="width">
652 # {{{URL}} } - <iframe src="URL"></iframe>
653 # {{{URL|height}} - <iframe src="URL" height="height"></iframe>
654 # Other Style
655 # ---- - <hr> (In the beginning of line)
656 # *Word* - <em>Word</em>
657 # _Word_ - <em>Word</em>
658 # **Word** - <strong>Word</strong>
659 # __Word__ - <strong>Word</strong>
660 # SPC+SPC+$ - <br>
661 cb='<input type="checkbox" class="s4-checkbox" disabled'
662 checkboxON="${cb} checked>"
663 checkboxOFF="${cb}>"
664 _hrefptn="[-A-Za-z0-9,.:;/~_%#&+?=@!]*"
665 _hrefptn="[A-Za-z0-9/~%+?=@!.][^][()<> ]*" # URL should start with ASCII
666 sed -e "s|\[\[\#\([0-9][0-9]*\)\]\]|<a href=\"?aid\1\">#\1</a>|g" \
667 -e "s|\[\[#\([^]&]*\)\]\]|<a href=\"?kwd=\1\&stage=searchart\">\#\1</a>|g" \
668 -e "s|\[\[\($_hrefptn\)\|\([^]]*\)\]\]|<a href=\"\1\">\2</a>|g" \
669 -e "s|\[\[\($_hrefptn\)\]\]|<a href=\"\1\">\1</a>|" \
670 -e "s|{{{\($_hrefptn\)\|\(.*\)}}}|<iframe src=\"\1\" height=\"\2\"></iframe>|g" \
671 -e "s|{{{\($_hrefptn\)}}}|<iframe src=\"\1\"></iframe>|g" \
672 -e "s|{{\($_hrefptn\)\|\(.*\)}}|<img src=\"\1\" width=\"\2\">|g" \
673 -e "s|{{\($_hrefptn\)}}|<img src=\"\1\">|g"\
674 -e "s|^href=\($_hrefptn\)|<a &>\1</a>|" \
675 -e "s|^iframe=\($_hrefptn\)|<iframe src=\"\1\"></iframe>|" \
676 -e "s|^video=\($_hrefptn\)|<video controls><source height=\"320\" src=\"\1\"></video>|" \
677 -e "s,^#### *\(.*\),<h4>\1</h4>," \
678 -e "s,^### *\(.*\),<h3>\1</h3>," \
679 -e "s,^## *\(.*\),<h2>\1</h2>," \
680 -e 's,^----*$,<hr>,' \
681 -e 's, \*\*\([^* |][^*|]*[^ |]\)\*\* ,<strong>\1</strong>,g' \
682 -e 's, __\([^_ |][^_]*[^ ]\)__ ,<strong>\1</strong>,g' \
683 -e 's, \*\([^* |][^*|]*[^ |]\)\* ,<em>\1</em>,g' \
684 -e 's, _\([^_ ][^_]*[^ ]\)_ ,<em>\1</em>,g' \
685 -e 's,~~\([^~][^~]*\)~~,<s>\1</s>,g' \
686 -e 's,\([^\\]\);;;,\1<br>,g;s,\\;;;,;;;,g' \
687 -e 's, $,<br>,' \
688 -e "s,- \[ *\]\([^|-]*\),${checkboxOFF}<label>\\1</label>,g" \
689 -e "s,- \[[^ ]\]\([^|-]*\),${checkboxON}<label>\\1</label>,g" \
691 }
692 minitbl() {
693 sed -n '
694 /^|.*|/ {; # If the line begin with "|" and has 2 or more "|"
695 s,|$,,; # Remove trailing "|" first
696 s,|\* *\([^|]*\) *,<th>\1</th>,g; # "|*..." to "<th>...</th>"
697 s,| *\([^|]*\) *,<td>\1</td>,g; # "|..." to "<td>...</td>"
698 s,^,<tr>,; s,$,</tr>,; # Enclose with "<tr>" and "</tr>"
699 H; # Concat this line to HoldSpace
700 s/.*//; # Delete PatternSpace for finalization
701 $ b cont
702 d; # If in final line, output the rest, else jump to next turn
703 }
704 :cont
705 x; # For non-"|" lines, check HoldSpace
706 /^./ {; # If HoldSpace has "|" table elements
707 s|^.|<table class="mini">|; # Enclose whole elements like this:
708 # . of ^. is workaround for FreeBSD sed
709 # s|$|</table>|; # <table class="mini">..\n..</table>
710 p; # Print whole "table" element
711 s/.*//; # Erase all when done.
712 x; s|^|</table>|; x; # Preppend /table to the next line
713 }
714 x; # Back to the newest line
715 p; # Print rest' | miniul
716 }
717 miniul() {
718 sed -e '
719 /^\* / {; # 行頭 "* "
720 x; s,^,<ul>,; x; # 1周目: ホールドスペース先頭に <ul> を
721 :top
722 s/\n//;
723 s/^ *//; # 2周目以降: 行頭空白削除
724 s,\* ,,; # まず行頭の "* " を消しておく
725 H; # 置き換え結果をホールドスペースに追加
726 s/.*//; # パターンスペースは消しておく
727 # ↓最終行なら残ったホールドスペース処理のため :cont へ
728 $ b cont
729 N; # 次の行を読む
730 s/\n//; # 空白始まりは継続行
731 /^ /b top
732 x; s/\n/<li>/; s,$,</li>,; # 継続行でなければ <li></li> で囲む
733 p; s/.*//;
734 x; # 次も "* " ならループを抜けない
735 /^\* /b top
736 s,^,</ul>,; # 次が一般行なら箇条書終わり
737 }
739 :cont
740 x; # 行頭| 以外の行:
741 /./ {; # ホールドスペースに文字列があれば
742 s/^\n/<li>/; s,$,</li></ul>,; # 箇条書を書き切って終わり
743 H; x
744 }
745 x' | miniol
746 }
747 miniol() {
748 sed -e '
749 /^[1-9]\. / {; # 行頭 "N. "
750 h;x; # 1周目: ホールドスペース先頭に <ol> を
751 s,^\([1-9][0-9]*\)\. .*,<ol start="\1">,; # 初期番号付きで追加
752 x;
753 :top
754 s/\n//;
755 s/^ *//; # 2周目以降: 行頭空白削除
756 H; # 置き換え結果をホールドスペースに追加
757 x;
758 s,[1-9][0-9]*\. ,,; # まず行頭の "N. " を消しておく
759 x;
760 s/.*//; # パターンスペースは消しておく
761 # ↓最終行なら残ったホールドスペース処理のため :cont へ
762 $ b cont
763 N; # 次の行を読む
764 s/\n//; # 空白始まりは継続行
765 /^ /b top
766 x; s/\n/<li>/; s,$,</li>,; # 継続行でなければ <li></li> で囲む
767 p; s/.*//;
768 x; # 次も "* " ならループを抜けない
769 /^[1-9][0-9]*\. /b top
770 s,^,</ol>,; # 次が一般行なら箇条書終わり
771 }
773 :cont
774 x; # 行頭| 以外の行:
775 /./ {; # ホールドスペースに文字列があれば
776 s/^\n/<li>/; s,$,</li></ol>,; # 箇条書を書き切って終わり
777 H; x
778 }
779 x'
780 }
781 acclog() (
782 # $1=table, $2=rowid
783 n=${2%%[!-0-9]*} # Remove non-digit chars from $2(should be rowid)
784 if [ -n "$n" ]; then
785 now=`date +"%F %T"`
786 #query "replace into acclog values('$user', '$1', '$n', '$now');"
787 #query "replace into acclog values('$user', '$1', $n, '$now');"
788 query "replace into tblaccesses values('$user', '$1', $n, '$now');"
789 fi
790 )
791 gecos() (
792 u=`sqlquote "${1:-$user}"`
793 query "select gecos from gecoses where name=$u;"
794 )
795 setpar() {
796 # 2020/5/14 Add dirty code to cache essential params
797 if [ x"$session" = x"$main_session" ]; then
798 case "$1" in
799 user) _user="$v" ;;
800 skey) _skey="$v" ;;
801 esac
802 fi
803 query "replace into par values('$session', '$1', '$2', \"$3\");"
804 }
805 unsetpar() {
806 for i; do
807 if [ x"$session" = x"$main_session" ]; then
808 case "$i" in
809 user|skey) unset _$i ;;
810 esac
811 fi
812 query "DELETE FROM par WHERE var='$i' AND sessid='$session';"
813 done
814 }
815 replpar() {
816 query "update par set val=\"$3\" where sessid='$session' and var='$1' and type='$2';"
817 }
818 getpar() {
819 # err GETPAR=$1, _user=$_user
820 val=""
821 if [ x"$session" = x"$main_session" ]; then
822 case "$1" in # Dirty cache mechanism for high-load average
823 user) val=$_user ;;
824 skey) val=$_skey ;;
825 esac
826 fi
827 val=${val:-`query "select val from par where var='$1' and sessid='$session' $2;"`}
828 ## err getpar/val1: "val=[$val]"
829 if [ -z "$val" ]; then
830 val=`query "select val from cookie where var='$1' and sessid='$session' $2;"`
831 fi
832 ## err getpar/val2: "val=[$val]"
833 case "$var" in
834 owner)
835 if [ x"$user" = x"$val" ]; then
836 echo $user; return
837 elif ismember $user $val; then
838 printf '%s' "$val"; return
839 fi ;;
840 esac
841 ## err getpar/ret: "val=[$val]"
842 printf '%s' "$val"
843 }
844 setskey() {
845 # For quick response...(?)
846 query "REPLACE INTO $sesstb VALUES('$1', '$2', datetime('now', 'localtime', '$timeout'));"
847 }
848 chkskey() {
849 # $1=sesskey, $user=LoginUserName
850 test -z "$1" && return 1
851 repl=`query "SELECT rowid,user FROM $sesstb WHERE user='$user' AND skey = '$1';"` || return 2
852 rowid=${repl%%\|*}; repuser=${repl#*\|}
853 if [ -n "$rowid" -a x"$user" = x"$repuser" ]; then
854 query "UPDATE $sesstb SET expire=datetime('now', 'localtime', '$timeout') WHERE rowid=$rowid;" # Errors can be ignored
855 return 0
856 fi
857 return 1
858 }
859 resetskey() {
860 if [ -n "$_user" ]; then
861 query "DELETE FROM $sesstb WHERE user='$_user';"
862 fi
863 }
864 getpartype() {
865 query "select type from par where var='$1' and sessid='$session' $2;"
866 }
867 getparcount() {
868 query "select count(*) from par where var='$1' and sessid='$session' $2;"
869 }
870 getparfilename() {
871 # null if type of $1 is not file
872 (f=`query "select val from par where var='$1' and sessid='$session' and type='file' $2;"`
873 [ -n "$f" ] && echo $f)
874 }
875 sqlquote() {
876 (v="$1"
877 case "$v" in
878 "") return ;; # null
879 "X'"*) # quoted hex string
880 echo $1 ;;
881 *\"*) # string including dbl-quote"
882 v=`printf '%s' "$v"|sed -e 's/\"/\"\"/g'`
883 printf '%s' "\"$v\""
884 return ;;
885 *.*.*|*-*-*|*[Ee]*[Ee]*|[Ee]*|*[\ -,:-df-~]*) # string
886 printf '%s' "\"$v\""
887 return ;;
888 *)
889 if expr "$v" : '[-0-9.Ee][-0-9.Ee]*$' >/dev/null 2>&1; then
890 printf '%s' $v # MAYBE numeric, maybe...
891 else
892 printf '%s' "\"$v\""
893 fi ;;
894 esac)
895 }
896 sqlquotestr() (
897 case "$1" in
898 *\'*) v=`printf '%s' "$1"| sed "s/'/''/g"`
899 printf '%s' "'$v'" ;;
900 *) printf '%s' "'$1'" ;;
901 esac
902 )
903 mktempd() {
904 TMPDIR=$tmpd mktemp -d -t $session.XXXX
905 }
906 getcachedir() { # $1=maintable
907 if [ -n "$imgcached" ]; then
908 echo $imgcached/$(echo ${1:-hoge}|md5)/$thumbxy
909 else
910 echo $tmpd/$thumbxy
911 fi
912 }
913 getval() {
914 # $1=table $2=col $3(optional)=condition
915 case `gettbl_coltype "/$1/$2"` in
916 user|author) # author added 2015-06-18 for article(author)
917 echo "$user" ;;
918 stamp|datetime)
919 date "+%F %T" ;;
920 serial)
921 (s=`getpar $2`
922 if [ -n "$s" ]; then echo $s; else echo "`date +%s`x$$"; fi) ;;
923 *)
924 getpar "$2" "$3";;
925 esac
926 }
928 getvalquote() {
929 # $1=table $2=col $3(optional)=condition
930 (v=`getval "$@"`
931 case "$v" in
932 "") echo NULL ;;
933 *) sqlquotestr "$v" ;;
934 esac)
935 }
936 getparquote() {
937 sqlquote "`getpar $1`"
938 }
939 getvalbyid() {
940 # $1=tbl $2=col $3=rowid $4=tmpdirForBinary
941 # If two or more values found, save them to $tmpd/${column}.$N and
942 # store the number of files into $tmpd/${column}.count and
943 # their each rowid stored into $tmpd/${column}.$N.rowid.
944 ## err gtb-$1=`gettblcols $1`, tbl=$1, col=$2, '$3'=$3
946 (for c in `gettblcols $1`; do
947 if [ x"$2" = x"$c" ]; then
948 ###sq $db "select $2 from $1 where rowid=$3"
949 query "select $2 from $1 where rowid=$3;"
950 return
951 fi
952 done
953 rowid=$3
954 pk=`gettblpkey $1`
955 key=`query "select $pk from $1 where rowid=$3;"`
956 getkey="(select $pk from $1 where rowid=$3)"
957 td=${4:-$tmpd}
958 [ -d $td ] || mkdir -p $td
959 ### err "select $pk from $1 where rowid=$3" - key=$key '$4(tmp)'=$4
960 for kt in s m; do
961 t=${1}_$kt
962 for c in `gettbl_${kt}_cols $1`; do
963 vcount=1 # count(val)
964 if [ x"$2" = x"$c" ]; then
965 #### cond="$t where $pk=\"$key\" and key=\"$c\"" #2015-07-22
966 cond="$t where $pk=$getkey and key=\"$c\""
967 val=`query "select val from $cond limit 1;"`
968 type=`query "select type from $cond limit 1;"`
969 if [ $kt = m ]; then
970 ###vcount=`sq $db "select count(val) from $cond"`
971 # Reset val to store filenames if type is string
972 val=`query "select val from $cond and type like 'file:%' order by rowid;"`
973 ## err gvb1-sql: "select count(val) from $cond;"
974 vcount=`query "select count(val) from $cond;"`
975 echo $vcount > $td/$c.count
976 i=0
977 ## err gvbid: i=$i vcount=$vcount
978 while [ $i -lt $vcount ]; do
979 slice="order by rowid limit 1 offset $i"
980 i=$((i+1))
981 fn=$c.$i
982 ## err td=$td, fn=$fn, type=$type, val="[$val]"
983 case $type in
984 file:*)
985 #file=$td/$val
986 r_f=`query "select rowid||'//'||val from $cond $slice;"`
987 f_rid=${r_f%%//*}
988 file=$td/${r_f##*//}
989 # FOR SPEED: Skip file generation if imgcache exists
990 [ -s "$file" -a -s "$td/$fn.rowid" -a -s "$file.rowid" ] \
991 && [ x"$f_rid" = x"`cat $td/$fn.rowid`" ] \
992 && continue
993 # err gvbid-get="select quote(bin) from $cond $slice;"
994 ## err output: "fn=[$fn] file=[$file]"
995 sq $db<<EOF | unhexize > "$file"
996 .output '$td/$fn.rowid'
997 select rowid from $cond $slice;
998 .output '$td/$fn'
999 select val from $cond $slice;
1000 .output '$td/${fn}.content-type'
1001 select substr(type, 6) from $cond $slice;
1002 .output stdout
1003 select quote(bin) from $cond $slice;
1004 EOF
1005 ## err gvbid-get2: "`ls -lF $file`"
1006 ## err i=$i - file=$file rowid=`cat $td/$fn.rowid`
1007 cp "$td/$fn.rowid" "$file.rowid" 2>&3 # for convenience
1008 cp "$file" "$file.orig" 2>&3
1009 ls -lh "$file" |
1010 awk '{print $5"B"}'|sed 's/BB/B/' > "$file.size"
1011 case "$type" in
1012 *:[Ii]mage*) mogrify -geometry $thumbxy "$file" ;;
1013 ### ここのアイコンを増やしたい
1014 *|*:[Aa]pplication*)
1015 convert -geometry $thumbxy $imgdir/file-icon.png \
1016 png:- > "$file"
1017 ;;
1018 esac
1019 ;;
1020 *)
1021 sq $db<<EOF
1022 .output $td/$fn.rowid
1023 select rowid from $cond $slice;
1024 .output $td/$fn
1025 select val from $cond $slice;
1026 EOF
1027 val=$val${val:+$nl}"`echo $fn`" # should be delimited by newline
1028 ;;
1029 esac
1030 done
1031 else
1032 rm -f $td/$c.count
1033 case $type in
1034 file:*)
1035 printf '%s\n' "$val" \
1036 | while read fn; do
1037 file=$td/$fn
1038 if [ ! -s "$file" ]; then
1039 ## sq $db "select quote(bin) from $cond and val=\"$fn\"" \
1040 query "select quote(bin) from $cond and val=\"$fn\";" \
1041 | unhexize > "$file"
1042 ##@@## -- echo ${type#file:} > "$file.content-type"
1043 case $type in
1044 *:[Ii]mage*) mogrify -geometry $thumbxy "$file" ;;
1045 *:[Aa]pplication*)
1046 convert -geometry $thumbxy $imgdir/file-icon.png \
1047 png:- > $file ;;
1048 esac
1049 fi
1050 done
1051 ;;
1052 esac
1053 fi
1054 printf '%s' "$val"
1055 return
1056 fi
1057 done
1058 done)
1060 getvalbypkey() (
1061 # $1=tbl $2=col $3=pkey $4=tmpdirForBinary
1062 pk=`gettblpkey $1`
1063 rowid=`query "select rowid from $1 where $pk='$3';"`
1064 getvalbyid "$1" "$2" $rowid $4
1066 getvalbycond() {
1067 # $1=tbl $2=col $3=SQL-Condition
1068 ###rowid=`sq $db "select rowid from $1 where $3"`
1069 rowid=`query "select rowid from $1 where $3;"`
1070 if [ -n "$rowid" ]; then
1071 getvalbyid "$1" "$2" $rowid "$4"
1072 fi
1074 getpwfield() {
1075 # getpwfield user column
1076 # val=`sqlite3 $db "select $2 from passwd where name='$1' $3"`
1077 val=`getvalbycond user $2 "name='$1'"`
1078 if [ -n "$val" ]; then
1079 echo "$val"
1080 return 0
1081 else
1082 return 1
1083 fi
1085 numericalize() {
1086 echo "${1%%[!0-9]*}"
1088 encode() {
1089 if [ -z "$sha1" ]; then
1090 if type sha1 >/dev/null 2>&1; then
1091 sha1=sha1
1092 elif type sha1sum >/dev/null 2>&1; then
1093 sha1=sha1sum
1094 elif type gsha1sum >/dev/null 2>&1; then
1095 sha1=gsha1sum
1096 fi
1097 fi
1098 $sha1 "$@" | cut -d' ' -f1
1100 if type gs >/dev/null 2>&1; then
1101 gs_pdfwrite() {
1102 gs -sDEVICE=pdfwrite -dPDFSETTINGS=/default \
1103 -dNOPAUSE -dQUIET -dBATCH -o "$2" "$1" >/dev/null 2>&1
1105 fi
1106 enjpeg() {
1107 if [ -z "$cjpeg" ]; then
1108 if type cjpeg >/dev/null 2>&1; then
1109 cjpeg="cjpeg"
1110 else
1111 cjpeg="convert - jpeg:-"
1112 fi
1113 fi
1114 $cjpeg "$@"
1116 mycrypt() (
1117 key=$1 salt=$2
1118 # err \$2=$2
1119 case $2 in
1120 '$'*'$'*) salt=${salt#\$4\$}
1121 salt=${salt%\$*} ;;
1122 esac
1123 echo -n '$4$'"$salt"'$'
1124 echo "$salt$key" | encode || exit 1 # Abort if fail to call encode
1126 hexize() {
1127 if [ -z "$hexize" ]; then
1128 if type xxd >/dev/null 2>&1; then
1129 hexize="xxd -p"
1130 else
1131 hexize_hd() {
1132 hexdump -ve '1/1 "%.2x"'
1134 hexize="hexize_hd"
1135 fi
1136 fi
1137 cat "$@" | $hexize | tr -d '\n'
1139 unhexize() {
1140 if [ -z "$unhex" ]; then
1141 if type xxd >/dev/null 2>&1; then
1142 unhex="xxd -p -r"
1143 elif type perl >/dev/null 2>&1; then
1144 cat >$tmpd/unhex.pl<<EOF
1145 s/([0-9a-f]{2})/print chr hex \$1/gie
1146 EOF
1147 # Perl refuses -e in setuid circumstances, which can be absurdly
1148 # avoided by creating scripts in a file where its parent directory is
1149 # world writable...:)
1150 unhex="perl -n $tmpd/unhex.pl"
1151 fi
1152 fi
1153 cat "$@" | $unhex
1154 # cat $1 | tee /tmp/uh.in| $unhex | tee /tmp/uh.out
1156 percenthex() {
1157 hexize "$@" | sed 's/\(..\)/%\1/g'
1159 htmlescape() {
1160 sed -e 's/\&/\&amp;/g' -e 's/"/\&quot;/g' -e "s/'/\&apos;/g" \
1161 -e "s/</\&lt;/g; s/>/\&gt;/g" -e 's/`/\&#096;/g' -e 's/(/\&#040;/g' \
1162 -e 's/`/\&#96/'
1164 enascii() {
1165 if [ -z "$enascii" ]; then
1166 if type kakasi >/dev/null 2>&1; then
1167 enascii="kakasi -Ha -Ka -Ja -Ea -ka"
1168 else
1169 enascii_now=`date +%FT%T`
1170 enascii_sed() {
1171 nkf -Z0Z1Z2 \
1172 | sed -e "s/^/$enascii_now/" -e "s|[^-0-9.A-z/,()_=]|x|g"
1174 enascii="enascii_sed"
1175 fi
1176 fi
1177 cat "$@" | $enascii
1179 size_h() {
1180 i="$1" oi=$1
1181 set -- B B KB MB GB TB
1182 while [ $((i)) -gt 9 -a -n "$1" ]; do # -gt 9 means $oi > 1024
1183 oi=$i
1184 i=$((i/1024))
1185 shift
1186 done
1187 echo ${oi}$1
1189 gettblconf() {
1190 if [ -z "$tconfs" ]; then
1191 ## tconfs=`sq $db \
1192 tconfs=`query \
1193 "select tbl||'/'||col||'='||keytype||'/'||objtype from $conftbl;"`
1194 fi
1195 # /tb1/col1=p/text /tb1/col2=s/text /tb1/col3=m/image /tb2/col1=p/text ...
1197 gettblkeys() {
1198 # $1=tbl
1199 gettblconf
1200 echo "$tconfs" | fgrep "/$1/" | \
1201 (type="" keys="" fks="" cols="" scols="" mcols="" hcols=""
1202 while IFS='=' read tc conf; do # tc=/tb1/col1 conf=s/text
1203 col=${tc##*/} type=${conf%%/*}
1204 case $type in
1205 *p*)
1206 cols=$cols"${cols:+:}$col"
1207 keys=$keys"${keys:+:}$col" ;;
1208 *f*) cols=$cols"${cols:+:}$col"
1209 fks=$fks"${fks:+:}$col" ;;
1210 *m*) mcols=$mcols"${mcols:+:}$col" ;;
1211 *s*) scols=$scols"${scols:+:}$col" ;;
1212 esac
1213 case $type in
1214 *h*) hcols=$hcols"${hcols:+:}$col" ;;
1215 esac
1216 done
1217 echo "_keys=$keys _fks=$fks _cols=$cols _scols=$scols _mcols=$mcols _hcols=$hcols")
1219 gettblpkey() {
1220 # $1=tbl
1221 gettblkeys $1 | cut -d ' ' -f 1 | sed -e 's/.*=//' -e 's/:/ /g'
1223 gettblfkey() {
1224 (x=`gettblkeys $1`
1225 x=${x#*_fks=} # cut before "_fks=" including
1226 echo ${x%% *} | tr ':' ' ')
1228 gettblcols() {
1229 (x=`gettblkeys $1`
1230 x=${x#*_cols=} # cut before "_cols=" including
1231 echo ${x%% *} | tr ':' ' ')
1233 gettbl_s_cols() {
1234 (x=`gettblkeys $1`
1235 x=${x#*_scols=} # cut before "_scols=" including
1236 echo ${x%% *} | tr ':' ' ')
1238 gettbl_m_cols() {
1239 (x=`gettblkeys $1`
1240 x=${x#*_mcols=} # cut before "_mcols=" including
1241 echo ${x%% *} | tr ':' ' ')
1243 gettbl_h_cols() {
1244 (x=`gettblkeys $1`
1245 x=${x#*_hcols=} # cut before "_hcols=" including
1246 echo ${x%% *} | tr ':' ' ')
1248 gettbl_coltype() (
1249 gettblconf
1250 x=`echo "$tconfs"|fgrep $1=`
1251 x=${x#*=} # cut before =
1252 echo ${x#*/} # cut before p/ including
1254 is_hidden() {
1255 # $1=Tbl $2=col
1256 gettblconf
1257 x=`echo "$tconfs"|fgrep /$1/$2=`
1258 x=${x#*=} # cut before =
1259 x=${x%%/*} # cut after /
1260 case $x in
1261 *h*) return 0 ;;
1262 *) return 1 ;;
1263 esac
1266 dbsetbyid() {
1267 # $1=tbl $2=id $3=col $4=val/filename - &optional - $5=content-type
1268 (t0=$1 t=$1 p=$2 c=$3
1269 tsc=$t/$c val=$4
1270 quotedp=$(sqlquotestr "$p")
1271 unset primary update
1272 gettblconf
1273 #err tsc=$tsc, tconfs="$tconfs"
1274 conf=`echo "$tconfs"|fgrep "$tsc"=`
1275 #err conf=$conf
1276 case ${conf#*=} in
1277 p*) primary=1 ;;
1278 f*) update=1 ;;
1279 u*) ;;
1280 m*) t=${t}_m;;
1281 s*) t=${t}_s;;
1282 esac
1283 #err t=$t
1284 type=string fn=""
1285 case $conf in
1286 */password)
1287 type=encoded ### val=`echo $val|encode`
1288 ;;
1289 */image*|*/document*)
1290 type=`file --mime-type - < "$val" | cut -d' ' -f2`
1291 bin="X'`hexize "$val"`'"
1292 ;;
1293 esac
1294 pkey=`echo "$tconfs"|grep "${t0}/.*=p"|sed 1q`
1295 pkey=${pkey#/*/} # cut $tbl/
1296 pkey=${pkey%=p/*} # cut =p/... -> primary key
1297 if [ "$primary" ]; then
1298 nulls=`echo "$tconfs"|grep "$t/.*=[fu]/"|sed 's/^.*/, NULL/'|tr -d '\n'`
1299 ###sq $db "replace into $t values(\"$val\"$nulls)"
1300 query "replace into $t values(\"$val\"$nulls);"
1301 elif [ "$update" ]; then
1302 query "update $1 set $c=\"$val\" where $pkey=$quotedp;"
1303 else
1304 query "replace into $t values($quotedp, \"$c\", \"$type\", \"$val\", \"$bin\");"
1305 fi
1308 expire() (
1309 at="${1:-$timeout}"
1310 FMT="${2:-%F %T}"
1311 TZ=GMT gdate -d "$at" +"$FMT"
1313 addsession() {
1314 # expireをセット
1315 # loginの先にどの画面に行くかの状態遷移表書式を決める
1316 expire=`expire ${2:-"+1min"}`
1317 query "replace into session values('$1', '$expire');"
1318 # Remove old session parameters
1319 now=`expire now`
1320 query "delete from session where expire < '$now';"
1322 gencookie() (
1323 path=${URL#*:/}
1324 path=${URL%/*}
1325 expire="`expire '' '%a, %d-%b-%Y %H:%M:%S GMT'`"
1326 for kv; do
1327 # echo "Set-Cookie: $kv; expires=$expire; Path=$path"
1328 echo "Set-Cookie: $kv; expires=$expire;"
1329 done
1331 contenttype() {
1332 echo "Content-type: ${1:-text/html; charset=utf-8}"
1333 contenttype() {} # Only need to work once
1335 putheader() {
1338 putfooter() {
1339 _m4 -D_TITLE_="${TITLE:-$myname}" $layout/footer.m4.html
1341 getcookie() {
1342 for kv in `echo $HTTP_COOKIE|sed 's/[;, ]/ /g'`; do
1343 k="${kv%%=*}"
1344 v="`echo ${kv#*=}|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
1345 ## err "GetCookie: $k=[$v]"
1346 case "$k" in
1347 user) _user="$v" ;;
1348 skey) _skey="$v" ;;
1349 esac
1350 query "replace into cookie values('$session', '$k', 'string', \"$v\");"
1351 done
1353 genrandom() {
1354 # $1=columns (default: 10)
1355 dd if=/dev/urandom count=1 2>/dev/null|nkf -MB \
1356 | tr -d '+=\n'|fold -w${1:-10}|sed -n 10p
1358 genserial() {
1359 echo $((($(date +%s)-1433084400)/10))c$$
1361 smail() {
1362 # smail rcpts subj (file)
1363 # $SMAIL_TO <- Recipient value of To: header
1364 # $MAIL_FROM <- From: header value
1365 from=`echo "${MAIL_FROM:-$admin}"|nkf -jM|tr : /|tr -d '\n'`
1366 rcpt=`echo $1|tr ' ' '\n'|sort -u|tr '\n' ' '` # uniq and strip newlines
1367 ## Gmail rejects below(Duplicated headers)
1368 ##rcptheader=`echo $1|tr ' ' '\n'|sort -u|sed '2,$s/^/To: /g'`
1369 rcptheader=`echo $1|tr ' ' '\n'|sort -u|tr '\n' ','|sed 's/,$//'`
1370 subj=`echo $2|nkf -jM|tr -d '\n'`
1371 sender=${SENDER:-$admin}
1372 # Do not call m4 with directly passing text
1373 _r=$tmpd/rcpt
1374 echo -n "${SMAIL_TO:-$rcptheader}" > $_r
1375 replyto=${REPLYTO:+"Reply-to: $REPLYTO$LF"}
1376 (_m4 -D_RCPT_="spaste(\`$_r')" -D_REPLYTO_="$replyto" -D_SUBJ_="\`$subj'" -D_FROM_="$from" $msgdir/mail-header.m4
1377 cat $3 | nkf -jd ) | sendmail -f $sender $rcpt
1379 smail_queue_flush() {
1380 # $1=timelimit
1381 timelimit=`query "SELECT datetime('now', 'localtime', '-6 hours');"`
1382 rowids=$(sq $workdb <<-EOF
1383 SELECT rowid FROM smailq
1384 GROUP BY rcpts, subj
1385 HAVING min(time) < '$timelimit';
1386 EOF
1388 for rid in $rowids; do
1389 sq $workdb \
1390 "SELECT hex(rcpts),hex(subj) FROM smailq WHERE rowid in ($rid)" \
1391 | if IFS='|' read hexr hexs; then
1392 # err "hexrcpt=[$hexr] hexsubj=[$hexs]"
1393 rcpt=`echo "$hexr"|unhexize`
1394 subj=`echo "$hexs"|unhexize`
1396 # err ROWID==$rowid "sql=<<SELECT hex(rcpts),hex(subj) FROM smailq WHERE rowid=$rowid;>>"
1397 # err "rcpt=[$rcpt] subj=[$subj]"
1398 if sq $workdb <<-EOF | smail "$rcpt" "$subj"
1399 .separator "\n" "------------------\n\n"
1400 SELECT time, text FROM smailq
1401 WHERE rcpts=(SELECT rcpts FROM smailq WHERE rowid=$rid)
1402 AND subj=(SELECT subj FROM smailq WHERE rowid=$rid)
1403 ORDER by time;
1404 EOF
1405 then
1406 cat <<-EOF | sq $workdb
1407 DELETE FROM smailq
1408 WHERE rcpts=(SELECT rcpts FROM smailq WHERE rowid=$rid)
1409 AND subj=(SELECT subj FROM smailq WHERE rowid=$rid);
1410 EOF
1411 fi
1412 fi
1413 done
1415 smail_queue() {
1416 # $1=Rcpts, $@=subj
1417 now=`query "SELECT datetime('now', 'localtime');"`
1418 if [ $? -eq 0 ]; then
1419 rcpts="X'"`echo "$1"|hexize`"'"
1420 subj="X'"`echo "$2"|hexize`"'"
1421 text="X'"`cat | hexize`"'"
1422 err "smail_queue: rcpts=[$1] s=[$2] t:hex=[$text]"
1423 mintime=$(cat <<-EOF | sq $workdb
1424 CREATE TABLE IF NOT EXISTS smailq(rcpts, subj, text, time);
1425 INSERT INTO smailq VALUES($rcpts, $subj, $text, '$now');
1426 SELECT min(time) FROM smailq WHERE rcpts=$rcpts AND subj=$subj;
1427 EOF
1429 ## XXX: Adhoc load mitigation
1430 case "$now" in
1431 *[01])
1432 err flush_queue=$mintime
1433 smail_queue_flush ;;
1434 esac
1435 fi
1437 setviastring() {
1438 table=par
1439 oifs="$IFS"
1440 IFS="&"
1441 for us in $1; do
1442 k=${us%%=*}
1443 v="`echo ${us#*=}|tr '%+' '= '|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
1444 setpar "$k" "string" "$v"
1445 done
1446 IFS="$oifs"
1448 checkdomain() (
1449 # Check the validity of domain by referring DNS
1450 item=$1
1451 err checkdomain $1
1452 host ${item#*@} 1>&3 2>&3
1453 host ${item#*@} >/dev/null 2>&1
1455 pwcheck() {
1456 # $1=passwd
1457 dbpswd=`getpwfield $user pswd`
1458 encpswd=`mycrypt "$1" "$dbpswd"`
1459 ## err user=$user, pswd=$1, db=$dbpswd, enc=$encpswd
1460 [ x"$dbpswd" = x"$encpswd" ]
1462 mypwhash() {
1463 mycrypt "`cat`" `genrandom 5`
1465 flag_profupdate() {
1466 # XXX: Sorry to use undeclared column in user.def
1467 # This is useful to mitigate account sync load
1468 query <<-EOF
1469 REPLACE INTO user_s(name, key, type, val)
1470 VALUES('$user', 'profupdate', 'string', datetime('now', 'localtime'));
1471 EOF
1472 touch $userupdateflag
1474 wasureta() {
1475 user=$1
1476 if ! checkdomain $user; then
1477 contenttype; echo
1478 _m4 -D_TITLE_='Invalid email' $layout/title-only.html
1479 echo "ユーザ名($user)には正しいメイルアドレスが必要です。" | html p
1480 putfooter
1481 exit 0
1482 fi
1483 newpswd=`genrandom` # newsalt=`genrandom 5`
1484 #encpswd=`mycrypt "$newpswd" "$newsalt"`
1485 encpswd=`echo $newpswd|mypwhash`
1486 dbsetbyid user $user pswd "$encpswd" && touch $userupdateflag
1487 # Avoid $user substitution with m4, because $url comes from user input.
1488 _m4 -D_PSWD_="$newpswd" -D_URL_="$url" -D_ADMIN_="$admin" \
1489 $msgdir/mail-newaccount.m4 \
1490 | sed "s/_USER_/$user/g" \
1491 | smail "`collectemail $user`" "New Account"
1493 checkauth() {
1494 user=`getpar user`
1495 skc=`getpar skey` # from cookie
1496 [ -z "$user" ] && return 2
1497 ##skey="`getpwfield $user skey`"
1498 if [ -n "$skc" ]; then
1499 if chkskey "$skc"; then
1500 gencookie "user=$user" "skey=$skc" # 2021-12-24: Update expire
1501 return 0
1502 fi
1503 fi
1504 pswd=`getpar pswd`
1505 quser=`sqlquotestr "$user"`
1506 dbuser=`query "SELECT name FROM user WHERE name=$quser;"`
1507 if [ $? != 0 ]; then # Maybe DB locked
1508 return 4 # 4=server too heavy
1509 elif [ -z "$dbuser" ]; then
1510 err "Login USER failed: [$user]"
1511 return 2 # 2=login fail
1512 elif [ x"$pswd" = x"wasureta" ]; then
1513 if [ -n "$S4MASTERDB" ]; then
1514 contenttype; echo
1515 echo "Reset password is valid in BASE world." | html p
1516 echo "パスワードリセットはベースワールドでおこなってください。" | html p
1517 cat <<-EOF | html p
1518 <a href="$S4MASTERURL">BASE World</a>
1519 EOF
1520 exit 0 # XXXXX more smart...
1521 fi
1522 wasureta "$user"
1523 err wasureta issued for "$user"
1524 return 1 # wasureta error
1525 fi
1526 # dbpswd="`sq $db \"select pswd from passwd where name='$user'\"`"
1527 # putheader; echo; echo user=$user, db=$dbpswd, enc=$encpswd
1528 if pwcheck "$pswd"; then
1529 newsession=`genrandom 34`
1530 if setskey "$user" "$newsession" &&
1531 dbsetbyid user "$user" login "`date '+%F %T'`"; then
1532 gencookie "user=$user" "skey=$newsession"
1533 return 0
1534 else
1535 return 4 # Heavy load??
1536 fi
1537 fi
1538 err "Login failed: [$user]"
1539 return 2 # Password mismatch
1541 showlogin() {
1542 args=`echo $myargs|tr ' ' '+'`
1543 test -z "$args" && resetskey
1544 s4name=${S4NAME:-s4}
1545 ( sed '/^<body/q' $layout/html.m4.html
1546 cat $layout/login.m4.html
1547 echo '</body></html>'
1548 ) | _m4 \
1549 -D_BODYCLASS_="login" \
1550 -D_TITLE_="$s4name" \
1551 -D_SYSNAME_="Welcome to $s4name" \
1552 -D_MYNAME_="$myname${args+?}$args" ${S4CSS:+-D_S4CSS_="$S4CSS"}
1553 exit 0
1555 dologin() {
1556 test -n "$S4WORLD" && syncaccount
1557 checkauth
1558 st=$?
1559 if [ $st != 0 ]; then
1560 contenttype; echo
1561 _m4 -D_USER_="$user" -D_URL_="$url" -D_ADMIN_="$admin" \
1562 -D _LOADAVG_="`uptime|awk '{print $(NF-2)}'|tr -d ,`" \
1563 $msgdir/login-fail-$st.m4.html
1564 showlogin # and EXIT
1565 fi
1566 err "Auth OK: [$user]"
1569 # Do instant jobs here
1570 dbsetup
1571 trap cleanup INT HUP EXIT TERM PIPE
1572 # trap cleanup INT HUP
1575 cgiinit() {
1576 tmpd=`tmpd=$tmpdir mktempd`
1577 tmpf=$tmpd/stream.$$
1578 tmpfiles=$tmpfiles" $tmpd"
1579 addsession $session
1580 getcookie
1581 case "$REQUEST_METHOD" in
1582 get|GET) s="$QUERY_STRING" ;;
1583 post|POST) ## dd count=$CONTENT_LENGTH bs=1 of=$tmpf 2>/dev/null #slow
1584 ## dd bs=$CONTENT_LENGTH count=1 of=$tmpf # NOT working
1585 # cat > $tmpf # too much?
1586 head -c $CONTENT_LENGTH > $tmpf # safe?
1587 err "CONTENT_LENGTH=$CONTENT_LENGTH$nl`ls -lF $tmpf`"
1588 s="`cat $tmpf`"
1589 ;;
1590 esac
1591 case "$CONTENT_TYPE" in
1592 *boundary*)
1593 bndry=${CONTENT_TYPE#*boundary=}
1594 #for us in `LC_CTYPE=C ./mpsplit.rb "$bndry" $tmpd < $tmpf`
1595 for us in `LC_CTYPE=C ./mpsplit.pl "$bndry" $tmpd < $tmpf`
1596 do
1597 k=${us%%\=*}
1598 #echo u=$us
1599 #v="`echo ${us#*=}|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
1600 v="`echo ${us#*=}|unhexize|sed -e 's/\"/\"\"/g'`"
1601 case "$k" in
1602 *:filename)
1603 mimetype=`file --mime-type - < "$tmpd/$v"|cut -d' ' -f2`
1604 type='file'; k=${k%:filename}
1605 # DO NOT ALLOW Space and '|' in file names
1606 newv=`echo "$v"|sed 's/[ \|]/X/g'`
1607 if [ x"$v" != x"$newv" ]; then
1609 fi
1610 err "k=[$k] v=[$v]" # debug@2020-05-31
1611 err "`ls -lF $tmpd/$v` MimeType=$mimetype"
1612 case "$mimetype" in
1613 [Ii]mage/x-xcf)
1614 bzip2 "$tmpd/$v"
1615 v=${v}.bz2
1616 ;;
1617 [Ii]mage/x-*|*/vnd.*) ;;
1618 [Ii]mage/[Hh][Ee][Ii][Ff])
1619 if type heif-convert >/dev/null 2>&1; then
1620 vjpg="${v%.heic}.jpg"
1621 err "Conv $v to $vjpg in $tmpd"
1622 convert -quality 75 -resize $maximagexy'>' \
1623 "$tmpd/$v" "$tmpd/$vjpg" >/dev/null 2>&1
1624 v=$vjpg
1625 else
1626 mimetype="Not supported"
1627 fi
1628 ;;
1629 [Ii]mage/*)
1630 mogrify -quality 75 -resize $maximagexy'>' "$tmpd/$v"
1631 err "Mogrified: `ls -lF $tmpd/$v`" # 2020-05-31
1632 ;;
1633 [Aa]pplication/[Pp][Dd][Ff])
1634 if [ x"`getpar comppdf`" = x"yes" ]; then
1635 if type gs_pdfwrite >/dev/null 2>&1; then
1636 nv=${v%.pdf}-compressed.pdf
1637 err Calling gs from $v to $nv
1638 if gs_pdfwrite "$tmpd/$v" "$tmpd/$nv"; then
1639 err "PDF compressed: `ls -lF $tmpd/*.pdf`"
1640 v=$nv
1641 fi
1642 fi
1643 fi
1644 esac
1645 if ! echo "$mimetype" | egrep "$file_accept_egrep" >/dev/null 2>&1
1646 then
1647 replpar text string " *添付できない形式です($v)* $file_warn"
1648 continue
1649 elif [ `wc -c < "$tmpd/$v"` -gt "$filesize_max" ]; then
1650 replpar text string \
1651 " *添付ファイル($v)は${filesize_max}バイト以下にしてください* $file_warn"
1652 continue
1653 fi
1654 ;;
1655 image|document|binary)
1656 # When no file name supplied to input[type="file"]
1657 continue ;;
1658 *)
1659 type='string'
1660 ;;
1661 esac
1662 #sq $db "replace into par values('$session', '$k', '$type', \"$v\")"
1663 setpar "$k" "$type" "$v"
1664 done
1665 ;;
1666 *)
1667 setviastring "$s"
1668 ;;
1669 esac
1671 email4group() {
1672 # Get for-$1=group email address(es) for $2...=users
1673 qgrp=`sqlquote "$1"`; shift
1674 users=`for i; do sqlquote "$i"; echo; done`
1675 users=`echo $users|tr ' ' ','`
1676 sql="WITH
1677 grpemails AS (
1678 SELECT gname, user, val email
1679 FROM grp_mem NATURAL JOIN grp_mem_s
1680 WHERE key='email' AND gname=$qgrp),
1681 useremails AS (
1682 SELECT user.name, val email
1683 FROM user
1684 LEFT JOIN user_m
1685 ON user.name=user_m.name AND user_m.key='email')
1686 SELECT DISTINCT coalesce(g.email, u.email, u.name)
1687 FROM useremails u LEFT JOIN grpemails g
1688 ON u.name=g.user
1689 WHERE u.name in ($users);"
1690 query "$sql"
1692 email4groupbyuid() {
1693 # Get for-$1=group email address(es) for $2...=user-ids
1694 g=$1; shift
1695 uids=`echo "$@"`
1696 uids=`echo $uids|tr ' ' ','`
1697 sql="SELECT DISTINCT name FROM user WHERE rowid IN ($uids);"
1698 email4group "$g" `query "$sql"`
1700 myemail4group() {
1701 # Get my email address for $1-specified group
1702 email4group "$1" "$user" | sed -e 1q -e 's/[ ,].*//'
1704 collectmembersbyid() {
1705 # Collect user names of group specified by grid
1706 rid=${1%%[!0-9]*} # Cleaning
1707 query "SELECT user FROM grp_mem \
1708 WHERE gname=(SELECT gname FROM grp WHERE rowid=$rid);"
1710 collectmembersbyid() {
1711 # Collect user names of group name
1712 qgrp=`sqlquote "$1"`
1713 query "SELECT user FROM grp_mem WHERE gname=$qgrp;"
1715 collectgecosesbyid() {
1716 # Collect user gecoses of group
1717 rid=${1%%[!0-9]*} # Cleaning
1718 query<<-EOF
1719 SELECT gecos
1720 FROM gecoses
1721 WHERE name IN (SELECT user FROM grp_mem
1722 WHERE gname=(SELECT gname FROM grp WHERE rowid=$rid));
1723 EOF
1725 collectemail() (
1726 # Collect email addresses for group $1
1727 # If $TEAM is set, filter by team name
1728 # If $EXCEPT is set as username(s) delimited by comma,
1729 # remove $EXCEPT from list: ....NOT IN ($EXCEPT)
1730 for e; do
1731 if isuser "$e"; then
1732 em=`query "select val from user_m where name='$e' and key='email';"`
1733 [ -n "$em" ] && echo "$em" || echo "$e"
1734 else
1735 qgrp=`sqlquote "$e"`
1736 if [ -z "$TEAM" ]; then
1737 gmem="grp_mem"
1738 else
1739 tm=`sqlquote "$TEAM"`
1740 gmem="(SELECT gname, user FROM grp_mem_m WHERE gname='$e' AND key='team' AND val=$tm)"
1741 fi
1742 ex=${EXCEPT:+"AND g.user NOT IN ($EXCEPT)"}
1743 sql="select coalesce(s.val,um.val,g.user) from
1744 $gmem g left join grp_mem_s s
1745 on g.gname=s.gname and g.user=s.user and s.key='email'
1746 left join user_m um on g.user=um.name and um.key='email'
1747 where g.gname=$qgrp $ex;"
1748 ## err CollectEmail: `echo "$sql"`
1749 query "$sql"
1750 fi
1751 done
1753 sendinvitation() (
1754 # $1=email
1755 iss="invite-`date +%s`-$user"
1756 addsession $iss +${memoplimitdays}days # 1 week due date
1757 query "DELETE FROM par WHERE var='invite' AND val='$1';"
1758 query "REPLACE INTO par VALUES('$iss', 'invite', 'string', '$1');"
1759 gecos=`gecos`
1760 name=$user${gecos:+"($gecos)"}
1761 regist="$urlbase?reg+$iss"
1762 _m4 -D_URL_="$urlbase" \
1763 -D_USER_="$name" \
1764 -D_EMAIL_="$1" \
1765 -D_REGIST_="$regist" \
1766 -D_ADMIN_="$admin" \
1767 $msgdir/mail-invite.m4 \
1768 | smail $1 "SNSへの御招待"
1769 return 0
1771 emaildomaincheck() {
1772 case "$1" in
1773 *@*@*) echo "無効なアドレスです"; return 1 ;;
1774 *@*)
1775 local=${1%@*} domain=${1#*@}
1776 if ! host $domain >/dev/null 2>&1; then
1777 echo "ドメイン($domain)が見付かりません。"
1778 return 2
1779 fi
1780 return 0
1781 ;;
1782 *) echo "正しいメイルアドレスをいれてください"; return 3 ;;
1783 esac
1785 invite() {
1786 email=`getpar email | tr '[A-Z]' '[a-z]'`
1787 if [ -n "$INVITE_ONLYFROM" ]; then
1788 if ! echo "$user" | grep -E -e "$INVITE_ONLYFROM" >/dev/null 2>&1; then
1789 echo "招待可能ユーザに登録されていません" | html p
1790 return
1791 fi
1792 fi
1793 case "$email" in
1794 *@*@*|*\ *) repo="無効なアドレスです" ;;
1795 *@*)
1796 local=${email%@*} domain=${email#*@}
1797 if ! repo=`emaildomaincheck $email`; then
1798 repo="招待アドレスのエラー: $repo"
1799 elif [ -n "`query \"select * from user where name='$email';\"`" ]; then
1800 repo="$email さんは既に加入しています。"
1801 elif sendinvitation $email; then
1802 repo="アドレス($email)宛に案内を送信しました。"
1803 else # Cannot be reached here
1804 repo="自動登録できない状況です。管理者に依頼してください。"
1805 fi ;;
1806 "") repo="招待したい人のメイルアドレスを入力してください。" ;;
1807 *) repo="無効なアドレスです" ;;
1808 esac
1809 addr=`query "select val from par where sessid like 'invite-%-$user';"`
1810 if [ -n "$addr" ]; then
1811 susp="<h2>招待済みで加入待ちのアドレス</h2><pre>$addr</pre>"
1812 fi
1813 if [ -f $invite_policy ]; then
1814 pol="spaste(\`$invite_policy')"
1815 else
1816 pol="$invite_policy"
1817 fi
1818 _m4 -D_TITLE_="招待" -D_REPORT_="\`$repo'" -D_ACTION_="?invite" \
1819 -D_BODYCLASS_="default" -D_SUSPENDED_="$susp" \
1820 -D_INVITE_POLICY_="$pol" \
1821 $layout/html.m4.html $layout/invite.m4.html
1823 regist() {
1824 # $1=session-id-for-invitation
1825 _m4 -D_TITLE_="Invitation" $layout/html.m4.html
1826 if [ -z "$1" ]; then
1827 echo "bye bye" | html p
1828 reutrn
1829 fi
1830 email=`session=$1 getpar invite | tr '[A-Z]' '[a-z]'` # Ensure lower case
1831 if [ -z "$email" ];then
1832 cat<<EOF
1833 <p>無効な招待状チケットです。</p>
1834 <p>招待状の有効期限(1週間)が切れているか、チケット番号が異なっています。
1835 加入している人に、再度招待してもらいましょう。</p>
1836 EOF
1837 return
1838 fi
1839 echo "$email さんようこそ" | html h2
1840 query "replace into user values('$email');"
1841 # Fake login password to wasureta
1842 query "replace into par values('$session', 'pswd', 'string', 'wasureta'),
1843 ('$session', 'user', 'string', '$email');"
1844 wasureta $email
1845 echo "このアドレスに初期パスワードを送信しました。" |html p
1846 echo "新着メイルを確認してログインしてください。" |html p
1847 addsession $1 # for removal after 1 minute
1848 _m4 -D_SYSNAME_="Initial Login" -D_MYNAME_="$myname?userconf" \
1849 $layout/login.m4.html
1850 return
1852 group_safename() {
1853 # Convert $1 to safe group name
1854 echo "$1" | tr -d '"'"'",
1856 groupupdate() {
1857 gname=`getpar gname`
1858 qgname=`sqlquote "$gname"`
1859 if [ -n "$gname" ]; then
1860 # See ALSO same job in showgroup()
1861 newgname=`group_safename "$gname"`
1862 err newgname=$newgname
1863 if [ x"$newgname" != x"$gname" ]; then
1864 err NewGNAME: gname=$newgname
1865 gname=$newgname
1866 echo "使用禁止文字を除去し $gname としました。" | html p
1867 replpar gname string "$gname"
1868 fi
1869 # Name confliction check
1870 parow=`getpar rowid`
1871 ## err parow=$parow
1872 qgname=`sqlquote "$gname"` # Set again in case gname modified
1873 query "BEGIN EXCLUSIVE;"
1874 ## err "select count(gname) from grp where rowid != ${parow:-0} and gname = $qgname;"
1875 count=$(query "select count(gname) from grp where rowid != ${parow:-0} and gname = $qgname;")
1876 if [ $count -gt 0 ]; then
1877 echo "そのグループ名は既にあります。" | html p
1878 query "END;"
1879 return
1880 fi
1881 par2table $formdir/grp.def
1882 query "END TRANSACTION;"
1883 # Remove orphan
1884 : <<EOF
1885 select a.id,b.val from (select * from blog where id in
1886 (select id from blog_s where key='owner'
1887 and val not in (select name from user union select gname from grp)))
1888 a left join blog_s b on a.id=b.id and b.key='owner';
1889 EOF
1890 rm=`getpar rm` cfm=`getpar confirm`
1891 ## err groupupdate:::: after par2tbl rmcfm=$rm$cfm
1892 if [ x"$rm$cfm" = x"yesyes" ]; then
1893 if [ -z "`query \"select gname from grp where gname=$qgname;\"`" ]; then
1894 sql="delete from blog where id in
1895 (select id from blog_s where key='owner' and val=$qgname);"
1896 err rm-grp cleaning sql=`echo $sql`
1897 query "$sql";
1898 unsetpar tag kwd
1899 grps # When removing a group, switch to grp-list
1900 return # and return
1901 fi
1902 fi
1903 [ -z "$parow" ] && joingrp "$gname" "$user" yes "" as-admin
1904 fi
1905 sql="select rowid from grp where gname=$qgname;"
1906 grid=$(query $sql)
1907 ## err grpupdate:new-grid=$grid, sql=$sql
1908 grp $grid
1910 groupclone() {
1911 # $1=grp-rowid of clone-base group
1912 rid=${1%%[!0-9]*} # Cleaning
1913 case "$1" in
1914 */noteam)
1915 noteam="AND key != 'team'" ;;
1916 esac
1917 qgrp=`query "SELECT quote(gname) FROM grp WHERE rowid=$rid;"`
1918 if [ -z "$qgrp" ]; then
1919 echo "無効なグループIDです($1)" | html p
1920 return
1921 fi
1922 if ! isgrpownerbygid "$user" "$rid"; then
1923 echo "グループ管理者のみがクローン可能です" | html p
1924 return
1925 fi
1926 i=0
1927 while true; do
1928 copy="-copy$i"
1929 newqname=`query "SELECT quote($qgrp || '$copy');"`
1930 # err Trying new grp=$newqname with copy=$copy
1931 test=`query "SELECT gname FROM grp WHERE gname=$newqname;"`
1932 if [ -n "$test" ]; then
1933 i=$((i++))
1934 continue
1935 fi
1936 break
1937 done
1938 # Creating New group "$newqname" with members of old group
1939 # err Creating new grp=$newqname with copy=$copy
1940 query<<-EOF
1941 BEGIN;
1942 INSERT INTO grp VALUES($newqname); -- Create NEW one
1943 REPLACE INTO grp_s(gname, key, val) -- Copy tag
1944 SELECT $newqname, key, val
1945 FROM grp_s WHERE gname=$qgrp AND key IN ('tag', 'mode');
1946 REPLACE INTO grp_s(gname, key, type, val) -- Copy gecos with "copy$n"
1947 SELECT $newqname, key, type, val || '$copy'
1948 FROM grp_s WHERE gname=$qgrp AND key='gecos';
1949 -- Copy members and their configuration --
1950 REPLACE INTO grp_mem SELECT $newqname, user
1951 FROM grp_mem WHERE gname=$qgrp;
1952 REPLACE INTO grp_mem_s SELECT $newqname, user, key, type, val, bin
1953 FROM grp_mem_s WHERE gname=$qgrp;
1954 REPLACE INTO grp_mem_m SELECT $newqname, user, key, type, val, bin
1955 FROM grp_mem_m WHERE gname=$qgrp $noteam;
1956 -- Copy administrators --
1957 REPLACE INTO grp_adm SELECT $newqname, user
1958 FROM grp_adm WHERE gname=$qgrp;
1959 COMMIT;
1960 EOF
1961 newrowid=`query "SELECT rowid FROM grp WHERE gname=$newqname;"`
1962 STOPCLONEMSG=1 groupconf "$newrowid"
1964 groupman() {
1965 note="<p>グループ名に使用できない文字は自動的に削除されます。</p>"
1967 GF_STAGE="grpconf"
1968 GF_STAGE=groupupdate
1969 DT_VIEW=grp dumptable html grp 'gname gecos:DESC mtime:TIME' 'order by b.TIME desc' \
1970 |_m4 -D_TITLE_="グループ作成" \
1971 -D_FORM_="$note`genform $formdir/grp.def`" \
1972 -D_DUMPTABLE_="syscmd(cat)" \
1973 $layout/html.m4.html $layout/form+dump.m4.html
1975 userconf() {
1976 [ -n "`getpar rowid`" ] && par2table $formdir/user.def
1977 _m4 -D_BODYCLASS_=userconf -D_TITLE_="ユーザ情報編集" $layout/html.m4.html
1978 GF_ACTION="?home" edittable "$formdir/user.def" "user" "$user"
1980 groupconf() {
1981 # $1=rowid in grp (2015-07-21 changed from gname)
1982 [ -n "`getpar rowid`" ] && par2table $formdir/grp.def
1983 _m4 -D_BODYCLASS_=groupconf -D_TITLE_="グループ情報編集" $layout/html.m4.html
1984 #rowid=`query "select rowid from grp where gname='$1';"`
1985 rowid=${1%%[!A-Z0-9a-z_]*}
1987 ### If user is not admin, lead to group home
1988 grp=`getgroupbyid $rowid`
1989 if ! isgrpowner "$user" "$grp"; then
1990 echo "<p><a href=\"?grp+$rowid\">`echo "$grp"|htmlescape`</a></p>"
1991 return
1992 fi
1994 # GF_ACTION="?grp+$1" edittable "$formdir/grp.def" "grp" "$rowid" #2015-0804
1995 GF_STAGE="groupupdate" edittable "$formdir/grp.def" "grp" "$rowid"
1996 if [ -z "$STOPCLONEMSG" ]; then
1997 ## Setup migration menu
1998 height="10em" ## Ugly!!
1999 if [ -n "$S4WORLDLIST" ]; then
2000 v=`fgrep -v "value=\"$worldconf\"" $worldoptionfile`
2001 err v=$v
2002 if [ -n "$v" ]; then
2003 migrate=$(cat<<-EOF
2004 `cgi_radio grpaction migrate id="migrate"`<label
2005 for="migrate">別Worldへ移住</label>
2006 <div style="height: $height;">
2007 <form action="?migrategrp">
2008 <p>移住先:<select name="migrateto">$nl$v$nl</select></p>
2009 <p>グループや掲示板のURLが変わります。
2010 外部からリンクしている場合は飛べなくなります。
2011 すでにリンクされた掲示板を多数含む場合は既存グループを温存し、
2012 「グループのクローン」で
2013 メンバーを引き継いだ上でそのクローンを移住するのがお勧めです。</p>
2014 <p><label>`cgi_checkbox emichk yes`確認</label></p>
2015 `cgi_hidden stage migrategrp`
2016 `cgi_hidden rowid $rowid`
2017 `cgi_submit OK`
2018 `cgi_reset Reset`
2019 </form>
2020 </div>
2021 EOF
2023 fi
2024 fi
2025 html div 'class="foldtabs"' <<-EOF
2026 `cgi_radio grpaction clone id="clone"`<label
2027 for="clone">グループのクローン作成</label>
2028 <div style="height: $height;">
2029 <p>構成メンバーが同じ新規グループを作成します。</p>
2030 <table>
2031 <tr><td><a href="?groupclone+$rowid">
2032 <button>クローン作成(チームも複製)</button></a></td>
2033 <td><p>(チームなどもそのままで掲示板なしの状態から)</p></td></tr>
2034 <tr><td><a href="?groupclone+$rowid/noteam">
2035 <button>作成(チームなし)</button></a></td>
2036 <td>(チームは引き継がずメンバーのみ同じグループを作成)</td></tr>
2037 </table>
2038 <p>ボタンを押すと即作成します。不要な場合はグループ編集で
2039 削除してください。</p>
2040 </div>
2041 $migrate
2042 `cgi_radio grpaction close id="x"`<label for="x" accesskey="x">×</label>
2043 <div style="height: $height; background: transparent;"></div>
2044 EOF
2045 fi
2047 migrategrp() {
2048 rowid=`getpar rowid`
2049 rowid=${rowid%%[!0-9]*}
2050 grp=`getgroupbyid $rowid`
2051 if ! isgrpowner "$user" "$grp"; then
2052 echo "<p><a href=\"?grp+$rowid\">`echo "$grp"|htmlescape`</a></p>"
2053 return
2054 fi
2055 if [ x`getpar emichk` != x"yes" ]; then
2056 echo "移住確認未チェックなので中止します。" | html p
2057 grp "$rowid"
2058 return
2059 fi
2060 destconf=`getpar migrateto`
2061 err destconf=$destconf
2062 if [ ! -e $destconf ]; then
2063 echo "移住先Worldが認識できないので中止します($destconf)。" | html p
2064 grp "$rowid"
2065 return
2066 fi
2067 if [ -n "$worldconf" ]; then
2068 srcconf=$worldconf
2069 else
2070 srcconf=s4-config.sh
2071 fi
2072 _m4 -D_TITLE_="移住操作" -D_BODYCLASS_="" $layout/html.m4.html
2073 echo "移住操作" | html h1
2074 echo '<pre>'
2075 set -- "$srcconf" "$destconf" "$rowid"
2076 err ./s4-migrate.sh "$srcconf" "$destconf" "$rowid"
2077 . ./s4-migrate.sh # Dot(.) sourcing might not pass arguments
2078 rc=$?
2079 echo "</pre>"
2080 if [ $rc -eq 0 ]; then
2081 echo "World [$world] への移住完了。" | html p
2082 echo "<p><a href=\"$dsturl?grp+$destrowid\">移住先</a></p>"
2083 elif [ -n "$faillist" ]; then
2084 echo "移住後削除失敗" | html p
2085 echo "空いている時間帯に再度試して下さい。" | html p
2086 else
2087 echo "移住失敗" | html p
2088 echo "移動先に重複がないか確認して下さい。" | html p
2089 fi
2090 return
2092 mems() {
2093 _m4 -D_TITLE_="参加者一覧" -D_BODYCLASS_=listmember $layout/html.m4.html
2094 kwd=`getpar kwd`
2095 listmember $kwd
2097 grps() {
2098 case "$S4WORLD" in
2099 $nonewgroupworld) ;;
2100 *) LINK_NEWGRP="<a href=\"?groupman\">新規グループ作成</a>" ;;
2101 esac
2102 _m4 -D_TITLE_="グループ一覧" -D_BODYCLASS_=listgroup $layout/html.m4.html
2103 kwd=`getpar kwd`
2104 listgroup "$kwd" \
2105 | _m4 -D_DUMPTABLE_="syscmd(cat)" \
2106 -D_TITLE_="グループ関連操作" \
2107 -D_FORM_="${LINK_NEWGRP}${NEWGRP_GUIDE}" \
2108 $layout/form+dump.m4.html
2110 grp() { # $1=group-rowid
2111 gpg=`getpar grp`
2112 grid=${1:-$gpg}
2113 grp=`getgroupbyid "$grid"`
2114 ## . ./s4-blog.sh
2115 jg=`getpar joingrp`
2116 if [ -n "$jg" ]; then
2117 [ -n "$jg" -a -n "$grp" ] &&
2118 joingrp "$grp" "$user" "$jg" "`getpar email`"
2119 fi
2120 htmlheader=$layout/html.m4.html
2121 showgroup "$grid"
2123 sql4interestblogs() {
2124 cat<<EOF
2125 CREATE TEMPORARY VIEW interestblogs AS
2126 SELECT blog.rowid rid, id, author
2127 FROM blog
2128 NATURAL JOIN
2129 (SELECT id, val owner FROM blog_s WHERE key='owner') bs
2130 WHERE CASE WHEN (SELECT name FROM user where name=bs.owner) IS NOT NULL
2131 THEN 1 -- blog owner is an user, READABLE when
2132 WHEN (SELECT user FROM grp_mem
2133 WHERE gname=bs.owner AND user='$user') IS NULL
2134 THEN 0
2135 ELSE 1
2136 END;
2137 EOF
2139 listnewblogsql() { # $1=user
2140 newdays=${WHATS_NEW_DAYS##*[!0-9]} # Shave non-digits
2141 newdays=${newdays%%[!0-9]*}
2142 newdays=${newdays:-14}
2143 basetime="datetime('now', 'localtime', '-${newdays} days')"
2144 deftime=`query "SELECT coalesce((SELECT max(time) FROM acclog
2145 WHERE user='$user'
2146 AND tblrowid IN
2147 ($blogreadflagrowid,
2148 $blogcutoffflagrowid)),
2149 $basetime -- "0"
2150 );"`
2151 cat<<EOF
2152 `sql4interestblogs`
2153 WITH article_ctime as (
2154 SELECT id,blogid,author,max(val) ctime
2155 FROM article join article_s s using(id)
2156 WHERE s.key='ctime' AND s.val > '$deftime'
2157 GROUP BY id
2158 ), blog_title_owner as (
2159 SELECT blg.rid brid, id,
2160 max(case key when 'title' then val end) title,
2161 max(case key when 'owner' then val end) owner
2162 FROM interestblogs blg, blog_s using(id) group by id
2163 ), blogall as (
2164 SELECT * FROM blog_title_owner b JOIN article_ctime ac ON b.id=ac.blogid
2165 ), news as (
2166 SELECT brid, bl.id blid, bl.title, ctime,
2167 coalesce(al.time, '$deftime') atime,
2168 count(bl.id) "新着", bl.author
2169 FROM blogall bl
2170 LEFT JOIN
2171 (SELECT * FROM acclog WHERE user='$user' AND tbl='blog') al
2172 ON bl.brid=al.tblrowid
2173 WHERE atime < bl.ctime
2174 GROUP by bl.id ORDER BY ctime desc,"新着" desc, bl.id
2175 LIMIT 10
2176 ) SELECT brid LINK, "新着",
2177 (SELECT count(*) FROM article WHERE blogid=blid) "総数",
2178 ctime, title,
2179 (SELECT gecos FROM gecoses WHERE name=author) gecos
2180 FROM news;
2181 EOF
2184 search_form() {
2185 # $1 = { author=<AUTHOR> | grid=<GroupRowid> }
2186 # $2(optional) = pre-input keywords
2187 help="(1)空白区切りの単語で本文検索
2188 (2)@YYYY-MM-DD 日付け(シェルパターン可)で日付け検索
2189 @2016-0[1-6] → 2016年1月から6月
2190 @>2016-01 @<2016-02-15 → 2016年1月から2月14日までの期間
2191 @week → 最近一週間
2192 (3)#番号 で記事ID検索
2193 (1)と(2)は組み合わせOK
2194 例: @2016-10-0[1-9] 芋煮
2195 → 2016年10月上旬でキーワード「芋煮」を含む記事検索
2196 ※クイズ板は検索対象から外されます。"
2197 auth=""
2198 placeholder="全記事からの検索"
2199 case "$1" in
2200 author=*)
2201 a=`echo "${1#author=}"|htmlescape`
2202 g=`gecos ${1#author=}`
2203 auth="<input type=\"hidden\" name=\"author\" value=\"$a\">"
2204 placeholder="このユーザの書込検索"
2205 help="★★ $g さんの書き込みから検索します$nl$help"
2206 ;;
2207 grid=*)
2208 a=`echo "${1#grid=}"`; a=$((0 + $a))
2209 auth="<input type=\"hidden\" name=\"grid\" value=\"$a\">"
2210 placeholder="このグループからの検索"
2211 ;;
2212 esac
2213 inikwd="$2" # no need to htmlescape
2214 cat<<-EOF
2215 <div class="right">
2216 <form action="$myname">$auth
2217 <input type="text" name="kwd" value="$inikwd" title="$help"
2218 placeholder=" $placeholder " width="10" accesskey="k">
2219 <!-- POST SENTENCE -->
2220 ${touchpanel:+<p class="help">$help</p>}
2221 <input type="hidden" name="stage" value="searchart">
2222 <!-- EOF -->
2223 </form>
2224 </div>
2225 EOF
2228 imgsrc_cache() (
2229 # $1 = directory for cache'ing
2230 # $2 = table (user_m or grp_m)
2231 # $3 = keycond (was: condition for choosingowner)
2232 # $4 = size : S = Small, M = Medium, O = Original
2233 dir="$1" tbl="$2"
2234 keycond="$3"
2235 whos="$keycond AND key='profimg' AND type LIKE 'file:image%'
2236 ORDER BY rowid DESC LIMIT 1"
2237 [ -d "$dir" ] || mkdir -p "$dir"
2238 tmpf=$tmpd/imgsrc_cache.$$
2239 case "$4" in
2240 [Ss]) size=S ;;
2241 [Oo]) size=O ;;
2242 *) size=M ;;
2243 esac
2244 # ImageCache filename storing schema:
2245 # <table_s>.{key, val}={"profimgcache_S", "$cacheimg_S"}
2246 sql0="SELECT val || '//' || type FROM $tbl WHERE $whos;"
2247 sql1="SELECT hex(bin) FROM $tbl WHERE $whos;"
2248 valtype=`query "$sql0"`
2249 filename=${valtype%%//*}
2250 filetype=${valtype##*//file:}
2251 if [ x"$filename" = x"${filename%.*}" ]; then
2252 # If nor filename extension found, set it to image type
2253 case "$filetype" in
2254 image/*) filename=$filename.${filetype#image/} ;;
2255 esac
2256 fi
2257 cacheimg_S=$dir/S_$filename
2258 cacheimg_M=$dir/M_$filename
2259 cacheimg_O=$dir/$filename
2260 cacheimg=$dir/${size}_$filename
2261 sumfile="$dir/$filename.sum"
2262 sum=`query "$sql1" | tee $tmpf | encode` # encode() is maybe sha1
2263 if test -s "$sumfile" && [ x"`cat \"$sumfile\"`" = x"$sum" ] \
2264 && test -s "$cacheimg_S" && test -s "$cacheimg_M" ; then
2265 # if cache is fresh and has the same checksum,
2266 echo "<img src=\"$cacheimg\">"
2267 else
2268 fifo=`mktemp "$tmpf.fifo.XXXXXXX"`
2269 rm -f $fifo # Safe, because $tmpf is in mktemp dir.
2270 fifo2=$fifo.2
2271 mkfifo $fifo $fifo2
2272 fmt=${filename##*.}
2273 ## [[ NOTE ]]
2274 ## a. convert oldimage newimage
2275 ## b. convert oldimage fmt:- | convert - newimage
2276 ## b is much smaller than a
2277 cat $tmpf | unhexize \
2278 | tee $fifo \
2279 | convert -define ${fmt}:size=${iconxy_M} \
2280 -resize ${iconxy_M}'>' - ${fmt}:- \
2281 | tee $fifo2 \
2282 | convert - "$cacheimg_M" &
2283 cat $fifo | convert -define ${fmt}:size=${iconxy_S} \
2284 -resize ${iconxy_S}'>' - ${fmt}:- \
2285 | convert - "$cacheimg_S" &
2286 printf '%s' "<img src=\"data:${filetype},"
2287 hexize "$fifo2" |sed 's/\(..\)/%\1/g' # Use medium as pre-cached image
2288 echo '">'
2289 echo "$sum" > $sumfile
2290 fi
2291 ## Now preparing cache image, done.
2292 ## Store this information to DB
2293 stbl=${tbl%_m}_s # user_s or grp_s
2294 pkey=${keycond%%=*} # Primary Key name
2295 pval=${keycond#*=} # Primary Key value
2296 query <<-EOF
2297 REPLACE INTO $stbl($pkey, key, type, val)
2298 VALUES($pval, '$iconcachekey', 'string', `sqlquote "$cacheimg_S"`);
2299 EOF
2302 showhome() {
2303 # $1=userRowIdToShow
2304 err showhome \$1=$1
2305 case "$1" in
2306 *@*) uname=`getvalbypkey user name "$1"` ;;
2307 *) uname=`getvalbyid user name $1` ;;
2308 esac
2309 ## err ShowHome: uname=$uname
2310 td=`getcachedir home/"$1"`
2311 gecos=`gecos "$uname"`
2312 ## err SH:gecos=$gecos
2313 GF_VIEWONLY=1
2314 cond="gname in (select gname from grp_mem where user='$uname')"
2315 search_form_args=""
2316 if [ x"$user" = x"$uname" ]; then
2317 if [ -z "$S4MASTERDB" ]; then
2318 usermenu="<a href=\"?userconf\" accesskey=\"e\"
2319 title=\"Shortcut: E${nl}Edit Profile\">プロフィールの編集</a> / "
2320 elif [ -n "$S4MASTERURL" ]; then
2321 usermenu="<a href=\"$S4MASTERURL\" accesskey=\"e\"
2322 title=\"Shortcut: E${nl}Main Site\">Base World</a> / "
2323 fi
2324 usermenu="$usermenu
2325 <a href=\"?blog\" accesskey=\"n\" title=\"Shortcut: N${nl}New blog\">新規話題の作成</a>"
2326 # Display folders
2327 sql="select count(id) from article_m where id
2328 in (select id from article where author='$user')
2329 and type like 'file:%';"
2330 ## err nfile-sql=`echo "$sql"`
2331 nfile=`query "$sql"`
2332 # err nfile=$nfile
2333 if [ $nfile -gt 0 ]; then
2334 usermenu="$usermenu / <a href=\"?lsmyfile\" accesskey=\"l\"
2335 title=\"Shortcut: L${nl}List All Attachment files\">過去の提出ファイル</a>"
2336 fi
2337 else
2338 latestlog=`query "SELECT max(time) FROM acclog WHERE user='$uname' \
2339 GROUP BY user;"`
2340 usermenu="<p>Last seen on $latestlog</p>"
2341 search_form_args="author=$uname"
2342 fi
2343 . ./s4-blog.sh
2345 tf=$tmpd/title.$$ pf=$tmpd/profile.$$ bf=$tmpd/blogs.$$ sf=$tmpd/search.$$
2346 search_form "$search_form_args" > $sf
2347 printf "%s さん%s" "$gecos" "${S4WORLDNAME:+@$S4WORLDNAME}" \
2348 | htmlescape > $tf
2349 { echo "<div class=\"noprofimg\">"
2350 viewtable $formdir/user.def user $1
2351 echo "</div>"
2352 } > $pf
2354 sqcond="WHERE name='$uname' AND key='profimg' AND type LIKE 'file:image%'"
2355 img=`query "SELECT type FROM user_m $sqcond LIMIT 1;"`
2356 imf=$tmpd/profimg.$$; touch $imf
2357 if [ -n "$img" ]; then
2358 if true; then
2359 tbl=user_m
2360 enticond="name='$uname'"
2361 imgsrc_cache "$td/main" user_m "$enticond" M
2362 else
2363 { printf '%s' "<IMG src=\"data:${img#file:},"
2364 query "SELECT hex(bin) FROM user_m $sqcond ORDER BY rowid LIMIT 1;" \
2365 | sed 's/\(..\)/%\1/g'
2366 echo '">'
2368 fi > $imf
2369 fi
2370 nblog=`query "SELECT count(id) FROM blog_s WHERE key='owner' AND \
2371 val='$uname';"`
2372 listblog $uname > $bf
2374 hometail=$tmpd/tail.$$
2375 mkfifo $hometail
2377 #Calling listgroupbytable, originally here
2380 # Display Most Recent Entry
2381 shortval=${dumpcollen:+"substr(val, 0, $dumpcollen)"}
2382 shortval=${shortval:-val}
2384 # The m.aid in the next line is suspicious. But works fine in SQLite3...
2385 # $hidden_mode is defined in global section
2386 DT_SQL="SELECT b.rowid || '#' || m.aid LINK,
2387 ctime,
2388 (SELECT $shortval FROM blog_s WHERE key='title' AND id=b.id) title,
2389 (SELECT gecos FROM gecoses
2390 WHERE name=(SELECT val FROM blog_s
2391 WHERE key='owner' AND id=b.id)) owner,
2392 (SELECT $shortval val FROM article_s WHERE id=m.aid AND key='text') text
2393 FROM blog b
2394 JOIN
2395 (SELECT distinct blogid, a.id aid, max(val) ctime
2396 FROM article a, article_s s
2397 ON a.id=s.id AND a.author='$uname' AND s.key='ctime'
2398 GROUP BY blogid ORDER BY val DESC LIMIT 50
2399 ) m
2400 ON b.id=m.blogid
2401 AND
2402 ('$uname' = '$user' -- user can read all own posts
2403 OR
2404 -- exclude posts of others in quiz/enquete blogs
2405 NOT EXISTS (SELECT * FROM blog_s
2406 WHERE id=b.id AND key='mode' AND val IN $hidden_mode));"
2407 # This should be as follows
2408 : <<EOF
2409 WITH arts AS(
2410 SELECT (SELECT rowid FROM blog WHERE id=a.blogid) brid,
2411 a.blogid, a.id id, s.val ctime
2412 FROM article a NATURAL JOIN article_s s
2413 WHERE s.key = 'ctime' AND a.author='$user'
2414 GROUP by s.id
2416 SELECT a0.brid,a0.blogid,a0.id,a0.ctime
2417 FROM arts a0
2418 JOIN
2419 (SELECT blogid,max(ctime) mct FROM arts a1 GROUP BY blogid) a1
2420 ON a0.blogid=a1.blogid AND a0.ctime=a1.mct
2421 ORDER BY ctime DESC LIMIT 50;
2422 EOF
2424 cat<<-EOF
2425 `cgi_radio foldtabs yes 'id="mre" accesskey="d"'`<label
2426 for="mre" title="Shortcut: D${nl}Recent Post">最近の書き込み先</label>
2427 <div class="lcto">
2428 `DT_VIEW=replyblog dumptable html blog`
2429 </div>
2430 EOF
2431 unset DT_SQL
2432 if [ x"$user" = x"$uname" ]; then
2433 # Display NEWS
2434 # 2016-06-26
2435 if [ x"`getpar readchk``getpar read`" = x"yesyes" ]; then
2436 acclog blog $blogreadflagrowid
2437 # echo "全部既読にしました" | html p
2438 fi
2439 # 2016-02-19 Counting NEWS without using dumptable.
2440 sql=`listnewblogsql "$user"`
2441 # echo "$sql" > tmp/listnew
2442 new10=`DT_SQL="$sql" DT_VIEW=replyblog dumptable html blog`
2443 cont=`echo "$new10"|grep "^<TR>"|wc -l`
2444 cont=$((cont-1))
2445 ##err newcount=$cont
2446 if [ $cont -gt 0 ]; then
2447 #echo "全体の新着記事${cont}傑" | html h2
2448 cgi_radio foldtabs yes 'id="new10" accesskey="f"'
2449 echo "<label for=\"new10\" title=\"Shortcut: F${nl}NEWS\">新着${cont}傑</label><div>"
2450 cat<<-EOF | html form 'action="?home"'
2451 `cgi_checkbox readchk yes 'id="read"'`<label
2452 for="read">新着ふくめて全部読んだことにする</label>
2453 `cgi_submit '確定'`
2454 `cgi_hidden read yes`
2455 EOF
2456 echo "$new10 <!-- new10 -->"
2457 echo "</div>"
2458 else # If news is 0, set log cut off flag
2459 acclog blog $blogcutoffflagrowid # for speed
2460 fi
2461 else # Not My Home ($user != $uname)
2462 : # DT_SQL=
2463 fi
2464 ) > $hometail & # Is background call safe to m4??
2466 listgroupbytable $formdir/grp.def "$cond" $uname |
2467 _m4 -D_BODYCLASS_=home -D_TITLE_="spaste(\`$tf')" \
2468 -D_PROFILE_="spaste(\`$pf')$usermenu" \
2469 -D_PROFIMG_="spaste(\`$imf')" \
2470 -D_BLOGS_="spaste(\`$bf')" \
2471 -D_SEARCH_="spaste(\`$sf')" \
2472 -D_NBLOG_="$nblog" \
2473 -D_GROUPS_="syscmd(\`cat')" \
2474 -D_HOMETAIL_="syscmd(\`cat $hometail')" \
2475 $layout/html.m4.html $layout/home.m4.html
2477 # Record access log
2478 [ -n "$1" ] && [ x"$1" != x"$user" ] && acclog user $1
2480 commission() { # $1=grp-rowid $2=user-rowid
2481 contenttype; echo
2482 ## err commission: "$@"
2483 gname=`getgroupbyid $1`
2484 echo "グループ $gname 管理者委任" \
2485 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
2486 if [ -n "$2" ]; then
2487 grp_reg_adm "$@"
2488 else
2489 echo "無効な指定です。普通のアクセスならここに来ないはず。"|html p
2490 fi
2492 listgroupbytable() {
2493 # $1=deffile $2...=condition $3(optional)=uname
2494 tagline=`grep :tag: $1`;
2495 and="${2:+and }" where=${2:+where }
2496 href="<a href=\"$myname?grp+"
2497 echo '<div class="listgroup">'
2498 sql="select val from grp_s where key='tag' $and$2 group by val;"
2499 ## err ListGRP: query sql="$sql"
2500 for tag in `query "$sql"`
2501 do
2502 ## err ListGrp: tag=$tag
2503 tn=${tagline%%=${tag}*}
2504 tn=${tn##*[ :]}
2505 sql="select rowid||':'||gname as 'グループ名',説明 from
2506 (select (select rowid from grp g where g.gname=grp_s.gname)
2507 as rowid,
2508 gname,
2509 max(case key when 'gecos' then val end) as '説明',
2510 max(case key when 'tag' then val end) as 'tag',
2511 max(case key when 'mtime' then val end) as mtime from grp_s
2512 $where$2 group by gname having tag='$tag' order by mtime desc);"
2513 ## err PersonalGroupList= `echo $sql`
2514 echo "<h2>$tn</h2>"
2515 echo '<table class="b listgroup">'
2516 sq -header -html $db "$sql" \
2517 | sed "s,\(<TR><TD>\)\([0-9]*\):\([^<]*\)</TD>,\1$href\2\">\3</a>,"
2518 echo '</table>'
2519 done
2520 if [ -n "$S4WORLDLIST" -a -n "$3" ]; then
2521 err "peekgrpworlds($user) BEGIN: `gdate +%S.%03N`"
2522 peekgrpworlds mem:"$3"
2523 err "peekgrpworlds($user) END: `gdate +%S.%03N`"
2524 fi
2525 echo '</div>'
2527 iconhref() (
2528 # $1=icon-file, $2=Href $3=title $4...=anchor
2529 data=`percenthex "$1"`
2530 ct=`file --mime-type - < "$1"|cut -d' ' -f2`
2531 ## err iconhref: \$1=$1 \$2=$2 \$3="$@"
2532 href=$2; title=$3; shift 3
2533 echo "<a href=\"$href\"><img title=\"$title\" src=\"data:$ct,$data\">$@</a>"
2535 iconhref2() (
2536 # $1=icon-file, $2=Href $3=title $4...=anchor
2537 src=$1
2538 href=$2; title=$3; shift 3
2539 anchor=`echo $@|htmlescape`
2540 echo "<a href=\"$href\"><img title=\"$title\" src=\"$src\">$anchor</a>"
2542 listentry() (
2543 # $1=user/group $2=SearchKeyword $3=condition(if any) $4=grprowid(if in grp)
2544 # Referring variable $iamowner=$grp to attach owner-request links
2545 ## err listentry: \$1=$1 \$2=$2 \$3=$3
2546 cond='' hiddens=''
2547 offset=`getpar offset`; offset=${offset%%[!0-9]*}
2548 if [ -z "$offset" ]; then
2549 offset=`getpar start`; offset=${offset%%[!0-9]*}
2550 offset=$((offset-1))
2551 fi
2552 offset=$((offset + 0)) # change to numeric forcibly
2553 [ $offset -lt 0 ] && offset=0
2554 limit=$listentlimit
2555 dir=`getcachedir "$1"`
2556 if [ x"$1" = x"user" ]; then
2557 hrb="$myname?home"
2558 deficon=person-default.png
2559 entity="ユーザ" tbl=user link=rowid nm=name # stage=mems
2560 [ -n "$4" ] && hiddens=`cgi_hidden grid $4`
2561 gcs=gecos
2562 else # if group
2563 hrb="$myname?grp"
2564 deficon=group-default.png
2565 entity="グループ" tbl=grp link=rowid nm=gname stage=grps
2566 gcs=name
2567 tagline=`grep :tag: $formdir/grp.def|cut -d: -f5-`
2568 if [ -n "$tagline" ]; then
2569 tagconv=`echo $tagline|sed 's/\([^= :]*\)=\([^= :]*\)/-D\2=\1/g'`
2570 ## err tagconv=$tagconv
2571 fi
2572 fi
2573 if [ ! -d $dir ]; then
2574 mkdir -p $dir
2575 fi
2576 if [ ! -s $dir/$deficon ]; then
2577 convert -geometry $thumbxy $imgdir/$deficon $dir/$deficon
2578 fi
2579 kwd=${2:-`getpar kwd`}
2580 if [ -n "$kwd" ]; then
2581 kwd=`echo $kwd | tr -d '";\n' | tr -d "'"`
2582 case "$kwd" in
2583 mem:*@*)
2584 byuser=${kwd#*mem:}
2585 qusr=`sqlquote "$byuser"`
2586 cond1="(a.gname IN (SELECT gname FROM grp_mem WHERE user=$qusr))"
2587 ;;
2588 esac
2589 if [ x"$1" = x"group" ]; then
2590 if [ -n "$cond1" ]; then
2591 enthead="`gecos "$byuser"|htmlescape` さんの所属"
2592 else
2593 cond1="(b.name like '%${kwd}%')"
2594 fi
2595 else
2596 cond1="(nick like '%${kwd}%' or b.name like '%${kwd}%')"
2597 fi
2598 fi
2599 tag=`getpar tag` tag2=`getpar tag2`
2600 if [ x"$tag" = x"NULL" ]; then
2601 tag="" tag2=""
2602 fi
2603 if [ -n "$tag$tag2" ]; then
2604 tag=${tag:-$tag2}
2605 qtag=`sqlquote "$tag"`
2606 cond2="tag=$qtag"
2607 fi
2608 if [ -n "$cond1$cond2" ]; then
2609 cond="$cond1${cond2:+ AND $cond2}"
2610 cond="WHERE ${cond# AND }"
2611 fi
2613 # XX: これ複雑すぎるかな。もっとシンプルにしたい。$3条件も。2015-07-08
2614 # grpは呼出し元の動的スコープ変数でよくないな...
2615 ##qgrp=`sqlquote $grp`
2616 getgrp="(select gname from grp where rowid=${rowid:--1})"
2617 sql="select a.rowid, a.$link,
2618 coalesce(b.$gcs, a.$nm) as nick,
2619 quote(a.$nm) as qname,
2620 (SELECT val FROM ${tbl}_s
2621 WHERE $nm=a.$nm AND key='$iconcachekey') icon,
2622 coalesce(b.gecos, a.$nm) /* If group, concat (Nusers) */
2623 || case when a.$nm in (select gname from grp)
2624 then printf('(%d名)',
2625 (select count(user) from grp_mem where gname=a.$nm))
2626 else ' <'||a.$nm||'>'
2627 end
2628 as name,
2629 b.tag,
2630 case when a.$nm in (select user from grp_adm
2631 where gname=$getgrp) then '管理者'
2632 when '$user' in (select user from grp_adm where gname=a.$nm)
2633 then 'ADMIN'
2634 when '$user' in (select user from grp_mem where gname=a.$nm)
2635 then 'Member'
2636 when '$iamowner' = '' then ''
2637 else ',not='||a.rowid end as ownerlink,
2638 CASE '$entity'
2639 WHEN 'グループ'
2640 THEN coalesce(
2641 (SELECT val FROM grp_s WHERE gname=a.$nm AND key='regmode'),
2642 'open')
2643 ||
2644 CASE WHEN '$user'
2645 IN (SELECT user FROM grp_mem WHERE gname=a.$nm)
2646 THEN ' ismember'
2647 ELSE ''
2648 END
2649 ELSE 'user'
2650 END regmode
2651 from $tbl a left join
2652 (select $nm as name,
2653 max(case key when 'gecos' then val end) as gecos,
2654 max(case key when 'tag' then val end) as tag,
2655 max(case key when 'mtime' then val end) as mtime,
2656 max(case key when 'wtime' then val end) as wtime,
2657 max(case key when 'login' then val end) as login
2658 from ${tbl}_s group by $nm)
2659 b on a.$nm=b.name $cond $3
2660 order by b.wtime desc, b.login desc,
2661 b.mtime desc, b.tag desc, a.rowid asc"
2662 # Give precedence to newer maintained groups (2016-09-24)
2663 # Note that mtime is stored only in grp_s.
2664 ## err LE:sql.1="$sql"
2665 total=`query "with x as ($sql) select count(*) from x;"`
2666 echo "$enthead${entity} 一覧" "${S4WORLDNAME:+@$S4WORLDNAME}" | html h2
2667 echo '<div class="listentry">' # List-entry div
2668 # Show owner/member filter button
2669 METHOD=GET
2670 hiddens="$hiddens
2671 `cgi_hidden stage \"$stage\"`"
2672 if [ x"$tbl" = x"grp" ]; then
2673 args=`grep "^種別:" $formdir/grp.def | cut -d: -f5`
2674 fh="<select name=\"tag\">$nl"
2675 fh="$fh<option value=\"NULL\"${tag:+ selected}>グループ種別...</option>"
2676 for l in $args; do
2677 val=${l#*=} tname=${l%=*}
2678 if [ x"$val" = x"$tag" ]; then
2679 s=" selected"
2680 selectedtags="(種別[${tname}]のみ)"
2681 else
2682 s=""
2683 fi
2684 form=$nl$form"<option value=\"$val\"$s>$tname</option>"
2685 done
2686 form="$fh$form</select><input type=\"submit\" value=\"で絞る\">"
2687 cat<<-EOF
2688 <form action="$myname" method="$METHOD">
2689 </form>
2690 以下一覧のうち: `cgi_checkbox onlymem no 'id="ismembtn"'`<label
2691 for="ismembtn">参加中以外隠す</label>
2692 `cgi_checkbox onlyadm no 'id="isadmbtn"'`<label
2693 for="isadmbtn">管理者参加以外隠す</label>
2694 EOF
2695 # limit=3
2696 hiddens=$hiddens" "`cgi_hidden tag2 "$tag"`
2697 fi
2698 if [ $total -gt $limit -o \( -n "$S4WORLDLIST" -a x"$tbl" = x"grp" \) ]; then
2699 echo '<div>'
2700 METHOD=GET cgi_form $stage <<EOF
2701 $form
2702 <label>次の語を含む${entity}で検索:
2703 `cgi_text kwd "$kwd"`</label>
2704 $hiddens
2705 EOF
2706 echo '</div>'
2707 else
2708 echo $selectedtags | html p
2709 fi
2710 test -n "$kwd" && hiddens="$hiddens$nl`cgi_hidden kwd \"$kwd\"`"
2711 cat<<EOF
2712 <form action="$myname" method="$METHOD">
2713 <p>${total}件中の<input class="hidesub" type="text" name="start"
2714 value="$((offset+1))" size="3">件めから${kwd:+" - 検索語: $kwd"}$hiddens
2715 <input type="submit" value="確定"></p>
2716 </form>
2717 EOF
2718 if [ $((offset+limit)) -lt $total ]; then
2719 nextbtn=$(
2720 cat<<EOF
2721 <div class="right clear"><form action="$myname" method="$METHOD">
2722 `cgi_submit 次の${limit}件`
2723 $hiddens
2724 `cgi_hidden offset $((offset + limit))`</form></div>
2725 EOF
2727 fi
2728 if [ $offset -gt 0 ]; then
2729 prevbtn=$(
2730 cat<<EOF
2731 <form action="$myname" method="$METHOD">
2732 `cgi_submit 前の${limit}件`
2733 $hiddens
2734 `cgi_hidden offset $((offset - limit))`</form>
2735 EOF
2737 fi
2738 pnbtn="$nextbtn$prevbtn"
2739 echo $pnbtn
2741 ## err ListEntry: `echo "$sql"\;`
2742 # sq $db here??? 2016-11-28
2743 query "$sql limit $limit ${offset:+offset $offset};" \
2744 | while IFS='|' read id lnk name qname icon gecos tag ownerp type; do
2745 # err name=$name owner=$ownerp lnk=$lnk
2746 # err newlnk=$lnk regmode=$regmode
2747 icondir=$dir/$id
2748 # Pick up only last icon
2749 htmlname=`echo $name|htmlescape`
2750 echo "<div class=\"iconlist xy$thumbxy $type $ownerp\">
2751 <p class=\"tag _$tag\">$tag</p>" \
2752 | _m4 $tagconv
2753 if [ -n "$NOSPEEDUP" ]; then
2754 files=`getvalbyid $tbl profimg $id $icondir`
2755 if [ -n "$files" ]; then
2756 icon=`echo "$files"|tail -1`
2757 iconhref2 "$icondir/$icon" "$hrb+$lnk" "$gecos"
2758 else
2759 iconhref "$dir/$deficon" "$hrb+$lnk" "$gecos"
2760 fi
2761 elif [ -n "$icon" -a -s "$icon" ]; then
2762 iconhref2 "$icon" "$hrb+$lnk" "$gecos"
2763 else
2764 cond="$nm=$qname"
2765 # err imgsrc_cache "$dir/list" ${tbl}_m "$cond" S
2766 # err query "SELECT type FROM ${tbl}_m $cond LIMIT 1;"
2767 img=`query "SELECT type FROM ${tbl}_m WHERE $cond AND key='profimg' LIMIT 1;"`
2768 # err "img=[$img]"
2769 if [ -n "$img" ]; then
2770 echo "<a href=\"$hrb+$lnk\">"
2771 imgsrc_cache "$icondir" ${tbl}_m "$nm=$qname" S
2772 echo "</a>"
2773 else
2774 iconhref2 "$dir/$deficon" "$hrb+$lnk" "$gecos"
2775 fi
2776 fi
2777 echo "<br>$htmlname${ownerp:+<br>($ownerp)}"
2778 echo "</div>"
2779 done
2780 echo "</div>" # End of List-entry div
2781 echo ${pnbtn:+"<hr>$nextbtn$prevbtn"}
2782 if [ -n "$kwd" -a x"$tbl" = x"grp" -a -n "$S4WORLDS" ]; then
2783 peekgrpworlds "$kwd"
2784 fi
2786 listmember() {
2787 listentry user "$@"
2789 listgroup() {
2790 listentry group "$@"
2792 hexteams() { # $1=gname, $2(optional)=user
2793 cond=${2:+" AND user='$2'"}
2794 query "SELECT DISTINCT hex(val) FROM grp_mem_m
2795 WHERE gname='$1' AND key='team'$cond ORDER by val;"
2797 showgroup() { # $1=group-rowid
2798 if [ -z "$1" ]; then
2799 grid=`getpar grid`
2800 grid=${grid%%[!0-9]*}
2801 [ -n "$grid" ] && grp=`getgroupbyid $grid`
2802 else
2803 grid=$1
2804 fi
2805 grp=`getgroupbyid $grid`
2806 qgrp=`sqlquote "$grp"`
2807 htmlgrp=`echo "$grp"|htmlescape`
2808 ## err showgroup2: grid=$grid grp=$grp qgrp="[$qgrp]"
2809 if isgroup "$grp"; then
2810 tf=$tmpd/title.$$
2811 sf=$tmpd/search.$$
2812 bodyclass=`query "SELECT val FROM grp_s
2813 WHERE gname=$qgrp AND key='regmode';"`
2814 if ismember "$user" "$grp"; then
2815 ismember="ismember"
2816 bodyclass="$bodyclass${bodyclass:+ }ismember"
2817 else
2818 ismember="" # bodyclass="group"
2819 fi
2820 bodyclass="$bodyclass grouphome"
2821 echo "<div class=\"search\">`search_form grid=\"$grid\"`</div>"> $sf
2822 echo "グループ $htmlgrp" > $tf
2824 showgroupsub $formdir/grp.def "$grid" | \
2825 _m4 -D_TITLE_="syscmd(\`cat $tf')" \
2826 -D_FORM_="syscmd(\`cat')" \
2827 -D_BODYCLASS_="$bodyclass" \
2828 -D_DUMPTABLE_="" \
2829 $htmlheader $sf $layout/form+dump.m4.html
2830 # $htmlheader $layout/form+dump.m4.html
2831 # $htmlheader is defined in grp()
2832 else # if $grp is removed at par2table
2833 listgroup
2834 fi
2836 showgroupsub() {
2837 # $1=def-file $2=group-rowid
2838 # Using $ismember
2839 rowid=$2
2840 grp=`getgroupbyid $2`
2841 qgrp=`sqlquote "$grp"`
2842 td=`getcachedir grp/"$2"`
2843 #rowid=`sq $db "select rowid from grp where gname=$qgrp"`
2844 if [ -z "$rowid" ]; then
2845 #rowid=`sq $db "select rowid from grp where rowid=$grp"`
2846 #grp=`sq $db "select gname from grp where rowid=$grp"`
2847 echo "showgroupsub: invalid argument($1 $2)" | html p
2848 return
2849 fi
2850 val=`getvalbyid grp profimg $rowid $tmpd`
2851 enticond="gname=$qgrp"
2852 img=`query "SELECT type FROM grp_m WHERE $enticond LIMIT 1;"`
2853 if [ -n "$img" ]; then
2854 cat<<-EOF
2855 <p class="groupimg">
2856 `imgsrc_cache $td/main grp_m "$enticond" M`</p>
2857 EOF
2858 fi
2859 echo "<div class=\"noprofimg\">"
2860 viewtable $1 grp $rowid
2861 echo "</div>"
2862 if isgrpowner "$user" "$grp"; then
2863 echo "<p><a href=\"?groupconf+$rowid\" accesskey=\"e\"
2864 title=\"Shortcut: e${nl}Edit Group\">グループ情報の編集</a>"
2865 iamowner=$rowid
2866 colmd=" mode"
2867 # We have to specify report-type blog to lshandoutall
2868 repblog=`query "
2869 WITH grpblogs AS (
2870 SELECT id FROM blog_s
2871 WHERE key='owner' AND val=(SELECT gname FROM grp WHERE rowid=$rowid)
2872 ), ownerMode AS (
2873 SELECT id,
2874 max(CASE key WHEN 'owner' THEN val END) owner,
2875 max(CASE key WHEN 'mode' THEN val END) mode
2876 FROM blog_s
2877 GROUP BY id
2878 ), blogid AS (
2879 SELECT id
2880 FROM grpblogs NATURAL LEFT JOIN ownerMode
2881 WHERE mode LIKE 'report%' LIMIT 1
2882 ) SELECT blog.rowid FROM blogid NATURAL LEFT JOIN blog;"`
2883 if [ -n "$repblog" ]; then
2884 csvid="gethandoutcsv"
2885 replink="/ <a href=\"?lshandoutall+$repblog#$csvid\">レポート全集計</a>"
2886 fi
2887 fi
2888 if [ -n "$ismember" ]; then
2889 #echo "${iamowner:+ / }<a href=\"?blog+$rowid\">グループの新規話題作成</a>"
2890 #echo "/ <a href=\"?grpaction+$rowid\">メンバーを個別選択しての操作</a></p>"
2891 # div.fold input[type="checkbox"]:checked ~ div {display: block;}
2892 cat<<-EOF
2893 ${iamowner:+ / }<a accesskey="n" title="Shortcut: n${nl}New blog"
2894 href="?blog+$rowid">グループの新規話題作成</a>
2895 / <a accesskey="m" title="Shortcut: m${nl}Operations on Members"
2896 href="?grpaction+$rowid">メンバーを個別選択しての操作</a>
2897 $replink</p>
2898 <form action="?send2mem" method="POST" enctype="multipart/form-data">
2899 <div class="fold clear">
2900 `cgi_checkbox send yes id="send"`<label
2901 for="send">グループ全員にメッセージ送信</label>
2902 <div>
2903 `cgi_textarea message "" "cols=60"`
2904 `cgi_submit 送信`
2905 `cgi_reset リセット`
2906 </div>
2907 `cgi_hidden grp $rowid`
2908 </div></form>
2909 EOF
2910 fi
2911 # 加入ボタン + 加入者リスト
2912 if [ -n "$ismember" ]; then
2913 ismem='checked' state="(参加中)"
2914 else
2915 nomem='checked' state="(現在非加入)"
2916 fi
2917 # このグループでの加入アドレス
2918 eml=`query "select val from grp_mem_s where gname=$qgrp and user='$user' \
2919 and key='email';"`
2920 ##err EML: "select val from grp_mem_s where gname='$2' and user='$user' \
2921 ## and key='email';"
2922 ##err email=$eml
2923 cat <<EOF
2924 <div class="fold clear">
2925 `cgi_checkbox reg yes id="reg"`<label
2926 for="reg">自身の加入状態を操作する</label>$state
2927 <div>
2928 EOF
2929 cgi_form grp <<EOF
2930 <p>このグループに</p>
2931 <table class="b">
2932 <tr><th>メンバーとして</th><td>
2933 <label>`cgi_radio joingrp "yes" $ismem`参加</label> /
2934 <label>`cgi_radio joingrp "no" $nomem`参加しない</label></td></tr>
2935 <tr><th>参加する場合のメイルアドレス<br>
2936 <small>(メインのアドレスとは違うものにする場合に記入<br>
2937 同じでよい場合は空欄に)</small></th>
2938 <td>`cgi_text email $eml`</td></tr>
2939 </table>
2940 `cgi_hidden grp $rowid`
2941 EOF
2942 if [ x`getgroupattr "$grp" regmode` = x'moderated' -a -z "$ismem" ]; then
2943 echo "moderated (承認加入の)グループなので実際に参加できるのは
2944 グループ管理者が承認操作をした後になります。" | html p 'class="warn"'
2945 fi
2946 echo '</div></div>'
2947 thelp="1ヶ月分のまとめには上部検索窓に @month と入れてください。"
2948 cat<<-EOF
2949 <div class="fold"> <!-- in showgroupsub -->
2950 <h2>話題一覧</h2>
2951 <form class="summary inline" action="$myname" title="$thelp">
2952 `cgi_hidden owner "$grp"`
2953 `cgi_hidden kwd "@week"`
2954 `cgi_hidden stage searchart`
2955 `cgi_submit "一週間のまとめ"`
2956 </form>
2957 <input type="checkbox" id="dtcheck" checked><label
2958 for="dtcheck">話題一覧表示</label>
2959 EOF
2960 cond="where a.id in (select id from blog_s where key='owner' and val=$qgrp) order by 稼動状態, ctime desc"
2961 colstate="state:稼動状態:frozen=rowclass=凍結"
2962 frzbtn='<button class="toggle-frozen"></button>'
2963 if [ -n "$ismember" ]; then
2964 DT_COUNT=author DT_COUNT_HEADER="書" DT_COUNT_BY=$user
2965 fi
2966 DT_CHLD=article:blogid \
2967 DT_QOWNER="$qgrp" \
2968 DT_VIEW=replyblog dumptable html blog \
2969 "ctime title heading team notify:通知$colmd $colstate" "$cond" \
2970 | if [ -n "$iamowner" ]
2971 then
2972 sed -Ee "s,(<TD class=\"稼動状態\">).*(</TD>),\1$frzbtn\2,"
2973 else
2974 cat
2975 fi
2976 ## DO not convert to frzbtn when not admin
2977 echo "</div> <!-- in showgroupsub -->"
2979 getgname="(select gname from grp where rowid=$rowid)"
2980 c="group by a.name having a.name in (select user from grp_mem where gname=$getgname)"
2981 cm="?commission+$rowid"
2982 thumbxy=50x50 listmember "`getpar kwd`" "$c" "$rowid" \
2983 |sed -e "s|\(<br>\)(,not=\(.*\))|\1|" # 間違って押しやすい
2984 # team list
2985 hexteams=`hexteams "$grp"`
2986 if [ -n "$hexteams" ]; then
2987 echo "チーム一覧" | html h2
2988 echo '<div class="dumptable"><table class="b">'
2989 sq $db -html -header<<-EOF
2990 SELECT val TEAM,
2991 group_concat((SELECT gecos FROM gecoses WHERE name=user), ',')
2992 MEMBERS
2993 FROM grp_mem_m WHERE gname=$qgrp AND key='team' GROUP BY val;
2994 EOF
2995 echo '</table></div>'
2996 fi
2998 grp_getbodyclass() {
2999 # Get css class name for document.
3000 # `moderated' for moderated groups
3001 # `ismember' for groups where user belongs
3002 # $1=GroupName (w/o quote)
3003 # $user=userNameCurrentlyLogin
3004 ## err grp_getbodyclass: 1="$1"
3005 qgrp=`sqlquote "$1"`
3006 query<<-EOF
3007 SELECT coalesce(
3008 (SELECT val FROM grp_s WHERE gname=$qgrp AND key='regmode'),
3009 'open')
3010 ||
3011 CASE WHEN '$user'
3012 IN (SELECT user FROM grp_mem WHERE gname=$qgrp)
3013 THEN ' ismember'
3014 ELSE ''
3015 END;
3016 EOF
3018 grpaction() { # $1=group-rowid
3019 err GRP_ACTION:IN
3020 grid=${1:-`getpar grp`}
3021 grp=`getgroupbyid "$grid"`
3022 htmlgrp=`echo "$grp" | htmlescape`
3023 myuid=`query "SELECT rowid FROM user WHERE name='$user';"`
3024 if [ -z "$grp" ]; then
3025 echo "無効な指定です。" | html p; return
3026 fi
3027 if ! ismember $user "$grp"; then
3028 echo "加入者のみに許可された操作です。" | html p; return
3029 fi
3030 isowner=""
3031 isgrpowner "$user" "$grp" && isowner="yes"
3032 err 2=$2 3=$3
3033 case "$2" in
3034 get:teamcsv)
3035 teamcsv "$grid"
3036 return
3037 ;;
3038 esac
3039 echo "グループ $grp 個別選択操作" \
3040 | _m4 -D_TITLE_="syscmd(\`cat')" \
3041 -D_BODYCLASS_="`grp_getbodyclass \"$grp\"`" \
3042 $layout/html.m4.html
3044 usel=`getpar usel`
3045 if [ -n "$usel" ]; then
3046 uids=$(echo `echo $usel`|tr ' ' ',')
3047 ## err grpaction-1: grp=$grp, `echo $sql`
3048 text=`getpar text|tr -d '\r'`
3050 rm=`getpar rm` cfm=`getpar confirm`
3051 ## err rm=$rm cfm=$cfm
3052 if [ x"$rm" = x"yes" ]; then
3053 if [ "$isowner" ]; then
3054 if [ x"$rm$cfm" = x"yesyes" ]; then
3055 # Eliminate
3056 cond="where gname=(select gname from grp where rowid=$grid) and user in (select name from user where rowid in ($uids))"
3057 for tbl in grp_mem grp_mem_s grp_mem_m grp_adm grp_adm_s grp_adm_m
3058 do
3059 sql="delete from $tbl $cond;"
3060 # echo "sql=$sql"
3061 query "$sql"
3062 err rmGRPuser "$sql"
3063 done
3064 num=`query "select count(*) from user where rowid in ($uids);"`
3065 #err num=$num
3066 if [ 0$num -gt 0 ]; then
3067 sql="select coalesce(b.val,a.name) from user a left join \
3068 user_s b on a.name=b.name and key='gecos' where a.rowid in ($uids);"
3069 # err `echo "$sql"`
3070 html pre<<EOF
3071 以下の${num}名のグループ $grp 登録を解除しました。
3072 `query "$sql"`
3073 EOF
3074 fi
3075 else
3076 echo "確認のチェックがないのでやめておきます。" | html p
3077 return
3078 fi
3079 else # not Group Owner
3080 echo "グループ管理者でないのでメンバー操作はできません。" | html p
3081 return
3082 fi
3083 cat<<EOF
3085 EOF
3086 elif [ x"$rm" = x"send" ]; then # if sendmsg mode
3087 if [ -z "$text" ]; then # if msg is empty
3088 echo "なにかメッセージを..." | html p
3089 return 0
3090 fi
3091 gecos=`gecos $user`
3092 safegc=`echo "$gecos" | tr -d '<>@,'`
3093 #fromad=`email4groupbyuid "$grp" "$myuid" | sed -e 1q -e 's/[ ,].*//'`
3094 fromad=`myemail4group "$grp"`
3095 ###mail_from="$safegc <$fromad>"
3096 mail_from="$safegc <$user>" # TEST: 2020/5/13
3097 test -n `getpar sender` &&
3098 export SENDER=$user # TEST: 2020/5/15
3099 replyto=$fromad
3101 ## Start parse of attachment files
3102 if [ -n "`getpar email`" ]; then
3103 ar=`getpar supprcpt`
3104 if [ -n "$ar" ]; then
3105 for a in $ar; do
3106 if checkdomain "$a"; then
3107 supprcpt="$supprcpt $a"
3108 else
3109 err "SupprcptErr=[$a] by $user"
3110 removercpt="$removercpt $a"
3111 fi
3112 done
3113 fi
3114 subj=`getpar subject`
3115 afiles=""
3116 for fn in `query "SELECT DISTINCT val FROM par WHERE var='image' AND sessid='$session';"`
3117 do
3118 f=$tmpd/$fn
3119 if [ -s $f ]; then
3120 afiles=$afiles"${afiles:+ }$f"
3121 fi
3122 done
3123 else
3124 preface=$(cat <<-EOF
3125 $url
3126 のグループ「$grp」のメンバーである $gecos さんから、
3127 あなた宛のメッセージです。
3128 ----------------------------------------------------------
3129 EOF
3131 fi
3132 rcpts="`email4groupbyuid "$grp" $usel` $fromad$supprcpt"
3133 rcpts=`echo $rcpts|tr ' ' '\n'|sort|uniq|tr '\n' ' '`
3134 subj="${subj:-$gecos さんからのメッセージ}"
3135 REPLYTO=$replyto
3136 MAIL_FROM=$mail_from
3137 export REPLYTO SMAIL_TO MAIL_FROM
3138 err "GrpActionSend: user=[$user], MAIL_FROM=[$mail_from], rcpts=[$rcpts], REPLYTO=[$replyto}"
3139 for r in $rcpts; do
3140 if [ x"$user" = x"$r" -o x"$fromad" = x"$r" ]; then
3141 SMAIL_TO=$rcpts # Show all rcpts to sender oneself
3142 else
3143 # Show sender and rcpts address for guest
3144 SMAIL_TO=`echo $r $user $fromad|tr ' ' '\n'|sort -u|tr '\n' ' '`
3145 fi
3146 if [ -n "$afiles" ];then
3147 ./sendmultipart.sh -t "$r" -s "$subj" -f "$mail_from" $afiles
3148 else
3149 smail "$r" "$subj"
3150 fi <<EOF
3151 ${preface:+$preface$nl}$text
3152 EOF
3153 done
3154 if [ $? = 0 ]; then
3155 echo "Note: 以下のメンバーにメッセージを送信しました。" | html p
3156 sql="select coalesce(b.val, a.name) from
3157 (select name from user where rowid in ($uids)) a
3158 left join user_s b on a.name=b.name and b.key='gecos';"
3159 html pre<<EOF
3160 `query "$sql"`
3161 ${supprcpt:+追加宛先 $supprcpt$nl}(送信者である $gecos さんも含まれます)
3162 ${removercpt:+アドレスエラーによる削除(送られません): <em class="warn">$removercpt</em>}
3163 EOF
3164 err SendDone: `echo $sql`
3165 fi
3166 elif [ x"$rm" = x"commission" ]; then
3167 grp_reg_adm $grid $usel
3168 elif [ x"$rm" = x"addteam" ]; then
3169 team=`getpar team|sed "s/'/''/g"` # for single quotation
3170 newteam=`echo $team|tr -d ,` # ..and strip spaces of both ends
3171 if [ x"$team" != x"$newteam" ]; then
3172 echo "チーム名に使えない文字を除去しました" | html p
3173 team=$newteam
3174 fi
3175 if [ -z "$team" -o x"$team" = x"なし" -o x"$team" = x"TEAM" ]; then
3176 cat<<-EOF | html p
3177 有効なチーム名を入力してください。
3178 カンマだけ、「なし」という名前は使えません。
3179 EOF
3180 echo "有効なチーム名を入力してください。" | html p
3181 else
3182 grp_add_team $grid "$team" $usel
3183 fi
3184 elif [ x"$rm" = x"rmteam" ]; then
3185 if [ x"yes" = x"`getpar teamconfirm`" ]; then
3186 rmteam=`getpar rmteam|sed "s/'/''/g"`
3187 if [ -n "`query \"SELECT val FROM grp_mem_m WHERE\
3188 gname='$grp' AND user='$user' AND key='team'\
3189 AND val='$rmteam';\"`" ]; then
3190 grp_rm_team $grid "$rmteam" $usel
3191 else
3192 echo "所属していないチームの除去操作はできません。"|html p
3193 fi
3194 else
3195 echo "確認チェックなしなのでチーム除去しませんでした。"|html p
3196 fi
3197 fi
3198 fi
3199 # POST count summary
3200 from=`getpar from`; to=`getpar to`
3201 from_input="<input type=\"date\" name=\"from\" placeholder=\"YYYY-MM-DD\" value=\"${from}\">"
3202 to_input="<input type=\"date\" name=\"to\" value=\"${to:-9999}\">"
3203 fromtonote="<p title=\"Count the Number of Posts from-to\">POST/ACCESS集計: $from_input - $to_input</p><!-- $from - $to -->"
3204 # New entry
3205 sql="WITH mems AS (
3206 SELECT g.rowid, name, gecos FROM grp_mem gm LEFT JOIN gecoses g
3207 ON gm.user=g.name
3208 WHERE gname=(SELECT gname FROM grp WHERE rowid=$grid)
3209 ), target_article AS (
3210 SELECT id FROM article_s
3211 WHERE key='ctime' AND val BETWEEN '${from:-0000}' AND '${to:-9999}'
3212 ), posts AS (
3213 SELECT author, count(author) post
3214 FROM article NATURAL JOIN article_s NATURAL JOIN target_article
3215 WHERE blogid IN (SELECT id FROM blog_s
3216 WHERE key='owner'
3217 AND val=(SELECT gname FROM grp WHERE rowid=$grid))
3218 AND key='text'
3219 GROUP BY author
3220 ), teams AS (
3221 SELECT user, group_concat(val, ', ') team
3222 FROM mems m LEFT JOIN grp_mem_m gm ON m.name=gm.user
3223 WHERE gname=(SELECT gname FROM grp WHERE rowid=$grid)
3224 AND key='team'
3225 GROUP BY user
3226 ), user_post AS (
3227 SELECT m.rowid, name, m.gecos, coalesce(post, 0) as POST
3228 FROM mems m LEFT JOIN posts
3229 ON m.name=posts.author
3230 GROUP by m.rowid
3231 ), user_view AS (
3232 SELECT user vuser,count(user) cnt
3233 FROM tblaccesses
3234 WHERE user IN (
3235 SELECT user FROM grp_mem
3236 WHERE gname=(SELECT gname FROM grp WHERE rowid=$grid))
3237 AND tbl='blog'
3238 AND tblrowid IN (
3239 SELECT rowid FROM blog
3240 WHERE id IN (
3241 SELECT id FROM blog_s
3242 WHERE key='owner' AND val=(
3243 SELECT gname FROM grp WHERE rowid=$grid)))
3244 AND time BETWEEN '${from:-0000}' AND '${to:-9999}'
3245 GROUP BY user ORDER BY cnt
3247 SELECT
3248 CASE
3249 WHEN (SELECT user FROM grp_adm
3250 WHERE gname=(SELECT gname FROM grp WHERE rowid=$grid)
3251 AND user=up.name) IS NOT NULL
3252 then 'k'
3253 ELSE ''
3254 END || rowid || ','
3255 || rtrim(substr(name, 1, instr(name, '@')), '@') UID,
3256 gecos NAME,
3257 post POST,
3258 (coalesce((SELECT cnt FROM user_view WHERE vuser=name), 0)) ACCESS,
3259 team _TEAM_
3260 FROM user_post up LEFT JOIN teams t
3261 ON up.name=t.user
3262 ORDER BY up.name;"
3263 ## Want to count ACCESS with JOIN, but failed to produce user-unique rows
3264 ## err grpaction: "`echo \"$sql\"`"
3265 tf=$tmpd/title.$$
3266 echo "グループ[<a href=\"?grp+$grid\">$htmlgrp</a>]参加メンバーに対する操作" > $tf
3267 cmmsg="`cgi_radio rm commission id=\"cmadmin\"`<label accesskey=\"f\"
3268 title=\"Shortcut: f${nl}Add to Administrator of the Group\"
3269 for=\"cmadmin\">管理者委任</label>
3270 <div><p>このグループでの全権を付与します。信頼できる人に託してください。
3271 </p></div>"
3272 excmsg="`cgi_radio rm yes id=\"conf\"`<label accesskey=\"g\"
3273 title=\"Shortcut: g${nl}Remove from the Group\"
3274 for=\"conf\">GRP登録解除</label>
3275 <div>本当に消します! `cgi_checkbox confirm yes` 確認
3276 <p>この操作による通知は本人に行きません。
3277 あらかじめ通知するか、登録解除してよい状況かしっかり確認してください。</p>
3278 </div>"
3279 submitBTN=`cgi_submit 送信`
3280 # Get team list to which current user belongs into $hexteams
3281 allhexteams=$(hexteams "$grp")
3282 if [ -n "$isowner" ]; then
3283 myhexteams="$allhexteams" # admin can remove all teams' attr
3284 else
3285 myhexteams=$(hexteams "$grp" "$user")
3286 fi
3287 if [ -n "$isowner" -a -n "$allhexteams" ]; then
3288 gettingcsv="<p>Download: <a href=\"?getteamcsv+$grid\">Team.csv</a> (Zoom Breakout Room 事前割り当てに使えます), <a href=\"?getteamcsv+$grid+name\">Team-with-name.csv</a>(参照用名前付)</p>"
3289 fi
3290 if [ -n "$myhexteams" ]; then
3291 rmteammsg="`cgi_radio rm rmteam 'id=\"cmrmteam\"'`<label accesskey=\"s\"
3292 title=\"Shortcut: s${nl}Strip a team tag from\"
3293 for=\"cmrmteam\">チーム属性除去</label>
3294 <div>チーム属性:`cgi_select_h rmteam \"2d2d2d\" $myhexteams`
3295 を除去します: `cgi_checkbox teamconfirm yes` 確認 $submitBTN
3296 <p>この操作による通知は本人に行きません。
3297 あらかじめ通知するか、登録解除してよい状況かしっかり確認してください。</p>
3298 </div><!-- end of $rmteammsg -->
3300 fi
3301 stf=$tmpd/selteam.$$
3302 cgi_select_h selteam "5445414d" $allhexteams > $stf
3303 b1='<label> <input type="checkbox" name="usel" value="'
3304 ba='<label class="admin"><input type="checkbox" name="usel" value="'
3305 br='<span id="reverse" title="Reverse Selection"></span>'
3306 # lnk='"> <span></span></label> [<a href="?home+\3">HOME</a>]'
3307 lnk='<a href="?home+\3">\5</a>'
3308 # (1) Join <TR> line and the next
3309 # (2) (<TR><TD)>(k?)(1234),(userid)</TD><TD>(GECOS)</TD>
3310 # ↓
3311 # <TR><TD>\2<label><input ...value="\3">\4</label></TD> \
3312 # <TD><a href="?home+\3">\5</a></TD>
3313 cgi_form grpaction<<EOF \
3314 | sed -e '/^<TR>/{; N; s/\n//; }' \
3315 | sed -E \
3316 -e "s|^(<TR><TD>)(k?)([0-9]*),(.*)</TD><TD>(.*)</TD>|\1\2$b1\3\">\4</label></td><td>$lnk</TD>|" \
3317 -e 's/^(<TR><TD[^>]*>)k(<label)/\1\2 class="admin"/' \
3318 -e "s|^(<TR><TH>)(UID)|\1$br \2|" \
3319 | _m4 -D_TITLE_="spaste(\`$tf')" \
3320 -D_SUBTITLE_="チェック後操作ボタン" \
3321 -D_FORM_="syscmd(cat)" -D_DUMPTABLE_="" \
3322 $layout/form+dump.m4.html \
3323 | _m4 -D_TEAM_="spaste(\`$stf')"
3324 <p>下でチェックした人を対象として:</p>
3325 <div class="foldtabs">
3326 `cgi_radio rm addteam 'id="cmteam"'`<label accesskey="a"
3327 title="Shortcut: a${nl}Add a team tag to"
3328 for="cmteam">同じチーム属性を付与</label>
3329 <div>チーム名:`cgi_text team "" 'id="inteam" list="teams"'`
3330 `cgi_datalist_h teams $allhexteams`$submitBTN
3331 </div>
3332 ${rmteammsg}
3333 `cgi_radio rm send id="sendmsg"`<label accesskey="d"
3334 title="Shortcut: d${nl}DirectMail to"
3335 for="sendmsg" title="Direct Message">DM送信</label>
3336 <div>
3337 `cgi_checkbox email yes 'id="email" class="fold"'`<label for="email"
3338 title="Using email format">email書式を使う</label>
3339 <div class="folded">
3340 <table>
3341 <tr><td>From: </td><td>$user</td></tr>
3342 <tr><td>このFrom:で送る</td>
3343 <td>`cgi_checkbox sender yes 'checked'`<small></small>
3344 </td></tr>
3345 <tr><td>Subject: </td><td>`cgi_text subject`</td></tr>
3346 <tr><td>追加宛先(通常空欄): </td><td>`cgi_text supprcpt ""`</td></tr>
3347 <tr><td>ファイル添付: </td>
3348 <td>`cgi_file image "" "multiple $file_accept title=\"$file_accept_help\""`<br><small>文書ファイルはPDFに変換してから</small></td></tr>
3349 </table>
3350 <p>(下記一覧から1人以上選択していない場合は送れません$submitBTN)</p>
3351 </div>
3352 <div>本文:`cgi_textarea text "" cols=72`
3353 </div>
3354 </div>
3355 ${isowner:+$cmmsg$excmsg}
3356 `cgi_radio rm close id="x"`<label for="x" accesskey="x">×</label>
3357 </div>
3358 <h4>$htmlgrp 参加者一覧</h4>$gettingcsv$fromtonote
3359 <table class="td3r td4r thl">
3360 `sq $db -header -html "$sql"`
3361 </table>
3362 `cgi_hidden grp $grid`
3363 `cgi_hidden myuid $myuid id="myuid"`
3364 EOF
3366 crview4article() { # $1=rowid of blog, $2(optional)=extra SQL
3367 # Create TEMPORARY VIEW
3368 query<<EOF
3369 CREATE TEMPORARY VIEW writeusers AS
3370 SELECT DISTINCT author FROM article
3371 WHERE id in (
3372 select id from article where blogid=(select id from blog where rowid=$1)
3373 );
3374 CREATE TEMPORARY VIEW movablegroups AS
3375 SELECT g.rowid growid , g.gname
3376 FROM (SELECT grp.rowid, grp.gname FROM grp JOIN grp_mem gm
3377 ON grp.gname=gm.gname -- そのユーザが属している
3378 AND user='$user') g -- グループに絞る
3379 WHERE (SELECT author FROM writeusers
3380 EXCEPT
3381 SELECT user FROM grp_mem gm WHERE gm.gname = g.gname)
3382 IS NULL;
3383 $2
3384 EOF
3386 sql4readableblogs() {
3387 # Create view of blogs that can be readable by $user
3388 # Blog is readable when:
3389 # 1: blog owner is an user
3390 # 2: else, 2.1: owner-group where the $user belongs
3391 # 2.2: else, owner-group is not moderated
3392 # blog(id, author), blog_s(id, key='owner', val= ->owner)
3394 # $hidden_mode is defined in global section at head
3396 cat<<EOF ## | tee tmp/sql.out
3397 CREATE TEMPORARY VIEW readableblogs AS
3398 SELECT blog.rowid rid, id, author
3399 FROM blog
3400 NATURAL JOIN
3401 (SELECT id,
3402 max(CASE key WHEN 'owner' THEN val END) owner,
3403 max(CASE key WHEN 'mode' THEN val END) mode
3404 FROM blog_s GROUP by id) bs
3405 WHERE CASE WHEN (SELECT name FROM user where name=bs.owner) IS NOT NULL
3406 THEN -- blog owner is an user, READABLE
3407 NOT mode IN $hidden_mode
3408 WHEN (SELECT val FROM grp_s
3409 WHERE gname=bs.owner AND key='regmode') = 'moderated'
3410 AND
3411 (SELECT user FROM grp_mem
3412 WHERE gname=bs.owner AND user='$user') IS NULL
3413 THEN 0
3414 WHEN mode IN $hidden_mode
3415 THEN 0 -- "quiz" mode blog cannot be searched
3416 ELSE 1
3417 END;
3418 EOF
3420 mvteamform() {
3421 owner=$1
3422 hexteams=$(hexteams "$owner" "$user")
3423 test -z "$hexteams" && return
3424 none="`echo なし|hexize`"
3425 cat<<-EOF
3426 <!-- <div class="fold">
3427 `cgi_checkbox mv2team send id="mv2team"`<label
3428 for="mv2team">この話題を以下のチームのものにする</label>
3429 <div> -->
3430 <p>この話題をチーム所有にする:
3431 チーム: `cgi_select_h mv2team $none $hexteams`</p>
3432 </form></div></div>
3433 EOF
3435 editheading() { # $1=rowid-of-heading
3436 rowid=${1%%[!A-Z0-9a-z_]*}
3437 if [ -z "$rowid" ]; then
3438 echo "話題番号が未指定です。" | html p
3439 return
3440 fi
3441 owner=`getvalbyid blog owner $rowid`
3442 title=`getvalbyid blog title $rowid`
3443 GF_ACTION="?blog" edittable $formdir/blog.def blog $rowid \
3444 | _m4 -D_TITLE_="修正" \
3445 -D_SUBTITLE_="[$title]@$owner" -D_DIARY_="" \
3446 -D_BLOGS_="" -D_DUMPTABLE_="" \
3447 -D_FORM_="syscmd(\`cat')" \
3448 $layout/html.m4.html $layout/form+dump.m4.html
3449 # Move to group
3450 if isuser "$owner"; then
3451 crview4article $rowid
3452 n=`query "SELECT count(*) FROM writeusers;"`
3453 ## err N=$n
3454 if [ $((n)) -gt 0 ]; then
3455 ## err ROWID=$rowid
3456 sql="SELECT growid || ':' || gname FROM movablegroups;"
3457 cat<<-EOF
3458 <div class="fold">
3459 `cgi_checkbox mv send id="mv"`<label
3460 for="mv">この話題をグループ所有に移動する</label>
3461 <div>
3462 <form action="?mvart" method="POST" enctype="multipart/form-data">
3463 移動先グループ:
3464 <select name="mv2grp">
3465 EOF
3466 query ".mode html"
3467 query<<-EOF |
3468 $sql
3469 .mode list
3470 EOF
3471 sed -e '/<\/TR>/d' -e 's,<TR>,,' -e 's,TD>,option>,g' \
3472 -e 's,n>\([0-9]*\):\(.*\)<,n value="\1">\2<,'
3473 cat<<-EOF
3474 </select>
3475 <p>(移動できるグループは、この「話題」に書き込んでいる人全てが
3476 そのグループに加入しているものに限られます)</p>
3477 <p>`cgi_checkbox cfm yes`<label>確認
3478 (この操作は元に戻すことができません)</label></p>
3479 `cgi_hidden blogrowid $rowid`
3480 `cgi_submit 移動`
3481 `cgi_reset Reset`
3482 </form>
3483 </div>
3484 </div>
3485 EOF
3486 fi
3487 # end of isuser "$owner"
3488 elif { hexteams=$(hexteams "$owner" ) # blog is of GROUP
3489 [ -n "$hexteams" ];}; then
3490 none="`echo なし|hexize`"
3491 cat<<-EOF
3492 <div class="fold">
3493 `cgi_checkbox mv2team send id="mv2team"`<label
3494 for="mv2team">この話題を以下のチームのものにする</label>
3495 <div><p>現在の所属チーム設定:
3496 `query "SELECT
3497 coalesce((SELECT val FROM blog_s
3498 WHERE id=(SELECT id FROM blog WHERE rowid=$rowid)
3499 AND key='team'),
3500 ':なし');"`</p>
3501 <form action="?mvart" method="POST" enctype="multipart/form-data">
3502 移動先チーム: `cgi_select_h mv2team $none $hexteams`
3503 <p>`cgi_checkbox cfm yes`<label>確認</label></p>
3504 `cgi_hidden blogrowid $rowid`<br>
3505 `cgi_submit 移動`
3506 `cgi_reset Reset`
3507 </form></div></div>
3508 EOF
3509 fi
3511 mvart() { # move diary to some group or team
3512 # or move blog of group to team which belong to the group
3513 blogrowid=`getpar blogrowid`
3514 cfm=`getpar cfm`
3515 ##### echo move blog:$blogrowid to $mv2grp | html p
3516 blogrowid=${blogrowid%%[!A-Z0-9a-z_]*} # Purify
3517 . ./s4-blog.sh
3518 if [ -z "$blogrowid" ]; then
3519 echo "無効な指定です(mvart)。" | html p
3520 return
3521 elif [ x"$cfm" != x"yes" ]; then
3522 echo "記事移動の確認にチェックがないので通常表示に戻ります。" | html p
3523 elif { mv2grp=`getpar mv2grp`
3524 mv2grp=${mv2grp%%[!A-Z0-9a-z_]*} # Purify
3525 [ -n "$mv2grp" ]; }; then
3526 crview4article $blogrowid
3527 ########## TRANSACTION BEGIN
3528 query "BEGIN;"
3529 n=`query "SELECT count(*) FROM writeusers;"`
3530 ## err Nwriteuser=$n
3531 if [ $((n)) -gt 0 ]; then
3532 query<<-EOF
3533 UPDATE blog_s SET val=(SELECT gname FROM grp WHERE rowid=$mv2grp)
3534 WHERE key='owner'
3535 AND id=(SELECT id FROM blog WHERE rowid=$blogrowid)
3536 AND $mv2grp IN (SELECT growid FROM movablegroups);
3537 EOF
3538 fi
3539 query "END;"
3540 ########## TRANSACTION END
3541 elif { mv2team=`getpar mv2team|sed "s/'/''/g"`
3542 [ -n "$mv2team" ];}; then
3543 # blog owner can move it to ANY team
3544 case "$mv2team" in
3545 'なし')
3546 cat<<-EOF
3547 DELETE FROM blog_s
3548 WHERE id=(SELECT id FROM blog WHERE rowid=$blogrowid)
3549 AND key='team';
3550 EOF
3551 ;;
3552 "") ;;
3553 *)cat<<-EOF
3554 BEGIN;
3555 REPLACE INTO blog_s(id, key, val)
3556 VALUES((SELECT id FROM blog WHERE rowid=$blogrowid),
3557 'team', '$mv2team');
3558 REPLACE INTO blog_s(id, key, val)
3559 VALUES((SELECT id FROM blog WHERE rowid=$blogrowid),
3560 'notify', 'all'); -- Change notify to all
3561 END;
3562 EOF
3563 esac | query
3564 fi
3565 blog_reply $blogrowid
3566 echo yes | html p
3568 editart() { # $1=article-rowid $2=blogrowid
3569 rowid=${1%%[!A-Z0-9a-z_]*}
3570 blogrowid=${2%%[!A-Z0-9a-z_]*}
3571 if [ -z "$rowid" -o -z "$blogrowid" ]; then
3572 echo "表示する記事番号が未指定です。" | html p
3573 return
3574 fi
3575 owner=`getvalbyid blog owner $blogrowid`
3576 title=`getvalbyid blog title $blogrowid`
3577 author=`getvalbyid article author $rowid`
3578 math=`getvalbyid blog mathjax $blogrowid`
3579 ## err EDITart: owner=$owner, author=$author
3580 if isgrpowner "$user" "$owner"; then
3581 : EDIT OK
3582 elif [ x"$owner" != x"$user" -a x"$author" != x"$user" ]; then
3583 echo "本人か所有者しか編集できません." | html p
3584 return
3585 fi
3586 aid=`query "select id from article where rowid=$rowid;"`
3587 tmpout=$tmpd/editart.$$.out
3588 GF_ACTION="?replyblog+$blogrowid#$aid" \
3589 edittable $formdir/article.def article $rowid \
3590 > $tmpout
3591 printf '%s' "${math:+$mathjax}" >> $tmpout
3592 # Cannot use pipelining to m4 with genform() because of stdin stack
3593 _m4 -D_TITLE_="コメントの修正" -D_DIARY_="" \
3594 -D_FORM_="syscmd(cat $tmpout)" \
3595 -D_SUBTITLE_="`gecos $owner`の「$title」" \
3596 -D_BLOGS_= -D_DUMPTABLE_= \
3597 $layout/html.m4.html $layout/form+dump.m4.html
3599 send2mem() {
3600 rowid=`getpar grp`
3601 rowid=${rowid%%[!0-9]*} # Cleaning
3602 if [ -z "$rowid" ]; then
3603 echo "グループが未指定です。" | html p
3604 return
3605 fi
3606 message=`getpar message`
3607 if [ -z "$message" ]; then
3608 echo "文章を入れてください。" | html p
3609 return
3610 fi
3611 grp=`getgroupbyid $rowid`
3612 members=`collectemail "$grp"`
3613 myuid=`query "SELECT rowid FROM user WHERE name='$user';"`
3614 mailfrom=`email4groupbyuid "$grp" "$myuid" | sed -e 1q -e 's/[ ,].*//'`
3615 mailfrom="`gecos "$user"` <$mailfrom>"
3616 sj="グループ「$grp」宛メッセージ(from `gecos $user`)"
3617 msg=$(cat<<-EOF
3618 $urlbase?grp+$rowid
3619 グループ $grp に所属する
3620 `gecos $user` さんよりメッセージ:
3622 $message
3623 EOF
3625 # smail rcpt subj (file)
3626 for m in $members; do
3627 echo "$msg" |
3628 MAIL_FROM=$mailfrom \
3629 SENDER=$noreply \
3630 REPLYTO=$mailfrom \
3631 smail "$m" "$sj"
3632 done
3633 cat<<EOF
3634 <p>以下のユーザに送信しました。</p>
3635 <pre>
3636 `collectgecosesbyid "$rowid" | sed 's/$/ さん/'`
3637 </pre>
3638 <p><a href="?grp+$rowid">グループ $grp</a>に戻る。</p>
3639 EOF
3641 joingrpadmit() {
3642 # $1=yes/no $2=session-key
3643 if [ -z "$2" ]; then
3644 echo "bye bye" | html p; return
3645 fi
3646 t_usr=`session=$2 getpar adduser`
3647 t_grp=`session=$2 getpar group`
3648 ## err joingrpadmit: t_usr=$t_usr, t_grp=$t_grp
3649 _m4 -D_TITLE_="joingrp" $layout/html.m4.html
3650 if [ -z "$t_usr" -o -z "$t_grp" ]; then
3651 echo "無効な加入依頼です。" | html p
3652 echo "有効期限が切れたか、
3653 他の管理者がいる場合は処理済みの可能性があります。" | html p
3654 return
3655 fi
3656 if ! isgrpowner "$user" "$t_grp"; then
3657 echo "グループ管理者のみの機能です。" | html p; return
3658 fi
3659 case $1 in
3660 yes) joingrp "$t_grp" "$t_usr" yes ;;
3661 no) joingrp "$t_grp" "$t_usr" no ;;
3662 *)
3663 echo "無効な指定です($1)。" | html p
3664 return ;;
3665 esac
3666 gid=$(query "select rowid from grp where gname=`sqlquote \"$t_grp\"`;")
3667 rcpts="`getgroupadminmails "$t_grp"` $user"
3668 ## err admit: msgdir=$msgdir, rcpts="["$rcpts"]"
3669 body="に
3670 $t_usr
3671 `[ x$1 = xyes ] && echo 'を追加' || echo 'の解除操作を'`
3672 しました。"
3673 echo "$t_grp$nl$body$nl$nl$url?grp+$gid" | smail "$rcpts" "joingrp $1"
3674 query "delete from session where id='$2';"
3675 echo "グループ <a href=\"?grp+$gid\">$t_grp</a>$nl$body" | html p
3678 joingrprequest() {
3679 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
3680 jss="joingrp-`date +%s`-`genrandom 12`"
3681 gecos=`gecos "$user"`
3682 addsession $jss +${memoplimitdays}days
3683 grpadmins=`getgroupadmins "$1"`
3684 query "replace into par values('$jss', 'group', 'string', `sqlquote \"$1\"`),
3685 ('$jss', 'adduser', 'string', `sqlquote \"$user\"`);"
3686 smail "$(collectemail $grpadmins)" "Join request to $1"<<EOF
3687 $url
3688 $user ${gecos:+($gecos)}さんから
3689 グループ $1
3690 に加入依頼がありました。
3692 承認する:
3693 $urlbase?joingrpadmit+yes+$jss
3695 白紙に戻す:
3696 $urlbase?joingrpadmit+no+$jss
3697 EOF
3698 echo "管理者に加入依頼を出しました。
3699 ${memoplimitdays}日以内に加入承認操作がされれば加入できますが、
3700 グループ運用方針に懸かることですので直接の問い合わせが重要です。" | html p
3702 joingrp() {
3703 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
3704 ## err joingrp: \$1=$1 \$2=$2 \$3=$3 \$4=$4
3705 if isgrpowner "$user" "$1"; then
3706 isowner="yes"
3707 elif [ -n "$5" ]; then
3708 isowner="yes"
3709 else
3710 isowner=""
3711 fi
3712 ## err jg:isgrpowner: isowner="$isowner"
3713 if [ -n "$isowner" ]; then
3714 : # GROUP OWNER CAN DO EVERYTHING ABOUT REGISTRATION/RETIREMENT
3715 elif [ x"$2" != x"$user" ]; then # if user is not login user
3716 echo "本人か、グループ管理者しか加入操作はできません。" | html p
3717 return
3718 elif [ x"$3" = x"no" ]; then
3719 : # Do not pursue those who leave
3720 elif [ x"$3" = x"yes" ] && ismember "$user" "$grp"; then
3721 : # Member can change own email address for the joining moderated group
3722 else # adding user is $user itself
3723 case `getgroupattr "$1" regmode` in
3724 moderated)
3725 joingrprequest "$@" # Request only
3726 return
3727 ;;
3728 *)
3729 ;;
3730 esac
3731 fi
3732 qgname=`sqlquote "$1"`
3733 grid=`query "SELECT rowid FROM grp WHERE gname=$qgname;"`
3734 cond="where gname=$qgname and user='$2'"
3735 if [ x"$3" = x"yes" ]; then
3736 query "replace into grp_mem values($qgname, '$2');"
3737 # Notify joingrp to admin
3738 action="に加入しました。"
3739 if [ -n "$4" ]; then
3740 if msg=`emaildomaincheck "$4"`; then
3741 query "replace into grp_mem_s values($qgname, '$user', 'email', \
3742 'string', '$4', NULL);"
3743 else
3744 echo $msg
3745 fi
3746 else
3747 query "delete from grp_mem_s $cond and key='email';"
3748 fi
3749 if [ -n "$5" ]; then # as ADMIN
3750 # Coming here means newly created group
3751 sql="select case\
3752 when (select count(*) from grp_mem where gname=$qgname)=1\
3753 then (select user from grp_mem\
3754 where gname=$qgname and user='$user')\
3755 else '' end; "
3756 err NewGrpChk: $sql
3757 if [ -n "`query \"$sql\"`" ]; then
3758 ## err ADMIN: "replace into grp_adm values($qgname, '$user');"
3759 query "replace into grp_adm values($qgname, '$user');"
3760 fi
3761 fi
3762 else
3763 query "begin;
3764 delete from grp_mem $cond;
3765 delete from grp_mem_s $cond;
3766 delete from grp_mem_m $cond;
3767 delete from grp_adm $cond;
3768 delete from grp_adm_s $cond;
3769 delete from grp_adm_m $cond;
3770 end;"
3771 action="から脱退しました。"
3772 fi
3773 smail_queue "$(collectemail `getgroupadmins $1`)" "Member change of $1"<<-EOF
3774 $url?grp+$grid
3775 $user (`gecos $user`)さんが
3776 グループ $1
3777 $action
3778 EOF
3780 grp_add_team() (
3781 # $1=grp-rowid $2=team $3...=user-rowid(s)
3782 grp=`getgroupbyid $1`
3783 team=$2; shift; shift
3784 [ -z "$grid" -o -z "$team" -o -z "$1" ] && return
3785 { echo "BEGIN;"
3786 for user; do
3787 echo "REPLACE INTO grp_mem_m(gname, user, key, type, val) VALUES(\
3788 '$grp',\
3789 (SELECT name FROM user WHERE rowid=$user),\
3790 'team', 'string', '$team');"
3791 done
3792 echo "END;"
3793 } | query
3795 grp_rm_team() (
3796 # $1=grp-rowid $2=team $3...=user-rowid(s)
3797 grid=$1
3798 qgrp=$(sqlquote "`getgroupbyid $grid`")
3799 team=$2; shift; shift
3800 [ -z "$grid" -o -z "$team" ] && return
3801 { echo "BEGIN;"
3802 for user; do
3803 echo "DELETE FROM grp_mem_m\
3804 WHERE gname=$qgrp \
3805 AND user=(SELECT name FROM user WHERE rowid=$user)\
3806 AND key='team' AND val='$team';"
3807 done
3808 cat<<-EOF
3809 DELETE FROM blog_s
3810 WHERE rowid=(
3811 SELECT rowid
3812 FROM blog_s a
3813 WHERE key='team'
3814 AND id IN (SELECT id FROM blog_s WHERE key='owner' AND val=$qgrp)
3815 AND NOT EXISTS (SELECT * FROM grp_mem_m
3816 WHERE key='team' AND val=a.val -- a.val=team
3817 AND gname = (SELECT val FROM blog_s b
3818 WHERE a.id=b.id AND key='owner')
3819 ));
3820 EOF
3822 echo "END;"
3823 } | query
3825 grp_reg_adm() {
3826 # $1=grp-rowid $2...=user-rowid
3827 grid=$1
3828 grp=`getgroupbyid "$1"`
3829 if [ -z "$grp" ]; then
3830 echo "無効なグループIDです" | html p; return
3831 fi
3832 if ! isgrpowner "$user" "$grp"; then
3833 echo "$grp グループの管理者しかこの操作はできません。" | html p; return
3834 fi
3835 shift
3836 for urid; do
3837 newadm=`query "select name from user where rowid=$urid;"`
3838 if [ -z "$newadm" ]; then
3839 echo "指定ユーザIDがおかしいようです。" | html p; return
3840 fi
3841 err GRP_reg_adm: "replace into grp_adm values(`sqlquote \"$grp\"`, '$newadm');"
3842 err ismember $newadm $grp
3843 if ismember $newadm "$grp"; then
3844 # OK, go ahead
3845 getgname="(select gname from grp where rowid=$grid)"
3846 query "replace into grp_adm values($getgname, '$newadm');"
3847 # confirm insertion
3848 sql="select * from grp_adm where gname=$getgname and user='$newadm'"
3849 if [ -n "`query \"$sql;\"`" ]; then
3850 echo "追加完了: $newadm" | html p
3851 else
3852 echo "追加失敗($1 $urid)" | html p
3853 fi
3854 fi
3855 showgroup $grid
3856 done
3858 dt_colhack() {
3859 # FROM: <TD>xxx:::yyy</TD>
3860 # TO: <TD class="xxx">yyy</TD>
3861 sed -Ee 's,<TD>([^:<"]+)'$asdelim'([^<]*)(</TD>|$),<TD class="\1">\2\3,g'
3863 dt_rowhack() {
3864 # From: <TR>
3865 # ....
3866 # <TD>rowclass=foo</TD>
3867 # </TR>
3868 # To: <TR class="foo">....<TD>foo</TD></TR>
3869 sed -e '
3870 /^<TR>/ {
3871 :loop
3872 s/\n//
3874 /<\/TR>/ {
3875 s/\n//
3876 s,^<TR>\(.*\)<TD\([^>]*\)>rowclass=\(.*\)\(</TD></TR>\),<TR class="\3">\1<TD\2>\3\4,
3879 $q
3880 b loop
3881 }'
3883 dumptable() {
3884 # $1=mode $2=Table $3=column-list-of-*_s(defaults to *) $4=conditions(if any)
3885 # textのフィールドだけ全てダンプにしたほうがいいか
3886 # $DT_VIEW sets link
3887 # 6/17の次: editリンクじゃなくてスレッドVIEWリンクでいいんちゃう?
3888 ### elink="<a href=\"$myname?edittable+$2+\\2\">EDIT</a>"
3889 VIEW=${DT_VIEW-replyblog}
3890 if [ -n "$VIEW" ]; then
3891 dvlink=" <a href=\"$myname?$VIEW+\\2\\3\">VI</a><a href=\"$myname?$VIEW+\\2#bottom\">EW</a>"
3892 fi
3893 sqlfile=$tmpd/dump.sql
3894 : > $sqlfile # ensure to be empty
3895 printf '.mode html\n.header 1\n' > $sqlfile
3896 # $DT_CHLD=ChildTable:BindColumn (eg. article:blogid)
3897 if [ -n "$DT_CHLD" ]; then
3898 _t=${DT_CHLD%:*} _i=${DT_CHLD#*:}
3899 cat<<-EOF >> $sqlfile
3900 -- presql
3901 CREATE TEMPORARY TABLE IF NOT EXISTS myacclog AS
3902 SELECT * FROM acclog WHERE user='$user' and tbl='$2';
3903 EOF
3904 # Speed up counting of new articles
3905 dt_count=${DT_COUNT:+"CREATE TEMPORARY VIEW _dtcount AS
3906 SELECT a.id $_i, $DT_COUNT, coalesce(cnt2, 0) cnt2
3907 FROM (SELECT DISTINCT id FROM _target) a
3908 LEFT JOIN
3909 (SELECT blogid,$DT_COUNT,count($DT_COUNT) cnt2
3910 FROM $_t
3911 WHERE $DT_COUNT = '$DT_COUNT_BY'
3912 GROUP BY $_i,$DT_COUNT) b
3913 ON a.id=b.$_i
3914 ;"}
3915 cat<<-EOF >> $sqlfile
3916 -- presql2
3917 DROP TABLE IF EXISTS _counts;
3918 CREATE TEMPORARY TABLE _counts AS
3919 SELECT $_i, count($_i) cnt
3920 FROM $_t GROUP BY $_i;
3921 /* Prepare NEW count table */
3922 CREATE TEMPORARY TABLE _target AS
3923 SELECT b.rowid trowid, b.id
3924 FROM "$2" b JOIN "$2_s" s
3925 ON b.id=s.id AND s.key='owner'
3926 ${DT_QOWNER:+ AND s.val=$DT_QOWNER};
3928 DROP TABLE IF EXISTS _children;
3929 CREATE TEMPORARY TABLE _children AS
3930 SELECT a.trowid trowid, $_i, a.id, s.val ctime
3931 FROM (SELECT t.trowid, t.id $_i, a.id
3932 FROM _target t LEFT JOIN "$_t" a ON t.id=a.$_i) a
3933 LEFT JOIN ${_t}_s s ON a.id=s.id AND s.key='ctime';
3935 DROP TABLE IF EXISTS _news;
3936 DROP VIEW IF EXISTS _news;
3938 -- CREATE TEMPORARY TABLE _news($_i, newcnt);
3939 -- INSERT INTO _news
3940 /* **COMPARE** the efficiency of TEMP-TABLE and VIEW !!! */
3941 CREATE TEMPORARY VIEW _news AS
3942 SELECT a.id $_i, coalesce(newcnt, 0) newcnt
3943 FROM (SELECT DISTINCT id FROM _target)
3944 a LEFT JOIN
3945 (SELECT $_i, count(ctime) newcnt
3946 FROM _children x
3947 WHERE ctime > coalesce((SELECT time from myacclog
3948 WHERE tblrowid=x.trowid),
3949 '1970-01-01')
3950 GROUP BY $_i) b
3951 ON a.id=b.$_i;
3952 $dt_count
3953 EOF
3954 cntall="(coalesce((select cnt from _counts where $_i=a.id), 0))"
3955 cntnew="(SELECT newcnt FROM _news where $_i=a.id)"
3956 cntmine=${DT_COUNT:+"(SELECT cnt2 FROM _dtcount WHERE $_i=a.id)"}
3957 cnt="$cntnew as '新着', $cntall as '総数', "
3958 cnt=$cnt${DT_COUNT:+"$cntmine as '$DT_COUNT_HEADER',"}
3959 dt_class=" td2r td3r td4r dumpblogs"
3960 fi
3961 # Construct join expression
3962 eav="" scols=""
3963 pk=`gettblpkey $2`
3964 substr=${dumpcollen:+"substr(%s, 0, $dumpcollen)"}
3965 substr=${substr:-%s}
3966 for col in ${3:-`gettbl_s_cols $2`}; do
3967 valvar=val
3968 fromtbl=b
3969 if gettblcols "$2" | grep -w "$col" >/dev/null 2>&1; then
3970 # If $col belongs to master table
3971 fromtbl=a; col=${col#a.}
3972 fi
3973 case $col in
3974 gecos) scols="$scols${scols:+, }${col#}"
3975 continue ;; # built-in column name
3976 *:*) as=${col#*:} # as can be 稼動状態:frozen=凍結中
3977 col=${col%%:*} # stage:稼動状態:frozen=凍結中 -> stage
3978 case "$as" in
3979 *:*=*) cnd=${as#*:}
3980 h=${cnd%%=*} v=${cnd#*=}
3981 h=`sqlquotestr "$h"`
3982 v=`sqlquotestr "$v"`
3983 if [ x"$fromtbl" = x"b" ]; then
3984 valvar="CASE val WHEN $h THEN $v END"
3985 else
3986 valvar="$h"
3987 fi
3988 as=${as%%:*} ;;
3989 esac
3990 ;;
3991 *) as=${col} ;;
3992 esac
3993 ss=`printf "$substr" "$valvar"`
3994 if [ x"$fromtbl" = x"b" ]; then
3995 eav=$eav${eav:+,}" \"$as$asdelim\"||coalesce(max(case key when '$col' then $ss end), '') as $as"
3996 else
3997 eav=$eav${eav:+,}" \"$as$asdelim\"||$ss as $as"
3998 fi
3999 scols="$scols${scols:+, }${fromtbl}.$as"
4000 done
4001 #case author when '$user' then a.rowid else '---' end as ID,
4002 if [ -n "$DT_SQL" ]; then
4003 echo "$DT_SQL"
4004 else
4005 cat<<-EOF
4006 SELECT a.rowid as LINK, $cnt $scols
4007 FROM $2 a LEFT JOIN
4008 (SELECT $pk,$eav,
4009 max(CASE key
4010 WHEN 'owner'
4011 THEN (SELECT gecos FROM gecoses WHERE name=val) END)
4012 as gecos
4013 FROM ${2}_s c GROUP BY $pk)
4014 b ON a.$pk=b.$pk $4;
4015 EOF
4016 fi >> $sqlfile
4017 ## err dt:SQL="`echo \"$presql$presql2$sql\"|tr -d '\n'`"
4018 sqlog<<-EOF
4019 *** SQL-file: $sqlfile ***
4020 `cat $sqlfile`
4021 EOF
4022 printf '.mode list\n.header 0\n' >> $sqlfile
4023 cat<<EOF | sed "s,\(<TR><TD>\)\([1-9][0-9]*\)\(#[0-9a-fxs]*\)*</TD>,\1$elink$dvlink</TD>," | dt_colhack | dt_rowhack
4024 <div> <!-- for folding by check button (s4-funcs.sh:dumptable()) -->
4025 <div class="dumptable">
4026 <table class="b$dt_class">
4027 `query ".read $sqlfile"`
4028 </table>
4029 </div> <!-- dumptable -->
4030 </div> <!-- for folding by check button (s4-funcs.sh:dumptable()) -->
4031 EOF
4034 clean_orphaned() {
4035 # This shoud be done by foreign_key rules, but some db lack them
4036 query<<-EOF
4037 -- Find blogs that have no parent
4038 WITH orphanedblog AS (
4039 SELECT blog.id,val FROM blog JOIN blog_s bs
4040 ON blog.id=bs.id AND key='owner'
4041 WHERE val NOT IN (SELECT gname FROM grp)
4042 AND val NOT IN (SELECT name FROM user)
4043 ) -- Remove them
4044 DELETE FROM blog WHERE id IN (SELECT id FROM orphanedblog);
4046 -- Find articles that have no parent blog
4047 WITH orphanedarticle AS (
4048 SELECT id FROM article
4049 WHERE blogid NOT IN (SELECT id FROM blog)
4050 ) -- Remove them
4051 DELETE FROM article WHERE id IN (SELECT id FROM orphanedarticle);
4052 EOF
4055 clearcachedir() (
4056 td=`getcachedir "$1"`
4057 err td=$td: ls- `ls $td`
4058 if [ -w "$td/image.1" ]; then
4059 err Removing td=$td
4060 rm -fr $td/ # Clear icon-image cache!
4061 fi
4064 par2table() (
4065 # copy current parameters of par into destination table
4066 # $1=definition-file
4067 # Using $user and $session
4068 # Return value:
4069 # 0: Stored successfully
4070 # 1: Insufficient fillings
4071 # 2: No permission to modify the record
4072 # 3: Invalid rowid
4073 # 4: SUCCESS to delete
4074 # 5: Stop deletion for lack of confirm check
4075 # 6: Password length too short
4076 # 7: Password mismatch
4077 # 8: Old password incorrect
4078 # 9: Duplicated post
4079 rowid=`getpar rowid`
4080 if [ ! -e $1 ]; then
4081 echo "テーブル定義ファイルが見付かりません" | html p
4082 exit 1
4083 fi
4084 tbl=${1%.def}
4085 tbl=${tbl##*/}
4086 if [ -n "$rowid" ]; then # Modify existing entry
4087 if [ x"$tbl" = x"user" ]; then
4088 rowowner=`query "select name from $tbl where rowid=$rowid;"`
4089 elif [ x"$tbl" = x"grp" ]; then
4090 sql="select gname from $tbl where rowid=$rowid;"
4091 ##err p2t:grp:q $sql
4092 isgrpowner "$user" "`query $sql`" && rowowner=$user
4093 elif [ x"$tbl" = x"blog" ]; then
4094 # Check if owner in blog_s
4095 blogowner=`getvalbyid blog owner "$rowid"`
4096 if isgrpowner "$user" "$blogowner"; then
4097 rowowner=$user
4098 origauthor=`getvalbyid blog author $rowid`
4099 if [ -n "$origauthor" -a x"$user" != x"$origauthor" ];
4100 then # Keep original author
4101 setpar author string "$origauthor" # if differs from $user
4102 fi # 2021-11-06 suggd.by Ruri
4103 else
4104 rowowner=`query "SELECT author FROM $tbl WHERE rowid=$rowid;"`
4105 fi
4106 else
4107 # 2016-12-05 There's no owner column in $tbl (need confirmation)
4108 rowowner=`query "SELECT author FROM $tbl WHERE rowid=$rowid;"`
4109 fi
4110 ### err rowowner=$rowowner
4111 if [ x"$user" != x"$rowowner" ]; then
4112 echo "他人のレコードはいじれないの" | html p
4113 return 2
4114 elif [ -z "$rowowner" ]; then
4115 echo "指定したレコードはないみたい" | html p
4116 return 3
4117 fi
4118 rm=`getpar rm` cfm=`getpar confirm`
4119 # Editing existent entry
4120 if [ x"$rm" = x"yes" ]; then
4121 if [ x"$rm$cfm" = x"yesyes" ]; then
4122 query "delete from $tbl where rowid=$rowid;"
4123 clearcachedir "$tbl/$rowid"
4124 if [ x"$tbl" = x"grp" -o x"$tbl" = x"blog" ]; then
4125 clean_orphaned
4126 fi
4127 return 4
4128 else
4129 echo "消去確認のチェックがないので消さなかったの..." | html p
4130 return 5
4131 fi
4132 fi
4133 fi
4135 ts=${tbl}_s tm=${tbl}_m val="" pval="" formaster=""
4136 if [ -n "$rowid" ]; then
4137 # Update of existing record
4138 for col in `gettblcols $tbl`; do
4139 val=`getparquote $col`
4140 [ -z "$val" ] && continue
4141 ## err query "update $tbl set $col=$val where rowid=$rowid"
4142 ## XX: THIS IS DIRTY hack to ensure non-foreign key in blog_s
4143 sql="update $tbl set $col=$val where rowid=$rowid;"
4144 if [ x"$tbl" = x"grp" -a x"$col" = x"gname" \
4145 -o x"tbl" = x"user" -a x"$col" = x"name" ]; then
4146 ## User name cannot be changed with interface provided with this
4147 ## script. But we offer the trigger to change owner user
4148 ## of blog_s table.
4149 #err "select quote($col) from $tbl where rowid=$rowid;"
4150 old=`query "select quote($col) from $tbl where rowid=$rowid;"`
4151 cat<<-EOF | query
4152 -- Here we cannot use BEGIN-COMMIT because groupupdate()
4153 -- should use EXCLUSIVE transaction outside of this.
4154 SAVEPOINT par2table;
4155 $sql
4156 update blog_s set val=$val
4157 where key='owner' and val=$old;
4158 RELEASE SAVEPOINT par2table;
4159 EOF
4160 ## XX: DIRTY Hack Ends here
4161 ## We should keep blog's owner as a single column which has
4162 ## foreign key constraint with primary key of grp/user.
4163 else
4164 query "$sql"
4165 fi
4166 done
4167 # Then, set up $pval for further insertion of tbl_s and tbl_m
4168 for col in `gettblpkey $tbl`; do
4169 val=`query "select $col from $tbl where rowid=$rowid;"|sed -e 's/\"/\"\"/g'`
4170 pval="$pval${pval:+, }\"$val\""
4171 done
4172 else
4173 # New entry
4174 # XXX: WORK-AROUND FOR SOME STUPID BROWSER
4175 # Avoid empty repost of article.
4176 if [ x"$tbl" = x"article" ]; then
4177 # If rowid is empty and ID exists in article-table, that is REPOST!
4178 aid=`getpar id`
4179 xaid=`query "SELECT id FROM $tbl WHERE id='$aid';"`
4180 if [ -n "$xaid" ]; then
4181 # REPOST of article
4182 html p <<-EOF
4183 書き込み直後のリロードなので上書きを回避します。
4184 最新記事は末尾の「再読み込み」ボタンから見てください。
4185 EOF
4186 err "Repost aid=$aid Browser=[$HTTP_USER_AGENT] user=$user"
4187 return 9 # STOP Duplicated posting
4188 fi
4189 fi
4190 # Generate values() for primary keys
4191 for col in `gettblpkey $tbl`; do
4192 # Genuine primary keys for _m and _s
4193 val=`getvalquote $tbl $col`
4194 [ -z "$val" ] && continue
4195 pval="$pval${pval:+, }$val"
4196 done
4197 ##err pval=$pval
4198 for col in `gettblfkey $tbl`; do
4199 # args for values() to insertion into master table
4200 val=`getvalquote $tbl $col`
4201 [ -z "$val" ] && continue
4202 formaster=$formaster"${formaster:+, }$val"
4203 done
4204 formaster="$pval${formaster:+, }$formaster"
4205 ## err formaster=$formaster
4206 if [ -z "$formaster" ]; then
4207 echo "項目を全て埋めてください" | html pre
4208 return 1
4209 fi
4210 ## err "replace into $tbl values($formaster);"
4211 query "replace into $tbl values($formaster);"
4212 ## Insertion to master table, done
4213 fi
4215 transaction=$tmpd/sqlfile.sql; touch $transaction
4216 for kt in s m; do
4217 tb2=${tbl}_$kt
4218 for col in `gettbl_${kt}_cols $tbl`; do
4219 ptype=`getpartype $col "limit 1"`
4221 # First, check update of existing entries in _m
4222 if [ $kt = m ]; then
4223 # sessID|address.1.22|string|Somewhere-x.y.z
4224 sql=""
4225 ##err dots from query "select var from par where var like '$col.%';"
4226 for v in `query "select var from par where var like '$col.%' AND sessid='$session';"`; do
4227 # v=address.1.22
4228 st_rowid=${v##*.}
4229 origcol=${v%%.*} # original column derived from
4230 ##err Updating for $v st_rowid=$st_rowid, partype=`getpartype $v`
4231 ##case `getpartype $v` in
4232 ## err CASE `gettbl_coltype $tbl/$origcol` in
4233 ## err edit flag = `getpar action.$v`
4234 case `getpar action.$v` in
4235 rm)
4236 if [ x`getpar confirm.$v` = x"yes" ]; then
4237 newsql="delete from $tb2"
4238 else
4239 echo "削除確認未チェック" | html p
4240 fi ;;
4241 edit)
4242 case `gettbl_coltype $tbl/$origcol` in
4243 image|document|binary)
4244 file=$tmpd/`getparfilename $v`
4245 if [ ! -s "$file" ]; then # Maybe stupid REPOST
4246 err "Empty REPOST by [$HTTP_USER_AGENT] user=$user"
4247 continue
4248 fi
4249 ## err type=file=$file
4250 [ -z "$file" ] && continue
4251 bn=`sqlquotestr "${file##*/}"`
4252 bin="X'"$(hexize "$file")"'"
4253 ct=`file --mime-type - < "$file" |cut -d' ' -f2`
4254 type=\"file:$ct\"
4255 newsql="update $tb2 set val=$bn, type=$type, bin=$bin"
4256 cachedir=`getcachedir "$tbl/$rowid"`
4257 err getcache tbl/rowid=$tbl/$rowid, rm -r $cachedir
4258 rm -rf $cachedir
4259 ;;
4260 *)
4261 newsql="update $tb2 set val=(select val from par where var \
4262 like '$col.%.$st_rowid' AND sessid='$session')"
4263 ;;
4264 esac
4265 ;;
4266 mv)
4267 # regularize filename and strip directory part
4268 newname=`getpar mv.$v|tr -d '":;#<>?^%$!'|tr -d "'"|tr ' ' _`
4269 newname=`basename $newname`
4270 oldname=`query "SELECT val FROM $tb2 WHERE rowid=$st_rowid;"`
4271 oldext=`expr "$oldname" : '.*\.\(.*\)'`
4272 newext=`expr "$newname" : '.*\.\(.*\)'`
4273 err "p2t(mv): oldname=$oldname $oldext -> newname($v)=$newname $newext"
4274 if [ -n "$newname" -a x"$oldext" = x"$newext" ];
4275 then
4276 newsql="UPDATE $tb2 SET val='$newname'"
4277 else
4278 html p<<-EOF
4279 $newname は取り扱えないファイル名です。
4280 空白を含まない名前にして下さい。
4281 拡張子の変更もできません。
4282 EOF
4283 continue
4284 fi
4285 ;;
4286 *) # maybe "keep", do not modify value
4287 continue
4288 ;;
4289 esac
4290 # err newsql=$newsql
4291 sql=$sql$nl"$newsql where rowid=$st_rowid;"
4292 clearcachedir "$tbl/$rowid"
4293 done
4295 if [ x"$bin" = x"NULL" ]; then
4296 ## err repl:normal sql=`echo $sql`
4297 if [ -n "$transaction" ]; then
4298 cat<<-EOF >> $transaction
4299 $sql
4300 DELETE FROM $tb2 WHERE type='string' AND val='';
4301 EOF
4302 else
4303 query "$sql
4304 delete from $tb2 where type='string' and val='';"
4305 ## err repl:normal done
4306 fi
4307 else
4308 # Binary update line is TOO LONG to pipelining
4309 sqlfile="$tmpd/sqlf.$$"
4310 if [ -n "$transaction" ]; then
4311 cat<<-EOF >> $transaction
4312 $sql
4313 EOF
4314 else
4315 cat<<-EOF > $sqlfile
4316 $sql
4317 EOF
4318 query ".read $sqlfile"
4319 fi
4320 fi
4321 # Rest of kt==m: set multiple mode
4322 nr=`getparcount $col`
4323 else
4324 nr=1 # for kt==s, number of records is 1
4325 fi
4327 i=0
4328 while [ $i -lt $nr ]; do
4329 limit="limit 1 offset $i"
4330 i=$((i+1)) # increase beforehand against continue
4331 val=`getvalquote $tbl $col "$limit"`
4332 ##XXX [ -z "$val" -o x"$val" = x'""' -o x"$val" = x"NULL" ] && continue
4333 [ -z "$val" -o x"$val" = x'""' ] && continue
4334 ## err $col=$val
4335 bin=NULL
4336 ## err partype$col=`getpartype $col "$limit"`
4337 ptype=`getpartype $col "$limit"` # partype should be obtained each time
4338 case $ptype in
4339 file) file=$tmpd/`getparfilename $col "$limit"`
4340 ## err parfile-$col=$file
4341 [ -z "$file" ] && continue
4342 bin="X'"$(hexize "$file")"'"
4343 ct=`file --mime-type - < "$file"|cut -d' ' -f2`
4344 type=\"file:$ct\" ;;
4345 "*"*) continue ;; # foreign table
4346 *) type=\"string\" ;;
4347 esac
4348 case `gettbl_coltype $tbl/$col` in
4349 [Cc][Hh][Ee][Cc][Kk][Bb][Oo][Xx]|[Tt][Ee][Xx][Tt])
4350 test x"$val" = x"NULL" && val="''"
4351 ;;
4352 password) # special care for password
4353 # name={password,pswd1,pswd2}
4354 p1=`getpar pswd1 "$limit"`
4355 if [ -z "$p1" ]; then
4356 continue # SKIP password setting, if p1 is empty
4357 else
4358 pswd=`getpar pswd "$limit"` p2=`getpar pswd2 "$limit"`
4359 ## err pswd=$pswd
4360 if pwcheck "$pswd"; then
4361 if [ x"$p1" = x"$p2" ]; then
4362 case "$p1" in
4363 ??????????*) ;;
4364 *) echo "パスワードは10字以上にしてください。" | html p
4365 return 6;;
4366 esac
4367 val="\"`echo $p1|mypwhash`\""
4368 else
4369 echo "2つの新パスワード不一致" | html p
4370 return 7
4371 fi
4372 else
4373 echo "旧パスワード違います" | html p
4374 return 8
4375 fi
4376 fi
4377 ;;
4378 esac
4379 ## err p2t: "replace into $tb2 values($pval, \"$col\", $type, $val, bin...);"
4380 #query "replace into $tb2 values($pval, \"$col\", $type, $val, $bin);"
4381 if [ x"$val" = x"NULL" -a x"$bin" = x"NULL" ]; then
4382 continue # Do not insert completely NULL record 2021-11-08
4383 fi
4384 sql="REPLACE into $tb2 values($pval, \"$col\", $type, $val, $bin);"
4385 if [ x"$bin" = x"NULL" ]; then
4386 ## err Normal-query: `echo $sql`
4387 if [ -n "$transaction" ]; then
4388 printf '%s' "$sql" >> $transaction
4389 else
4390 query "$sql"
4391 fi
4392 else
4393 sqlfile="$tmpd/query.$$"
4394 ## err sqlfile=`ls -lF $sqlfile`
4395 if [ -n "$transaction" ]; then
4396 cat<<-EOF >> $transaction
4397 $sql
4398 EOF
4399 else
4400 cat<<-EOF >> $sqlfile
4401 $sql
4402 EOF
4403 query ".read $sqlfile"
4404 fi
4405 fi
4406 ## err p2t done
4407 done
4408 done
4409 done
4410 [ -n "$transaction" -a -s "$transaction" ] && cat <<-EOF | query
4411 -- We cannot use transaction here, because groupupdate may use it.
4412 SAVEPOINT pa2table_insert;
4413 .read $transaction
4414 RELEASE SAVEPOINT pa2table_insert;
4415 EOF
4416 rc=$?
4417 [ $rc -eq 0 -a x"$tbl" = x"user" ] && flag_profupdate
4418 ## err "Table:$tbl update done "
4419 return $rc
4421 genform() {
4422 # $1 = form definition file
4423 # $2, $3 (optional)= table name and ROWID
4424 # If $GF_VIEWONLY set and nonNull, output values without form
4425 # If $GF_ARGS set, use it as content-strings in the form
4426 # If $GF_OWNER set, use it as value of name="owner"
4427 # If $GF_STAGE set, use it as value of name="stage"
4428 forms="" hiddens="" rowid=$3
4429 if [ ! -e "$1" ]; then
4430 echo "そのようなデータベースはないようです($2)。" | html p
4431 return
4432 elif [ -n "$2" ]; then
4433 rec=`query "select * from $2 where rowid='$rowid';"`
4434 if [ -z "$rec" ]; then
4435 pk=`gettblpkey $2`
4436 ###rec=`sq $db "select rowid from $2 where $pk='$rowid'"`
4437 rec=`query "select rowid from $2 where $pk='$rowid';"`
4438 rowid=$rec
4439 rec=$3
4440 fi
4441 if [ -z "$rec" ]; then
4442 echo "そんなレコードはないみたいね..." | html p
4443 return
4444 fi
4445 fi
4446 if [ -z "$GF_VIEWONLY" ]; then
4447 rm='<input id="rm" name="rm" type="checkbox"
4448 value="yes"><label for="rm">このエントリの削除</label>
4449 <span>ほんとうに消しますよ(確認)!
4450 <input name="confirm" type=checkbox value="yes">はい</span>'
4451 fi
4452 # Image Cache dir
4453 ## err genform: getcache=$2/$rowid
4454 td=`getcachedir "$2/$rowid"`
4455 while IFS=: read -r prompt name keytype type args; do
4456 [ -z "${prompt%%\#*}" ] && continue # skip comment line(#)
4457 sp="${args:+ }"
4458 form="" val=""
4459 if [ -n "$rowid" ]; then
4460 # err genform2a: Seeking for "$2.$name, type=$type"
4461 rawval=`getvalbyid $2 $name $rowid $td`
4462 val=`printf '%s\n' "$rawval"|htmlescape`
4463 ## err genform3a: getvalbyid $2 $name $rowid $td
4464 ## err genform3b: val="[$val]" type="$type"
4465 fi
4466 if [ -n "$GF_VIEWONLY" ]; then
4467 is_hidden "$2" "$name" && continue
4468 fi
4469 case "$type" in
4470 text*)
4471 cgiform=cgi_multi_$type
4472 if [ -s $td/$name.count -a -n "$val" ]; then
4473 form=`$cgiform $name $td`
4474 val=$(printf '%s\n' "$val"|
4475 while read fn; do
4476 echo "<tr><td>`cat $td/$fn|htmlescape|hreflink`
4477 </td></tr>$nl"
4478 done)
4479 val="<table>$nl$val$nl</table>"
4480 else
4481 #form="<input name=\"$name\" value=\"$val\" type=\"$type\"$sp$args>$nl"
4483 form=`cgi_$type $name "$rawval" "$args"`
4484 fi
4485 ;;
4486 [Rr][Aa][Dd][Ii][Oo])
4487 fh="<label><input type=\"radio\" name=\"$name\""
4488 form="`echo $args|sed -e \
4489 \"s,\([^ =][^=]*\)=\([^= ][^= ]*\),$fh value=\\"\2\\">\1</label>,g\"`"
4490 ;;
4491 [Cc][Hh][Ee][Cc][Kk][Bb][Oo][Xx])
4492 checked=${val:+ checked}
4493 form="<label><input type=\"checkbox\" name=\"$name\" value=\"${args#*=}\"$checked>${args%=*}</label>"
4494 ;;
4495 [Ss][Ee][Ll][Ee][Cc][Tt])
4496 fh="<select name=\"$name\">$nl"
4497 form=$(for l in $args; do
4498 echo "<option value=\"${l#*=}\">${l%=*}</option>"
4499 done)
4500 if [ -n "$val" ]; then
4501 form=`echo $form|sed -e "s,\(value=.$val.\),\\1 selected,"`
4502 fi
4503 form="$fh$form</select>"
4504 ;;
4505 [Ii][Mm][Aa][Gg][Ee]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Bb]inary)
4506 if [ -s $td/$name.count ]; then
4507 form=`cgi_multi_file $name $td "$args"`
4508 if [ -n "$val" ]; then
4509 hrfb="$myname?showattc+$2_m"
4510 val=$(echo "$rawval" \
4511 | while read fn; do
4512 data=`percenthex "$td/$fn"`
4513 #ct=`cat $td/$fn.content-type`
4514 ct=`file --mime-type - < "$td/$fn"|cut -d' ' -f2`
4515 ri=`cat "$td/$fn.rowid"`
4516 ## err fn=$fn, name=$name, ri=$ri; ls -lF "$td/" 1>&3
4517 #imgsrc="<img src=\"data:$ct,$data\">"
4518 #echo "<a href=\"$hrfb+$ri\">$imgsrc</a><br>"
4519 iconhref2 "$td/$fn" "$hrfb+$ri" ""
4520 done)
4521 fi
4522 else
4523 form="<input type=\"file\" name=\"$name\" $args>"
4524 if [ -n "$val" ]; then
4525 imgs=$(echo "$rawval"\
4526 |while read fn;do
4527 data=`percenthex "$td/$fn"`
4528 echo "<img src=\"data:image/png,$data\">$fn<br>"
4529 done)
4530 form=$form"<br>$imgs"
4531 val=$imgs # 2015-06-15
4532 else
4533 form="<input type=\"file\" name=\"$name\" $args>"
4534 fi
4535 fi
4536 ;;
4537 [Hh][Ii][Dd][Dd][Ee][Nn])
4538 if [ -n "$GF_STAGE" -a x"$name" = x"stage" ]; then
4539 args="value=\"$GF_STAGE\""
4540 fi
4541 form="<input type=\"hidden\" name=\"$name\" $args>"
4542 prompt='' # Remove prompt
4543 ;;
4544 [Aa][Uu][Tt][Hh][Oo][Rr])
4545 [ -n "$GF_VIEWONLY" ] && continue
4546 form="<input type=\"hidden\" name=\"author\" value=\"$user\">"
4547 prompt="" ;;
4548 [Oo][Ww][Nn][Ee][Rr])
4549 [ -n "$GF_VIEWONLY" ] && continue
4550 val=${GF_OWNER:-$val}
4551 val=${val:-$user}
4552 form="<input type=\"hidden\" name=\"owner\" value=\"$val\">"
4553 prompt="" ;;
4554 [Uu][Ss][Ee][Rr])
4555 # XXX: is null $user ok?
4556 #form="<input type=\"hidden\" name=\"user\" value=\"$user\">"
4557 [ -n "$GF_VIEWONLY" ] && continue
4558 form="$user"
4559 ;;
4560 [Pp]assword)
4561 [ -n "$GF_VIEWONLY" ] && continue
4562 form="`cgi_passwd`"
4563 val=""
4564 ;;
4565 [Ss][Ee][Rr][Ii][Aa][Ll]|[Ss][Tt][Aa][Mm][Pp])
4566 [ -n "$GF_VIEWONLY" ] && continue
4567 if [ -z "$rowid" ]; then
4568 val=`genserial`
4569 fi
4570 form="<input type=\"hidden\" name=\"$name\" value=\"$val\">"
4571 prompt="" ;;
4572 [Ss][Ee][Ss][Ss][Ii][Oo][Nn])
4573 prompt=""
4574 ;;
4575 parent|path|blog*)
4576 prompt=""
4577 ;;
4578 "*"*)
4579 tail=$tail"``"
4580 continue ;;
4581 esac
4582 if [ -n "$prompt" ]; then
4583 if [ -n "${GF_VIEWONLY}" ]; then
4584 form=$val
4585 else
4587 fi
4588 forms=$forms" <tr class=\"$name\"><th>$prompt</th><td>$form</td></tr>$nl"
4589 else
4590 hiddens=$hiddens$nl"$form"
4591 fi
4592 done < $1
4593 # enctype="multipart/form-data"
4594 cat<<-EOF
4595 <form action="${GF_ACTION:-$myname}" method="POST" enctype="multipart/form-data">
4596 EOF
4597 test -n "$rowid" && printf '%s\n' "$rm" # Workaround for utf8 buggy NetBSD sh
4598 cat<<EOF
4599 <table class="b $2">
4600 $forms
4601 </table>$hiddens
4602 ${GF_STAGE:+`cgi_hidden stage $GF_STAGE`}
4603 ${rowid:+<input type="hidden" name="rowid" value="$rowid">}
4604 EOF
4605 if [ -z $GF_VIEWONLY ]; then
4606 cat<<EOF
4607 <input type="submit" name="sub" value="OK">
4608 <input type="reset" name="res" value="Reset">
4609 EOF
4610 fi
4611 cat<<EOF
4612 $GF_ARGS</form>
4613 $tail
4614 EOF
4616 edittable() {
4617 # $1=form-def $2=table $3 rowid
4618 genform "$@"
4620 viewtable() {
4621 GF_VIEWONLY=1 genform "$@"
4623 showattc() {
4624 # $1=table_m $2=rowid &optional $3=RawFlag
4625 ## err \$1=$1 \$2=$2 \$3=$3
4626 if ! isfilereadable $user $1 $2; then
4627 contenttype; echo
4628 echo "このファイルは管理者のみしか見られません" | html p
4629 putfooter; exit
4630 fi
4631 idir=`umask 002; mktempd` || exit 1
4632 # tmpfiles=$tmpfiles"${tmpfiles+ }$idir"
4633 bin=$idir/$myname-$$.bin
4634 sql="select quote(bin) from $1 where rowid='$2';"
4635 ## err showattc: sql: $sql
4636 sq $db "$sql" | unhexize > $bin
4637 tv=`query "select type||'//'||val from $1 where rowid='$2';"`
4638 type=${tv%//*} fn=${tv#*//}
4639 ## err tv=$tv type=$type fn=$fn, tp2=${tv%\|*}
4640 ct=${type#file:}
4641 case $ct in # all text/* changed to text/plain
4642 text/*|application/csv|application/json)
4643 charset=`nkf -g $bin|cut -d' ' -f1`
4644 case $charset in
4645 ASCII*) charset="" ;;
4646 esac
4647 if [ -z "$3" ]; then
4648 ct="text/html${charset:+; charset=$charset}"
4649 link="?showattc+$1+$2+raw"
4650 nkf -e $bin | htmlescape | nkf --oc="$charset" \
4651 | sed 's,^,<span></span>,' \
4652 | _m4 -D_TITLE_="$fn" -D_CONTENT_TYPE_="$ct" \
4653 -D_LINK_="$link" \
4654 -D_BODY_="syscmd(\`cat')" $layout/pretty.m4.txt
4655 exit $?
4656 fi
4657 ct="text/plain${charset:+; charset=$charset}"
4658 ;;
4659 video/*)
4660 if [ -z "$3" ]; then
4661 _m4 -D_TITLE_="$fn" \
4662 -D_SRC_="?showattc+$1+$2+raw" $layout/videoplay.m4.html
4663 exit $?
4664 fi
4665 esac
4666 contenttype "$ct"
4667 echo "Content-Disposition: filename=\"$fn\""
4668 echo "Content-Length: " `cat $bin | wc -c`; echo
4669 #echo "Content-Type: " ${type#file:}; echo
4670 cat $bin
4673 # Some default stupid handler on CGI values
4675 default_storedb() {
4676 # ARG: $1=table-def-file
4677 # RET: $tbl=table-name, $col=mail-column, $cols=columns
4678 tbl=`basename $1`
4679 tbl=${tbl%.def}
4680 cols="`grep :text $1|cut -d: -f2`"
4681 col=`echo "$cols"|head -1`
4682 vcol=`getpar $col`
4683 err default0: \$1=$1 col=$col cols="[$cols]" vcol=$vcol
4684 if [ -n "$vcol" ]; then
4685 par2table $1
4686 else
4687 return 2 # No insertion occurred
4688 fi
4691 default_view() { # $1=def-file
4692 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
4693 ## DT_VIEW="edittable+$tbl" dumptable html $tbl "name memo file" \
4694 default_storedb "$@"
4695 query "select rowid from $tbl order by rowid desc;" \
4696 | while read rowid; do
4697 viewtable $1 $tbl $rowid
4698 done | _m4 -D_TITLE_="$tbl" \
4699 -D_FORM_="`genform $1`" \
4700 -D_DUMPTABLE_="syscmd(cat)" \
4701 $layout/html.m4.html $layout/form+dump.m4.html
4703 default_viewtext() { # $1=def-file
4704 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
4705 default_storedb "$@"
4706 DT_VIEW="viewtable+$tbl" dumptable html $tbl "name memo file" \
4707 | _m4 -D_TITLE_="$tbl" \
4708 -D_FORM_="`genform $1`" \
4709 -D_DUMPTABLE_="syscmd(cat)" \
4710 $layout/html.m4.html $layout/form+dump.m4.html
4712 default_smail() {
4713 default_storedb "$@"
4714 if [ $? -eq 2 ]; then
4715 _m4 -D_TITLE_="入力" \
4716 -D_FORM_="`genform $1`" \
4717 -D_DUMPTABLE_="" \
4718 $layout/html.m4.html $layout/form+dump.m4.html
4719 return
4720 fi
4721 cond=""
4722 for pk in `gettblpkey $tbl`; do
4723 pv=$(sqlquote "$(getpar $pk)")
4724 cond="$cond${cond:+ and }$pk=$pv"
4725 done
4726 sql="select rowid from $tbl where $cond;"
4727 rowid=`query "$sql"`
4728 ## err smail1 - "$sql" "-> rowid=$rowid"
4730 while IFS=: read prompt name keytype type args; do # Read from $1
4731 val=`getpar $name`
4732 if [ -n "$val" ]; then
4733 text="$text
4734 $prompt
4735 $name=$val
4736 ---------------------------------------------------------"
4737 fi
4738 case "$type" in
4739 image|document|file)
4740 fn="`getvalbyid $tbl $name $rowid $tmpd`"
4741 fns=$(echo "$fn"|while read fn; do
4742 err mv $tmpd/$fn.orig $tmpd/$fn
4743 mv $tmpd/$fn.orig $tmpd/$fn
4744 rm $tmpd/$fn.rowid # Remove cache flag
4745 ## err "`ls $tmpd/$fn`"
4746 echo $fn
4747 done)
4748 files="$files $fns"
4749 ;;
4750 esac
4751 done < $1
4752 ## err FILES=$files "`ls -lF $tmpd`"
4753 subj="from ${REMOTE_ADDR}"
4754 (echo "$url"
4755 echo "への書き込みがありました。"
4756 echo "------"
4757 echo "$text"
4758 ) | (cd $tmpd &&
4759 err LS="`ls -lF`" &&
4760 $mydir/sendmultipart.sh -t "$admin" -s "$subj" $files)
4761 _m4 -D_TITLE_="入力完了" $layout/html.m4.html
4762 echo "以下の内容で送信しました。" | html p
4763 viewtable $1 $tbl \
4764 `query "select rowid from $tbl order by rowid desc limit 1;"`
4765 echo "戻る" | html a "href=\"?\""