s4

view s4-funcs.sh @ 1015:40128a291cae

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