s4

view s4-funcs.sh @ 946:420ad90116e6

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