s4

view s4-funcs.sh @ 677:bdf33fbd977b

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