s4

view s4-funcs.sh @ 987:e71a86651d72

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