s4

view s4-funcs.sh @ 542:0b38ca08718a

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