s4

view s4-funcs.sh @ 941:79c7e64884c4

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