s4

view s4-funcs.sh @ 278:2f1607d8b56b

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