s4

view s4-funcs.sh @ 516:575e321179c3

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