s4

view s4-funcs.sh @ 495:59cd8278a5b5

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