s4

view s4-funcs.sh @ 515:0b47ddf9c8de

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