s4

view s4-funcs.sh @ 950:4099e5a30e27

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