s4

view s4-funcs.sh @ 510:2a70b6c7ffad

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