s4

view s4-funcs.sh @ 686:0a6ab304f1c6

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