s4

view s4-funcs.sh @ 489:075897fee2c0

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