s4

view s4-funcs.sh @ 540:b61881cfe57f

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