s4

view s4-funcs.sh @ 571:27442a1d95c0

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