s4

view s4-funcs.sh @ 496:18f7f10566bf

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