s4

view s4-funcs.sh @ 850:f11b80b4d005

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