s4

view s4-funcs.sh @ 661:14a7c23f3f06

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