s4

view s4-funcs.sh @ 917:070e933c7896

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