s4

view s4-funcs.sh @ 508:944739c3ac5c

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