s4

view s4-funcs.sh @ 607:d0068e9e6bd7

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