s4

view s4-funcs.sh @ 486:44b4a45e2617

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