s4

view s4-funcs.sh @ 257:5d0dbbf9310c

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