s4

view s4-funcs.sh @ 274:0f7bc60985d6

Count only text in grpaction list
author HIROSE Yuuji <yuuji@gentei.org>
date Mon, 01 Aug 2016 22:30:02 +0859
parents 31fbfcd4fc25
children b82007959b54
line source
1 #!/bin/sh
2 # Here's global variable table. Do not use this names.
3 # $HGid$
5 [ -f s4-config.sh ] && . ./s4-config.sh
7 myname=`basename ${SCRIPT_NAME:-$0}`
8 mydir=`dirname ${SCRIPT_FILENAME:-$0}`
9 myargs="$@"
10 PATH=/usr/local/sqlite3/bin:/usr/local/vim7/bin:/usr/iekei/ImageMagick/bin:/usr/local/ImageMagick/bin:$PATH
11 tmpdir=${TMPDIR:-tmp}
12 dbdir=${DBDIR:-db}
13 tmpfiles=""
14 db=${DB:-$dbdir/cgi.sq3}
15 admin=${ADMIN:-hostmaster@example.org}
16 templ=${TEMPL:-templ}
17 layout=${LAYOUT:-$templ/default}
18 formdir=${FORMDIR:-$templ/form}
19 imgdir=${IMGDIR:-img}
20 url=${URL:-"${REQUEST_SCHEME:-http${HTTPS:+s}}://$HTTP_HOST$REQUEST_URI"}
21 urlbase=${url%%\?*}
22 msgdir=$templ/msg
23 timeout="+2 days"
24 memoplimitdays="7"
25 dumpcollen=22
26 #thumbxy=120x120
27 thumbxy=96x96
28 maximagexy=1600x1600
29 ### maximagexy=400x400
30 file_accept='accept="image/*,text/*,audio/*,application/vnd.oasis.*,application/pdf,application/x-*"'
31 blogreadflagrowid=0
33 tconfs=""
34 imgcached=cache/img.`date +%Y/%m`
35 conftbl=_tblconf
36 nl="
37 "
38 . ./s4-cgi.sh
40 : <<EOF
42 !! 検索等でblogテーブル参照時は sql4readableblogs() で定義される
43 !! readableblogs テーブルを使うこと
44 資料配布、グループ管理・ML、ファイル交換、クリッカー、アンケート
45 レポート提出管理
46 ひとつのarticleをheadingにして新規ツリーを作成、あるといいかも。
48 [2016]
49 7/12 根本への反省
50 * cgi自身の $1, $2 での切り替えでなく、CGI変数での受け渡しにすべき。
51 arg1/arg2/arg3 的に $1 に / 区切りでつけた方がよかったかな。
53 [以下2015]
54 8/4 ○グループに承認加入モードを追加
55 ○グループに参加していない場合は grpaction できない
56 Web
57 締切設定
59 8/2 ○s4.cgi生成系 → index.cgi生成
60 ○自分の提出物リスト
62 7/19 ○設置
63 ○一斉送信
64 ○getparfilename の tmpd の扱い
65 ○やっぱりs4にしようかな
66 7/18 ○書込著者からホームへのリンク
67 7/17 ○個人blogに「レポート提出用」がついたときの挙動
68 ○添付ファイル回収
69 ○imgcacheは別ディレクトリにしないと + .htaccess
70 7/15 ○レポート提出モードの表示を付ける
71 管理者権限での削除? → まだいいか
73 7/13 ○前回アクセス基準の新着数は欲しいなあ
74 ○レポート提出はどうしよう
75 → ○blogにモードを追加:
76 ○レポート提出モード
77 添付ファイル (誰が見たかログ)
78 クリッカーは別立てメニューにしないと(管理者がON/OFF)
79 ○添付ファイルの読み出し権(6/22から) ← モードで対処
82 7/9 ○管理者の追加
83 △グループメンバの操作 → 要不要を吟味
84 ○グループ情報編集の行先はそのグループがいい?
85 ○新規グループの作成はどこから入るか
86 △グループホームとユーザホームを揃える
88 7/8 ○グループ一覧をユーザ一覧と揃える。
90 7/6の次 ○グループのconf編集の入口
91 ○グループ検索
93 6/22の次 ○ホーム画面、○招待状、親記事追跡、○編集ボタン、削除ボタン、
96 6/7の次 ○blogを作ってみる || userconfig || _mのまとめ編集(削除)
97 6/7の次の次 ○userconfigの画面だけ作ってみる。
99 ○ 5/28の次 edittableに「削除」ボタンを足す
100 ○6/1 par2tableを triplex 対応に
101 select "yuuji@gentei.org",var,"text",NULL,val from par where var in (select col from _tblconf where tbl="/user" and keytype in ('p', 's'));
102 →とすると 一気に
104 ## form.def を考えなおそう:
105 ## userのように必須カラムを決まった位置に付ける?
106 ## 必須カラム、owner(foreign key passwd(name)), update datetime
107 ## ユーザ管理とグループ管理はデフォルトで持たせてしまえ
109 ## 縦持ちデータの入力/編集を供給する関数 single + multi
110 ## 持てるテーブル構造はシステム標準5種 + ユーザ定義2種類
111 ## 1. passwd
112 ## 2. grp
113 ## 3. grp_mem
114 ## 4. topic 記事のIDとなる
115 ## 5. topic_cont 特定IDの記事の内容物
116 ## 6. list 繰り返し登場あり
117 ## 7. hash 繰り返し登場なし
119 ## ● listの定義:
120 ## create table list(id unique, parentID, type, value);
121 ## ● hashの定義:
122 ## create table hash(parentID, type, value, primary key(parentID, type));
124 ## グループ属性: community, friend
125 ## ○ blob使えるのかな。streamで行けるのか? xxdで行けた。ありがたい。
126 ## form-defとtableは1対1対応でいいか
127 ## csv2sq3 で .csv.sq3 の Makefile
129 ## 書き込みオブジェクトとは何か?
130 ## topic : id, belongto, title, owner, mode
131 ## type := root | comment
132 ## topic_cont : id, topicid(F), ppath, contenttype, filename, content,
133 ## unique(id, filename)
134 ## type := body(single) | attachment(multi)
136 ## group := name(P), tag, gecos, owner(F), mode
137 ## tag := personal | friend | ... any string
138 ## group_member := gname(F), type, name(F), UNIQUE(gname, type, name)
139 ## type := "u" | "g"
140 ## できたー!
141 ## with recursive allmem as (select * from grp_mem where gname='bar' union all select grp_mem.* from grp_mem,allmem where allmem.name=grp_mem.gname) select * from allmem where type='u';
142
143 ↓以下に変更
144 with recursive allmem as
145 (select gname,val from grp_m where gname='foo'
146 union all select grp_m.gname,grp_m.val from
147 grp_m,allmem where allmem.val=grp_m.gname)
148 select val from allmem where val in (select name from user);
151 with recursive allmem as
152 (select gname,val from grp_m where gname='foo'
153 union all select grp_m.gname,grp_m.val from grp_m,allmem
154 where allmem.val=grp_m.gname)
155 select a.*, coalesce(b.val,a.val) from allmem a left join grp_mem_s b
156 on a.gname=b.gname and a.val=b.user and b.key='email'
157 where a.val in (select name from user);
160 ## triggerもできた。
161 ## 5/22から:グループ作成画面
162 ## 埋め込み画像 data:CONTENT-TYPE;base64,.....
164 ## 考え得るノードタイプ
165 ## 日報 - 個人所属かグループ所属か
166 ## 課題提出 - 個人所属かグループ所属か
167 ## グループ管理
168 ## 個人情報管理
169 ##
171 ## 例: group:sip - topic:1:sip:Aperture:yuuji:rw
172 ## - topic:2:sip:ISO:yuuji:rw
173 ## topic_cont 1:1:/:body:text...Aperture
174 ## 2:1:/1:body:text..Aperture
175 ## 3:1:/1:attachment:binary..Aperture
176 ## 4:1:/2:body:text..Aperture
177 ## 5:1:/2:attachment:binary..Aperture
178 ## 6:2:/:body:text..ISO
179 ## 7:2:/6:body:text..ISO
180 ## 8:2:/6:attachment:binary..
182 ## ログテーブル
183 ## time, who, action, tbl, id idなんか取れるかな
187 ■表設計
188 * 3つの表に分散管理
189 id格納表 + hash表 + list表
190 * *_s *_m
194 user, user_map, user_col
196 ■抽象エントリタイプ
197 * user
198 idとして機能 → table中の owner に自動挿入(?)
199 * group
200 権限判定に利用
201 * serial
202 自動idとして機能
203 * password
204 入力 type=passwordで入力
205 変更 oldpasswd, password×2 で確認後修正
206 * session
207 password認証後のセッションキーとして機能
208 * text
209 入力 type=text
210 * textarea
211 入力 textarea
212 * image|document
213 入力 type=fileで入力し、mime-typeを確認
214 * owner
215 入力時の $user で、外部キー制約が付く
216 * gowner
217 グループとしての所有者で、外部キー制約が付く
218 * timestamp
219 datetime()
220 * parent
221 木構造の場合の親の位置
222 * path
223 木構造の場合の自分の位置
225 格納タイプ
226 * list
227 表 parentID, key, val でUNIQUE(parentID, key, val)
228 * hash
229 表 parentID, key, val でUNIQUE(parentID, key)
231 オブジェクトタイプ
232 * entry
233 id, title, owner
234 * textpart
235 id, parentID, text
236 * binarypart
237 id, parentID, contenttype, filename, content
238 * content
239 hash(textpart), list(binarypart)
240 * topic
241 id, hash(content), list(reply)
242 * reply
243 id, parentID, content
244 * blog
245 list(entry)
246 blog = [topic, list(reply)]
249 blog = [ {"title" => "hoge", "owner" => "yuuji", "date" => "2015-04-27",
250 "text" => "hogehoge ..",
251 "reply" => [ {"serial" => 1,
252 "author" => "taro",
253 "date" => "2015-04-28",
254 "parent" => "/",
255 "path" => "/1",
256 "text" => "blah, blah, ....",
257 "image" => ["a.jpg", "b.jpg"] },
258 {"serial" => 2,
259 "author" => "hanako",
260 "date" => "2015-04-29",
261 "parent" => "/",
262 "path" => "/2",
263 "text" => "blah, blah, ....",
264 "image" => [] }]},
265 {"title" => "buha", ...} ]
268 user:=
269 ユーザ名(英数字):name:p:text:length="20" maxlength="40"
270 パスワード:pswd:s:password:length="20" maxlength="40"
271 説明(日本語OK):gecos:s:text:length="20" maxlength="40"
272 セッションキー:skey:s:session
273 メイルアドレス:email:m:text:length="20" maxlength="40"
274 住所:address:m:textarea:maxlength="400"
275 プロフィール画像:profimg:m:image:maxlength="400K"
276 履歴書:profpdf:m:document:maxlength="4M"
278 変換表
279 /user/email=m
281 blog:=
282 シリアル:id:p:serial
283 タイトル:title:s:text:
284 所有者:owner:s:owner:
285 時刻:ctime:s:stamp:
286 リード文:heading:s:textarea:
287 リプライ:reply:m:*article:
289 article:=
290 シリアル:id:p:serial
291 筆者:author:s:owner
292 時刻:ctime:s:stamp:
293 参照元:parent:s:parent:
294 パス:path:s:path:
295 本文:text:s:textarea:
296 画像:image:m:image:
298 履歴書:profpdf:m:document:maxlength="4M"
301 EOF
303 sq() {
304 # ./args.rb -cmd ".timeout 3000" "$@"
305 sqlite3 -cmd 'PRAGMA foreign_keys=ON' -cmd ".timeout 3000" "$@"
306 }
307 dbsetup() {
308 [ -d $tmpdir ] || mkdir -m 1777 $tmpdir
309 [ -d $dbdir ] || mkdir -m 1775 $dbdir
310 sqi=$tmpdir/sqi.$$
311 sqo=$tmpdir/sqo.$$
312 mkfifo $sqi $sqo
313 #tail -f $sqi | sq $db & # "tail -f" is too heavy. DO NOT USE!!
314 sq $db < $sqi &
315 sq3pid="`jobs -p` $!"
316 exec 2>> $tmpdir/error.out
317 exec 3>> $tmpdir/debug.out
318 exec 5> $sqi # Turning $sqi access through fd5 for continuous open state
319 rm $sqi
320 }
321 cleanup() {
322 echo .quit >&5
323 kill $sq3pid
324 kill $sq3pid
325 rm -f $sqo $sqi
326 rm -rf $tmpfiles
327 }
328 query() {
329 echo ".once $sqo" >&5
330 if [ -z "$1" ]; then
331 cat
332 else
333 echo "$@"
334 fi >&5
335 cat $sqo
336 }
337 _m4() {
338 #_S4NAME_=f,f,f
339 m4 ${_S4NAME_:+"-D_S4NAME_=${_S4NAME_}"} "$@"
340 }
341 ismember() {
342 # $1=user, $2=group
343 err ismem: "select user from grp_mem where gname=$(sqlquote $2) and user='$1';"
344 test -n "`query \"select user from grp_mem where gname=$(sqlquote $2) and user='$1';\"`"
345 }
346 isuser() { # Check if $1 is a valid user
347 test -n "`query \"select name from user where name='$1';\"`"
348 }
349 isgroup() { # Check if $1 is a valid group
350 err isgroup: "select gname from grp where gname=$(sqlquote $1);"
351 test -n "`query \"select gname from grp where gname=$(sqlquote $1);\"`"
352 }
353 isgrpowner() (
354 # $1=user, $2=group
355 gn=`sqlquote "$2"`
356 sql="select user from grp_adm where gname=$gn and user='$1';"
357 err isgrpowner: $sql
358 test -n "`query $sql`"
359 )
360 getgroupadminmails() {
361 # $1=group
362 for i in $(getgroupadmins $1); do
363 email4group "$1" "$i" ;
364 done
365 }
366 getgroupadmins() { # $1=group
367 # This function is called in a backquote, so needn't to be subshellized
368 qgrp=`sqlquote "$1"`
369 query "select user from grp_adm where gname=$qgrp;"
370 }
371 getgroupattr() { # $1=group $2=attr
372 # This function is called in a backquote, so needn't to be subshellized
373 getvalbyid grp $2 \
374 $(query "select rowid from grp where gname=`sqlquote $1`;")
375 }
376 getgroupbyid() {
377 # $1=id|gname
378 sql="select coalesce((select gname from grp where gname=$(sqlquote $1)),
379 (select gname from grp where rowid=$(sqlquote $1)));"
380 # err ggbyid: `echo $sql`
381 query $sql
382 }
383 isfilereadable() { # $1=user $2=tbl $3=rowid
384 # Return true if user($1) can read attachment files in tbl($2):rowid($3)
385 [ -z "$1" -o -z "$2" -o -z "$3" ] && return 1 # invalid argument
387 # Return true when anonymous mode
388 [ "$anonymousmode" ] && return 0
389 # case `getvalbyid blog mode $2` in
390 # normal|*open*|"") return 0 ;;
391 # *closed*)
392 # owner=`getvalbyid blog owner $2`
393 # if isgrp $owner; then
394 # isgrpowner $1 $owner && return 0 || return 1
395 # elif isuser $owner; then
396 # [ x"$1" = x"$owner" ] && return 0 || return 1
397 # fi
398 # esac
399 # ↑ 要はこういう処理を↓で一気にやっている
400 sql="with getblog as (\
401 select key,val from blog_s where id=(\
402 select blogid from article where id in\
403 (select id from $2 where rowid=$3))),\
404 getowner as (select val from getblog where key='owner'),\
405 getmode as (select val from getblog where key='mode')\
406 select case\
407 when (select author from article where\
408 id=(select id from $2 where rowid=$3))='$1' \
409 then 'author'\
410 when (select val from getmode) in ('report-open', 'normal')\
411 then 'open'\
412 when (select val from getmode) is null \
413 then 'open'
414 when (select val from getowner) in (select gname from grp)\
415 then (select user from grp_adm where \
416 gname=(select val from getowner) and \
417 user='$1')\
418 when (select author from article where\
419 id=(select id from $2 where rowid=$3))='$1'
420 then 'user+author'
421 else '' end;"
422 err isfilereadable: sql="`echo $sql`"
423 # caseのネストで内側のcaseがスカラーtrueを返しても外側はtrue扱いにならない
424 result=`query "$sql"`
425 [ -n "$result" ] && return 0
426 return 2
427 }
428 linkhome() {
429 # $1=UserOrGroup
430 echo -n '<a href="?'
431 if isuser $1; then
432 err "select 'home+'||rowid from user where name='$1';"
433 query "select 'home+'||rowid from user where name='$1';"
434 else
435 echo -n "grp+$1"
436 fi
437 echo "\">`gecos $1`</a>"
438 }
439 hreflink() {
440 _hrefptn="[-A-Za-z0-9,.:;/~_%#&+?=@!]*"
441 sed -e "s|^href=\($_hrefptn\)|<a &>\1</a>|" \
442 -e "s|^iframe=\($_hrefptn\)|<iframe src=\"\1\"></iframe>|"
443 }
444 acclog() (
445 # $1=table, $2=rowid
446 n=${2%%[!0-9]*} # Remove non-digit chars from $2(should be rowid)
447 if [ -n "$n" ]; then
448 now=`date +"%F %T"`
449 #query "replace into acclog values('$user', '$1', '$n', '$now');"
450 query "replace into acclog values('$user', '$1', $n, '$now');"
451 fi
452 )
453 gecos() (
454 u=`sqlquote ${1:-$user}`
455 #gecos=`query "select val from user_s where name='$u' and key='gecos';"`
456 # sql="select case when (select name from user where name=$u) is not null
457 # then coalesce(
458 # (select val from user_s where name=$u and key='gecos'),
459 # $u)
460 # when (select gname from grp where gname=$u) is not null
461 # then coalesce(
462 # (select val from grp_s where gname=$u and key='gecos'),
463 # $u)
464 # else $u
465 # end;"
466 #query "$sql"
467 query "select gecos from gecoses where name=$u;"
468 )
469 setpar() {
470 query "replace into par values('$session', '$1', '$2', \"$3\");"
471 }
472 replpar() {
473 query "update par set val=\"$3\" where sessid='$session' and var='$1' and type='$2';"
474 }
475 getpar() {
476 err getpar: "select val from par where var='$1' and sessid='$session' $2;"
477 val=`query "select val from par where var='$1' and sessid='$session' $2;"`
478 err getpar/val1: "val=[$val]"
479 if [ -z "$val" ]; then
480 val=`query "select val from cookie where var='$1' and sessid='$session' $2;"`
481 fi
482 err getpar/val2: "val=[$val]"
483 case "$var" in
484 owner)
485 if [ x"$user" = x"$val" ]; then
486 echo $user; return
487 elif ismember $user $val; then
488 echo $val; return
489 fi ;;
490 esac
491 err getpar/ret: "val=[$val]"
492 echo "$val"
493 }
495 getpartype() {
496 query "select type from par where var='$1' and sessid='$session' $2;"
497 }
498 getparcount() {
499 query "select count(*) from par where var='$1' and sessid='$session' $2;"
500 }
501 getparfilename() {
502 # null if type of $1 is not file
503 (f=`query "select val from par where var='$1' and sessid='$session' and type='file' $2;"`
504 [ -n "$f" ] && echo $f)
505 }
506 sqlquote() {
507 (v="$1"
508 case "$v" in
509 "") return ;; # null
510 "X'"*) # quoted hex string
511 echo $1 ;;
512 *\"*) # string including dbl-quote"
513 v=`echo "$v"|sed -e 's/\"/\"\"/g'`
514 echo "\"$v\""
515 return ;;
516 *.*.*|*-*-*|*[Ee]*[Ee]*|[Ee]*|*[\ -,:-df-~]*) # string
517 echo "\"$v\""
518 return ;;
519 *)
520 if expr "$v" : '[-0-9.Ee][-0-9.Ee]*$' >/dev/null 2>&1; then
521 echo $v # MAYBE numeric, maybe...
522 else
523 echo "\"$v\""
524 fi ;;
525 esac)
526 }
527 mktempd() {
528 TMPDIR=$tmpd mktemp -d -t $session
529 }
530 getcachedir() { # $1=maintable
531 if [ -n "$imgcached" ]; then
532 echo $imgcached/$(echo ${1:-hoge}|md5)/$thumbxy
533 else
534 echo $tmpd/$thumbxy
535 fi
536 }
537 getval() {
538 # $1=table $2=col $3(optional)=condition
539 case `gettbl_coltype "/$1/$2"` in
540 user|author) # author added 2015-06-18 for article(author)
541 echo "$user" ;;
542 stamp|datetime)
543 date "+%F %T" ;;
544 serial)
545 (s=`getpar $2`
546 if [ -n "$s" ]; then echo $s; else echo "`date +%s`x$$"; fi) ;;
547 *)
548 getpar "$2" "$3";;
549 esac
550 }
552 getvalquote() {
553 # $1=table $2=col $3(optional)=condition
554 (v=`getval "$@"`
555 case "$v" in
556 "") echo NULL ;;
557 *) sqlquote "$v" ;;
558 esac)
559 }
560 getparquote() {
561 sqlquote `getpar $1`
562 }
563 getbinbyid() {
564 # $1=tbl $2=col $3=rowid $4=tmpdirForBinary
566 }
567 getvalbyid() {
568 # $1=tbl $2=col $3=rowid $4=tmpdirForBinary
569 # If two or more values found, save them to $tmpd/${column}.$N and
570 # store the number of files into $tmpd/${column}.count and
571 # their each rowid stored into $tmpd/${column}.$N.rowid.
572 ## err gtb-$1=`gettblcols $1`, tbl=$1, col=$2, '$3'=$3
574 (for c in `gettblcols $1`; do
575 if [ x"$2" = x"$c" ]; then
576 err "select $2 from $1 where rowid=$3"
577 ###sq $db "select $2 from $1 where rowid=$3"
578 query "select $2 from $1 where rowid=$3;"
579 return
580 fi
581 done
582 rowid=$3
583 pk=`gettblpkey $1`
584 key=`query "select $pk from $1 where rowid=$3;"`
585 getkey="(select $pk from $1 where rowid=$3)"
586 ### err "select $pk from $1 where rowid=$3" - key=$key '$4(tmp)'=$4
587 for kt in s m; do
588 td=${4:-$tmpd}
589 [ -d $td ] || mkdir -p $td
590 t=${1}_$kt
591 for c in `gettbl_${kt}_cols $1`; do
592 vcount=1 # count(val)
593 if [ x"$2" = x"$c" ]; then
594 #### cond="$t where $pk=\"$key\" and key=\"$c\"" #2015-07-22
595 cond="$t where $pk=$getkey and key=\"$c\""
596 val=`query "select val from $cond limit 1;"`
597 type=`query "select type from $cond limit 1;"`
598 if [ $kt = m ]; then
599 ###vcount=`sq $db "select count(val) from $cond"`
600 # Reset val to store filenames if type is string
601 val=`query "select val from $cond and type like 'file:%' order by rowid;"`
602 err gvb1-sql: "select count(val) from $cond;"
603 vcount=`query "select count(val) from $cond;"`
604 echo $vcount > $td/$c.count
605 i=0
606 err gvbid: i=$i vcount=$vcount
607 while [ $i -lt $vcount ]; do
608 slice="order by rowid limit 1 offset $i"
609 i=$((i+1))
610 fn=$c.$i
611 err td=$td, fn=$fn, type=$type, val="[$val]"
612 case $type in
613 file:*)
614 #file=$td/$val
615 file=$td/`query "select val from $cond $slice;"`
616 # FOR SPEED: Skip file generation if imgcache exists
617 [ -s $file -a -s $td/$fn.rowid -a -s $file.rowid ] && continue
618 # err gvbid-get="select quote(bin) from $cond $slice;"
619 sq $db<<EOF | unhexize > $file
620 .output $td/$fn.rowid
621 select rowid from $cond $slice;
622 .output $td/$fn
623 select val from $cond $slice;
624 .output $file.content-type
625 select substr(type, 6) from $cond $slice;
626 .output stdout
627 select quote(bin) from $cond $slice;
628 EOF
629 ## err gvbid-get2: "`ls -lF $file`"
630 ## err i=$i - file=$file rowid=`cat $td/$fn.rowid`
631 cp $td/$fn.rowid $file.rowid 2>&3 # for convenience
632 cp $file $file.orig 2>&3
633 ls -lh $file | awk '{print $5"B"}'|sed 's/BB/B/' > $file.size
634 case $type in
635 *:[Ii]mage*) mogrify -geometry $thumbxy $file ;;
636 ### ここのアイコンを増やしたい
637 *|*:[Aa]pplication*)
638 convert -geometry $thumbxy $imgdir/file-icon.png \
639 png:- > $file
640 ;;
641 esac
642 ;;
643 *)
644 sq $db<<EOF
645 .output $td/$fn.rowid
646 select rowid from $cond $slice;
647 .output $td/$fn
648 select val from $cond $slice;
649 EOF
650 val=$val${val:+$nl}"`echo $fn`" # should be delimited by newline
651 ;;
652 esac
653 done
654 else
655 rm -f $td/$c.count
656 case $type in
657 file:*)
658 echo "$val" \
659 | while read fn; do
660 file=$td/$fn
661 if [ ! -s $file ]; then
662 ## sq $db "select quote(bin) from $cond and val=\"$fn\"" \
663 query "select quote(bin) from $cond and val=\"$fn\";" \
664 | unhexize > $file
665 echo ${type#file:} > $file.content-type
666 # err TTTTTTTTTTTTTTTT: $type
667 case $type in
668 *:[Ii]mage*) mogrify -geometry $thumbxy $file ;;
669 *:[Aa]pplication*)
670 convert -geometry $thumbxy $imgdir/file-icon.png \
671 png:- > $file ;;
672 esac
673 fi
674 done
675 ;;
676 esac
677 fi
678 echo "$val" # Keep newlines by ""
679 return
680 fi
681 done
682 done)
683 }
684 getvalbypkey() (
685 # $1=tbl $2=col $3=pkey $4=tmpdirForBinary
686 pk=`gettblpkey $1`
687 rowid=`query "select rowid from $1 where $pk='$3';"`
688 getvalbyid "$1" "$2" $rowid $4
689 )
690 getvalbycond() {
691 # $1=tbl $2=col $3=SQL-Condition
692 ###rowid=`sq $db "select rowid from $1 where $3"`
693 rowid=`query "select rowid from $1 where $3;"`
694 if [ -n "$rowid" ]; then
695 getvalbyid "$1" "$2" $rowid "$4"
696 fi
697 }
698 getpwfield() {
699 # getpwfield user column
700 # val=`sqlite3 $db "select $2 from passwd where name='$1' $3"`
701 val=`getvalbycond user $2 "name='$1'"`
702 if [ -n "$val" ]; then
703 echo "$val"
704 return 0
705 else
706 return 1
707 fi
708 }
709 encode() {
710 if [ -z "$sha1" ]; then
711 if type sha1 >/dev/null 2>&1; then
712 sha1=sha1
713 elif type sha1sum >/dev/null 2>&1; then
714 sha1=sha1sum
715 elif type gsha1sum >/dev/null 2>&1; then
716 sha1=gsha1sum
717 fi
718 fi
719 $sha1 "$@" | cut -d' ' -f1
720 }
721 mycrypt() (
722 key=$1 salt=$2
723 err \$2=$2
724 case $2 in
725 '$'*'$'*) salt=${salt#\$4\$}
726 salt=${salt%\$*} ;;
727 esac
728 echo -n '$4$'"$salt"'$'
729 echo "$salt$key" | encode || exit 1 # Abort if fail to call encode
730 )
731 hexize() {
732 if [ -z "$hexize" ]; then
733 if type xxd >/dev/null 2>&1; then
734 hexize="xxd -p"
735 else
736 hexize_hd() {
737 hexdump -ve '1/1 "%.2x"'
738 }
739 hexize="hexize_hd"
740 fi
741 fi
742 cat $1 | $hexize | tr -d '\n'
743 }
744 unhexize() {
745 if [ -z "$unhex" ]; then
746 if type xxd >/dev/null 2>&1; then
747 unhex="xxd -p -r"
748 elif type perl >/dev/null 2>&1; then
749 cat >$tmpd/unhex.pl<<EOF
750 s/([0-9a-f]{2})/print chr hex \$1/gie
751 EOF
752 # Perl refuses -e in setuid circumstances, which can be absurdly
753 # avoided by creating scripts in a file where its parent directory is
754 # world writable...:)
755 unhex="perl -n $tmpd/unhex.pl"
756 fi
757 fi
758 cat $1 | $unhex
759 # cat $1 | tee /tmp/uh.in| $unhex | tee /tmp/uh.out
760 }
761 percenthex() {
762 hexize $1 | sed 's/\(..\)/%\1/g'
763 }
764 htmlescape() {
765 sed -e 's/\&/\&amp;/g' -e 's/"/\&quot;/g' -e "s/'/\&apos;/g" \
766 -e "s/</\&lt;/g; s/>/\&gt;/g"
767 }
768 enascii() {
769 if [ -z "$enascii" ]; then
770 if type kakasi >/dev/null 2>&1; then
771 enascii="kakasi -Ha -Ka -Ja -Ea -ka"
772 else
773 enascii_now=`date +%FT%T`
774 enascii_sed() {
775 nkf -Z0Z1Z2 \
776 | sed -e "s/^/$enascii_now/" -e "s|[^-0-9.A-z/,()_=]|x|g"
777 }
778 enascii="enascii_sed"
779 fi
780 fi
781 cat "$@" | $enascii
782 }
783 size_h() {
784 i="$1" oi=$1
785 set -- B B KB MB GB TB
786 while [ $((i)) -gt 9 -a -n "$1" ]; do # -gt 9 means $oi > 1024
787 oi=$i
788 i=$((i/1024))
789 shift
790 done
791 echo ${oi}$1
792 }
793 gettblconf() {
794 if [ -z "$tconfs" ]; then
795 ## tconfs=`sq $db \
796 tconfs=`query \
797 "select tbl||'/'||col||'='||keytype||'/'||objtype from $conftbl;"`
798 fi
799 # /tb1/col1=p/text /tb1/col2=s/text /tb1/col3=m/image /tb2/col1=p/text ...
800 }
801 gettblkeys() {
802 # $1=tbl
803 gettblconf
804 echo "$tconfs" | fgrep "/$1/" | \
805 (type="" keys="" fks="" cols="" scols="" mcols="" hcols=""
806 while IFS='=' read tc conf; do # tc=/tb1/col1 conf=s/text
807 col=${tc##*/} type=${conf%%/*}
808 case $type in
809 *p*)
810 cols=$cols"${cols:+:}$col"
811 keys=$keys"${keys:+:}$col" ;;
812 *f*) cols=$cols"${cols:+:}$col"
813 fks=$fks"${fks:+:}$col" ;;
814 *m*) mcols=$mcols"${mcols:+:}$col" ;;
815 *s*) scols=$scols"${scols:+:}$col" ;;
816 esac
817 case $type in
818 *h*) hcols=$hcols"${hcols:+:}$col" ;;
819 esac
820 done
821 echo "_keys=$keys _fks=$fks _cols=$cols _scols=$scols _mcols=$mcols _hcols=$hcols")
822 }
823 gettblpkey() {
824 # $1=tbl
825 gettblkeys $1 | cut -d ' ' -f 1 | sed -e 's/.*=//' -e 's/:/ /g'
826 }
827 gettblfkey() {
828 (x=`gettblkeys $1`
829 x=${x#*_fks=} # cut before "_fks=" including
830 echo ${x%% *} | tr ':' ' ')
831 }
832 gettblcols() {
833 (x=`gettblkeys $1`
834 x=${x#*_cols=} # cut before "_cols=" including
835 echo ${x%% *} | tr ':' ' ')
836 }
837 gettbl_s_cols() {
838 (x=`gettblkeys $1`
839 x=${x#*_scols=} # cut before "_scols=" including
840 echo ${x%% *} | tr ':' ' ')
841 }
842 gettbl_m_cols() {
843 (x=`gettblkeys $1`
844 x=${x#*_mcols=} # cut before "_mcols=" including
845 echo ${x%% *} | tr ':' ' ')
846 }
847 gettbl_h_cols() {
848 (x=`gettblkeys $1`
849 x=${x#*_hcols=} # cut before "_hcols=" including
850 echo ${x%% *} | tr ':' ' ')
851 }
852 gettbl_coltype() (
853 gettblconf
854 x=`echo "$tconfs"|fgrep $1=`
855 x=${x#*=} # cut before =
856 echo ${x#*/} # cut before p/ including
857 )
858 is_hidden() {
859 # $1=Tbl $2=col
860 gettblconf
861 x=`echo "$tconfs"|fgrep /$1/$2=`
862 x=${x#*=} # cut before =
863 x=${x%%/*} # cut after /
864 case $x in
865 *h*) return 0 ;;
866 *) return 1 ;;
867 esac
868 }
870 dbsetbyid() {
871 # $1=tbl $2=id $3=col $4=val/filename - &optional - $5=content-type
872 (t0=$1 t=$1 p=$2 c=$3
873 tsc=$t/$c val=$4
874 unset primary update
875 gettblconf
876 #err tsc=$tsc, tconfs="$tconfs"
877 conf=`echo "$tconfs"|fgrep "$tsc"=`
878 #err conf=$conf
879 case ${conf#*=} in
880 p*) primary=1 ;;
881 f*) update=1 ;;
882 u*) ;;
883 m*) t=${t}_m;;
884 s*) t=${t}_s;;
885 esac
886 #err t=$t
887 type=text fn=""
888 case $conf in
889 */password)
890 type=encoded ### val=`echo $val|encode`
891 ;;
892 */image*|*/document*)
893 type=`file --mime-type $val`
894 bin="X'`hexize $val`'"
895 ;;
896 esac
897 pkey=`echo "$tconfs"|grep "${t0}/.*=p"|sed 1q`
898 pkey=${pkey#/*/} # cut $tbl/
899 pkey=${pkey%=p/*} # cut =p/... -> primary key
900 if [ "$primary" ]; then
901 nulls=`echo "$tconfs"|grep "$t/.*=[fu]/"|sed 's/^.*/, NULL/'|tr -d '\n'`
902 ###sq $db "replace into $t values(\"$val\"$nulls)"
903 query "replace into $t values(\"$val\"$nulls);"
904 elif [ "$update" ]; then
905 ###sq $db "update $1 set $c=\"$val\" where $pkey=\"$p\""
906 query "update $1 set $c=\"$val\" where $pkey=\"$p\";"
907 else
908 #err "replace into $t values(\"$p\", \"$c\", \"$type\", \"$val\", \"$bin\")"
909 ###sq $db "replace into $t values(\"$p\", \"$c\", \"$type\", \"$val\", \"$bin\")"
910 query "replace into $t values(\"$p\", \"$c\", \"$type\", \"$val\", \"$bin\");"
911 fi
912 )
913 }
914 expire() (
915 at="${1:-$timeout}"
916 FMT="${2:-%F %T}"
917 TZ=GMT gdate -d "$at" +"$FMT"
918 )
919 addsession() {
920 # expireをセット
921 # loginの先にどの画面に行くかの状態遷移表書式を決める
922 expire=`expire ${2:-"+1min"}`
923 err addsession "$1" exp=$expire
924 sq $db "replace into session values('$1', '$expire')"
925 # Remove old session parameters
926 now=`expire now`
927 sq $db "delete from session where expire < '$now'"
928 }
929 gencookie() (
930 for kv; do
931 expire="`expire '' '%a, %d-%b-%Y %H:%M:%S GMT'`"
932 echo "Set-Cookie: $kv; expires=$expire"
933 done
934 )
935 contenttype() {
936 echo "Content-type: ${1:-text/html; charset=utf-8}"
937 contenttype() {} # Only need to work once
938 }
939 putheader() {
941 }
942 putfooter() {
943 _m4 -D_TITLE_="${TITLE:-$myname}" $layout/footer.m4.html
944 }
945 getcookie() (
946 for kv in `echo $HTTP_COOKIE|sed 's/[;, ]/ /g'`; do
947 k="${kv%%=*}"
948 v="`echo ${kv#*=}|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
949 query "replace into cookie values('$session', '$k', 'string', \"$v\");"
950 done
951 )
952 genrandom() {
953 # $1=columns (default: 10)
954 dd if=/dev/urandom count=1 2>/dev/null|nkf -MB|fold -w${1:-10}|sed -n 10p
955 }
956 genserial() {
957 echo $((($(date +%s)-1433084400)/10))c$$
958 }
959 smail() {
960 # smail rcpts subj (file)
961 # $SMAIL_TO <- Recipient value of To: header
962 rcpt=`echo $1` # strip newlines
963 subj=`echo $2|nkf -jM|tr -d '\n'`
964 (_m4 -D_RCPT_="${SMAIL_TO:-$rcpt}" -D_SUBJ_="\`$subj'" -D_FROM_=$admin $msgdir/mail-header.m4
965 cat $3 | nkf -jd ) | sendmail -f $admin $rcpt
966 }
967 setviastring() {
968 table=$1
969 oifs="$IFS"
970 IFS="&"
971 for us in $2; do
972 k=${us%%=*}
973 v="`echo ${us#*=}|tr '%+' '= '|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
974 sq $db "replace into $table values('$session', '$k', 'string', \"$v\")";
975 #echo $k=$v
976 done
977 IFS="$oifs"
978 }
979 checkdomain() (
980 # Check the validity of domain by referring DNS
981 item=$1
982 err checkdomain $1
983 host ${item#*@} 1>&3 2>&3
984 host ${item#*@} >/dev/null 2>&1
985 )
986 pwcheck() {
987 # $1=passwd
988 dbpswd=`getpwfield $user pswd`
989 encpswd=`mycrypt "$1" "$dbpswd"`
990 err user=$user, pswd=$1, db=$dbpswd, enc=$encpswd
991 [ x"$dbpswd" = x"$encpswd" ]
992 }
993 mypwhash() {
994 mycrypt `cat` `genrandom 5`
995 }
996 wasureta() {
997 user=$1
998 if ! checkdomain $user; then
999 contenttype; echo
1000 _m4 -D_TITLE_='Invalid email' $layout/title-only.html
1001 echo "ユーザ名($user)には正しいメイルアドレスが必要です。" | html p
1002 putfooter
1003 exit 0
1004 fi
1005 newpswd=`genrandom` # newsalt=`genrandom 5`
1006 #encpswd=`mycrypt "$newpswd" "$newsalt"`
1007 encpswd=`echo $newpswd|mypwhash`
1008 dbsetbyid user $user pswd "$encpswd"
1009 # Avoid $user substitution with m4, because $url comes from user input.
1010 _m4 -D_PSWD_="$newpswd" -D_URL_="$url" -D_ADMIN_="$admin" \
1011 $msgdir/mail-newaccount.m4 \
1012 | sed "s/_USER_/$user/g" \
1013 | smail $user "New Account"
1015 checkauth() {
1016 user=`getpar user`
1017 skc=`getpar skey` # from cookie
1018 [ -z "$user" ] && return 3
1019 skey="`getpwfield $user skey`"
1020 err user=$user skey=$skey
1021 if [ -n "$skey" ]; then
1022 if [ x"$skey" = x"$skc" ]; then
1023 return 0
1024 fi
1025 fi
1026 pswd=`getpar pswd`
1027 if [ x"$pswd" = x"wasureta" ]; then
1028 wasureta $user
1029 return 1 # wasureta error
1030 fi
1031 # dbpswd="`sq $db \"select pswd from passwd where name='$user'\"`"
1032 # putheader; echo; echo user=$user, db=$dbpswd, enc=$encpswd
1033 if pwcheck "$pswd"; then
1034 newsession=`genrandom 50`
1035 dbsetbyid user $user skey "$newsession"
1036 gencookie "user=$user" "skey=$newsession"
1037 return 0
1038 fi
1039 return 2 # Password mismatch
1041 showlogin() {
1042 args=`echo $myargs|tr ' ' '+'`
1043 _m4 -D_SYSNAME_="Welcome" -D_MYNAME_="$myname${args+?}$args" \
1044 $layout/login.m4.html
1045 exit 0
1047 dologin() {
1048 checkauth
1049 st=$?
1050 if [ $st != 0 ]; then
1051 contenttype; echo
1052 _m4 -D_USER_="$user" -D_URL_="$url" -D_ADMIN_="$admin" \
1053 $msgdir/login-fail-$st.m4.html
1054 showlogin # and EXIT
1055 fi
1058 # Do instant jobs here
1059 dbsetup
1060 trap cleanup INT HUP EXIT TERM PIPE
1061 # trap cleanup INT HUP
1063 err() {
1064 echo "$@" 1>&3
1067 cgiinit() {
1068 session=`date +%F-$$`
1069 tmpf=tmp/stream
1070 tmpd=`tmpd=$tmpdir mktempd`
1071 tmpfiles=$tmpfiles" $tmpd"
1072 addsession $session
1073 getcookie
1074 case "$REQUEST_METHOD" in
1075 get|GET) s="$QUERY_STRING" ;;
1076 post|POST) ## dd count=$CONTENT_LENGTH bs=1 of=$tmpf 2>/dev/null #slow
1077 ## dd bs=$CONTENT_LENGTH count=1 of=$tmpf # NOT working
1078 # cat > $tmpf # too much?
1079 head -c $CONTENT_LENGTH > $tmpf # safe?
1080 (echo CL=$CONTENT_LENGTH; ls -lF $tmpf) 1>&3
1081 s="`cat tmp/stream`"
1082 tmpfiles=$tmpfiles"${tmpfiles+ }$tmpf"
1083 ;;
1084 esac
1085 case "$CONTENT_TYPE" in
1086 *boundary*)
1087 bndry=${CONTENT_TYPE#*boundary=}
1088 #for us in `LC_CTYPE=C ./mpsplit.rb "$bndry" $tmpd < $tmpf`
1089 for us in `LC_CTYPE=C ./mpsplit.pl "$bndry" $tmpd < $tmpf`
1090 do
1091 k=${us%%\=*}
1092 #echo u=$us
1093 #v="`echo ${us#*=}|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
1094 v="`echo ${us#*=}|unhexize|sed -e 's/\"/\"\"/g'`"
1095 # err k=$k v=$v
1096 case "$k" in
1097 *:filename)
1098 type='file'; k=${k%:filename}
1099 (echo k=$k; ls -lF $tmpd/$v; file --mime-type $tmpd/$v) 1>&3
1100 case `file --mime-type $tmpd/$v|cut -d' ' -f2` in
1101 [Ii]mage/x-xcf)
1102 bzip2 $tmpd/$v
1103 v=${v}.bz2
1104 ;;
1105 [Ii]mage/x-*|*/vnd.*) ;;
1106 [Ii]mage/*)
1107 mogrify -resize $maximagexy'>' $tmpd/$v
1108 ;;
1109 esac
1110 ;;
1111 *)
1112 type='string'
1113 ;;
1114 esac
1115 #sq $db "replace into par values('$session', '$k', '$type', \"$v\")"
1116 setpar "$k" "$type" "$v"
1117 done
1118 ;;
1119 *)
1120 setviastring par "$s"
1121 ;;
1122 esac
1124 email4group() {
1125 # Get for-$1=group email address(es) for $2...=users
1126 qgrp=`sqlquote "$1"`; shift
1127 users=`for i; do sqlquote "$i"; done`
1128 users=`echo $users|tr ' ' ','`
1129 sql="select coalesce(s.val, g.user) from grp_mem g
1130 left join grp_mem_s s on g.gname=s.gname and g.user=s.user
1131 and s.key='email'
1132 where g.gname=$qgrp and g.user in ($users);"
1133 err `echo $sql`
1134 query "$sql"
1136 email4groupbyuid() {
1137 # Get for-$1=group email address(es) for $2...=user-ids
1138 qgrp=`sqlquote "$1"`; shift
1139 err em4gbid-IN: "\$1=$1 qgrp=$qgrp"
1140 uids=`echo "$@"`
1141 uids=`echo $uids|tr ' ' ','`
1142 sql="WITH
1143 grpemails AS (
1144 SELECT gname, user, val email
1145 FROM grp_mem NATURAL JOIN grp_mem_s
1146 WHERE key='email' AND gname=$qgrp),
1147 useremails AS (
1148 SELECT user.rowid rid, user.name, val email
1149 FROM user
1150 LEFT JOIN user_s
1151 ON user.name=user_s.name AND user_s.key='email')
1152 SELECT DISTINCT coalesce(g.email, u.name)
1153 FROM useremails u LEFT JOIN grpemails g
1154 ON u.name=g.user
1155 WHERE u.rid in ($uids);"
1156 err email4gByid `echo $sql`
1157 query "$sql"
1159 collectemail() (
1160 # Collect email addresses for group $1
1161 # If $TEAM is set, filter by team name
1162 # If $EXCEPT is set as username(s) delimited by comma,
1163 # remove $EXCEPT from list: ....NOT IN ($EXCEPT)
1164 for e; do
1165 if isuser "$e"; then
1166 em=`query "select val from user_m where name='$e' and key='email';"`
1167 [ -n "$em" ] && echo "$em" || echo "$e"
1168 else
1169 # sql="with recursive allmem as
1170 # (select gname,val from grp_m where gname='$1'
1171 # union all select grp_m.gname,grp_m.val from grp_m,allmem
1172 # where allmem.val=grp_m.gname)
1173 # select coalesce(b.val,a.val)
1174 # from allmem a left join grp_mem_s b
1175 # on a.gname=b.gname and a.val=b.user and b.key='email'
1176 # where a.val in (select name from user) limit 10000;"
1177 # tmp tmp tmp tmp tmp tmp tmp
1178 qgrp=`sqlquote "$e"`
1179 if [ -z "$TEAM" ]; then
1180 gmem="grp_mem"
1181 else
1182 tm=`sqlquote "$TEAM"`
1183 gmem="(SELECT gname, user FROM grp_mem_m WHERE gname='$e' AND key='team' AND val=$tm)"
1184 fi
1185 ex=${EXCEPT:+"AND g.user NOT IN ($EXCEPT)"}
1186 sql="select coalesce(s.val,um.val,g.user) from
1187 $gmem g left join grp_mem_s s
1188 on g.gname=s.gname and g.user=s.user and s.key='email'
1189 left join user_m um on g.user=um.name and um.key='email'
1190 where g.gname=$qgrp $ex;"
1191 err CollectEmail: `echo "$sql"`
1192 query "$sql"
1193 fi
1194 done
1196 sendinvitation() (
1197 # $1=email
1198 iss="invite-`date +%s`-$user"
1199 addsession $iss +${memoplimitdays}days # 1 week due date
1200 query "replace into par values('$iss', 'invite', 'string', \"$1\");"
1201 gecos=`gecos`
1202 name=$user"${gecos:+($gecos)}"
1203 regist="$urlbase?reg+$iss"
1204 _m4 -D_URL_="$urlbase" \
1205 -D_USER_="$name" \
1206 -D_EMAIL_="$1" \
1207 -D_REGIST_="$regist" \
1208 -D_ADMIN_="$admin" \
1209 $msgdir/mail-invite.m4 \
1210 | smail $1 "BBSへの御招待"
1211 return 0
1213 emaildomaincheck() {
1214 case "$1" in
1215 *@*@*) echo "無効なアドレスです"; return 1 ;;
1216 *@*)
1217 local=${1%@*} domain=${1#*@}
1218 if ! host $domain >/dev/null 2>&1; then
1219 echo "ドメイン($domain)が見付かりません。"
1220 return 2
1221 fi
1222 return 0
1223 ;;
1224 *) echo "正しいメイルアドレスをいれてください"; return 3 ;;
1225 esac
1227 invite() {
1228 email=`getpar email`
1229 case $email in
1230 *@*@*) repo="無効なアドレスです" ;;
1231 *@*)
1232 local=${email%@*} domain=${email#*@}
1233 if ! repo=`emaildomaincheck $email`; then
1234 repo="招待アドレスのエラー: $repo"
1235 elif [ -n "`query \"select * from user where name='$email';\"`" ]; then
1236 repo="$email さんは既に加入しています。"
1237 elif sendinvitation $email; then
1238 repo="アドレス($email)宛に案内を送信しました。"
1239 fi ;;
1240 "") repo="招待したい人のメイルアドレスを入力してください。" ;;
1241 *) repo="無効なアドレスです" ;;
1242 esac
1243 addr=`query "select val from par where sessid like 'invite-%-$user';"`
1244 err addr=$addr
1245 if [ -n "$addr" ]; then
1246 susp="<h2>招待済みで加入待ちのアドレス</h2><pre>$addr</pre>"
1247 fi
1248 _m4 -D_TITLE_="招待" -D_REPORT_="\`$repo'" -D_ACTION_="?invite" \
1249 -D_BODYCLASS_="default" -D_SUSPENDED_="$susp" \
1250 $layout/html.m4.html $layout/invite.m4.html
1252 regist() {
1253 # $1=session-id-for-invitation
1254 _m4 -D_TITLE_="Invitation" $layout/html.m4.html
1255 if [ -z "$1" ]; then
1256 echo "bye bye" | html p
1257 reutrn
1258 fi
1259 email=`session=$1 getpar invite`
1260 if [ -z "$email" ];then
1261 cat<<EOF
1262 <p>無効な招待状チケットです。</p>
1263 <p>招待状の有効期限(1週間)が切れているか、チケット番号が異なっています。
1264 加入している人に、再度招待してもらいましょう。</p>
1265 EOF
1266 return
1267 fi
1268 echo "$email さんようこそ" | html h2
1269 query "replace into user values('$email');"
1270 # Fake login password to wasureta
1271 query "replace into par values('$session', 'pswd', 'string', 'wasureta'),
1272 ('$session', 'user', 'string', '$email');"
1273 wasureta $email
1274 echo "このアドレスに初期パスワードを送信しました。" |html p
1275 echo "新着メイルを確認してログインしてください。" |html p
1276 addsession $1 # for removal after 1 minute
1277 _m4 -D_SYSNAME_="Initial Login" -D_MYNAME_="$myname?userconf" \
1278 $layout/login.m4.html
1279 return
1281 group_safename() {
1282 # Convert $1 to safe group name
1283 echo "$1" | tr -d '"'"'",
1285 groupupdate() {
1286 gname=`getpar gname`
1287 qgname=`sqlquote $gname`
1288 err Enter:groupupdate
1289 if [ -n "$gname" ]; then
1290 # See ALSO same job in showgroup()
1291 newgname=`group_safename "$gname"`
1292 err newgname=$newgname
1293 if [ x"$newgname" != x"$gname" ]; then
1294 err NewGNAME: gname=$newgname
1295 gname=$newgname
1296 echo "使用禁止文字を除去し $gname としました。" | html p
1297 replpar gname string "$gname"
1298 fi
1299 # Name confliction check
1300 parow=`getpar rowid`
1301 err parow=$parow
1302 qgname=`sqlquote $gname` # Set again in case gname modified
1303 query "BEGIN EXCLUSIVE;"
1304 err "select count(gname) from grp where rowid != ${parow:-0} and gname = $qgname;"
1305 count=$(query "select count(gname) from grp where rowid != ${parow:-0} and gname = $qgname;")
1306 if [ $count -gt 0 ]; then
1307 echo "そのグループ名は既にあります。" | html p
1308 query "END;"
1309 return
1310 fi
1311 par2table $formdir/grp.def
1312 query "END TRANSACTION;"
1313 # Remove orphan
1314 : <<EOF
1315 select a.id,b.val from (select * from blog where id in
1316 (select id from blog_s where key='owner'
1317 and val not in (select name from user union select gname from grp)))
1318 a left join blog_s b on a.id=b.id and b.key='owner';
1319 EOF
1320 rm=`getpar rm` cfm=`getpar confirm`
1321 err groupupdate:::: after par2tbl rmcfm=$rm$cfm
1322 if [ x"$rm$cfm" = x"yesyes" ]; then
1323 if [ -z "`query \"select gname from grp where gname=$qgname;\"`" ]; then
1324 sql="delete from blog where id in
1325 (select id from blog_s where key='owner' and val=$qgname);"
1326 err rm-grp cleaning sql=`echo $sql`
1327 query "$sql";
1328 fi
1329 fi
1330 [ -z "$parow" ] && joingrp "$gname" "$user" yes "$user" as-admin
1331 fi
1332 sql="select rowid from grp where gname=$qgname;"
1333 grid=$(query $sql)
1334 err grpupdate:new-grid=$grid, sql=$sql
1335 grp $grid
1337 groupman() {
1338 note="<p>グループ名に使用できない文字は自動的に削除されます。</p>"
1340 GF_STAGE="grpconf"
1341 GF_STAGE=groupupdate
1342 DT_VIEW=grp dumptable html grp 'gname gecos:DESC mtime:TIME' 'order by b.TIME desc' \
1343 |_m4 -D_TITLE_="グループ作成" \
1344 -D_FORM_="$note`genform $formdir/grp.def`" \
1345 -D_DUMPTABLE_="syscmd(cat)" \
1346 $layout/html.m4.html $layout/form+dump.m4.html
1348 userconf() {
1349 [ -n "`getpar rowid`" ] && par2table $formdir/user.def
1350 _m4 -D_BODYCLASS_=userconf -D_TITLE_="ユーザ情報編集" $layout/html.m4.html
1351 GF_ACTION="?home" edittable "$formdir/user.def" "user" "$user"
1353 groupconf() {
1354 # $1=rowid in grp (2015-07-21 changed from gname)
1355 [ -n "`getpar rowid`" ] && par2table $formdir/grp.def
1356 _m4 -D_BODYCLASS_=groupconf -D_TITLE_="グループ情報編集" $layout/html.m4.html
1357 #rowid=`query "select rowid from grp where gname='$1';"`
1358 rowid=${1%%[!A-Z0-9a-z_]*}
1359 err gcon \$1=$1 rowid=$rowid
1360 # GF_ACTION="?grp+$1" edittable "$formdir/grp.def" "grp" "$rowid" #2015-0804
1361 GF_STAGE="groupupdate" edittable "$formdir/grp.def" "grp" "$rowid"
1363 mems() {
1364 _m4 -D_TITLE_="参加者一覧" -D_BODYCLASS_=listmember $layout/html.m4.html
1365 kwd=`getpar kwd`
1366 listmember $kwd
1368 grps() {
1369 _m4 -D_TITLE_="グループ一覧" -D_BODYCLASS_=listgroup $layout/html.m4.html
1370 kwd=`getpar kwd`
1371 listgroup $kwd \
1372 | _m4 -D_DUMPTABLE_="syscmd(cat)" \
1373 -D_TITLE_="グループ関連操作" \
1374 -D_FORM_="<a href=\"?groupman\">新規グループ作成</a>" \
1375 $layout/form+dump.m4.html
1377 grp() { # $1=group-rowid
1378 gpg=`getpar grp`
1379 grid=${1:-$gpg}
1380 grp=`getgroupbyid "$grid"`
1381 err grp: getpar-grp"(gpg)=[$grp]"
1382 ## . ./s4-blog.sh
1383 jg=`getpar joingrp`
1384 if [ -n "$jg" ]; then
1385 err jg=$jg, grp=$grp
1386 [ -n "$jg" -a -n "$grp" ] &&
1387 joingrp "$grp" "$user" "$jg" "`getpar email`"
1388 fi
1389 htmlheader=$layout/html.m4.html
1390 showgroup "$grid"
1392 listnewblogsql() { # $1=user
1393 cat<<EOF
1394 `sql4readableblogs`
1395 with article_ctime as (
1396 select id,blogid,author,val ctime
1397 from article join article_s s using(id)
1398 where s.key='ctime'
1399 ), blog_title_owner as (
1400 select blg.rid brid, id,
1401 max(case key when 'title' then val end) title,
1402 max(case key when 'owner' then val end) owner
1403 from readableblogs blg, blog_s using(id) group by id
1404 ), acclog_user as (
1405 select * from acclog where user='$user' and tbl='blog'
1406 ), blogall as (
1407 select b.id, brid, b.owner, b.title, ctime, ac.author
1408 from blog_title_owner b, article_ctime ac
1409 on b.id=ac.blogid
1411 select brid LINK,
1412 sum(max(coalesce(l.time, "0"),
1413 coalesce((SELECT time FROM acclog_user
1414 WHERE tblrowid=$blogreadflagrowid), "0"))
1415 < ctime
1416 ) "新着",
1417 count(id) "総数", ctime, title,
1418 /* owner gecos */
1419 (select gecos from gecoses where name=bl.owner) gecos
1420 from blogall bl left join acclog_user l
1421 on bl.brid=round(l.tblrowid)
1422 group by bl.id
1423 having "新着" > 0 order by ctime desc, "新着" desc, bl.id
1424 limit 10;
1425 EOF
1428 search_form() {
1429 cat<<-EOF
1430 <div class="fr">
1431 <form action="$myname">
1432 <input type="text" name="kwd" value=""
1433 placeholder=" 全記事からの検索 " width="10">
1434 <input type="hidden" name="stage" value="searchart">
1435 </form>
1436 </div>
1437 EOF
1440 showhome() {
1441 # $1=userRowIdToShow
1442 err showhome \$1=$1
1443 case "$1" in
1444 *@*) uname=`getvalbypkey user name "$1"` ;;
1445 *) uname=`getvalbyid user name $1` ;;
1446 esac
1447 err ShowHome: uname=$uname
1448 gecos=`gecos "$uname"`
1449 err SH:gecos=$gecos
1450 GF_VIEWONLY=1
1451 cond="gname in (select gname from grp_mem where user='$uname')"
1452 if [ x"$user" = x"$uname" ]; then
1453 conflink="<a href=\"?userconf\">プロフィールの編集</a> /
1454 <a href=\"?blog\">新規話題の作成</a>"
1455 # Display folders
1456 sql="select count(id) from article_m where id
1457 in (select id from article where author='$user')
1458 and type like 'file:%';"
1459 err nfile-sql=`echo "$sql"`
1460 nfile=`query "$sql"`
1461 err nfile=$nfile
1462 if [ $nfile -gt 0 ]; then
1463 conflink="$conflink /
1464 <a href=\"?lsmyfile\">過去の提出ファイル</a>"
1465 fi
1466 fi
1467 . ./s4-blog.sh
1469 tf=$tmpd/title.$$ pf=$tmpd/profile.$$ bf=$tmpd/blogs.$$ sf=$tmpd/search.$$
1470 search_form > $sf
1471 echo "$gecos さん" > $tf
1472 viewtable $formdir/user.def user $1 > $pf
1474 sqcond="WHERE name='$uname' AND key='profimg' AND type LIKE 'file:image%'"
1475 img=`query "SELECT type FROM user_m $sqcond LIMIT 1;"`
1476 imf=$tmpd/profimg.$$; touch $imf
1477 if [ -n "$img" ]; then
1478 { printf '%s' "<img src=\"data:${img#file:},"
1479 query "SELECT hex(bin) FROM user_m $sqcond LIMIT 1;" \
1480 | sed 's/\(..\)/%\1/g'
1481 echo '">'
1482 } > $imf
1483 fi
1484 nblog=`query "SELECT count(id) FROM blog_s WHERE key='owner' AND \
1485 val='$uname';"`
1486 listblog $uname > $bf
1488 hometail=$tmpd/tail.$$
1489 mkfifo $hometail
1491 #Calling listgroupbytable, originally here
1494 if [ x"$user" = x"$uname" ]; then
1495 # Display Most Recent Entry
1496 shortval=${dumpcollen:+"substr(val, 0, $dumpcollen)"}
1497 shortval=${shortval:-val}
1499 # The m.aid in the next line is suspicious. But works fine in SQLite3...
1500 DT_SQL="SELECT b.rowid || '#' || m.aid LINK,
1501 ctime,
1502 (SELECT $shortval FROM blog_s WHERE key='title' AND id=b.id) title,
1503 (SELECT gecos FROM gecoses
1504 WHERE name=(SELECT val FROM blog_s
1505 WHERE key='owner' AND id=b.id)) owner,
1506 (SELECT $shortval val FROM article_s WHERE id=m.aid AND key='text') text
1507 FROM blog b
1508 JOIN
1509 (SELECT distinct blogid, a.id aid, max(val) ctime
1510 FROM article a, article_s s
1511 ON a.id=s.id AND a.author='$user' AND s.key='ctime'
1512 GROUP BY blogid ORDER BY val DESC LIMIT 50
1513 ) m
1514 ON b.id=m.blogid;"
1515 # This should be as follows
1516 : <<EOF
1517 WITH arts AS(
1518 SELECT (SELECT rowid FROM blog WHERE id=a.blogid) brid,
1519 a.blogid, a.id id, s.val ctime
1520 FROM article a NATURAL JOIN article_s s
1521 WHERE s.key = 'ctime' AND a.author='$user'
1522 GROUP by s.id
1524 SELECT a0.brid,a0.blogid,a0.id,a0.ctime
1525 FROM arts a0
1526 JOIN
1527 (SELECT blogid,max(ctime) mct FROM arts a1 GROUP BY blogid) a1
1528 ON a0.blogid=a1.blogid AND a0.ctime=a1.mct
1529 ORDER BY ctime DESC LIMIT 50;
1530 EOF
1532 cat<<-EOF
1533 `cgi_radio foldtabs yes id="mre"`<label
1534 for="mre">最近の書き込み先</label>
1535 <div class="lcto">
1536 `DT_VIEW=replyblog dumptable html blog`
1537 </div>
1538 EOF
1539 unset DT_SQL
1540 # Display NEWS
1541 if false; then
1542 cond="where 新着 > 0 order by 新着 desc,ctime desc limit 10"
1543 new10=`DT_CHLD=article:blogid \
1544 DT_VIEW=replyblog dumptable html blog "ctime title gecos" "$cond"`
1545 else
1546 # 2016-06-26
1547 if [ x"`getpar readchk``getpar read`" = x"yesyes" ]; then
1548 acclog blog $blogreadflagrowid
1549 # echo "全部既読にしました" | html p
1550 fi
1551 # 2016-02-19 Counting NEWS without using dumptable.
1552 sql=`listnewblogsql "$user"`
1553 new10=`DT_SQL="$sql" DT_VIEW=replyblog dumptable html blog`
1554 fi
1555 cont=`echo "$new10"|grep "^<TR>"|wc -l`
1556 cont=$((cont-1))
1557 err newcount=$cont
1558 if [ $cont -gt 0 ]; then
1559 #echo "全体の新着記事${cont}傑" | html h2
1560 cgi_radio foldtabs yes 'id="new10"'
1561 echo "<label for=\"new10\">新着${cont}傑</label><div>"
1562 cat<<-EOF | html form 'action="?home"'
1563 `cgi_checkbox readchk yes 'id="read"'`<label
1564 for="read">新着ふくめて全部読んだことにする</label>
1565 `cgi_submit '確定'`
1566 `cgi_hidden read yes`
1567 EOF
1568 echo "$new10 <!-- new10 -->"
1569 echo "</div>"
1570 fi
1571 fi
1572 ) > $hometail & # Is background call safe to m4??
1574 listgroupbytable $formdir/grp.def $cond |
1575 _m4 -D_BODYCLASS_=home -D_TITLE_="spaste(\`$tf')" \
1576 -D_PROFILE_="spaste(\`$pf')$conflink" \
1577 -D_PROFIMG_="spaste(\`$imf')" \
1578 -D_BLOGS_="spaste(\`$bf')" \
1579 -D_SEARCH_="spaste(\`$sf')" \
1580 -D_NBLOG_="$nblog" \
1581 -D_GROUPS_="syscmd(\`cat')" \
1582 -D_HOMETAIL_="syscmd(\`cat $hometail')" \
1583 $layout/html.m4.html $layout/home.m4.html
1585 # Record access log
1586 [ -n "$1" ] && [ x"$1" != x"$user" ] && acclog user $1
1588 commission() { # $1=grp-rowid $2=user-rowid
1589 contenttype; echo
1590 err commission: "$@"
1591 gname=`getgroupbyid $1`
1592 echo "グループ $gname 管理者委任" \
1593 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1594 if [ -n "$2" ]; then
1595 grp_reg_adm "$@"
1596 else
1597 echo "無効な指定です。普通のアクセスならここに来ないはず。"|html p
1598 fi
1600 listgroupbytable() {
1601 # $1=deffile $2...=condition
1602 tagline=`grep :tag: $1`; shift
1603 and="${1:+and }" where=${1:+where }
1604 href="<a href=\"$myname?grp+"
1605 echo '<div class="listgroup">'
1606 NGsql="select distinct tag from\
1607 (select gname, max(case key when 'tag' then val end) as tag, \
1608 max(case key when 'ctime' then val end) as ctime\
1609 from grp_s group by gname order by ctime);"
1610 sql="select val from grp_s where key='tag' $and$* group by val;"
1611 err ListGRP: query sql="$sql"
1612 for tag in `query "$sql"`
1613 do
1614 err ListGrp: tag=$tag
1615 tn=${tagline%%=${tag}*}
1616 tn=${tn##*[ :]}
1617 sql="select rowid||':'||gname as 'グループ名',説明 from
1618 (select (select rowid from grp g where g.gname=grp_s.gname)
1619 as rowid,
1620 gname,
1621 max(case key when 'gecos' then val end) as '説明',
1622 max(case key when 'tag' then val end) as 'tag',
1623 max(case key when 'mtime' then val end) as mtime from grp_s
1624 $where$* group by gname having tag='$tag' order by mtime desc);"
1625 err PersonalGroupList= `echo $sql`
1626 echo "<h2>$tn</h2>"
1627 echo '<table class="b listgroup">'
1628 sq -header -html $db "$sql" \
1629 | sed "s,\(<TR><TD>\)\([0-9]*\):\([^ ]*\)</TD>,\1$href\2\">\3</a>,"
1630 echo '</table>'
1631 done
1632 echo '</div>'
1634 iconhref() (
1635 # $1=icon-file, $2=Href $3=title $4...=anchor
1636 data=`percenthex $1`
1637 ct=`file --mime-type $1|cut -d' ' -f2`
1638 err iconhref: \$1=$1 \$2=$2 \$3="$@"
1639 href=$2; title=$3; shift 3
1640 echo "<a href=\"$href\"><img title=\"$title\" src=\"data:$ct,$data\">$@</a>"
1642 iconhref2() (
1643 # $1=icon-file, $2=Href $3=title $4...=anchor
1644 src=$1
1645 href=$2; title=$3; shift 3
1646 echo "<a href=\"$href\"><img title=\"$title\" src=\"$src\">$@</a>"
1648 listentry() (
1649 # $1=user/group $2=SearchKeyword $3=condition(if any) $4=grprowid(if in grp)
1650 # Referring variable $iamowner=$grp to attach owner-request links
1651 err listentry: \$1=$1 \$2=$2 \$3=$3
1652 cond='' hiddens=''
1653 offset=`getpar offset`
1654 offset=${offset%%[!0-9]*}
1655 offset=$((offset + 0)) # change to numeric forcibly
1656 [ $offset -lt 0 ] && offset=0
1657 limit=30
1658 dir=`getcachedir "$1"`
1659 if [ x"$1" = x"user" ]; then
1660 hrb="$myname?home"
1661 deficon=person-default.png
1662 entity="ユーザ" tbl=user link=rowid nm=name # stage=mems
1663 [ -n "$4" ] && hiddens=`cgi_hidden grid $4`
1664 gcs=gecos
1665 else # if group
1666 hrb="$myname?grp"
1667 deficon=group-default.png
1668 entity="グループ" tbl=grp link=rowid nm=gname stage=grps
1669 gcs=name
1670 tagline=`grep :tag: $formdir/grp.def|cut -d: -f5-`
1671 if [ -n "$tagline" ]; then
1672 tagconv=`echo $tagline|sed 's/\([^= :]*\)=\([^= :]*\)/-D\2=\1/g'`
1673 err tagconv=$tagconv
1674 fi
1675 fi
1676 if [ ! -d $dir ]; then
1677 mkdir -p $dir
1678 fi
1679 if [ ! -s $dir/$deficon ]; then
1680 convert -geometry $thumbxy $imgdir/$deficon $dir/$deficon
1681 fi
1682 if [ -n "$2" ]; then
1683 cond="where nick like '%$2%' or b.name like '%$2%'"
1684 fi
1686 # XX: これ複雑すぎるかな。もっとシンプルにしたい。$3条件も。2015-07-08
1687 # grpは呼出し元の動的スコープ変数でよくないな...
1688 ##qgrp=`sqlquote $grp`
1689 getgrp="(select gname from grp where rowid=${rowid:--1})"
1690 sql="select a.rowid, a.$link,
1691 coalesce(b.$gcs, a.$nm) as nick,
1692 coalesce(b.gecos, a.$nm) /* If group, concat (Nusers) */
1693 || case when a.$nm in (select gname from grp)
1694 then printf('(%d名)',
1695 (select count(user) from grp_mem where gname=a.$nm))
1696 else ' <'||a.$nm||'>'
1697 end
1698 as name,
1699 b.tag,
1700 case when a.$nm in (select user from grp_adm
1701 where gname=$getgrp) then '(管理者)'
1702 when '$user' in (select user from grp_adm where gname=a.$nm)
1703 then '(ADMIN)'
1704 when '$user' in (select user from grp_mem where gname=a.$nm)
1705 then '(Member)'
1706 when '$iamowner' = '' then ''
1707 else ',not='||a.rowid end as ownerlink,
1708 CASE '$entity'
1709 WHEN 'グループ'
1710 THEN coalesce(
1711 (SELECT val FROM grp_s WHERE gname=a.$nm AND key='regmode'),
1712 'open')
1713 ||
1714 CASE WHEN '$user'
1715 IN (SELECT user FROM grp_mem WHERE gname=a.$nm)
1716 THEN ' ismember'
1717 ELSE ''
1718 END
1719 ELSE 'user'
1720 END regmode
1721 from $tbl a left join
1722 (select $nm as name,
1723 max(case key when 'gecos' then val end) as gecos,
1724 max(case key when 'tag' then val end) as tag
1725 from ${tbl}_s group by $nm)
1726 b on a.$nm=b.name $cond $3
1727 order by b.tag desc, a.rowid asc"
1728 err LE:sql.1="$sql"
1729 total=`query "with x as ($sql) select count(*) from x;"`
1730 echo "${entity} 一覧" | html h2
1731 if [ $total -gt $limit ]; then
1732 echo '<div class="right">'
1733 cgi_form $stage <<EOF
1734 <label>次の語を含む${entity}で検索:
1735 `cgi_text kwd $kwd`</label>
1736 EOF
1737 echo '</div>'
1738 fi
1739 hiddens="$hiddens
1740 `cgi_hidden kwd \"$kwd\"`
1741 `cgi_hidden stage \"$stage\"`"
1742 cat<<EOF
1743 <p>${total}件中の$((offset+1))件めから${kwd:+" - 検索語: $kwd"}</p>
1744 EOF
1745 if [ $((offset+limit)) -lt $total ]; then
1746 nextbtn=$(
1747 cat<<EOF
1748 <div class="right clear"><form action="$myname" method="POST">
1749 `cgi_submit 次の${limit}件`
1750 $hiddens
1751 `cgi_hidden offset $((offset + limit))`</form></div>
1752 EOF
1754 fi
1755 if [ $offset -gt 0 ]; then
1756 prevbtn=$(
1757 cat<<EOF
1758 <form action="$myname" method="POST">
1759 `cgi_submit 前の${limit}件`
1760 $hiddens
1761 `cgi_hidden offset $((offset - limit))`</form>
1762 EOF
1764 fi
1765 pnbtn="$nextbtn$prevbtn"
1766 echo $pnbtn
1768 err ListEntry: `echo "$sql"\;`
1769 query "$sql limit $limit ${offset:+offset $offset};" \
1770 | while IFS='|' read id lnk name gecos tag ownerp type; do
1771 err name=$name owner=$ownerp lnk=$lnk
1772 err newlnk=$lnk regmode=$regmode
1773 icondir=$dir/$id
1774 files=`getvalbyid $tbl profimg $id $icondir`
1775 # Pick up only first icon
1776 echo "<div class=\"iconlist xy$thumbxy $type\">
1777 <p class=\"tag _$tag\">$tag</p>" \
1778 | _m4 $tagconv
1779 if [ -n "$files" ]; then
1780 icon=`echo "$files"|head -1`
1781 iconhref2 $icondir/$icon "$hrb+$lnk" "$gecos"
1782 else
1783 iconhref $dir/$deficon "$hrb+$lnk" "$gecos"
1784 fi
1785 echo "<br>$name${ownerp:+<br>$ownerp}"
1786 echo "</div>"
1787 done
1788 echo ${pnbtn:+"<hr>$nextbtn$prevbtn"}
1790 listmember() {
1791 listentry user "$@"
1793 listgroup() {
1794 listentry group "$@"
1796 hexteams() { # $1=gname, $2(optional)=user
1797 cond=${2:+" AND user='$2'"}
1798 query "SELECT DISTINCT hex(val) FROM grp_mem_m
1799 WHERE gname='$1' AND key='team'$cond;"
1801 showgroup() { # $1=group-rowid
1802 # gname=`getpar gname`
1803 # if [ -n "$gname" ]; then
1804 # err UPdating/Removing of group:::::::
1805 # # See ALSO same job in groupman()
1806 # newgname=`echo "$gname"|tr -d '\"'"'"`
1807 # ###newgname=$gname
1808 # err gname=$gname newgname=$newgname
1809 # if [ x"$newgname" != x"$gname" ]; then
1810 # err NewGNAME: gname=$newgname
1811 # gname=$newgname
1812 # echo "使用禁止文字を除去し $gname としました。" | html p
1813 # replpar gname string "$gname"
1814 # fi
1815 # par2table $formdir/grp.def
1816 # fi
1817 if [ -z "$1" ]; then
1818 grid=`getpar grid`
1819 grid=${grid%%[!0-9]*}
1820 [ -n "$grid" ] && grp=`getgroupbyid $grid`
1821 else
1822 grid=$1
1823 fi
1824 grp=`getgroupbyid $grid`
1825 qgrp=`sqlquote $grp`
1826 err showgroup2: grp=$grp qgrp="[$(sqlquote $grp)]"
1827 if isgroup "$grp"; then
1828 tf=$tmpd/title.$$
1829 bodyclass=`query "SELECT val FROM grp_s
1830 WHERE gname=$qgrp AND key='regmode';"`
1831 if ismember "$user" "$grp"; then
1832 ismember="ismember"
1833 qgrp=`sqlquote $grp`
1834 bodyclass="$bodyclass${bodyclass:+ }ismember"
1835 else
1836 ismember="" # bodyclass="group"
1837 fi
1838 echo "グループ $grp" > $tf
1839 showgroupsub $formdir/grp.def "$grid" | \
1840 _m4 -D_TITLE_="spaste(\`$tf')" \
1841 -D_FORM_="syscmd(\`cat')" \
1842 -D_BODYCLASS_="$bodyclass" \
1843 -D_DUMPTABLE_="" \
1844 $htmlheader $layout/form+dump.m4.html
1845 # $htmlheader is defined in grp()
1846 else # if $grp is removed at par2table
1847 listgroup
1848 fi
1850 showgroupsub() {
1851 # $1=def-file $2=group-rowid
1852 # Using $ismember
1853 rowid=$2
1854 grp=`getgroupbyid $2`
1855 qgrp=`sqlquote $grp`
1856 #rowid=`sq $db "select rowid from grp where gname=$qgrp"`
1857 if [ -z "$rowid" ]; then
1858 #rowid=`sq $db "select rowid from grp where rowid=$grp"`
1859 #grp=`sq $db "select gname from grp where rowid=$grp"`
1860 echo "showgroupsub: invalid argument($1 $2)" | html p
1861 return
1862 fi
1863 val=`getvalbyid grp profimg $rowid $tmpd`
1864 # 6/14の次グループのHOMEで出す情報を作る Done
1865 viewtable $1 grp $rowid
1866 if isgrpowner "$user" "$grp"; then
1867 echo "<p><a href=\"?groupconf+$rowid\">グループ情報の編集</a>"
1868 iamowner=$rowid
1869 fi
1870 if [ -n "$ismember" ]; then
1871 echo "${iamowner:+ / }<a href=\"?blog+$rowid\">グループの新規話題作成</a>"
1872 echo "/ <a href=\"?grpaction+$rowid\">メンバーを個別選択しての操作</a></p>"
1873 # div.fold input[type="checkbox"]:checked ~ div {display: block;}
1874 cat<<EOF
1875 <form action="?send2mem" method="POST" enctype="multipart/form-data">
1876 <div class="fold">
1877 `cgi_checkbox send yes id="send"`<label
1878 for="send">グループ全員にメッセージ送信</label>
1879 <div>
1880 `cgi_textarea message "" "cols=60"`
1881 `cgi_submit 送信`
1882 `cgi_reset リセット`
1883 </div>
1884 `cgi_hidden grp $rowid`
1885 </div></form>
1886 EOF
1887 fi
1888 # 加入ボタン + 加入者リスト
1889 err ismember $user $grp
1890 if [ -n "$ismember" ]; then
1891 ismem='checked' state="(参加中)"
1892 else
1893 nomem='checked' state="(現在非加入)"
1894 fi
1895 # このグループでの加入アドレス
1896 eml=`query "select val from grp_mem_s where gname=$qgrp and user='$user' \
1897 and key='email';"`
1898 err EML: "select val from grp_mem_s where gname='$2' and user='$user' \
1899 and key='email';"
1900 err email=$eml
1901 cat <<EOF
1902 <div class="fold">
1903 `cgi_checkbox reg yes id="reg"`<label
1904 for="reg">自身の加入状態を操作する</label>$state
1905 <div>
1906 EOF
1907 cgi_form grp <<EOF
1908 <p>このグループに</p>
1909 <table class="b">
1910 <tr><th>メンバーとして</th><td>
1911 <label>`cgi_radio joingrp "yes" $ismem`参加</label> /
1912 <label>`cgi_radio joingrp "no" $nomem`参加しない</label></td></tr>
1913 <tr><th>参加する場合のメイルアドレス<br>
1914 <small>(メインのアドレスとは違うものにする場合に記入<br>
1915 同じでよい場合は空欄に)</small></th>
1916 <td>`cgi_text email $eml`</td></tr>
1917 </table>
1918 `cgi_hidden grp $rowid`
1919 EOF
1920 if [ x`getgroupattr $grp regmode` = x'moderated' -a -z "$ismem" ]; then
1921 echo "moderated (承認加入の)グループなので実際に参加できるのは
1922 グループ管理者が承認操作をした後になります。" | html p 'class="warn"'
1923 fi
1924 echo '</div></div>'
1925 echo '<h2>話題一覧</h2>'
1926 cgi_form searchart<<EOF
1927 <label>`cgi_text kwd`という語を含むコメントを検索</label>
1928 `cgi_hidden owner $grp`
1929 EOF
1930 cond="where a.id in (select id from blog_s where key='owner' and val=$qgrp) order by ctime desc"
1931 DT_CHLD=article:blogid \
1932 DT_VIEW=replyblog dumptable html blog 'ctime team title heading' "$cond"
1934 getgname="(select gname from grp where rowid=$rowid)"
1935 c="group by a.name having a.name in (select user from grp_mem where gname=$getgname)"
1936 cm="?commission+$rowid"
1937 thumbxy=50x50 listmember "" "$c" "$rowid" \
1938 |sed -e "s|\(<br>\),not=\(.*\)|\1|" # 間違って押しやすい
1939 # team list
1940 hexteams=`hexteams "$grp"`
1941 if [ -n "$hexteams" ]; then
1942 echo "チーム一覧" | html h2
1943 echo '<div class="dumptable"><table class="b">'
1944 sq $db -html -header<<-EOF
1945 SELECT val TEAM,
1946 group_concat((SELECT gecos FROM gecoses WHERE name=user), ',')
1947 MEMBERS
1948 FROM grp_mem_m WHERE gname=$qgrp AND key='team' GROUP BY val;
1949 EOF
1950 echo '</table></div>'
1951 fi
1953 grpaction() { # $1=group-rowid
1954 err GRP_ACTION:IN
1955 grid=${1:-`getpar grp`}
1956 grp=`getgroupbyid "$grid"`
1957 if [ -z "$grp" ]; then
1958 echo "無効な指定です。" | html p; return
1959 fi
1960 if ! ismember $user $grp; then
1961 echo "加入者のみに許可された操作です。" | html p; return
1962 fi
1963 echo "グループ $grp 個別選択操作" \
1964 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1966 isowner=""
1967 isgrpowner "$user" "$grp" && isowner="yes"
1968 usel=`getpar usel`
1969 if [ -n "$usel" ]; then
1970 uids=$(echo `echo $usel`|tr ' ' ',')
1971 err grpaction-1: grp=$grp, `echo $sql`
1972 text=`getpar text`
1974 rm=`getpar rm` cfm=`getpar confirm`
1975 err rm=$rm cfm=$cfm
1976 if [ x"$rm" = x"yes" ]; then
1977 if [ "$isowner" ]; then
1978 if [ x"$rm$cfm" = x"yesyes" ]; then
1979 # Eliminate
1980 cond="where gname=(select gname from grp where rowid=$grid) and user in (select name from user where rowid in ($uids))"
1981 for tbl in grp_mem grp_mem_s grp_mem_m; do
1982 sql="delete from $tbl $cond;"
1983 # echo "sql=$sql"
1984 query "$sql"
1985 err rmGRPuser "$sql"
1986 done
1987 num=`query "select count(*) from user where rowid in ($uids);"`
1988 #err num=$num
1989 if [ 0$num -gt 0 ]; then
1990 sql="select coalesce(b.val,a.name) from user a left join \
1991 user_s b on a.name=b.name and key='gecos' where a.rowid in ($uids);"
1992 # err `echo "$sql"`
1993 html pre<<EOF
1994 以下の${num}名のグループ $grp 登録を解除しました。
1995 `query "$sql"`
1996 EOF
1997 fi
1998 else
1999 echo "確認のチェックがないのでやめておきます。" | html p
2000 return
2001 fi
2002 else # not Group Owner
2003 echo "グループ管理者でないのでメンバー操作はできません。" | html p
2004 return
2005 fi
2006 cat<<EOF
2008 EOF
2009 elif [ x"$rm" = x"send" ]; then # if sendmsg mode
2010 if [ -z "$text" ]; then # if msg is empty
2011 echo "なにかメッセージを..." | html p
2012 return 0
2013 fi
2014 smail "`email4groupbyuid "$grp" $usel` $user" \
2015 "`gecos $user` さんからのメッセージ" <<EOF
2016 $url
2017 のグループ「$grp」のメンバーである `gecos $user` さんから、
2018 あなた宛へのメッセージです。
2019 ----------------------------------------------------------
2020 $text
2021 EOF
2022 if [ $? = 0 ]; then
2023 echo "Note: 以下のメンバーにメッセージを送信しました。" | html p
2024 sql="select coalesce(b.val, a.name) from
2025 (select name from user where rowid in ($uids)) a
2026 left join user_s b on a.name=b.name and b.key='gecos';"
2027 html pre<<EOF
2028 `query "$sql"`
2029 (送信者である `gecos $user` さんも含まれます)
2030 EOF
2031 err SendDone: `echo $sql`
2032 fi
2033 elif [ x"$rm" = x"commission" ]; then
2034 grp_reg_adm $grid $usel
2035 elif [ x"$rm" = x"addteam" ]; then
2036 team=`getpar team|sed "s/'/''/g"` # for single quotation
2037 newteam=`echo "$team"|tr -d ,`
2038 if [ x"$team" != x"$newteam" ]; then
2039 echo "チーム名に使えない文字を除去しました" | html p
2040 team=newteam
2041 fi
2042 if [ -z "$team" -o x"$team" = x"なし" ]; then
2043 cat<<-EOF | html p
2044 有効なチーム名を入力してください。
2045 カンマだけ、「なし」という名前は使えません。
2046 EOF
2047 echo "有効なチーム名を入力してください。" | html p
2048 else
2049 grp_add_team $grid "$team" $usel
2050 fi
2051 elif [ x"$rm" = x"rmteam" ]; then
2052 if [ x"yes" = x"`getpar teamconfirm`" ]; then
2053 rmteam=`getpar rmteam|sed "s/'/''/g"`
2054 if [ -n "`query \"SELECT val FROM grp_mem_m WHERE\
2055 gname='$grp' AND user='$user' AND key='team'\
2056 AND val='$rmteam';\"`" ]; then
2057 grp_rm_team $grid "$rmteam" $usel
2058 else
2059 echo "所属していないチームの除去操作はできません。"|html p
2060 fi
2061 else
2062 echo "確認チェックなしなのでチーム除去しませんでした。"|html p
2063 fi
2064 fi
2065 fi
2066 # New entry
2067 sql="select /* Ahh, ugly SQL, I wanna fix... */
2068 case
2069 when (select user from grp_adm where
2070 gname=(select gname from grp where rowid=$grid)
2071 and user=a.name) is not null
2072 then 'k'
2073 else ''
2074 end || a.rowid||
2075 ','||coalesce(val,a.name) as NAME,
2076 (SELECT count(author) /* Put post count for scoring 2016-08-01 */
2077 FROM article NATURAL JOIN article_s
2078 WHERE blogid IN
2079 (SELECT id FROM blog_s
2080 WHERE key='owner'
2081 AND val=(SELECT gname FROM grp where rowid=$grid))
2082 AND author=a.name AND key='text')
2083 as POST,
2084 (SELECT group_concat(val, ',')
2085 FROM grp_mem_m
2086 WHERE gname='$grp' AND user=a.name AND key='team') as TEAM
2087 from
2088 (select rowid,name from user where name in
2089 (select user from grp_mem where
2090 gname=(select gname from grp where rowid=$grid)))
2091 a left join user_s on a.name=user_s.name and key='gecos'
2092 order by coalesce(val,a.name);"
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>
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=\"?\""