s4

view s4-funcs.sh @ 842:de2ef47143a0

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