s4

view s4-funcs.sh @ 512:24a5010a131b

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