s4

view s4-funcs.sh @ 513:3fcaea2892a8

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