s4

view s4-funcs.sh @ 509:d34b8f36501c

Progressive search does not restrict author
author HIROSE Yuuji <yuuji@gentei.org>
date Thu, 14 Mar 2019 10:28:42 +0900
parents 944739c3ac5c
children 2a70b6c7ffad
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_m
1286 ON user.name=user_m.name AND user_m.key='email')
1287 SELECT DISTINCT coalesce(g.email, u.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 # $1 = { author=<AUTHOR> | grp=<GROUP> }
1643 # $2(optional) = pre-input keywords
1644 help="(1)空白区切りの単語で本文検索
1645 (2)@YYYY-MM-DD 日付け(シェルパターン可)で日付け検索
1646 @2016-0[1-6] → 2016年1月から6月
1647 @>2016-01 @<2016-02-15 → 2016年1月から2月14日までの期間
1648 @week → 最近一週間
1649 (3)#番号 で記事ID検索
1650 (1)と(2)は組み合わせOK
1651 例: @2016-10-0[1-9] 芋煮
1652 → 2016年10月上旬でキーワード「芋煮」を含む記事検索
1653 ※クイズ板は検索対象から外されます。"
1654 auth=""
1655 placeholder="全記事からの検索"
1656 case "$1" in
1657 author=*)
1658 a=`echo "${1#author=}"|htmlescape`
1659 g=`gecos ${1#author=}`
1660 auth="<input type=\"hidden\" name=\"author\" value=\"$a\">"
1661 placeholder="このユーザの書込検索"
1662 help="★★ $g さんの書き込みから検索します$nl$help"
1663 ;;
1664 grp=*)
1665 a=`echo "${1#grp=}"|htmlescape`
1666 g=`gecos ${1#grp=}`
1667 auth="<input type=\"hidden\" name=\"owner\" value=\"$a\">"
1668 placeholder="このグループからの検索"
1669 ;;
1670 esac
1671 inikwd="$2" # no need to htmlescape
1672 cat<<-EOF
1673 <div class="right">
1674 <form action="$myname">$auth
1675 <input type="text" name="kwd" value="$inikwd" title="$help"
1676 placeholder=" $placeholder " width="10" accesskey="k">
1677 <!-- POST SENTENCE -->
1678 ${touchpanel:+<p class="help">$help</p>}
1679 <input type="hidden" name="stage" value="searchart">
1680 <!-- EOF -->
1681 </form>
1682 </div>
1683 EOF
1686 imgsrc_cache() (
1687 # $1 = directory for cache'ing
1688 # $2 = table (user_m or grp_m)
1689 # $3 = keycond (was: condition for choosingowner)
1690 # $4 = size : S = Small, M = Medium, O = Original
1691 dir="$1" tbl="$2"
1692 keycond="$3"
1693 whos="$keycond AND key='profimg' AND type LIKE 'file:image%'
1694 ORDER BY rowid DESC LIMIT 1"
1695 [ -d "$dir" ] || mkdir -p "$dir"
1696 tmpf=$tmpd/imgsrc_cache.$$
1697 case "$4" in
1698 [Ss]) size=S ;;
1699 [Oo]) size=O ;;
1700 *) size=M ;;
1701 esac
1702 # ImageCache filename storing schema:
1703 # <table_s>.{key, val}={"profimgcache_S", "$cacheimg_S"}
1704 sql0="SELECT val || '//' || type FROM $tbl WHERE $whos;"
1705 sql1="SELECT hex(bin) FROM $tbl WHERE $whos;"
1706 valtype=`query "$sql0"`
1707 filename=${valtype%%//*}
1708 filetype=${valtype##*//file:}
1709 if [ x"$filename" = x"${filename%.*}" ]; then
1710 # If nor filename extension found, set it to image type
1711 case "$filetype" in
1712 image/*) filename=$filename.${filetype#image/} ;;
1713 esac
1714 fi
1715 cacheimg_S=$dir/S_$filename
1716 cacheimg_M=$dir/M_$filename
1717 cacheimg_O=$dir/$filename
1718 cacheimg=$dir/${size}_$filename
1719 sumfile="$dir/$filename.sum"
1720 sum=`query "$sql1" | tee $tmpf | encode` # encode() is maybe sha1
1721 if test -s "$sumfile" && [ x"`cat \"$sumfile\"`" = x"$sum" ] \
1722 && test -s "$cacheimg_S" && test -s "$cacheimg_M" ; then
1723 # if cache is fresh and has the same checksum,
1724 echo "<img src=\"$cacheimg\">"
1725 else
1726 fifo=`mktemp "$tmpf.fifo.XXXXXXX"`
1727 rm -f $fifo # Safe, because $tmpf is in mktemp dir.
1728 fifo2=$fifo.2
1729 mkfifo $fifo $fifo2
1730 fmt=${filename##*.}
1731 ## [[ NOTE ]]
1732 ## a. convert oldimage newimage
1733 ## b. convert oldimage fmt:- | convert - newimage
1734 ## b is much smaller than a
1735 cat $tmpf | unhexize \
1736 | tee $fifo \
1737 | convert -define ${fmt}:size=${iconxy_M} \
1738 -resize ${iconxy_M}'>' - ${fmt}:- \
1739 | tee $fifo2 \
1740 | convert - "$cacheimg_M" &
1741 cat $fifo | convert -define ${fmt}:size=${iconxy_S} \
1742 -resize ${iconxy_S}'>' - ${fmt}:- \
1743 | convert - "$cacheimg_S" &
1744 printf '%s' "<img src=\"data:${filetype},"
1745 hexize "$fifo2" |sed 's/\(..\)/%\1/g' # Use medium as pre-cached image
1746 echo '">'
1747 echo "$sum" > $sumfile
1748 fi
1749 ## Now preparing cache image, done.
1750 ## Store this information to DB
1751 stbl=${tbl%_m}_s # user_s or grp_s
1752 pkey=${keycond%%=*} # Primary Key name
1753 pval=${keycond#*=} # Primary Key value
1754 query <<-EOF
1755 REPLACE INTO $stbl($pkey, key, type, val)
1756 VALUES($pval, '$iconcachekey', 'string', `sqlquote "$cacheimg_S"`);
1757 EOF
1760 showhome() {
1761 # $1=userRowIdToShow
1762 err showhome \$1=$1
1763 case "$1" in
1764 *@*) uname=`getvalbypkey user name "$1"` ;;
1765 *) uname=`getvalbyid user name $1` ;;
1766 esac
1767 ## err ShowHome: uname=$uname
1768 td=`getcachedir home/"$1"`
1769 gecos=`gecos "$uname"`
1770 ## err SH:gecos=$gecos
1771 GF_VIEWONLY=1
1772 cond="gname in (select gname from grp_mem where user='$uname')"
1773 search_form_args=""
1774 if [ x"$user" = x"$uname" ]; then
1775 usermenu="<a href=\"?userconf\" accesskey=\"e\"
1776 title=\"E\">プロフィールの編集</a> /
1777 <a href=\"?blog\" accesskey=\"n\" title=\"N\">新規話題の作成</a>"
1778 # Display folders
1779 sql="select count(id) from article_m where id
1780 in (select id from article where author='$user')
1781 and type like 'file:%';"
1782 ## err nfile-sql=`echo "$sql"`
1783 nfile=`query "$sql"`
1784 # err nfile=$nfile
1785 if [ $nfile -gt 0 ]; then
1786 usermenu="$usermenu / <a href=\"?lsmyfile\" accesskey=\"l\"
1787 title=\"L\">過去の提出ファイル</a>"
1788 fi
1789 else
1790 latestlog=`query "SELECT max(time) FROM acclog WHERE user='$uname' \
1791 GROUP BY user;"`
1792 usermenu="<p>Last seen on $latestlog</p>"
1793 search_form_args="author=$uname"
1794 fi
1795 . ./s4-blog.sh
1797 tf=$tmpd/title.$$ pf=$tmpd/profile.$$ bf=$tmpd/blogs.$$ sf=$tmpd/search.$$
1798 search_form "$search_form_args" > $sf
1799 echo "$gecos さん" > $tf
1800 { echo "<div class=\"noprofimg\">"
1801 viewtable $formdir/user.def user $1
1802 echo "</div>"
1803 } > $pf
1805 sqcond="WHERE name='$uname' AND key='profimg' AND type LIKE 'file:image%'"
1806 img=`query "SELECT type FROM user_m $sqcond LIMIT 1;"`
1807 imf=$tmpd/profimg.$$; touch $imf
1808 if [ -n "$img" ]; then
1809 if true; then
1810 tbl=user_m
1811 enticond="name='$uname'"
1812 imgsrc_cache "$td/main" user_m "$enticond" M
1813 else
1814 { printf '%s' "<IMG src=\"data:${img#file:},"
1815 query "SELECT hex(bin) FROM user_m $sqcond ORDER BY rowid LIMIT 1;" \
1816 | sed 's/\(..\)/%\1/g'
1817 echo '">'
1819 fi > $imf
1820 fi
1821 nblog=`query "SELECT count(id) FROM blog_s WHERE key='owner' AND \
1822 val='$uname';"`
1823 listblog $uname > $bf
1825 hometail=$tmpd/tail.$$
1826 mkfifo $hometail
1828 #Calling listgroupbytable, originally here
1831 # Display Most Recent Entry
1832 shortval=${dumpcollen:+"substr(val, 0, $dumpcollen)"}
1833 shortval=${shortval:-val}
1835 # The m.aid in the next line is suspicious. But works fine in SQLite3...
1836 DT_SQL="SELECT b.rowid || '#' || m.aid LINK,
1837 ctime,
1838 (SELECT $shortval FROM blog_s WHERE key='title' AND id=b.id) title,
1839 (SELECT gecos FROM gecoses
1840 WHERE name=(SELECT val FROM blog_s
1841 WHERE key='owner' AND id=b.id)) owner,
1842 (SELECT $shortval val FROM article_s WHERE id=m.aid AND key='text') text
1843 FROM blog b
1844 JOIN
1845 (SELECT distinct blogid, a.id aid, max(val) ctime
1846 FROM article a, article_s s
1847 ON a.id=s.id AND a.author='$uname' AND s.key='ctime'
1848 GROUP BY blogid ORDER BY val DESC LIMIT 50
1849 ) m
1850 ON b.id=m.blogid;"
1851 # This should be as follows
1852 : <<EOF
1853 WITH arts AS(
1854 SELECT (SELECT rowid FROM blog WHERE id=a.blogid) brid,
1855 a.blogid, a.id id, s.val ctime
1856 FROM article a NATURAL JOIN article_s s
1857 WHERE s.key = 'ctime' AND a.author='$user'
1858 GROUP by s.id
1860 SELECT a0.brid,a0.blogid,a0.id,a0.ctime
1861 FROM arts a0
1862 JOIN
1863 (SELECT blogid,max(ctime) mct FROM arts a1 GROUP BY blogid) a1
1864 ON a0.blogid=a1.blogid AND a0.ctime=a1.mct
1865 ORDER BY ctime DESC LIMIT 50;
1866 EOF
1868 cat<<-EOF
1869 `cgi_radio foldtabs yes 'id="mre" accesskey="d"'`<label
1870 for="mre" title="D">最近の書き込み先</label>
1871 <div class="lcto">
1872 `DT_VIEW=replyblog dumptable html blog`
1873 </div>
1874 EOF
1875 unset DT_SQL
1876 if [ x"$user" = x"$uname" ]; then
1877 # Display NEWS
1878 # 2016-06-26
1879 if [ x"`getpar readchk``getpar read`" = x"yesyes" ]; then
1880 acclog blog $blogreadflagrowid
1881 # echo "全部既読にしました" | html p
1882 fi
1883 # 2016-02-19 Counting NEWS without using dumptable.
1884 sql=`listnewblogsql "$user"`
1885 # echo "$sql" > tmp/listnew
1886 new10=`DT_SQL="$sql" DT_VIEW=replyblog dumptable html blog`
1887 cont=`echo "$new10"|grep "^<TR>"|wc -l`
1888 cont=$((cont-1))
1889 err newcount=$cont
1890 if [ $cont -gt 0 ]; then
1891 #echo "全体の新着記事${cont}傑" | html h2
1892 cgi_radio foldtabs yes 'id="new10" accesskey="f"'
1893 echo "<label for=\"new10\" title=\"F\">新着${cont}傑</label><div>"
1894 cat<<-EOF | html form 'action="?home"'
1895 `cgi_checkbox readchk yes 'id="read"'`<label
1896 for="read">新着ふくめて全部読んだことにする</label>
1897 `cgi_submit '確定'`
1898 `cgi_hidden read yes`
1899 EOF
1900 echo "$new10 <!-- new10 -->"
1901 echo "</div>"
1902 else # If news is 0, set log cut off flag
1903 acclog blog $blogcutoffflagrowid # for speed
1904 fi
1905 else # Not My Home ($user != $uname)
1906 : # DT_SQL=
1907 fi
1908 ) > $hometail & # Is background call safe to m4??
1910 listgroupbytable $formdir/grp.def $cond |
1911 _m4 -D_BODYCLASS_=home -D_TITLE_="spaste(\`$tf')" \
1912 -D_PROFILE_="spaste(\`$pf')$usermenu" \
1913 -D_PROFIMG_="spaste(\`$imf')" \
1914 -D_BLOGS_="spaste(\`$bf')" \
1915 -D_SEARCH_="spaste(\`$sf')" \
1916 -D_NBLOG_="$nblog" \
1917 -D_GROUPS_="syscmd(\`cat')" \
1918 -D_HOMETAIL_="syscmd(\`cat $hometail')" \
1919 $layout/html.m4.html $layout/home.m4.html
1921 # Record access log
1922 [ -n "$1" ] && [ x"$1" != x"$user" ] && acclog user $1
1924 commission() { # $1=grp-rowid $2=user-rowid
1925 contenttype; echo
1926 ## err commission: "$@"
1927 gname=`getgroupbyid $1`
1928 echo "グループ $gname 管理者委任" \
1929 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1930 if [ -n "$2" ]; then
1931 grp_reg_adm "$@"
1932 else
1933 echo "無効な指定です。普通のアクセスならここに来ないはず。"|html p
1934 fi
1936 listgroupbytable() {
1937 # $1=deffile $2...=condition
1938 tagline=`grep :tag: $1`; shift
1939 and="${1:+and }" where=${1:+where }
1940 href="<a href=\"$myname?grp+"
1941 echo '<div class="listgroup">'
1942 NGsql="select distinct tag from\
1943 (select gname, max(case key when 'tag' then val end) as tag, \
1944 max(case key when 'ctime' then val end) as ctime\
1945 from grp_s group by gname order by ctime);"
1946 sql="select val from grp_s where key='tag' $and$* group by val;"
1947 ## err ListGRP: query sql="$sql"
1948 for tag in `query "$sql"`
1949 do
1950 ## err ListGrp: tag=$tag
1951 tn=${tagline%%=${tag}*}
1952 tn=${tn##*[ :]}
1953 sql="select rowid||':'||gname as 'グループ名',説明 from
1954 (select (select rowid from grp g where g.gname=grp_s.gname)
1955 as rowid,
1956 gname,
1957 max(case key when 'gecos' then val end) as '説明',
1958 max(case key when 'tag' then val end) as 'tag',
1959 max(case key when 'mtime' then val end) as mtime from grp_s
1960 $where$* group by gname having tag='$tag' order by mtime desc);"
1961 ## err PersonalGroupList= `echo $sql`
1962 echo "<h2>$tn</h2>"
1963 echo '<table class="b listgroup">'
1964 sq -header -html $db "$sql" \
1965 | sed "s,\(<TR><TD>\)\([0-9]*\):\([^<]*\)</TD>,\1$href\2\">\3</a>,"
1966 echo '</table>'
1967 done
1968 echo '</div>'
1970 iconhref() (
1971 # $1=icon-file, $2=Href $3=title $4...=anchor
1972 data=`percenthex "$1"`
1973 ct=`file --mime-type - < "$1"|cut -d' ' -f2`
1974 ## err iconhref: \$1=$1 \$2=$2 \$3="$@"
1975 href=$2; title=$3; shift 3
1976 echo "<a href=\"$href\"><img title=\"$title\" src=\"data:$ct,$data\">$@</a>"
1978 iconhref2() (
1979 # $1=icon-file, $2=Href $3=title $4...=anchor
1980 src=$1
1981 href=$2; title=$3; shift 3
1982 echo "<a href=\"$href\"><img title=\"$title\" src=\"$src\">$@</a>"
1984 listentry() (
1985 # $1=user/group $2=SearchKeyword $3=condition(if any) $4=grprowid(if in grp)
1986 # Referring variable $iamowner=$grp to attach owner-request links
1987 ## err listentry: \$1=$1 \$2=$2 \$3=$3
1988 cond='' hiddens=''
1989 offset=`getpar offset`; offset=${offset%%[!0-9]*}
1990 if [ -z "$offset" ]; then
1991 offset=`getpar start`; offset=${offset%%[!0-9]*}
1992 offset=$((offset-1))
1993 fi
1994 offset=$((offset + 0)) # change to numeric forcibly
1995 [ $offset -lt 0 ] && offset=0
1996 limit=30
1997 dir=`getcachedir "$1"`
1998 if [ x"$1" = x"user" ]; then
1999 hrb="$myname?home"
2000 deficon=person-default.png
2001 entity="ユーザ" tbl=user link=rowid nm=name # stage=mems
2002 [ -n "$4" ] && hiddens=`cgi_hidden grid $4`
2003 gcs=gecos
2004 else # if group
2005 hrb="$myname?grp"
2006 deficon=group-default.png
2007 entity="グループ" tbl=grp link=rowid nm=gname stage=grps
2008 gcs=name
2009 tagline=`grep :tag: $formdir/grp.def|cut -d: -f5-`
2010 if [ -n "$tagline" ]; then
2011 tagconv=`echo $tagline|sed 's/\([^= :]*\)=\([^= :]*\)/-D\2=\1/g'`
2012 ## err tagconv=$tagconv
2013 fi
2014 fi
2015 if [ ! -d $dir ]; then
2016 mkdir -p $dir
2017 fi
2018 if [ ! -s $dir/$deficon ]; then
2019 convert -geometry $thumbxy $imgdir/$deficon $dir/$deficon
2020 fi
2021 if [ -n "$2" ]; then
2022 cond="where nick like '%$2%' or b.name like '%$2%'"
2023 fi
2025 # XX: これ複雑すぎるかな。もっとシンプルにしたい。$3条件も。2015-07-08
2026 # grpは呼出し元の動的スコープ変数でよくないな...
2027 ##qgrp=`sqlquote $grp`
2028 getgrp="(select gname from grp where rowid=${rowid:--1})"
2029 sql="select a.rowid, a.$link,
2030 coalesce(b.$gcs, a.$nm) as nick,
2031 quote(a.$nm) as qname,
2032 (SELECT val FROM ${tbl}_s
2033 WHERE $nm=a.$nm AND key='$iconcachekey') icon,
2034 coalesce(b.gecos, a.$nm) /* If group, concat (Nusers) */
2035 || case when a.$nm in (select gname from grp)
2036 then printf('(%d名)',
2037 (select count(user) from grp_mem where gname=a.$nm))
2038 else ' <'||a.$nm||'>'
2039 end
2040 as name,
2041 b.tag,
2042 case when a.$nm in (select user from grp_adm
2043 where gname=$getgrp) then '(管理者)'
2044 when '$user' in (select user from grp_adm where gname=a.$nm)
2045 then '(ADMIN)'
2046 when '$user' in (select user from grp_mem where gname=a.$nm)
2047 then '(Member)'
2048 when '$iamowner' = '' then ''
2049 else ',not='||a.rowid end as ownerlink,
2050 CASE '$entity'
2051 WHEN 'グループ'
2052 THEN coalesce(
2053 (SELECT val FROM grp_s WHERE gname=a.$nm AND key='regmode'),
2054 'open')
2055 ||
2056 CASE WHEN '$user'
2057 IN (SELECT user FROM grp_mem WHERE gname=a.$nm)
2058 THEN ' ismember'
2059 ELSE ''
2060 END
2061 ELSE 'user'
2062 END regmode
2063 from $tbl a left join
2064 (select $nm as name,
2065 max(case key when 'gecos' then val end) as gecos,
2066 max(case key when 'tag' then val end) as tag,
2067 max(case key when 'mtime' then val end) as mtime,
2068 max(case key when 'wtime' then val end) as wtime,
2069 max(case key when 'login' then val end) as login
2070 from ${tbl}_s group by $nm)
2071 b on a.$nm=b.name $cond $3
2072 order by b.wtime desc, b.login desc,
2073 b.mtime desc, b.tag desc, a.rowid asc"
2074 # Give precedence to newer maintained groups (2016-09-24)
2075 # Note that mtime is stored only in grp_s.
2076 ## err LE:sql.1="$sql"
2077 total=`query "with x as ($sql) select count(*) from x;"`
2078 echo "${entity} 一覧" | html h2
2079 if [ $total -gt $limit ]; then
2080 echo '<div class="right">'
2081 cgi_form $stage <<EOF
2082 <label>次の語を含む${entity}で検索:
2083 `cgi_text kwd $kwd`</label>
2084 EOF
2085 echo '</div>'
2086 fi
2087 hiddens="$hiddens
2088 `cgi_hidden kwd \"$kwd\"`
2089 `cgi_hidden stage \"$stage\"`"
2090 cat<<EOF
2091 <form action="$myname" method="POST">
2092 <p>${total}件中の<input class="hidesub" type="text" name="start"
2093 value="$((offset+1))" size="3">件めから${kwd:+" - 検索語: $kwd"}$hiddens
2094 <input type="submit" value="確定"></p>
2095 </form>
2096 EOF
2097 if [ $((offset+limit)) -lt $total ]; then
2098 nextbtn=$(
2099 cat<<EOF
2100 <div class="right clear"><form action="$myname" method="POST">
2101 `cgi_submit 次の${limit}件`
2102 $hiddens
2103 `cgi_hidden offset $((offset + limit))`</form></div>
2104 EOF
2106 fi
2107 if [ $offset -gt 0 ]; then
2108 prevbtn=$(
2109 cat<<EOF
2110 <form action="$myname" method="POST">
2111 `cgi_submit 前の${limit}件`
2112 $hiddens
2113 `cgi_hidden offset $((offset - limit))`</form>
2114 EOF
2116 fi
2117 pnbtn="$nextbtn$prevbtn"
2118 echo $pnbtn
2120 ## err ListEntry: `echo "$sql"\;`
2121 # sq $db here??? 2016-11-28
2122 query "$sql limit $limit ${offset:+offset $offset};" \
2123 | while IFS='|' read id lnk name qname icon gecos tag ownerp type; do
2124 err name=$name owner=$ownerp lnk=$lnk
2125 err newlnk=$lnk regmode=$regmode
2126 icondir=$dir/$id
2127 # Pick up only last icon
2128 echo "<div class=\"iconlist xy$thumbxy $type\">
2129 <p class=\"tag _$tag\">$tag</p>" \
2130 | _m4 $tagconv
2131 if [ -n "$NOSPEEDUP" ]; then
2132 files=`getvalbyid $tbl profimg $id $icondir`
2133 if [ -n "$files" ]; then
2134 icon=`echo "$files"|tail -1`
2135 iconhref2 "$icondir/$icon" "$hrb+$lnk" "$gecos"
2136 else
2137 iconhref "$dir/$deficon" "$hrb+$lnk" "$gecos"
2138 fi
2139 elif [ -n "$icon" -a -s "$icon" ]; then
2140 iconhref2 "$icon" "$hrb+$lnk" "$gecos<br>$mt"
2141 else
2142 cond="$nm=$qname"
2143 # err imgsrc_cache "$dir/list" ${tbl}_m "$cond" S
2144 # err query "SELECT type FROM ${tbl}_m $cond LIMIT 1;"
2145 img=`query "SELECT type FROM ${tbl}_m WHERE $cond AND key='profimg' LIMIT 1;"`
2146 # err "img=[$img]"
2147 if [ -n "$img" ]; then
2148 echo "<a href=\"$hrb+$lnk\">"
2149 imgsrc_cache "$icondir" ${tbl}_m "$nm=$qname" S
2150 echo "</a>"
2151 else
2152 iconhref2 "$dir/$deficon" "$hrb+$lnk" "$gecos"
2153 fi
2154 fi
2155 echo "<br>$name${ownerp:+<br>$ownerp}"
2156 echo "</div>"
2157 done
2158 echo ${pnbtn:+"<hr>$nextbtn$prevbtn"}
2160 listmember() {
2161 listentry user "$@"
2163 listgroup() {
2164 listentry group "$@"
2166 hexteams() { # $1=gname, $2(optional)=user
2167 cond=${2:+" AND user='$2'"}
2168 query "SELECT DISTINCT hex(val) FROM grp_mem_m
2169 WHERE gname='$1' AND key='team'$cond;"
2171 showgroup() { # $1=group-rowid
2172 if [ -z "$1" ]; then
2173 grid=`getpar grid`
2174 grid=${grid%%[!0-9]*}
2175 [ -n "$grid" ] && grp=`getgroupbyid $grid`
2176 else
2177 grid=$1
2178 fi
2179 grp=`getgroupbyid $grid`
2180 qgrp=`sqlquote "$grp"`
2181 ## err showgroup2: grid=$grid grp=$grp qgrp="[$qgrp]"
2182 if isgroup "$grp"; then
2183 tf=$tmpd/title.$$
2184 sf=$tmpd/search.$$
2185 bodyclass=`query "SELECT val FROM grp_s
2186 WHERE gname=$qgrp AND key='regmode';"`
2187 if ismember "$user" "$grp"; then
2188 ismember="ismember"
2189 qgrp=`sqlquote "$grp"`
2190 bodyclass="$bodyclass${bodyclass:+ }ismember"
2191 else
2192 ismember="" # bodyclass="group"
2193 fi
2194 bodyclass="$bodyclass grouphome"
2195 echo "<div class=\"search\">`search_form grp=\"$grp\"`</div>"> $sf
2196 echo "グループ $grp" > $tf
2198 showgroupsub $formdir/grp.def "$grid" | \
2199 _m4 -D_TITLE_="syscmd(\`cat $tf')" \
2200 -D_FORM_="syscmd(\`cat')" \
2201 -D_BODYCLASS_="$bodyclass" \
2202 -D_DUMPTABLE_="" \
2203 $htmlheader $sf $layout/form+dump.m4.html
2204 # $htmlheader $layout/form+dump.m4.html
2205 # $htmlheader is defined in grp()
2206 else # if $grp is removed at par2table
2207 listgroup
2208 fi
2210 showgroupsub() {
2211 # $1=def-file $2=group-rowid
2212 # Using $ismember
2213 rowid=$2
2214 grp=`getgroupbyid $2`
2215 qgrp=`sqlquote "$grp"`
2216 td=`getcachedir grp/"$2"`
2217 #rowid=`sq $db "select rowid from grp where gname=$qgrp"`
2218 if [ -z "$rowid" ]; then
2219 #rowid=`sq $db "select rowid from grp where rowid=$grp"`
2220 #grp=`sq $db "select gname from grp where rowid=$grp"`
2221 echo "showgroupsub: invalid argument($1 $2)" | html p
2222 return
2223 fi
2224 val=`getvalbyid grp profimg $rowid $tmpd`
2225 enticond="gname=$qgrp"
2226 img=`query "SELECT type FROM grp_m WHERE $enticond LIMIT 1;"`
2227 if [ -n "$img" ]; then
2228 cat<<-EOF
2229 <p class="groupimg">
2230 `imgsrc_cache $td/main grp_m "$enticond" M`</p>
2231 EOF
2232 fi
2233 echo "<div class=\"noprofimg\">"
2234 viewtable $1 grp $rowid
2235 echo "</div>"
2236 if isgrpowner "$user" "$grp"; then
2237 echo "<p><a href=\"?groupconf+$rowid\">グループ情報の編集</a>"
2238 iamowner=$rowid
2239 colmd=" mode"
2240 fi
2241 if [ -n "$ismember" ]; then
2242 echo "${iamowner:+ / }<a href=\"?blog+$rowid\">グループの新規話題作成</a>"
2243 echo "/ <a href=\"?grpaction+$rowid\">メンバーを個別選択しての操作</a></p>"
2244 # div.fold input[type="checkbox"]:checked ~ div {display: block;}
2245 cat<<EOF
2246 <form action="?send2mem" method="POST" enctype="multipart/form-data">
2247 <div class="fold clear">
2248 `cgi_checkbox send yes id="send"`<label
2249 for="send">グループ全員にメッセージ送信</label>
2250 <div>
2251 `cgi_textarea message "" "cols=60"`
2252 `cgi_submit 送信`
2253 `cgi_reset リセット`
2254 </div>
2255 `cgi_hidden grp $rowid`
2256 </div></form>
2257 EOF
2258 fi
2259 # 加入ボタン + 加入者リスト
2260 if [ -n "$ismember" ]; then
2261 ismem='checked' state="(参加中)"
2262 else
2263 nomem='checked' state="(現在非加入)"
2264 fi
2265 # このグループでの加入アドレス
2266 eml=`query "select val from grp_mem_s where gname=$qgrp and user='$user' \
2267 and key='email';"`
2268 ##err EML: "select val from grp_mem_s where gname='$2' and user='$user' \
2269 ## and key='email';"
2270 ##err email=$eml
2271 cat <<EOF
2272 <div class="fold clear">
2273 `cgi_checkbox reg yes id="reg"`<label
2274 for="reg">自身の加入状態を操作する</label>$state
2275 <div>
2276 EOF
2277 cgi_form grp <<EOF
2278 <p>このグループに</p>
2279 <table class="b">
2280 <tr><th>メンバーとして</th><td>
2281 <label>`cgi_radio joingrp "yes" $ismem`参加</label> /
2282 <label>`cgi_radio joingrp "no" $nomem`参加しない</label></td></tr>
2283 <tr><th>参加する場合のメイルアドレス<br>
2284 <small>(メインのアドレスとは違うものにする場合に記入<br>
2285 同じでよい場合は空欄に)</small></th>
2286 <td>`cgi_text email $eml`</td></tr>
2287 </table>
2288 `cgi_hidden grp $rowid`
2289 EOF
2290 if [ x`getgroupattr $grp regmode` = x'moderated' -a -z "$ismem" ]; then
2291 echo "moderated (承認加入の)グループなので実際に参加できるのは
2292 グループ管理者が承認操作をした後になります。" | html p 'class="warn"'
2293 fi
2294 echo '</div></div>'
2295 echo '<h2>話題一覧</h2>'
2296 thelp="1ヶ月分のまとめには上部検索窓に @month と入れてください。"
2297 cat<<-EOF
2298 <form class="summary" action="$myname" title="$thelp">
2299 `cgi_hidden owner "$grp"`
2300 `cgi_hidden kwd "@week"`
2301 `cgi_hidden stage searchart`
2302 `cgi_submit "一週間のまとめ"`
2303 </form>
2304 EOF
2305 cond="where a.id in (select id from blog_s where key='owner' and val=$qgrp) order by ctime desc"
2306 colstate="state:稼動状態:frozen=rowclass=凍結"
2307 DT_CHLD=article:blogid \
2308 DT_VIEW=replyblog dumptable html blog \
2309 "ctime title heading team notify:通知$colmd $colstate" "$cond"
2311 getgname="(select gname from grp where rowid=$rowid)"
2312 c="group by a.name having a.name in (select user from grp_mem where gname=$getgname)"
2313 cm="?commission+$rowid"
2314 thumbxy=50x50 listmember "" "$c" "$rowid" \
2315 |sed -e "s|\(<br>\),not=\(.*\)|\1|" # 間違って押しやすい
2316 # team list
2317 hexteams=`hexteams "$grp"`
2318 if [ -n "$hexteams" ]; then
2319 echo "チーム一覧" | html h2
2320 echo '<div class="dumptable"><table class="b">'
2321 sq $db -html -header<<-EOF
2322 SELECT val TEAM,
2323 group_concat((SELECT gecos FROM gecoses WHERE name=user), ',')
2324 MEMBERS
2325 FROM grp_mem_m WHERE gname=$qgrp AND key='team' GROUP BY val;
2326 EOF
2327 echo '</table></div>'
2328 fi
2330 grp_getbodyclass() {
2331 # Get css class name for document.
2332 # `moderated' for moderated groups
2333 # `ismember' for groups where user belongs
2334 # $1=GroupName (w/o quote)
2335 # $user=userNameCurrentlyLogin
2336 ## err grp_getbodyclass: 1="$1"
2337 qgrp=`sqlquote "$1"`
2338 query<<-EOF
2339 SELECT coalesce(
2340 (SELECT val FROM grp_s WHERE gname=$qgrp AND key='regmode'),
2341 'open')
2342 ||
2343 CASE WHEN '$user'
2344 IN (SELECT user FROM grp_mem WHERE gname=$qgrp)
2345 THEN ' ismember'
2346 ELSE ''
2347 END;
2348 EOF
2350 grpaction() { # $1=group-rowid
2351 err GRP_ACTION:IN
2352 grid=${1:-`getpar grp`}
2353 grp=`getgroupbyid "$grid"`
2354 if [ -z "$grp" ]; then
2355 echo "無効な指定です。" | html p; return
2356 fi
2357 if ! ismember $user "$grp"; then
2358 echo "加入者のみに許可された操作です。" | html p; return
2359 fi
2360 echo "グループ $grp 個別選択操作" \
2361 | _m4 -D_TITLE_="syscmd(\`cat')" \
2362 -D_BODYCLASS_="`grp_getbodyclass \"$grp\"`" \
2363 $layout/html.m4.html
2365 isowner=""
2366 isgrpowner "$user" "$grp" && isowner="yes"
2367 usel=`getpar usel`
2368 if [ -n "$usel" ]; then
2369 uids=$(echo `echo $usel`|tr ' ' ',')
2370 ## err grpaction-1: grp=$grp, `echo $sql`
2371 text=`getpar text`
2373 rm=`getpar rm` cfm=`getpar confirm`
2374 ## err rm=$rm cfm=$cfm
2375 if [ x"$rm" = x"yes" ]; then
2376 if [ "$isowner" ]; then
2377 if [ x"$rm$cfm" = x"yesyes" ]; then
2378 # Eliminate
2379 cond="where gname=(select gname from grp where rowid=$grid) and user in (select name from user where rowid in ($uids))"
2380 for tbl in grp_mem grp_mem_s grp_mem_m; do
2381 sql="delete from $tbl $cond;"
2382 # echo "sql=$sql"
2383 query "$sql"
2384 err rmGRPuser "$sql"
2385 done
2386 num=`query "select count(*) from user where rowid in ($uids);"`
2387 #err num=$num
2388 if [ 0$num -gt 0 ]; then
2389 sql="select coalesce(b.val,a.name) from user a left join \
2390 user_s b on a.name=b.name and key='gecos' where a.rowid in ($uids);"
2391 # err `echo "$sql"`
2392 html pre<<EOF
2393 以下の${num}名のグループ $grp 登録を解除しました。
2394 `query "$sql"`
2395 EOF
2396 fi
2397 else
2398 echo "確認のチェックがないのでやめておきます。" | html p
2399 return
2400 fi
2401 else # not Group Owner
2402 echo "グループ管理者でないのでメンバー操作はできません。" | html p
2403 return
2404 fi
2405 cat<<EOF
2407 EOF
2408 elif [ x"$rm" = x"send" ]; then # if sendmsg mode
2409 if [ -z "$text" ]; then # if msg is empty
2410 echo "なにかメッセージを..." | html p
2411 return 0
2412 fi
2413 gecos=`gecos $user`
2414 mkfrom=`getpar mkfrom`
2415 if [ x"$mkfrom" = x"yes" ]; then
2416 safegc=`echo "$gecos" | tr -d '<>@'`
2417 myuid=`query "SELECT rowid FROM user WHERE name='$user';"`
2418 fromad=`email4groupbyuid "$grp" "$myuid"`
2419 mail_from="$safegc <$fromad>"
2420 else
2421 mail_from="$admin"
2422 fi
2423 MAIL_FROM=$mail_from \
2424 smail "`email4groupbyuid "$grp" $usel` $user" \
2425 "$gecos さんからのメッセージ" <<EOF
2426 $url
2427 のグループ「$grp」のメンバーである $gecos さんから、
2428 あなた宛へのメッセージです。
2429 ----------------------------------------------------------
2430 $text
2431 EOF
2432 if [ $? = 0 ]; then
2433 echo "Note: 以下のメンバーにメッセージを送信しました。" | html p
2434 sql="select coalesce(b.val, a.name) from
2435 (select name from user where rowid in ($uids)) a
2436 left join user_s b on a.name=b.name and b.key='gecos';"
2437 html pre<<EOF
2438 `query "$sql"`
2439 (送信者である $gecos さんも含まれます)
2440 EOF
2441 err SendDone: `echo $sql`
2442 fi
2443 elif [ x"$rm" = x"commission" ]; then
2444 grp_reg_adm $grid $usel
2445 elif [ x"$rm" = x"addteam" ]; then
2446 team=`getpar team|sed "s/'/''/g"` # for single quotation
2447 newteam=`echo $team|tr -d ,` # ..and strip spaces of both ends
2448 if [ x"$team" != x"$newteam" ]; then
2449 echo "チーム名に使えない文字を除去しました" | html p
2450 team=newteam
2451 fi
2452 if [ -z "$team" -o x"$team" = x"なし" ]; then
2453 cat<<-EOF | html p
2454 有効なチーム名を入力してください。
2455 カンマだけ、「なし」という名前は使えません。
2456 EOF
2457 echo "有効なチーム名を入力してください。" | html p
2458 else
2459 grp_add_team $grid "$team" $usel
2460 fi
2461 elif [ x"$rm" = x"rmteam" ]; then
2462 if [ x"yes" = x"`getpar teamconfirm`" ]; then
2463 rmteam=`getpar rmteam|sed "s/'/''/g"`
2464 if [ -n "`query \"SELECT val FROM grp_mem_m WHERE\
2465 gname='$grp' AND user='$user' AND key='team'\
2466 AND val='$rmteam';\"`" ]; then
2467 grp_rm_team $grid "$rmteam" $usel
2468 else
2469 echo "所属していないチームの除去操作はできません。"|html p
2470 fi
2471 else
2472 echo "確認チェックなしなのでチーム除去しませんでした。"|html p
2473 fi
2474 fi
2475 fi
2476 # POST count summary
2477 from=`getpar from`; to=`getpar to`
2478 from_input="<input type=\"date\" name=\"from\" placeholder=\"YYYY-MM-DD\" value=\"${from}\">"
2479 to_input="<input type=\"date\" name=\"to\" value=\"${to:-9999}\">"
2480 fromtonote="<p>POST集計: $from_input - $to_input</p><!-- $from - $to -->"
2481 # New entry
2482 sql="WITH mems AS (
2483 SELECT g.rowid, name, gecos FROM grp_mem gm LEFT JOIN gecoses g
2484 ON gm.user=g.name
2485 WHERE gname=(SELECT gname FROM grp WHERE rowid=$grid)
2486 ), target_article AS (
2487 SELECT id FROM article_s
2488 WHERE key='ctime' AND val BETWEEN '${from:-0000}' AND '${to:-9999}'
2489 ), posts AS (
2490 SELECT author, count(author) post
2491 FROM article NATURAL JOIN article_s NATURAL JOIN target_article
2492 WHERE blogid IN (SELECT id FROM blog_s
2493 WHERE key='owner'
2494 AND val=(SELECT gname FROM grp WHERE rowid=$grid))
2495 AND key='text'
2496 GROUP BY author
2497 ), teams AS (
2498 SELECT user, group_concat(val, ', ') team
2499 FROM grp_mem_m
2500 WHERE gname=(SELECT gname FROM grp WHERE rowid=$grid)
2501 AND key='team'
2502 GROUP BY user
2503 ), user_post AS (
2504 SELECT m.rowid, name, m.gecos, coalesce(post, 0) as POST
2505 FROM mems m LEFT JOIN posts
2506 ON m.name=posts.author
2507 GROUP by m.rowid
2509 SELECT
2510 CASE
2511 WHEN (SELECT user FROM grp_adm
2512 WHERE gname=(SELECT gname FROM grp WHERE rowid=$grid)
2513 AND user=up.name) IS NOT NULL
2514 then 'k'
2515 ELSE ''
2516 END || rowid || ',' || gecos NAME,
2517 post POST, team TEAM
2518 FROM user_post up LEFT JOIN teams t
2519 ON up.name=t.user
2520 ORDER BY gecos;"
2521 ## err grpaction: "`echo \"$sql\"`"
2522 tf=$tmpd/title.$$
2523 echo "グループ[<a href=\"?grp+$grid\">$grp</a>]参加メンバーに対する操作" > $tf
2524 cmmsg="`cgi_radio rm commission id=\"cmadmin\"`<label
2525 for=\"cmadmin\">グループ管理者委任</label>
2526 <div><p>このグループでの全権を付与します。信頼できる人に託してください。
2527 </p></div>"
2528 excmsg="`cgi_radio rm yes id=\"conf\"`<label
2529 for=\"conf\">グループ登録解除</label>
2530 <div>本当に消します! `cgi_checkbox confirm yes` 確認
2531 <p>この操作による通知は本人に行きません。
2532 あらかじめ通知するか、登録解除してよい状況かしっかり確認してください。</p>
2533 </div>"
2534 # Get team list to which current user belongs into $hexteams
2535 myhexteams=$(hexteams "$grp" "$user")
2536 allhexteams=$(hexteams "$grp")
2537 if [ -n "$myhexteams" ]; then
2538 rmteammsg="`cgi_radio rm rmteam 'id=\"cmrmteam\"'`<label
2539 for=\"cmrmteam\">チーム属性除去</label>
2540 <div>チーム属性:`cgi_select_h rmteam \"2d2d2d\" $myhexteams`
2541 を除去します: `cgi_checkbox teamconfirm yes` 確認
2542 <p>この操作による通知は本人に行きません。
2543 あらかじめ通知するか、登録解除してよい状況かしっかり確認してください。</p>
2544 </div><!-- end of $rmteammsg -->
2546 fi
2547 b1='<label> <input type="checkbox" name="usel" value="'
2548 ba='<label class="admin"><input type="checkbox" name="usel" value="'
2549 #b2='"> <span>' b3='</span></label>'
2550 # | sed -e "s|^\(<TR><TD>\)k\([0-9]*\),\([^<]*\)|\1$ba\2$b2\3$b3|" \
2551 # -e "s|^\(<TR><TD>\)\([0-9]*\),\([^<]*\)|\1$b1\2$b2\3$b3|" \
2552 lnk='"> <span>\3</span></label> [<a href="?home+\2">HOME</a>]'
2553 cgi_form grpaction<<EOF \
2554 | sed -e "s|^\(<TR><TD>\)k\([0-9]*\),\([^<]*\)|\1$ba\2$lnk|" \
2555 -e "s|^\(<TR><TD>\)\([0-9]*\),\([^<]*\)|\1$b1\2$lnk|" \
2556 | _m4 -D_TITLE_="spaste(\`$tf')" \
2557 -D_SUBTITLE_="チェック後操作ボタン" \
2558 -D_FORM_="syscmd(cat)" -D_DUMPTABLE_="" \
2559 $layout/form+dump.m4.html
2560 <p>下でチェックした人を対象として:</p>
2561 <div class="foldtabs">
2562 `cgi_radio rm addteam 'id="cmteam"'`<label
2563 for="cmteam">同じチーム属性を付与</label>
2564 <div>チーム名:`cgi_text team "" 'id="inteam" list="teams"'`
2565 `cgi_datalist_h teams $allhexteams`
2566 </div>
2567 ${rmteammsg}
2568 `cgi_radio rm send id="sendmsg"`<label
2569 for="sendmsg">メッセージ送信</label>
2570 <div>
2571 `cgi_checkbox mkfrom yes 'id="mkfrom" checked'`<label for="mkfrom"
2572 >差出人を自分に(チェックを外すと相手が返事できない)</label><br>
2573 `cgi_textarea text "" cols=40`
2574 </div>
2575 ${isowner:+$cmmsg$excmsg}
2576 `cgi_radio rm close id="x"`<label for="x">×</label>
2577 </div>
2578 <h4>$grp 参加者一覧</h4>$fromtonote
2579 <table class="td2r">
2580 `sq $db -header -html "$sql"`
2581 </table>
2582 `cgi_hidden grp $grid`
2583 EOF
2585 crview4article() { # $1=rowid of blog, $2(optional)=extra SQL
2586 # Create TEMPORARY VIEW
2587 query<<EOF
2588 CREATE TEMPORARY VIEW writeusers AS
2589 SELECT DISTINCT author FROM article
2590 WHERE id in (
2591 select id from article where blogid=(select id from blog where rowid=$1)
2592 );
2593 CREATE TEMPORARY VIEW movablegroups AS
2594 SELECT g.rowid growid , g.gname
2595 FROM (SELECT grp.rowid, grp.gname FROM grp JOIN grp_mem gm
2596 ON grp.gname=gm.gname -- そのユーザが属している
2597 AND user='$user') g -- グループに絞る
2598 WHERE (SELECT author FROM writeusers
2599 EXCEPT
2600 SELECT user FROM grp_mem gm WHERE gm.gname = g.gname)
2601 IS NULL;
2602 $2
2603 EOF
2605 sql4readableblogs() {
2606 # Create view of blogs that can be readable to $user
2607 # Blog is readable when:
2608 # 1: blog owner is an user
2609 # 2: else, 2.1: owner-group where the $user belongs
2610 # 2.2: else, owner-group is not moderated
2611 # blog(id, author), blog_s(id, key='owner', val= ->owner)
2612 cat<<EOF ## | tee tmp/sql.out
2613 CREATE TEMPORARY VIEW readableblogs AS
2614 SELECT blog.rowid rid, id, author
2615 FROM blog
2616 NATURAL JOIN
2617 (SELECT id,
2618 max(CASE key WHEN 'owner' THEN val END) owner,
2619 max(CASE key WHEN 'mode' THEN val END) mode
2620 FROM blog_s GROUP by id) bs
2621 WHERE CASE WHEN (SELECT name FROM user where name=bs.owner) IS NOT NULL
2622 THEN 1 -- blog owner is an user, READABLE
2623 WHEN (SELECT val FROM grp_s
2624 WHERE gname=bs.owner AND key='regmode') = 'moderated'
2625 AND
2626 (SELECT user FROM grp_mem
2627 WHERE gname=bs.owner AND user='$user') IS NULL
2628 THEN 0
2629 WHEN mode = 'quiz'
2630 THEN 0 -- "quiz" mode blog cannot be searched
2631 ELSE 1
2632 END;
2633 EOF
2635 editheading() { # $1=rowid-of-heading
2636 rowid=${1%%[!A-Z0-9a-z_]*}
2637 if [ -z "$rowid" ]; then
2638 echo "話題番号が未指定です。" | html p
2639 return
2640 fi
2641 owner=`getvalbyid blog owner $rowid`
2642 title=`getvalbyid blog title $rowid`
2643 GF_ACTION="?blog" edittable $formdir/blog.def blog $rowid \
2644 | _m4 -D_TITLE_="修正" \
2645 -D_SUBTITLE_="[$title]@$owner" -D_DIARY_="" \
2646 -D_BLOGS_="" -D_DUMPTABLE_="" \
2647 -D_FORM_="syscmd(\`cat')" \
2648 $layout/html.m4.html $layout/form+dump.m4.html
2649 # Move to group
2650 if isuser "$owner"; then
2651 crview4article $rowid
2652 n=`query "SELECT count(*) FROM writeusers;"`
2653 ## err N=$n
2654 if [ $((n)) -gt 0 ]; then
2655 ## err ROWID=$rowid
2656 sql="SELECT growid || ':' || gname FROM movablegroups;"
2657 cat<<-EOF
2658 <div class="fold">
2659 `cgi_checkbox mv send id="mv"`<label
2660 for="mv">この話題をグループ所有に移動する</label>
2661 <div>
2662 <form action="?mvart" method="POST" enctype="multipart/form-data">
2663 移動先グループ:
2664 <select name="mv2grp">
2665 EOF
2666 query ".mode html"
2667 query<<-EOF |
2668 $sql
2669 .mode list
2670 EOF
2671 sed -e '/<\/TR>/d' -e 's,<TR>,,' -e 's,TD>,option>,g' \
2672 -e 's,n>\([0-9]*\):\(.*\)<,n value="\1">\2<,'
2673 cat<<-EOF
2674 </select>
2675 <p>(移動できるグループは、この「話題」に書き込んでいる人全てが
2676 そのグループに加入しているものに限られます)</p>
2677 <p>`cgi_checkbox cfm yes`<label>確認
2678 (この操作は元に戻すことができません)</label></p>
2679 `cgi_hidden blogrowid $rowid`
2680 `cgi_submit 移動`
2681 `cgi_reset Reset`
2682 </form>
2683 </div>
2684 </div>
2685 EOF
2686 fi
2687 # end of isuser "$owner"
2688 elif { hexteams=$(hexteams "$owner" ) # blog is of GROUP
2689 [ -n "$hexteams" ];}; then
2690 none="`echo なし|hexize`"
2691 cat<<-EOF
2692 <div class="fold">
2693 `cgi_checkbox mv2team send id="mv2team"`<label
2694 for="mv2team">この話題を以下のチームのものにする</label>
2695 <div><p>現在の所属チーム設定:
2696 `query "SELECT
2697 coalesce((SELECT val FROM blog_s
2698 WHERE id=(SELECT id FROM blog WHERE rowid=$rowid)
2699 AND key='team'),
2700 ':なし');"`</p>
2701 <form action="?mvart" method="POST" enctype="multipart/form-data">
2702 移動先チーム: `cgi_select_h mv2team $none $hexteams`
2703 <p>`cgi_checkbox cfm yes`<label>確認</label></p>
2704 `cgi_hidden blogrowid $rowid`<br>
2705 `cgi_submit 移動`
2706 `cgi_reset Reset`
2707 </form></div></div>
2708 EOF
2709 fi
2711 mvart() { # move diary to some group or team
2712 # or move blog of group to team which belong to the group
2713 blogrowid=`getpar blogrowid`
2714 cfm=`getpar cfm`
2715 ##### echo move blog:$blogrowid to $mv2grp | html p
2716 blogrowid=${blogrowid%%[!A-Z0-9a-z_]*} # Purify
2717 . ./s4-blog.sh
2718 if [ -z "$blogrowid" ]; then
2719 echo "無効な指定です(mvart)。" | html p
2720 return
2721 elif [ x"$cfm" != x"yes" ]; then
2722 echo "記事移動の確認にチェックがないので通常表示に戻ります。" | html p
2723 elif { mv2grp=`getpar mv2grp`
2724 mv2grp=${mv2grp%%[!A-Z0-9a-z_]*} # Purify
2725 [ -n "$mv2grp" ]; }; then
2726 crview4article $blogrowid
2727 ########## TRANSACTION BEGIN
2728 query "BEGIN;"
2729 n=`query "SELECT count(*) FROM writeusers;"`
2730 ## err Nwriteuser=$n
2731 if [ $((n)) -gt 0 ]; then
2732 query<<-EOF
2733 UPDATE blog_s SET val=(SELECT gname FROM grp WHERE rowid=$mv2grp)
2734 WHERE key='owner'
2735 AND id=(SELECT id FROM blog WHERE rowid=$blogrowid)
2736 AND $mv2grp IN (SELECT growid FROM movablegroups);
2737 EOF
2738 fi
2739 query "END;"
2740 ########## TRANSACTION END
2741 elif { mv2team=`getpar mv2team|sed "s/'/''/g"`
2742 [ -n "$mv2team" ];}; then
2743 # blog owner can move it to ANY team
2744 case "$mv2team" in
2745 'なし')
2746 cat<<-EOF
2747 DELETE FROM blog_s
2748 WHERE id=(SELECT id FROM blog WHERE rowid=$blogrowid)
2749 AND key='team';
2750 EOF
2751 ;;
2752 "") ;;
2753 *)cat<<-EOF
2754 BEGIN;
2755 REPLACE INTO blog_s(id, key, val)
2756 VALUES((SELECT id FROM blog WHERE rowid=$blogrowid),
2757 'team', '$mv2team');
2758 REPLACE INTO blog_s(id, key, val)
2759 VALUES((SELECT id FROM blog WHERE rowid=$blogrowid),
2760 'notify', 'all'); -- Change notify to all
2761 END;
2762 EOF
2763 esac | query
2764 fi
2765 blog_reply $blogrowid
2766 echo yes | html p
2768 editart() { # $1=article-rowid $2=blogrowid
2769 rowid=${1%%[!A-Z0-9a-z_]*}
2770 blogrowid=${2%%[!A-Z0-9a-z_]*}
2771 if [ -z "$rowid" -o -z "$blogrowid" ]; then
2772 echo "表示する記事番号が未指定です。" | html p
2773 return
2774 fi
2775 owner=`getvalbyid blog owner $blogrowid`
2776 title=`getvalbyid blog title $blogrowid`
2777 author=`getvalbyid article author $rowid`
2778 ## err EDITart: owner=$owner, author=$author
2779 if isgrpowner "$user" "$owner"; then
2780 : EDIT OK
2781 elif [ x"$owner" != x"$user" -a x"$author" != x"$user" ]; then
2782 echo "本人か所有者しか編集できません." | html p
2783 return
2784 fi
2785 aid=`query "select id from article where rowid=$rowid;"`
2786 tmpout=$tmpd/editart.$$.out
2787 GF_ACTION="?replyblog+$blogrowid#$aid" \
2788 edittable $formdir/article.def article $rowid \
2789 > $tmpout
2790 rm -f /tmp/editart.out
2791 # Cannot use pipelining to m4 with genform() because of stdin stack
2792 _m4 -D_TITLE_="コメントの修正" -D_DIARY_="" \
2793 -D_FORM_="syscmd(cat $tmpout)" \
2794 -D_SUBTITLE_="`gecos $owner`の「$title」" \
2795 -D_BLOGS_= -D_DUMPTABLE_= \
2796 $layout/html.m4.html $layout/form+dump.m4.html
2798 send2mem() {
2799 rowid=`getpar grp`
2800 rowid=${rowid%%[!0-9]*} # Cleaning
2801 if [ -z "$rowid" ]; then
2802 echo "グループが未指定です。" | html p
2803 return
2804 fi
2805 message=`getpar message`
2806 if [ -z "$message" ]; then
2807 echo "文章を入れてください。" | html p
2808 return
2809 fi
2810 grp=`getgroupbyid $rowid`
2811 members=`collectemail $grp`
2812 # smail rcpt subj (file)
2813 SMAIL_TO="`echo "$grp" | nkf -jM | tr -d '\n'` readers <$admin>" \
2814 smail "$members" "グループ $grp 宛メッセージ(from `gecos $user`)" <<EOF
2815 $urlbase?grp+$rowid
2816 グループ $grp に所属する
2817 `gecos $user` さんよりメッセージ:
2819 $message
2820 EOF
2821 cat<<EOF
2822 <p>以下のユーザに送信しました。</p>
2823 <pre>
2824 `collectgecosesbyid "$rowid" | sed 's/$/ さん/'`
2825 </pre>
2826 <p><a href="?grp+$rowid">グループ $grp</a>に戻る。</p>
2827 EOF
2829 joingrpadmit() {
2830 # $1=yes/no $2=session-key
2831 if [ -z "$2" ]; then
2832 echo "bye bye" | html p; return
2833 fi
2834 t_usr=`session=$2 getpar user`
2835 t_grp=`session=$2 getpar group`
2836 ## err joingrpadmit: t_usr=$t_usr, t_grp=$t_grp
2837 _m4 -D_TITLE_="joingrp" $layout/html.m4.html
2838 if [ -z "$t_usr" -o -z "$t_grp" ]; then
2839 echo "無効な加入依頼です。" | html p
2840 echo "有効期限が切れたか、
2841 他の管理者がいる場合は処理済みの可能性があります。" | html p
2842 return
2843 fi
2844 if ! isgrpowner "$user" "$t_grp"; then
2845 echo "グループ管理者のみの機能です。" | html p; return
2846 fi
2847 case $1 in
2848 yes) joingrp "$t_grp" "$t_usr" yes ;;
2849 no) joingrp "$t_grp" "$t_usr" no ;;
2850 *)
2851 echo "無効な指定です($1)。" | html p
2852 return ;;
2853 esac
2854 gid=$(query "select rowid from grp where gname=`sqlquote \"$t_grp\"`;")
2855 rcpts="`getgroupadminmails $t_grp` $user"
2856 ## err admit: msgdir=$msgdir, rcpts="["$rcpts"]"
2857 body="グループ <a href=\"?grp+$gid\">$t_grp</a>
2858
2859 $t_usr
2860 `[ x$1 = xyes ] && echo 'を追加' || echo 'の解除操作を'`
2861 しました。"
2862 (echo "$body"; echo; echo "$url?grp+$gid") | smail "$rcpts" "joingrp $1"
2863 query "delete from session where id='$2';"
2864 echo "$body" | html p
2867 joingrprequest() {
2868 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
2869 jss="joingrp-`date +%s`-`genrandom 12`"
2870 addsession $jss +${memoplimitdays}days
2871 query "replace into par values('$jss', 'group', 'string', `sqlquote \"$1\"`),
2872 ('$jss', 'user', 'string', `sqlquote \"$user\"`);"
2873 smail "$(collectemail `getgroupadmins $1`)" "Join request to $1"<<EOF
2874 $url
2875 $user さんから
2876 グループ $1
2877 に加入依頼がありました。
2879 承認する:
2880 $urlbase?joingrpadmit+yes+$jss
2882 白紙に戻す:
2883 $urlbase?joingrpadmit+no+$jss
2884 EOF
2885 echo "管理者に加入依頼を出しました。
2886 ${memoplimitdays}日以内に加入承認操作がされれば加入できますが、
2887 グループ運用方針に懸かることですので直接の問い合わせが重要です。" | html p
2889 joingrp() {
2890 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
2891 ## err joingrp: \$1=$1 \$2=$2 \$3=$3 \$4=$4
2892 if isgrpowner "$user" "$1"; then
2893 isowner="yes"
2894 elif [ -n "$5" ]; then
2895 isowner="yes"
2896 else
2897 isowner=""
2898 fi
2899 ## err jg:isgrpowner: isowner="$isowner"
2900 if [ -n "$isowner" ]; then
2901 : # GROUP OWNER CAN DO EVERYTHING ABOUT REGISTRATION/RETIREMENT
2902 elif [ x"$2" != x"$user" ]; then # if user is not login user
2903 echo "本人か、グループ管理者しか加入操作はできません。" | html p
2904 return
2905 elif [ x"$3" = x"no" ]; then
2906 : # Do not pursue those who leave
2907 elif [ x"$3" = x"yes" ] && ismember "$user" "$grp"; then
2908 : # Member can change own email address for the joining moderated group
2909 else # adding user is $user itself
2910 case `getgroupattr $1 regmode` in
2911 moderated)
2912 joingrprequest "$@" # Request only
2913 return
2914 ;;
2915 *)
2916 ;;
2917 esac
2918 fi
2919 qgname=`sqlquote "$1"`
2920 grid=`query "SELECT rowid FROM grp WHERE gname=$qgname;"`
2921 cond="where gname=$qgname and user='$2'"
2922 if [ x"$3" = x"yes" ]; then
2923 query "replace into grp_mem values($qgname, '$2');"
2924 # Notify joingrp to admin
2925 action="に加入しました。"
2926 if [ -n "$4" ]; then
2927 if msg=`emaildomaincheck "$4"`; then
2928 query "replace into grp_mem_s values($qgname, '$user', 'email', \
2929 'string', '$4', NULL);"
2930 else
2931 echo $msg
2932 fi
2933 else
2934 query "delete from grp_mem_s $cond and key='email';"
2935 fi
2936 if [ -n "$5" ]; then # as ADMIN
2937 # Coming here means newly created group
2938 sql="select case\
2939 when (select count(*) from grp_mem where gname=$qgname)=1\
2940 then (select user from grp_mem\
2941 where gname=$qgname and user='$user')\
2942 else '' end; "
2943 err NewGrpChk: $sql
2944 if [ -n "`query \"$sql\"`" ]; then
2945 ## err ADMIN: "replace into grp_adm values($qgname, '$user');"
2946 query "replace into grp_adm values($qgname, '$user');"
2947 fi
2948 fi
2949 else
2950 query "delete from grp_mem $cond;
2951 delete from grp_mem_s $cond;
2952 delete from grp_mem_m $cond;"
2953 action="から脱退しました。"
2954 fi
2955 smail "$(collectemail `getgroupadmins $1`)" "Member change of $1"<<-EOF
2956 $url?grp+$grid
2957 $user (`gecos $user`)さんが
2958 グループ $1
2959 $action
2960 EOF
2962 grp_add_team() (
2963 # $1=grp-rowid $2=team $3...=user-rowid(s)
2964 grp=`getgroupbyid $1`
2965 team=$2; shift; shift
2966 [ -z "$grid" -o -z "$team" -o -z "$1" ] && return
2967 { echo "BEGIN;"
2968 for user; do
2969 echo "REPLACE INTO grp_mem_m(gname, user, key, type, val) VALUES(\
2970 '$grp',\
2971 (SELECT name FROM user WHERE rowid=$user),\
2972 'team', 'string', '$team');"
2973 done
2974 echo "END;"
2975 } | query
2977 grp_rm_team() (
2978 # $1=grp-rowid $2=team $3...=user-rowid(s)
2979 grid=$1
2980 qgrp=$(sqlquote "`getgroupbyid $grid`")
2981 team=$2; shift; shift
2982 [ -z "$grid" -o -z "$team" ] && return
2983 { echo "BEGIN;"
2984 for user; do
2985 echo "DELETE FROM grp_mem_m\
2986 WHERE gname=$qgrp \
2987 AND user=(SELECT name FROM user WHERE rowid=$user)\
2988 AND key='team' AND val='$team';"
2989 done
2990 cat<<-EOF
2991 DELETE FROM blog_s
2992 WHERE rowid=(
2993 SELECT rowid
2994 FROM blog_s a
2995 WHERE key='team'
2996 AND id IN (SELECT id FROM blog_s WHERE key='owner' AND val=$qgrp)
2997 AND NOT EXISTS (SELECT * FROM grp_mem_m
2998 WHERE key='team' AND val=a.val -- a.val=team
2999 AND gname = (SELECT val FROM blog_s b
3000 WHERE a.id=b.id AND key='owner')
3001 ));
3002 EOF
3004 echo "END;"
3005 } | query
3007 grp_reg_adm() {
3008 # $1=grp-rowid $2...=user-rowid
3009 grid=$1
3010 grp=`getgroupbyid "$1"`
3011 if [ -z "$grp" ]; then
3012 echo "無効なグループIDです" | html p; return
3013 fi
3014 if ! isgrpowner "$user" "$grp"; then
3015 echo "$grp グループの管理者しかこの操作はできません。" | html p; return
3016 fi
3017 shift
3018 for urid; do
3019 newadm=`query "select name from user where rowid=$urid;"`
3020 if [ -z "$newadm" ]; then
3021 echo "指定ユーザIDがおかしいようです。" | html p; return
3022 fi
3023 err GRP_reg_adm: "replace into grp_adm values(`sqlquote \"$grp\"`, '$newadm');"
3024 err ismember $newadm $grp
3025 if ismember $newadm "$grp"; then
3026 # OK, go ahead
3027 getgname="(select gname from grp where rowid=$grid)"
3028 query "replace into grp_adm values($getgname, '$newadm');"
3029 # confirm insertion
3030 sql="select * from grp_adm where gname=$getgname and user='$newadm'"
3031 if [ -n "`query \"$sql;\"`" ]; then
3032 echo "追加完了: $newadm" | html p
3033 else
3034 echo "追加失敗($1 $urid)" | html p
3035 fi
3036 fi
3037 showgroup $grid
3038 done
3040 dt_rowhack() {
3041 # From: <TR>
3042 # ....
3043 # <TD>rowclass=foo</TD>
3044 # </TR>
3045 # To: <TR class="foo">....<TD>foo</TD></TR>
3046 sed -e '
3047 /^<TR>/ {
3048 :loop
3049 s/\n//
3051 /<\/TR>/ {
3052 s/\n//
3053 s,^<TR>\(.*\)<TD>rowclass=\(.*\)\(</TD></TR>\),<TR class="\2">\1<TD>\2\3,
3056 $q
3057 b loop
3058 }'
3060 dumptable() {
3061 # $1=mode $2=Table $3=column-list-of-*_s(defaults to *) $4=conditions(if any)
3062 # textのフィールドだけ全てダンプにしたほうがいいか
3063 # $DT_VIEW sets link
3064 # 6/17の次: editリンクじゃなくてスレッドVIEWリンクでいいんちゃう?
3065 ### elink="<a href=\"$myname?edittable+$2+\\2\">EDIT</a>"
3066 VIEW=${DT_VIEW-replyblog}
3067 if [ -n "$VIEW" ]; then
3068 dvlink=" <a href=\"$myname?$VIEW+\\2\\3\">VI</a><a href=\"$myname?$VIEW+\\2#bottom\">EW</a>"
3069 fi
3070 # $DT_CHLD=ChildTable:BindColumn
3071 if [ -n "$DT_CHLD" ]; then
3072 _t=${DT_CHLD%:*} _i=${DT_CHLD#*:}
3073 cntall="(select count($_i) from $_t where $_i=a.id)"
3074 # XXX: Dirty workaround for slow subquery of acclog
3075 presql="CREATE TEMPORARY TABLE IF NOT EXISTS myacclog AS
3076 SELECT * FROM acclog WHERE user='$user' and tbl='$2';"
3077 cntnew="(select count(val) from ${_t}_s where key='ctime' \
3078 and id in (select id from $_t where $_i=a.id) \
3079 and val > coalesce((select time from myacclog where \
3080 tblrowid=a.rowid),\
3081 '1970-01-01'))"
3082 cnt="$cntnew as '新着', $cntall as '総数',"
3083 dt_class=" td2r td3r dumpblogs"
3084 fi
3085 # Construct join expression
3086 eav="" scols=""
3087 pk=`gettblpkey $2`
3088 substr=${dumpcollen:+"substr(%s, 0, $dumpcollen)"}
3089 substr=${substr:-%s}
3090 for col in ${3:-`gettbl_s_cols $2`}; do
3091 valvar=val
3092 case $col in
3093 gecos) scols="$scols${scols:+, }${col#}"
3094 continue ;; # built-in column name
3095 *:*) as=${col#*:} # as can be 稼動状態:frozen=凍結中
3096 col=${col%%:*} # stage:稼動状態:frozen=凍結中 -> stage
3097 case "$as" in
3098 *:*=*) cnd=${as#*:}
3099 h=${cnd%%=*} v=${cnd#*=}
3100 h=`sqlquotestr "$h"`
3101 v=`sqlquotestr "$v"`
3102 valvar="CASE val WHEN $h THEN $v END"
3103 as=${as%%:*} ;;
3104 esac
3105 ;;
3106 *) as=${col} ;;
3107 esac
3108 ss=`printf "$substr" "$valvar"`
3109 eav=$eav${eav:+,}" max(case key when '$col' then $ss end) as $as"
3110 scols="$scols${scols:+, }b.$as"
3111 done
3112 #case author when '$user' then a.rowid else '---' end as ID,
3113 sql=${DT_SQL:-"select \
3114 a.rowid as LINK,\
3115 $cnt\
3116 $scols from $2 a left join\
3117 (select $pk,$eav,
3118 max(case key when 'owner'
3119 then (SELECT gecos FROM gecoses WHERE name=val) END) as gecos
3120 from ${2}_s c group by $pk) b on a.$pk=b.$pk $4;"}
3121 ## err dt:SQL="`echo \"$presql$sql\"|tr -d '\n'`"
3122 cat<<EOF | sed "s,\(<TR><TD>\)\([1-9][0-9]*\)\(#[0-9a-fxs]*\)*</TD>,\1$elink$dvlink</TD>," | dt_rowhack
3123 <div> <!-- for folding by check button (s4-funcs.sh:dumptable()) -->
3124 <div class="dumptable">
3125 <table class="b$dt_class">
3126 `sq -header -cmd ".mode $1" $db "$presql$sql"`
3127 </table>
3128 </div> <!-- dumptable -->
3129 </div> <!-- for folding by check button (s4-funcs.sh:dumptable()) -->
3130 EOF
3133 par2table() (
3134 # copy current parameters of par into destination table
3135 # $1=definition-file
3136 # Using $user and $session
3137 # Return value:
3138 # 0: Stored successfully
3139 # 1: Insufficient fillings
3140 # 2: No permission to modify the record
3141 # 3: Invalid rowid
3142 # 4: SUCCESS to delete
3143 # 5: Stop deletion for lack of confirm check
3144 # 6: Password length too short
3145 # 7: Password mismatch
3146 # 8: Old password incorrect
3147 rowid=`getpar rowid`
3148 if [ ! -e $1 ]; then
3149 echo "テーブル定義ファイルが見付かりません" | html p
3150 exit 1
3151 fi
3152 tbl=${1%.def}
3153 tbl=${tbl##*/}
3154 if [ -n "$rowid" ]; then # Modify existing entry
3155 if [ x"$tbl" = x"user" ]; then
3156 rowowner=`query "select name from $tbl where rowid=$rowid;"`
3157 elif [ x"$tbl" = x"grp" ]; then
3158 sql="select gname from $tbl where rowid=$rowid;"
3159 ##err p2t:grp:q $sql
3160 isgrpowner "$user" "`query $sql`" && rowowner=$user
3161 else
3162 # 2016-12-05 There's no owner column in $tbl (need confirmation)
3163 rowowner=`query "SELECT author FROM $tbl WHERE rowid=$rowid;"`
3164 fi
3165 ### err rowowner=$rowowner
3166 if [ x"$user" != x"$rowowner" ]; then
3167 echo "他人のレコードはいじれないの" | html p
3168 return 2
3169 elif [ -z "$rowowner" ]; then
3170 echo "指定したレコードはないみたい" | html p
3171 return 3
3172 fi
3173 rm=`getpar rm` cfm=`getpar confirm`
3174 # Editing existent entry
3175 if [ x"$rm" = x"yes" ]; then
3176 if [ x"$rm$cfm" = x"yesyes" ]; then
3177 query "delete from $tbl where rowid=$rowid;"
3178 return 4
3179 else
3180 echo "消去確認のチェックがないので消さなかったの..." | html p
3181 return 5
3182 fi
3183 fi
3184 fi
3186 ts=${tbl}_s tm=${tbl}_m val="" pval="" formaster=""
3187 if [ -n "$rowid" ]; then
3188 # Update of existing record
3189 for col in `gettblcols $tbl`; do
3190 val=`getparquote $col`
3191 [ -z "$val" ] && continue
3192 ## err query "update $tbl set $col=$val where rowid=$rowid"
3193 ## XX: THIS IS DIRTY hack to ensure non-foreign key in blog_s
3194 sql="update $tbl set $col=$val where rowid=$rowid;"
3195 if [ x"$tbl" = x"grp" -a x"$col" = x"gname" \
3196 -o x"tbl" = x"user" -a x"$col" = x"name" ]; then
3197 ## User name cannot be changed with interface provided with this
3198 ## script. But we offer the trigger to change owner user
3199 ## of blog_s table.
3200 #err "select quote($col) from $tbl where rowid=$rowid;"
3201 old=`query "select quote($col) from $tbl where rowid=$rowid;"`
3202 cat<<-EOF | query
3203 -- Here we cannot use BEGIN-COMMIT because groupupdate()
3204 -- should use EXCLUSIVE transaction outside of this.
3205 SAVEPOINT par2table;
3206 $sql
3207 update blog_s set val=$val
3208 where key='owner' and val=$old;
3209 RELEASE SAVEPOINT par2table;
3210 EOF
3211 ## XX: DIRTY Hack Ends here
3212 ## We should keep blog's owner as a single column which has
3213 ## foreign key constraint with primary key of grp/user.
3214 else
3215 query "$sql"
3216 fi
3217 done
3218 # Then, set up $pval for further insertion of tbl_s and tbl_m
3219 for col in `gettblpkey $tbl`; do
3220 val=`query "select $col from $tbl where rowid=$rowid;"|sed -e 's/\"/\"\"/g'`
3221 pval="$pval${pval:+, }\"$val\""
3222 done
3223 else
3224 # New entry
3225 # Generate values() for primary keys
3226 for col in `gettblpkey $tbl`; do
3227 # Genuine primary keys for _m and _s
3228 val=`getvalquote $tbl $col`
3229 [ -z "$val" ] && continue
3230 pval="$pval${pval:+, }$val"
3231 done
3232 ##err pval=$pval
3233 for col in `gettblfkey $tbl`; do
3234 # args for values() to insertion into master table
3235 val=`getvalquote $tbl $col`
3236 [ -z "$val" ] && continue
3237 formaster=$formaster"${formaster:+, }$val"
3238 done
3239 formaster="$pval${formaster:+, }$formaster"
3240 ## err formaster=$formaster
3241 if [ -z "$formaster" ]; then
3242 echo "項目を全て埋めてください" | html pre
3243 return 1
3244 fi
3245 ## err "replace into $tbl values($formaster);"
3246 query "replace into $tbl values($formaster);"
3247 ## Insertion to master table, done
3248 fi
3250 for kt in s m; do
3251 tb2=${tbl}_$kt
3252 for col in `gettbl_${kt}_cols $tbl`; do
3253 ptype=`getpartype $col "limit 1"`
3255 # First, check update of existing entries in _m
3256 if [ $kt = m ]; then
3257 # sessID|address.1.22|string|Somewhere-x.y.z
3258 sql=""
3259 ##err dots from query "select var from par where var like '$col.%';"
3260 for v in `query "select var from par where var like '$col.%';"`; do
3261 # v=address.1.22
3262 st_rowid=${v##*.}
3263 origcol=${v%%.*} # original column derived from
3264 ##err Updating for $v st_rowid=$st_rowid, partype=`getpartype $v`
3265 ##case `getpartype $v` in
3266 ## err CASE `gettbl_coltype $tbl/$origcol` in
3267 ## err edit flag = `getpar action.$v`
3268 case `getpar action.$v` in
3269 rm)
3270 if [ x`getpar confirm.$v` = x"yes" ]; then
3271 newsql="delete from $tb2"
3272 else
3273 echo "削除確認未チェック" | html p
3274 fi ;;
3275 edit)
3276 case `gettbl_coltype $tbl/$origcol` in
3277 image|document|binary)
3278 file=$tmpd/`getparfilename $v`
3279 ## err type=file=$file
3280 [ -z "$file" ] && continue
3281 bn=`sqlquotestr "${file##*/}"`
3282 bin="X'"$(hexize "$file")"'"
3283 ct=`file --mime-type - < "$file" |cut -d' ' -f2`
3284 type=\"file:$ct\"
3285 newsql="update $tb2 set val=$bn, type=$type, bin=$bin"
3286 cachedir=`getcachedir "$tbl/$rowid"`
3287 err getcache tbl/rowid=$tbl/$rowid, rm -r $cachedir
3288 rm -rf $cachedir
3289 ;;
3290 *)
3291 newsql="update $tb2 set val=(select val from par where var \
3292 like '$col.%.$st_rowid')"
3293 ;;
3294 esac
3295 ;;
3296 *) # maybe "keep", do not modify value
3297 continue
3298 ;;
3299 esac
3300 # err newsql=$newsql
3301 sql=$sql$nl"$newsql where rowid=$st_rowid;"
3302 done
3304 if [ x"$bin" = x"NULL" ]; then
3305 ## err repl:normal sql=`echo $sql`
3306 query "$sql
3307 delete from $tb2 where type='string' and val='';"
3308 ## err repl:normal done
3309 else
3310 sqlfile="$tmpd/sqlf.$$"
3311 echo "$sql" > $sqlfile
3312 ## err repl:sqlfile=`ls -lF $sqlfile`
3313 query ".read $sqlfile"
3314 ## err repl:done
3315 fi
3316 # Rest of kt==m: set multiple mode
3317 nr=`getparcount $col`
3318 else
3319 nr=1 # for kt==s, number of records is 1
3320 fi
3322 i=0
3323 while [ $i -lt $nr ]; do
3324 limit="limit 1 offset $i"
3325 i=$((i+1)) # increase beforehand against continue
3326 val=`getvalquote $tbl $col "$limit"`
3327 [ -z "$val" -o x"$val" = x'""' -o x"$val" = x"NULL" ] && continue
3328 ## err $col=$val
3329 bin=NULL
3330 ## err partype$col=`getpartype $col "$limit"`
3331 case $ptype in
3332 file) file=$tmpd/`getparfilename $col "$limit"`
3333 ## err parfile-$col=$file
3334 [ -z "$file" ] && continue
3335 bin="X'"$(hexize "$file")"'"
3336 ct=`file --mime-type - < "$file"|cut -d' ' -f2`
3337 type=\"file:$ct\" ;;
3338 "*"*) continue ;; # foreign table
3339 *) type=\"string\" ;;
3340 esac
3341 case `gettbl_coltype $tbl/$col` in
3342 password) # special care for password
3343 # name={password,pswd1,pswd2}
3344 p1=`getpar pswd1 "$limit"`
3345 if [ -z "$p1" ]; then
3346 continue # SKIP password setting, if p1 is empty
3347 else
3348 pswd=`getpar pswd "$limit"` p2=`getpar pswd2 "$limit"`
3349 ## err pswd=$pswd
3350 if pwcheck "$pswd"; then
3351 if [ x"$p1" = x"$p2" ]; then
3352 case "$p1" in
3353 ??????????*) ;;
3354 *) echo "パスワードは10字以上にしてください。" | html p
3355 return 6;;
3356 esac
3357 val="\"`echo $p1|mypwhash`\""
3358 else
3359 echo "2つの新パスワード不一致" | html p
3360 return 7
3361 fi
3362 else
3363 echo "旧パスワード違います" | html p
3364 return 8
3365 fi
3366 fi
3367 ;;
3368 esac
3369 ## err p2t: "replace into $tb2 values($pval, \"$col\", $type, $val, bin...);"
3370 #query "replace into $tb2 values($pval, \"$col\", $type, $val, $bin);"
3371 sql="replace into $tb2 values($pval, \"$col\", $type, $val, $bin);"
3372 if [ x"$bin" = x"NULL" ]; then
3373 ## err Normal-query: `echo $sql`
3374 query "$sql"
3375 else
3376 sqlfile="$tmpd/query.$$"
3377 echo "$sql" > $sqlfile
3378 ## err sqlfile=`ls -lF $sqlfile`
3379 query ".read $sqlfile"
3380 fi
3381 ## err p2t done
3382 done
3383 done
3384 done
3385 return 0
3386 ##err donee
3388 genform() {
3389 # $1 = form definition file
3390 # $2, $3 (optional)= table name and ROWID
3391 # If $GF_VIEWONLY set and nonNull, output values without form
3392 # If $GF_ARGS set, use it as content-strings in the form
3393 # If $GF_OWNER set, use it as value of name="owner"
3394 # If $GF_STAGE set, use it as value of name="stage"
3395 forms="" hiddens="" rowid=$3
3396 if [ ! -e "$1" ]; then
3397 echo "そのようなデータベースはないようです($2)。" | html p
3398 return
3399 elif [ -n "$2" ]; then
3400 rec=`query "select * from $2 where rowid='$rowid';"`
3401 if [ -z "$rec" ]; then
3402 pk=`gettblpkey $2`
3403 ###rec=`sq $db "select rowid from $2 where $pk='$rowid'"`
3404 rec=`query "select rowid from $2 where $pk='$rowid';"`
3405 rowid=$rec
3406 rec=$3
3407 fi
3408 if [ -z "$rec" ]; then
3409 echo "そんなレコードはないみたいね..." | html p
3410 return
3411 fi
3412 fi
3413 if [ -z "$GF_VIEWONLY" ]; then
3414 rm='<input id="rm" name="rm" type="checkbox"
3415 value="yes"><label for="rm">このエントリの削除</label>
3416 <span>ほんとうに消しますよ(確認)!
3417 <input name="confirm" type=checkbox value="yes">はい</span>'
3418 fi
3419 # Image Cache dir
3420 ## err genform: getcache=$2/$rowid
3421 td=`getcachedir "$2/$rowid"`
3422 while IFS=: read prompt name keytype type args; do
3423 [ -z "${prompt%%\#*}" ] && continue # skip comment line(#)
3424 sp="${args:+ }"
3425 form="" val=""
3426 if [ -n "$rowid" ]; then
3427 # err genform2a: Seeking for "$2.$name, type=$type"
3428 rawval=`getvalbyid $2 $name $rowid $td`
3429 val=`echo "$rawval"|htmlescape`
3430 ## err genform3a: getvalbyid $2 $name $rowid $td
3431 ## err genform3b: val="[$val]" type="$type"
3432 fi
3433 if [ -n "$GF_VIEWONLY" ]; then
3434 is_hidden "$2" "$name" && continue
3435 fi
3436 case "$type" in
3437 text*)
3438 cgiform=cgi_multi_$type
3439 if [ -s $td/$name.count -a -n "$val" ]; then
3440 form=`$cgiform $name $td`
3441 val=$(echo "$val"|
3442 while read fn; do
3443 echo "<tr><td>`cat $td/$fn|htmlescape|hreflink`
3444 </td></tr>$nl"
3445 done)
3446 val="<table>$nl$val$nl</table>"
3447 else
3448 #form="<input name=\"$name\" value=\"$val\" type=\"$type\"$sp$args>$nl"
3449 form=`cgi_$type $name "$rawval" "$args"`
3450 fi
3451 ;;
3452 [Rr][Aa][Dd][Ii][Oo])
3453 fh="<label><input type=\"radio\" name=\"$name\""
3454 form="`echo $args|sed -e \
3455 \"s,\([^ =][^=]*\)=\([^= ][^= ]*\),$fh value=\\"\2\\">\1</label>,g\"`"
3456 ;;
3457 [Cc][Hh][Ee][Cc][Kk][Bb][Oo][Xx])
3458 form="<label><input type=\"checkbox\" name=\"$name\" value=\"${args#*=}\">${args%=*}</label>"
3459 ;;
3460 [Ss][Ee][Ll][Ee][Cc][Tt])
3461 fh="<select name=\"$name\">$nl"
3462 form=$(for l in $args; do
3463 echo "<option value=\"${l#*=}\">${l%=*}</option>"
3464 done)
3465 if [ -n "$val" ]; then
3466 form=`echo $form|sed -e "s,\(value=.$val.\),\\1 selected,"`
3467 fi
3468 form="$fh$form</select>"
3469 ;;
3470 [Ii][Mm][Aa][Gg][Ee]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Bb]inary)
3471 if [ -s $td/$name.count ]; then
3472 form=`cgi_multi_file $name $td "$args"`
3473 if [ -n "$val" ]; then
3474 hrfb="$myname?showattc+$2_m"
3475 val=$(echo "$rawval" \
3476 | while read fn; do
3477 data=`percenthex "$td/$fn"`
3478 #ct=`cat $td/$fn.content-type`
3479 ct=`file --mime-type - < "$td/$fn"|cut -d' ' -f2`
3480 ri=`cat "$td/$fn.rowid"`
3481 ## err fn=$fn, name=$name, ri=$ri; ls -lF "$td/" 1>&3
3482 #imgsrc="<img src=\"data:$ct,$data\">"
3483 #echo "<a href=\"$hrfb+$ri\">$imgsrc</a><br>"
3484 iconhref2 "$td/$fn" "$hrfb+$ri" ""
3485 done)
3486 fi
3487 else
3488 form="<input type=\"file\" name=\"$name\" $args>"
3489 if [ -n "$val" ]; then
3490 imgs=$(echo "$rawval"\
3491 |while read fn;do
3492 data=`percenthex "$td/$fn"`
3493 echo "<img src=\"data:image/png,$data\">$fn<br>"
3494 done)
3495 form=$form"<br>$imgs"
3496 val=$imgs # 2015-06-15
3497 else
3498 form="<input type=\"file\" name=\"$name\" $args>"
3499 fi
3500 fi
3501 ;;
3502 [Hh][Ii][Dd][Dd][Ee][Nn])
3503 if [ -n "$GF_STAGE" -a x"$name" = x"stage" ]; then
3504 args="value=\"$GF_STAGE\""
3505 fi
3506 form="<input type=\"hidden\" name=\"$name\" $args>"
3507 prompt='' # Remove prompt
3508 ;;
3509 [Aa][Uu][Tt][Hh][Oo][Rr])
3510 [ -n "$GF_VIEWONLY" ] && continue
3511 form="<input type=\"hidden\" name=\"author\" value=\"$user\">"
3512 prompt="" ;;
3513 [Oo][Ww][Nn][Ee][Rr])
3514 [ -n "$GF_VIEWONLY" ] && continue
3515 val=${GF_OWNER:-$val}
3516 val=${val:-$user}
3517 form="<input type=\"hidden\" name=\"owner\" value=\"$val\">"
3518 prompt="" ;;
3519 [Uu][Ss][Ee][Rr])
3520 # XXX: is null $user ok?
3521 #form="<input type=\"hidden\" name=\"user\" value=\"$user\">"
3522 [ -n "$GF_VIEWONLY" ] && continue
3523 form="$user"
3524 ;;
3525 [Pp]assword)
3526 [ -n "$GF_VIEWONLY" ] && continue
3527 form="`cgi_passwd`"
3528 val=""
3529 ;;
3530 [Ss][Ee][Rr][Ii][Aa][Ll]|[Ss][Tt][Aa][Mm][Pp])
3531 [ -n "$GF_VIEWONLY" ] && continue
3532 if [ -z "$rowid" ]; then
3533 val=`genserial`
3534 fi
3535 form="<input type=\"hidden\" name=\"$name\" value=\"$val\">"
3536 prompt="" ;;
3537 [Ss][Ee][Ss][Ss][Ii][Oo][Nn])
3538 prompt=""
3539 ;;
3540 parent|path|blog*)
3541 prompt=""
3542 ;;
3543 "*"*)
3544 tail=$tail"``"
3545 continue ;;
3546 esac
3547 if [ -n "$prompt" ]; then
3548 if [ -n "${GF_VIEWONLY}" ]; then
3549 form=$val
3550 else
3552 fi
3553 forms=$forms" <tr class=\"$name\"><th>$prompt</th><td>$form</td></tr>$nl"
3554 else
3555 hiddens=$hiddens$nl"$form"
3556 fi
3557 done < $1
3558 # enctype="multipart/form-data"
3559 cat<<EOF
3560 <form action="${GF_ACTION:-$myname}" method="POST" enctype="multipart/form-data">
3561 ${rowid:+$rm}
3562 <table class="b $2">
3563 $forms
3564 </table>$hiddens
3565 ${GF_STAGE:+`cgi_hidden stage $GF_STAGE`}
3566 ${rowid:+<input type="hidden" name="rowid" value="$rowid">}
3567 EOF
3568 if [ -z $GF_VIEWONLY ]; then
3569 cat<<EOF
3570 <input type="submit" name="sub" value="OK">
3571 <input type="reset" name="res" value="Reset">
3572 EOF
3573 fi
3574 cat<<EOF
3575 $GF_ARGS</form>
3576 $tail
3577 EOF
3579 edittable() {
3580 # $1=form-def $2=table $3 rowid
3581 genform "$@"
3583 viewtable() {
3584 GF_VIEWONLY=1 genform "$@"
3586 showattc() {
3587 # $1=table_m $2=rowid &optional $3=RawFlag
3588 ## err \$1=$1 \$2=$2 \$3=$3
3589 if ! isfilereadable $user $1 $2; then
3590 contenttype; echo
3591 echo "このファイルは管理者のみしか見られません" | html p
3592 putfooter; exit
3593 fi
3594 idir=`umask 002; mktempd` || exit 1
3595 # tmpfiles=$tmpfiles"${tmpfiles+ }$idir"
3596 bin=$idir/$myname-$$.bin
3597 sql="select quote(bin) from $1 where rowid='$2';"
3598 ## err showattc: sql: $sql
3599 sq $db "$sql" | unhexize > $bin
3600 tv=`query "select type||'//'||val from $1 where rowid='$2';"`
3601 type=${tv%//*} fn=${tv#*//}
3602 ## err tv=$tv type=$type fn=$fn, tp2=${tv%\|*}
3603 ct=${type#file:}
3604 case $ct in # all text/* changed to text/plain
3605 text/*)
3606 charset=`nkf -g $bin|cut -d' ' -f1`
3607 case $charset in
3608 ASCII*) charset="" ;;
3609 esac
3610 if [ -z "$3" ]; then
3611 ct="text/html${charset:+; charset=$charset}"
3612 link="?showattc+$1+$2+raw"
3613 nkf -e $bin | htmlescape | nkf --oc="$charset" \
3614 | sed 's,^,<span></span>,' \
3615 | _m4 -D_TITLE_="$fn" -D_CONTENT_TYPE_="$ct" \
3616 -D_LINK_="$link" \
3617 -D_BODY_="syscmd(\`cat')" $layout/pretty.m4.txt
3618 exit $?
3619 fi
3620 ct="text/plain${charset:+; charset=$charset}"
3621 ;;
3622 esac
3623 contenttype "$ct"
3624 echo "Content-Disposition: filename=\"$fn\""
3625 echo "Content-Length: " `cat $bin | wc -c`; echo
3626 #echo "Content-Type: " ${type#file:}; echo
3627 cat $bin
3630 # Some default stupid handler on CGI values
3632 default_storedb() {
3633 # ARG: $1=table-def-file
3634 # RET: $tbl=table-name, $col=mail-column, $cols=columns
3635 tbl=`basename $1`
3636 tbl=${tbl%.def}
3637 cols="`grep :text $1|cut -d: -f2`"
3638 col=`echo "$cols"|head -1`
3639 vcol=`getpar $col`
3640 err default0: \$1=$1 col=$col cols="[$cols]" vcol=$vcol
3641 if [ -n "$vcol" ]; then
3642 par2table $1
3643 else
3644 return 2 # No insertion occurred
3645 fi
3648 default_view() { # $1=def-file
3649 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
3650 ## DT_VIEW="edittable+$tbl" dumptable html $tbl "name memo file" \
3651 default_storedb "$@"
3652 query "select rowid from $tbl order by rowid desc;" \
3653 | while read rowid; do
3654 viewtable $1 $tbl $rowid
3655 done | _m4 -D_TITLE_="$tbl" \
3656 -D_FORM_="`genform $1`" \
3657 -D_DUMPTABLE_="syscmd(cat)" \
3658 $layout/html.m4.html $layout/form+dump.m4.html
3660 default_viewtext() { # $1=def-file
3661 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
3662 default_storedb "$@"
3663 DT_VIEW="viewtable+$tbl" dumptable html $tbl "name memo file" \
3664 | _m4 -D_TITLE_="$tbl" \
3665 -D_FORM_="`genform $1`" \
3666 -D_DUMPTABLE_="syscmd(cat)" \
3667 $layout/html.m4.html $layout/form+dump.m4.html
3669 default_smail() {
3670 default_storedb "$@"
3671 if [ $? -eq 2 ]; then
3672 _m4 -D_TITLE_="入力" \
3673 -D_FORM_="`genform $1`" \
3674 -D_DUMPTABLE_="" \
3675 $layout/html.m4.html $layout/form+dump.m4.html
3676 return
3677 fi
3678 cond=""
3679 for pk in `gettblpkey $tbl`; do
3680 pv=$(sqlquote "$(getpar $pk)")
3681 cond="$cond${cond:+ and }$pk=$pv"
3682 done
3683 sql="select rowid from $tbl where $cond;"
3684 rowid=`query "$sql"`
3685 ## err smail1 - "$sql" "-> rowid=$rowid"
3687 while IFS=: read prompt name keytype type args; do # Read from $1
3688 val=`getpar $name`
3689 if [ -n "$val" ]; then
3690 text="$text
3691 $prompt
3692 $name=$val
3693 ---------------------------------------------------------"
3694 fi
3695 case "$type" in
3696 image|document|file)
3697 fn="`getvalbyid $tbl $name $rowid $tmpd`"
3698 fns=$(echo "$fn"|while read fn; do
3699 err mv $tmpd/$fn.orig $tmpd/$fn
3700 mv $tmpd/$fn.orig $tmpd/$fn
3701 rm $tmpd/$fn.rowid # Remove cache flag
3702 ## err "`ls $tmpd/$fn`"
3703 echo $fn
3704 done)
3705 files="$files $fns"
3706 ;;
3707 esac
3708 done < $1
3709 ## err FILES=$files "`ls -lF $tmpd`"
3710 subj="from ${REMOTE_ADDR}"
3711 (echo "$url"
3712 echo "への書き込みがありました。"
3713 echo "------"
3714 echo "$text"
3715 ) | (cd $tmpd &&
3716 err LS="`ls -lF`" &&
3717 $mydir/sendmultipart.sh -t "$admin" -s "$subj" $files)
3718 _m4 -D_TITLE_="入力完了" $layout/html.m4.html
3719 echo "以下の内容で送信しました。" | html p
3720 viewtable $1 $tbl \
3721 `query "select rowid from $tbl order by rowid desc limit 1;"`
3722 echo "戻る" | html a "href=\"?\""