s4

view s4-funcs.sh @ 297:8933d3701c00

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