s4

view s4-funcs.sh @ 502:46a8da2740d9

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