s4

view s4-blog.sh @ 411:e30fe590a53e

Allow replymark in other than the first line.
author HIROSE Yuuji <yuuji@gentei.org>
date Fri, 10 Feb 2017 12:22:09 +0859
parents c1b4a9dba1c9
children a765ed027dbd
line source
1 #
2 type cgiinit >/dev/null 2>&1 || . ./s4-funcs.sh
4 # Global error flags
5 BLOG_NOTMEM=1
6 BLOG_FROZEN=2
7 FROZEN_TAG='<span class="frozen">[凍結]</span>'
9 blog_genform() {
10 #
11 t=$1
12 }
14 blog_writable() (
15 # $1=articleid $2=user
16 # Return: $?=0 - Writable
17 # =1 - NOT Writable because user is not a member
18 # =2 - NOT Writable because blog is frozen
19 blogowner=`getvalbyid blog owner "$1"`
20 state=`getvalbyid blog state "$1"`
21 rc=0
22 [ x"$blogowner" = x"$2" ] || isuser "$blogowner" || ismember "$2" "$blogowner" || rc=$((rc+$BLOG_NOTMEM))
23 [ "$state" = "frozen" ] && rc=$((rc+$BLOG_FROZEN))
24 return $rc
25 )
26 blog_readable() {
27 # $1=articleid $2=user
28 mode=`getgroupattr $grp regmode`
29 }
30 blog_notify_reply() (
31 # $1=blogid $2=ReplyingUser $3=WrittenText $4(optional)=Action
32 blogid="${1%%[!A-Z0-9a-z_]*}"
33 blogowner=`getvalbyid blog owner "$blogid"`
34 [ x"$2" = x"$blogowner" ] && return # If author=blogowner, unnecessary
35 blogtitle=`getvalbyid blog title "$blogid"`
36 blogurl="$urlbase?replyblog+$blogid"
37 action=${4:-書き込み}
38 mode=`getvalbyid blog notify "$blogid"`
39 ### EXCEPT=`sqlquote "$user"` ## User should receive to feal some annoyance
40 case $mode in
41 admin)
42 if isgroup "$blogowner"; then
43 emails=`getgroupadminmails $blogowner`
44 else
45 emails=`collectemail $blogowner`
46 fi
47 notifyto=`getpar notifyto`
48 if [ -n "$notifyto" ]; then
49 emails=$emails" `email4groupbyuid \"$blogowner\" $notifyto`"
50 fi
51 ;;
52 no) emails="" ;;
53 *) team=`query "SELECT val FROM blog_s
54 WHERE id=(SELECT id FROM blog WHERE rowid=$blogid)
55 AND key='team';"`
56 # team cannot get `getvalbyid blog team "$blogid"` because it's not
57 # defined in blog.def. Yes, it is Illegal USE!!
58 emails=`TEAM=$team collectemail $blogowner` ;;
59 esac
60 ## 2017-0210 Respond to the direct reply mark such as: >#1234
61 replymark=`echo "$3"|nkf -Z0|grep '^ *>#'`
62 authgecos=`gecos $2`
63 if [ -z "$4" -a -n "$replymark" ]; then
64 # If the action is new subscription($4="") and has ">#123" marks...
65 ids=`echo "$replymark"|sed 's/[^#0-9]*#\([0-9]*\)[^#0-9]*/\1 /g'`
66 ids=`echo $ids|tr ' ' ','`
67 # -> 123,345,347
68 unames=`query "SELECT distinct author FROM article \
69 WHERE rowid in ($ids)\
70 AND blogid=(SELECT id FROM blog WHERE rowid=$blogid);"`
71 if [ -n "$unames" ]; then
72 emails=$emails" `email4group \"$blogowner\" $unames`"
73 action="あなたへの返信"
74 fi
75 fi
76 test -z "$emails" && return
77 err notify: user=$user Admins=`getgroupadmins $blogowner` Mode=$mode Emails="[$emails]"
78 SMAIL_TO="`echo "$blogowner" | nkf -jM | tr -d '\n'` readers <$admin>" \
79 smail "$emails" "${action}通知 $urlbase"<<EOF
80 [$blogtitle]板に${action}がありました。
81 場所: $blogurl
82 所有: $blogowner
83 題目: $blogtitle
84 筆者: $authgecos
85 内容:
86 `echo "$3"|sed 's/^/> /'`
87 EOF
88 )
90 blog_showentry() {
91 # $1=table $2=rowid
92 # if [ -n "$2" ]; then
93 # if [ -n "$imgcached" ]; then
94 # bstmpdir=$tmpdir/$imgcached/$thumbxy
95 # else
96 # bstmpdir=$tmpd
97 # # tmpd=`mktempd`
98 # # tmpfiles=$tmpfiles" $tmpd"
99 # fi
100 # fi
101 td=`getcachedir "article/$2"`
102 [ -d "$td" ] || mkdir -p $td
103 tbl=${1%%[!A-Z0-9a-z_]*} rowid=${2%%[!A-Z0-9a-z_]*}
104 err blow_showentry: rowid=$rowid, '$2'=$2 user=$user
105 ts=${tbl}_s tm=${tbl}_m
106 at=article as=article_s am=article_m
107 serial=$(($(date +%s)-1420038000))s$$
108 blog_writable $rowid $user
109 rc=$?
110 if [ $rc = 0 ]; then
111 iswritable=true
112 ismem=true
113 else
114 iswritable=false
115 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ]; then
116 ismem=false
117 else
118 ismem=true
119 fi
120 fi
121 # This function grasps blog entry definiton directly.
122 # blog: id
123 # blog_s: title,ctime,heading
124 # blog_m: *article
126 # 2015-10-05 check readable
127 if ! $iswritable; then
128 blogowner=`getvalbyid blog owner "$2"`
129 # err blogowner=$blogowner
130 if isgroup $blogowner; then
131 regmode=`getgroupattr $blogowner regmode`
132 # err regmode=$regmode
133 if [ x"$regmode" = x"moderated" ]; then
134 # if ! ismember $user $blogowner; then
135 if ! $ismem; then
136 echo "加入してからどうぞ" | html p
137 return
138 fi
139 fi
140 fi
141 fi
142 case `getvalbyid blog notify "$2"` in # "all", "admin" or "no" (or NULL)
143 admin) notifyto=1 ;;
144 *) notifyto="" ;;
145 esac
147 # err "SELECT id from $tbl where rowid=$rowid"
148 id=`query "select id from $tbl where rowid=$rowid;"`
149 #err id=$id
150 #err "select val from $ts where key='title' and id='$id';"
153 #(1)Display root article
154 cat<<EOF
155 <form class="replyblog" action="$myname?replyblog+${rowid}#bottom" method="POST" enctype="multipart/form-data">
156 <table class="bloghead">
157 EOF
159 href="<a href=\"?editheading+$rowid\" accesskey=\"e\" title=\"E\"> 編集 </a>"
160 if $ismem; then
161 case `getvalbyid blog mode $rowid` in
162 *report*)
163 href2="<a href=\"?lshandout+$rowid\" accesskey=\"l\" title=\"L\"> 提出状況 </a>"
164 ;;
165 esac
166 href3="(<a href=\"?gethandout+$rowid\" accesskey=\"f\" title=\"F\">ファイル取得</a>)"
167 fi
168 href4='<a href="#bottom" accesskey="b" title="B"> 末尾へ</a>'
170 query<<-EOF |
171 SELECT coalesce((SELECT "yes" FROM blog
172 WHERE rowid=$rowid AND author='$user'),
173 ''),
174 max(CASE key WHEN 'ctime' THEN val END) ctime,
175 max(CASE key WHEN 'heading' THEN hex(val) END) heading,
176 CASE (SELECT val FROM $ts WHERE key="mode" AND id="$id")
177 WHEN 'report-closed' THEN 'レポート提出用(closed)'
178 WHEN 'report-open' THEN 'レポート提出用(open)'
179 ELSE ''
180 END
181 FROM $ts WHERE id='$id' GROUP BY id;
182 EOF
183 { IFS='|' read edit ctime hexhead blogtype
184 cat<<-EOF
185 <tr><td>${edit:+$href }$ctime $blogtype $href2$href3 $href4</td></tr>
186 <tr class="preface${frozen_class:+ }$frozen_class">
187 <td>`echo "$hexhead"|unhexize|hreflink|minitbl`</td></tr>
188 </table>
190 <table class="blog_replies">
191 EOF
192 }
193 lkhome="<a href=\"$myname?home" lke='">'
194 lkedit="<a href=\"$myname?editart"
195 hlink="$myname?home" elink="$myname?editart"
196 catlink="$myname?showattc+article_m"
197 deficon="img/file-icon.png"
198 # 2016-08-15 Newer flag introduced
199 atime=`query "SELECT time FROM acclog
200 WHERE tbl='blog' AND tblrowid=$rowid AND user='$user';"`
201 iconcleaner=$tmpd/iconcleaner.$$
202 # *** DO NOT USE query() and use "sq $db" here ***
203 # because the next block in pipe line uses query() repeatedly.
204 sq $db<<EOF |
205 WITH a_s AS (
206 SELECT id,
207 max(CASE key WHEN 'ctime' THEN val END) TIME,
208 max(CASE key WHEN 'text' THEN val END) TEXT
209 FROM article_s
210 GROUP by id
211 )
212 SELECT a.id,
213 CASE author
214 WHEN '$user' THEN a.rowid||'+'||$rowid
215 ELSE ''
216 END edit,
217 CASE -- 「通知送信」ボタンの有無
218 WHEN '$notifyto' = '' THEN '' -- 不要モードならなし
219 WHEN '$user' = author THEN '' -- 筆者自身ならなし
220 ELSE "yes"
221 END notify,
222 (SELECT rowid FROM user WHERE name=author) user_rid,
223 coalesce((SELECT val FROM user_s
224 WHERE name=author AND key='gecos'),
225 author) uname,
226 (SELECT val FROM user_s WHERE name=author AND key='$iconcachekey')
227 icon,
228 a.rowid,
229 s.TIME,
230 CASE WHEN s.TIME > '$atime' THEN 'new' ELSE '' END newer,
231 hex(s.TEXT),
232 (SELECT group_concat(rowid||':'||length(bin)||':'||hex(val), ' ')
233 FROM article_m
234 WHERE id=a.id AND key='image') imxgids
235 FROM (select rowid,id,author from article where blogid in
236 (select id from blog where rowid=$rowid)) a
237 LEFT JOIN
238 a_s s
239 ON a.id=s.id;
240 EOF
241 while IFS='|' read id edit notify uid uname icon aid tm new hte imgids; do
242 cachefile="$td/$id.row.html"
243 stampfile="$td/$id.row.stamp"
244 editlink="${edit:+<a href="$elink+$edit">編集</a> }"
245 nt="<label style=\"font-size: 70%;\"><input type=\"checkbox\"\
246 name=\"notifyto\" value=\"$uid\">返信通知送信</label>"
248 # First, check the availability of user-icon.
249 # If not existent, clear and reset row cache by rm $stampfile
250 if [ ! -s "$icon" ]; then
251 rm -f "$stampfile"; unset stampfile
252 fi
253 if test -s "$stampfile" &&
254 test -s "$cachefile" &&
255 { ts=`cat "$stampfile"`; test -n "$ts"; } &&
256 test "$ts" '>' "$tm"; then
257 : Nothing to do
258 else
259 { ######## New ROW creation begins here ######## >$cachefile
260 tdcls="__NEWCLS__repatt"
261 if [ -s "$icon" ]; then
262 icfn=`echo "$icon"|htmlescape`
263 picon="<p class=\"proficon\"><a href=\"$hlink+$uid\"><img src=\"$icfn\"></a></p>"
264 else
265 echo "DELETE FROM user_s WHERE key='$iconcachekey' AND
266 val=`sqlquotestr \"$icon\"`;" >> $iconcleaner
267 picon=""
268 fi
270 cat<<EOF
271 <tr id="$id">
272 <td class="$tdcls">${picon}__EDIT__#$aid
273 <a href="$hlink+$uid">$uname</a>
274 $tm
275 <__NOTIFY__></td>
276 EOF
277 echo -n "<td id=\"$aid\" class=\"repl\">"
278 echo "$hte"|unhexize|htmlescape|hreflink|minitbl
279 usecache='' tsfile=$td/$id.stamp
280 for i in $imgids; do
281 mrid=${i%%:*}; i=${i#*:}; sz=`size_h ${i%%:*}`
282 fn=`echo "${i#*:}"|unhexize`
283 fnb=$fn"(${sz})"
284 case "$fn" in
285 *.[Pp][Nn][Gg]|*.[Jj][Pp][Gg]|*.[Jj][Pp][Ee][Gg]|*.[GgTt][Ii][Ff])
286 # fmt=${fn##*.} # convert - jpg:- is slow...why
287 case "$fn" in
288 *.[Pp][Nn][Gg]) fmt=png ;;
289 *.[Gg][Ii][Ff]) fmt=gif ;;
290 *) fmt=jpeg ;;
291 esac
292 outfile=$td/$mrid-${fn%.*}.$fmt
293 #err fn=$fn outfile=$outfile
294 #err "usecache=$usecache `ls -l $outfile`"
295 #err tm=$tm
296 #err tsfile=$tsfile=`cat $tsfile`
297 if [ -s "$outfile" ] && # $outfile should be > 0
298 { [ "$usecache" ] || # And usecache flag is true, or...
299 { [ -s "$tsfile" ] && [ x"`cat $tsfile`" = x"$tm" ]
300 };}; then
301 usecache=1 # Set usecache flag on
302 cat<<-EOF
303 <a href="$catlink+$mrid"><img src="$outfile">
304 $fnb</a>
305 EOF
306 # !!NOTE!! Create row stamp ONLY WHEN imgcache is active
307 else
308 query "SELECT hex(bin) FROM article_m WHERE rowid=$mrid;" \
309 | unhexize \
310 | convert -define ${fmt}:size=100x100 -resize 100x100'>' \
311 - ${fmt}:- \
312 | tee "$outfile" \
313 | hexize \
314 | sed -e 's/\(..\)/%\1/g' \
315 -e "s|^|<a href=\"$catlink+$mrid\"><img src=\"data:image/$fmt,|" \
316 -e "s|\$|\">$fnb</a>|"
317 unset stampfile # img data stream is not suitable to cache
318 echo $tm > $tsfile
319 fi
320 ;;
321 *)
322 echo "<a href=\"$catlink+$mrid\"><img src=\"$deficon\">$fnb</a>"
323 ;;
324 esac
325 done
326 echo "</td></tr>"
327 } > "$cachefile" ######## New ROW Creation Ends here ########
328 test -n "$stampfile" && date "+%F %T" > $stampfile
329 fi
330 # Printing a cached row
331 sed -e "/^<td class=/s/__NEWCLS__/$new${new:+ }/" \
332 -e "/^<td class=/s,__EDIT__,$editlink," \
333 -e "/^<__NOTIFY__>/s,,${notify:+$nt}," \
334 $cachefile
335 done
337 textform='<div class="fold">
338 <input type="checkbox" id="cmt" checked><label
339 accesskey="c" title="C" for="cmt">コメントする</label><div>
340 <table class="b">
341 <tr><td><textarea name="text" cols="72" rows="4"></textarea></td></tr>
342 <tr><td>添付ファイル:
343 <input type="file" name="image"'" $file_accept multiple></td></tr>"'
344 </table>
345 <input type="submit" value="送信">
346 <input type="reset" value="リセット"></div></div>
347 '
348 cat<<-EOF
349 </table> <!-- end of s4-blog:blog_showentry() main table -->
350 <p class="update_link"><a
351 href="?reload/$rowid" accesskey="r" title="R">再読込</a> / <a
352 href="#title" accesskey="t" title="T">先頭へ</a></p>
353 EOF
354 $iswritable && cat<<-EOF
355 <div class="blogcomment">
356 <input type="hidden" name="blogid" value="$id">
357 <input type="hidden" name="id" value="`genserial`">
358 <input type="hidden" name="stage" value="replyblog">
359 $textform
360 </div>
361 </form> <!-- End of s4-blog:blog_showentry() main form -->
362 <p id="bottom"> </p>
363 EOF
364 # Clean up orphaned icon cache
365 [ -s $iconcleaner ] && query ".read '$iconcleaner'"
366 # Record access log
367 acclog blog $rowid
368 }
370 lshandout() {
371 # $1=rowid of blog
372 blog_writable $1 $user
373 r=$? # =0: writable, $BLOG_NOTMEM bit set => not member
374 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ] ; then
375 echo "メンバー以外は利用できません。" | html p; return
376 fi
377 time=`getvalbyid blog ctime $1|colrm 11`
378 owner=`getvalbyid blog owner $1`
379 title=`getvalbyid blog title $1`
380 ge=`gecos $owner`
381 fh=$tmpd/formhead
382 echo "$time [$title]@${ge:-$owner}" > $fh
383 lshandoutsub $owner "$@" \
384 |_m4 -D_TITLE_="提出状況" \
385 -D_FORMHEAD_="syscmd(cat $fh)" \
386 -D_FORM_="syscmd(cat)" -D_DUMPHEAD_= -D_DUMPTABLE_= \
387 $layout/html.m4.html $layout/form+dump-whead.m4.html
388 gn=`echo $owner|htmlescape`
389 echo "<p><a href=\"?lshandoutall+$1\">グループ $gn すべてのレポート板集計</a></p>"
390 }
391 gethandoutcsv() {
392 # contenttype; echo
393 CATCSV=1 lshandoutall "$1"
394 }
395 gethandoutcsv2() {
396 # contenttype; echo
397 SQL=$(cat<<-EOF) gethandoutcsv "$1"
398 WITH this_blog_articles AS (
399 SELECT rtb.id bid, rtb.brid, a.id aid, author, title, ctime
400 FROM report_type_blogs rtb JOIN article a ON rtb.id=a.blogid
401 ), text_or_file AS (
402 SELECT bid, author, title, ctime, 'text' shu, count(val) cnt
403 FROM this_blog_articles tba, article_s s
404 ON tba.aid=s.id
405 WHERE key='text'
406 GROUP by bid, author
407 UNION
408 SELECT bid, author, title, ctime, 'file' shu, count(val) cnt
409 FROM this_blog_articles tba, article_m m
410 ON tba.aid=m.id
411 WHERE key='image'
412 GROUP by bid, author
413 ), count_list AS (
414 SELECT author,
415 substr(ctime, 1, 10)||upper(substr(shu, 1, 1)) unit,
416 cnt
417 FROM text_or_file
418 )
419 SELECT gecos "名前",
420 substr(author, 1, instr(author, '@')-1) "uname",
421 unit,
422 cnt "post"
423 FROM count_list cl JOIN gecoses g ON cl.author=g.name;
424 EOF
425 }
426 lshandout_ulink_table() {
427 # NO Args. Read stdin as SQL
428 echo '<table class="b td3rr td3evw">'
429 hrb="<a href=\"?home+"
430 # echo "$sql" | sq -header -html $db \ # Formerly, this is called via sq()
432 printf ".mode html\n.header ON\n" | query
433 cat | query \
434 | sed -e "s,\(<TR><TD>\)\([^ ]*\) \(.*\)</TD>,\1$hrb\2\">\3</TD>," -e 's,<TD>0</TD>,<TD class="warn">0</TD>,'
435 echo '</table>'
436 printf ".mode list\n.header OFF\n" | query
437 }
438 lshandoutall() {
439 # $1=rowid of blog
440 blog_writable $1 $user
441 r=$? # =0: writable, $BLOG_NOTMEM bit set => not member
442 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ] ; then
443 echo "メンバー以外は利用できません。" | html p; return
444 fi
445 rowid=$(($1 + 0))
446 owner=`getvalbyid blog owner $1`
447 qowner=`sqlquotestr "$owner"`
449 query<<-EOF
450 CREATE TEMPORARY TABLE IF NOT EXISTS report_type_blogs AS
451 WITH blog_owner_mode AS (
452 SELECT id,
453 blog.rowid brid,
454 max(CASE key WHEN 'owner' THEN val END) owner,
455 max(CASE key WHEN 'mode' THEN val END) mode,
456 max(CASE key WHEN 'title' THEN val END) title,
457 max(CASE key WHEN 'ctime' THEN val END) ctime
458 FROM blog NATURAL JOIN blog_s
459 GROUP BY id
460 )
461 SELECT id, brid, title, ctime FROM blog_owner_mode
462 WHERE owner=$qowner AND mode LIKE '%report%';
463 /* ↑これでレポート形式の blogid 一覧を得る */
464 EOF
465 if [ -z "$CATCSV" ]; then
466 _m4 -D_TITLE_="提出状況" $layout/html.m4.html
467 ge=`gecos "$owner"`
468 tbls=""
469 grptxt=`echo "${ge:-$owner}"|htmlescape`
470 echo "<h1>$grptxt 書き込み状況一覧</h1>"
471 fi
472 if [ -z "$SQL" ]; then
473 bridlist=`query "SELECT brid FROM report_type_blogs;"`
474 for brid in $bridlist; do # Skip this loop if $SQL set
475 brid=$(($brid + 0)) # Ensure to be a number
476 [ $brid = 0 ] && continue
477 time=`getvalbyid blog ctime $brid|colrm 11`
478 title=`getvalbyid blog title $brid|htmlescape`
479 state=`getvalbyid blog state $brid|htmlescape`
480 tt="handout_$brid"
481 [ "$state" = "frozen" ] && frozen=" $FROZEN_TAG" || frozen=""
482 if [ -z "$CATCSV" ]; then
483 echo "<h2>$time - <a href=\"?replyblog+$brid\">$title</a>$frozen</h2>"
484 lshandoutsub "$owner" $brid "$tt"
485 else
486 lshandoutsub "$owner" $brid "$tt" >/dev/null # Only create temp.table
487 fi
488 tbls="$tbls${tbls:+ NATURAL JOIN }$tt"
489 done
490 fi
491 sql=${SQL:-"SELECT * FROM $tbls;"}
492 if [ -z "$CATCSV" ]; then
493 echo "<hr><h2>総合</h2>"
494 echo "$sql" | lshandout_ulink_table
495 echo "<h2>総合(<a href=\"?gethandoutcsv+$rowid\">CSV</a>)</h2>"
496 printf ".mode csv\n.header ON\n" | query
497 echo '<pre class="list">'
498 echo "$sql" | query | sed 's/^"[0-9]* /"/'
499 echo "</pre>"
500 echo "<pre><a href=\"?gethandoutcsv2+$rowid\">縦持ちCSV</a></pre>"
501 else
502 contenttype "Application/CSV"
503 printf ".mode csv\n.header ON\n" | query >/dev/null
504 fn=report-count.csv
505 printf 'Content-Disposition: filename="%s"\n' "$fn"
506 outfile=$tmpd/out-$$.csv
507 echo "$sql" | query | sed 's/^"[0-9]* /"/' > $outfile
508 echo "Content-Length: " `cat $outfile | wc -c`; echo
510 cat $outfile
511 exit 0
512 fi
513 printf ".mode list\n.header OFF\n.separator |\n" | query
514 }
515 lshandoutsub() {
516 # $1=owner $2=rowid of blog &optional $3=temp_table name
517 qgname=`sqlquote "$1"`
518 if isgroup $1; then
519 sample="(select user from grp_mem where gname=$qgname)"
520 else
521 sample="(select distinct author as user from arts)"
522 echo "(集計は板への投稿者のみ)" | html p
523 fi
524 tmpname="${3:-handout_$2}"
525 sql="CREATE TEMPORARY TABLE IF NOT EXISTS $tmpname AS
526 with arts as (select id,author from article \
527 where blogid=(select id from blog where rowid=$2))\
528 select (select rowid from user where name=c0.user)||' '|| \
529 (select gecos from gecoses where name=c0.user) as 'メンバー',\
530 substr(c0.user, 1, instr(c0.user, '@')-1) 'uname',\
531 sum(case when c1.key is not null then 1 else 0 end)\
532 as '[$title] コメント記入',\
533 sum(case when c2.key is not null then 1 else 0 end)\
534 as '[$title] ファイルの提出'\
535 from $sample c0 \
536 left join (select id,author from arts) a\
537 on c0.user=a.author\
538 left join (select id,key from article_s where key='text') c1\
539 on a.id=c1.id left join (select id,key from article_m ) c2\
540 on c1.id=c2.id group by c0.user order by c0.user;\
541 \
542 SELECT * FROM $tmpname;"
543 err ishandoutsub: sql="$sql"
544 echo "$sql" | lshandout_ulink_table
545 }
546 gethandout() {
547 # $1=rowid of blog
548 blog_writable $1 $user
549 r=$? # =0: writable, $BLOG_NOTMEM bit set => not member
550 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ] ; then
551 echo "メンバー以外は利用できません。" | html p; return
552 fi
553 i=0
554 bd=$tmpd/archive.$$
555 mkdir $bd
556 query "select m.rowid,author,m.val from article a join article_m m\
557 on a.id=m.id where blogid=(select id from blog where rowid=$1)\
558 and m.key in ('image', 'document', 'binary');" \
559 | while IFS='|' read rowid author filename; do
560 err isfilereadable $user article_m $rowid
561 isfilereadable $user article_m $rowid || continue
562 err ok
563 i=$((i+1))
564 dir=`printf $bd/%03d $i`
565 mkdir $dir
566 query "select quote(bin) from article_m where rowid=$rowid;" \
567 | unhexize > $dir/$filename
568 done
569 if [ ! -d $bd/001 ]; then
570 contenttype; echo
571 echo "取得できるファイルがありませんでした。" | html p
572 return
573 fi
574 (cd $bd
575 ## err cdto$bd; (pwd; ls -lFa) 1>&3
576 tar zcf .archive.tar.gz * && mv .archive.tar.gz archive.tar.gz
577 err Creating tar archive "`ls -l archive.tar.gz`"
578 )
579 arc=$bd/archive.tar.gz
580 echo "Content-type: application/x-gzip"
581 echo "Content-Length: `cat $arc|wc -c`"
582 echo "Content-Disposition: filename=\"archive.tar.gz\""
583 echo
584 cat $arc
585 }
586 lsmyfile() { # $1(optional)=SortBy
587 case "$1" in
588 ""|CTIME-DESC)
589 by="CTIME" ord="DESC" ;;
590 CTIME*) by="CTIME" ;;
591 FILE*) by="FILE" ;;
592 OWNER*) by="OWNER" ;;
593 TITLE*) by="TITLE" ;;
594 esac
595 case "$1" in
596 *DESC) ord="DESC" ;;
597 esac
598 case "$ord" in
599 DESC) lkod="" jord="降順" ;;
600 *) lkod="-DESC" jord="昇順" ;;
601 esac
602 sql="select m.val||'/'||m.rowid FILE,
603 coalesce(
604 case when (select name from user where name=bs.owner)
605 is not null
606 then (select val from user_s where name=bs.owner
607 and key='gecos')
608 when (select gname from grp where gname=bs.owner)
609 is not null
610 then (select val from grp_s where gname=bs.owner
611 and key='gecos')
612 else
613 null
614 end,
615 bs.owner
616 ) OWNER,
617 a_s.val CTIME,
618 ',t,'||bs.title||':'||b.rowid||'#'||a.id TITLE
619 from (select rowid,id,val from article_m where id
620 in (select id from article where author='$user')
621 and type like 'file:%')
622 m left join article a on m.id=a.id
623 left join article_s a_s on a.id=a_s.id and a_s.key='ctime'
624 left join (select id,
625 max(case key when 'owner' then val end) as owner,
626 max(case key when 'title' then val end) as title
627 from blog_s group by id)
628 bs on a.blogid=bs.id
629 left join blog b on bs.id=b.id
630 where m.val is not null order by $by $ord;"
631 err lshandoutbyauthor: sql=`echo "$sql"`
632 title="個人提出ファイル"
633 _m4 -D_TITLE_=$title $layout/html.m4.html
634 hra="<a href=\"?lsmyfile+"
635 hrb="<a href=\"?showattc+article_m+"
636 hrc="<a href=\"?replyblog+"
637 (echo '<table class="b">'
638 echo "$sql"|sq -html -header $db ) \
639 | sed -e "s|\(<TR><TD>\)\([^/]*\)/\([0-9]*\)|\1$hrb\3\">\2</a>|" \
640 -e "s|,t,\(.*\):\([^<]*\)\(</TD>\)|$hrc\2\">\1</a>\3|" \
641 -e "s|\(<TH>\)\([A-Z]*\)\(</TH>\)|\1$hra\2$lkod\">\2</a>|" \
642 | _m4 -D_TITLE_=$title -D_FORM_="<p>($by$jord)</p>" \
643 -D_DUMPTABLE_="syscmd(cat)" $layout/form+dump.m4.html
644 echo '</table>'
645 }
646 searchart() {
647 kwd=`getpar kwd|nkf -wZ1` # Convert Zenkaku-SPC to ASCII-SPC
648 kwdgrp=""
649 authcond=""
650 if [ -z "$kwd" ]; then
651 echo "検索語を指定してください" | html p; return
652 fi
653 if expr x"$kwd" : 'x#[1-9][0-9]*$' >/dev/null 1>&2; then
654 # Like '#1234', assume as artID
655 rowid=$((${kwd#\#} + 0)) # Force to be a number
656 kc="ar.rowid = $rowid"
657 else
658 for k in `echo "$kwd" | sed "s/'/''/g"`; do # With wrap quotes
659 if expr x"$k" : 'x@[><= ]*[1-9][][0-9]*-[][0-9:-]*$' >/dev/null >&2; then
660 # '@<2016-10-10' -> ctime < '2016-10-10'
661 # '@>=2016-10-10' -> ctime >= '2016-10-10'
662 # '@2016-10-10' -> ctime GLOB '@2016-10-10'
663 k=${k#@}
664 case "$k" in
665 [\<\>]*) op=${k%%[!<>=]*}; ctime=${k##*[><= ]} ;;
666 *) op='GLOB'; ctime="${k##*[><= ]}*" ;;
667 esac
668 kc=$kc${kc:+" AND "}"ctime $op '${ctime}'"
669 # Not sure GROUP BY a.blogid is comfortable for searchers...?
670 ##### kwdgrp=" GROUP BY a.blogid" ## Add this to lessen results
671 elif [ x"$k" = x"@today" -o x"$k" = x"@今日" ]; then
672 ctime=`date +%F`
673 kc=$kc${kc:+" AND "}"ctime GLOB '${ctime}*'"
674 elif [ x"$k" = x"@week" ]; then
675 ctime=`query "SELECT datetime('now', 'localtime', '-7 days');"`
676 kc=$kc${kc:+" AND "}"ctime > '${ctime}'"
677 elif [ x"$k" = x"@month" ]; then
678 ctime=`query "SELECT datetime('now', 'localtime', '-1 month');"`
679 kc=$kc${kc:+" AND "}"ctime > '${ctime}'"
680 elif [ x"$k" = x"@year" ]; then
681 ctime=`query "SELECT datetime('now', 'localtime', '-1 year');"`
682 kc=$kc${kc:+" AND "}"ctime > '${ctime}'"
683 else
684 kc=$kc${kc:+" AND "}"content LIKE '%$k%'"
685 fi
686 done
687 fi
688 kwd=`echo "$kwd"|htmlescape`
689 owner=`getpar owner`
690 owner=${owner:-$1}
691 echo "「$kwd」による検索結果" | html p
692 if [ -n "$owner" ]; then
693 cond="where key='owner' and val='$owner'"
694 if isuser $owner; then
695 echo "(`linkhome $owner` さんの記録からの検索)" | html p
696 else
697 linkhome $owner 1>&3
698 echo "(`linkhome $owner` グループからの検索)" | html p
699 fi
700 elif { author=`getpar author`; test -n "$author"; }; then
701 atptn=`sqlquotestr $author`
702 #kc="$kc${kc:+ AND }author=$atptn"
703 authcond="WHERE author=$atptn"
704 if isuser $author; then
705 echo "(`linkhome $author` さんの書き込みからの検索)" | html p
706 fi
707 fi
708 # article_s: id=article-id, key='text', val='TEXT'
709 # article: id=article-id, blogid=blogkd
710 # blog: id=blog-id, author=LeaderAuthor
711 # blog_s: id=blog-id, key='title', val='BLOG-TITLE'
712 # WANT: blog-ROWid,article-id,val(TEXT)
713 sql2="`sql4readableblogs` -- Extract user-readable blogs
714 -- 0.3sec
715 WITH artsm AS (
716 SELECT a.id,ctime, text || ' ' || coalesce(files, '') content
717 FROM article a
718 LEFT JOIN
719 (SELECT ars.id, ctime, text, coalesce(files, '') files
720 FROM (SELECT id,
721 max(CASE key WHEN 'ctime' THEN val END) ctime,
722 max(CASE key WHEN 'text' THEN val END) text
723 FROM article_s
724 GROUP BY id) ars
725 LEFT JOIN
726 (SELECT id, group_concat(val) files
727 FROM article_m
728 WHERE type LIKE 'file:%'
729 GROUP BY id) arm
730 ON ars.id=arm.id
731 ) ar
732 ON a.id=ar.id
733 ), ar AS (
734 SELECT a.rowid, a.blogid, a.id, a.author, ctime, content
735 FROM article a JOIN artsm ON a.id=artsm.id
736 $authcond
737 ), bl AS (
738 SELECT blg.rid, blg.*, blog_s.val TITLE
739 FROM readableblogs blg JOIN blog_s ON blg.id=blog_s.id AND blog_s.key='title'
740 )
741 SELECT bl.rid||'#'||ar.id '',
742 bl.title TITLE,
743 (SELECT gecos FROM gecoses WHERE name=ar.author) AUTHOR,
744 substr(ctime, 0, 11) DATE,
745 substr(content, 0, 78) TEXT
746 FROM ar JOIN bl
747 ON ar.blogid=bl.id
748 WHERE $kc AND bl.id IN (SELECT id FROM blog_s $cond)
749 ORDER by DATE DESC, TITLE, ctime;"
750 sedopt="s,<TR><TD>\([^<]*\)</TD>,<TR><TD><a\
751 href=\"?replyblog+\1\">VIEW</a></TD>,"
752 # echo "$sql2" > tmp/sql.out
753 result=$tmpd/result.$$
754 cat<<EOF
755 <table class="b searchart">
756 `sq -header -html $db "$sql2"|sed "$sedopt"|tee $result`
757 </table>
758 EOF
759 if [ -s "$result" ]; then
760 found=$((`grep "^<TR><TD>" $result | wc -l` + 0)) # Cast to INT
761 one=${found%1}
762 echo "$found match${one:+es} found"
763 else
764 echo orz...
765 fi
766 }
767 listblog() (
768 # $1={user,group}
769 qow=`sqlquote $1`
770 cond="where a.id in (select id from blog_s where key='owner' and val=$qow) order by ctime desc"
771 DT_CHLD=article:blogid
772 cgi_form searchart<<EOF
773 <label>`cgi_text kwd`という語を含む記事をこの一覧から検索</label>
774 `cgi_hidden owner $user`
775 EOF
776 dumptable html blog 'ctime title heading' "$cond"
777 )
779 blog_addentry() {
780 # $1=GRPname(if it is a group)
781 grprowid=$1
782 rowid=`getpar rowid`
783 ## err blog_addentry0: rowid=$rowid
784 if [ -n "$grprowid" ]; then
785 owner=`getgroupbyid $grprowid`
786 else
787 owner=`getpar owner`
788 fi
789 err blog-add: \$1=$1 rowid=$rowid owner=$owner
790 if isgroup $owner; then
791 groupmode=1 listing=$owner guide="[${owner}]" GF_OWNER=$owner
792 else
793 usermode=1 listing=$user guide="[個人]"
794 fi
796 if [ -n "`getpar title`" ]; then
797 if [ "$usermode" ]; then
798 err usermode: user=$user owner=$owner
799 if [ x"$user" != x"$owner" ]; then
800 echo "他人の日記は書けません" | html p
801 return 2
802 fi
803 elif [ "$groupmode" ]; then # if write to group log
804 grp=$owner #\`getpar grp\`
805 err ismember: $user $grp
806 if ! ismember "$user" "$grp"; then
807 echo "(話題作成はこのグループに加入してから)" | html p
808 return 3
809 fi
810 fi
811 par2table $formdir/blog.def
812 serial=`getpar serial`
813 ## err SERIAL: $serial ROWID=$rowid listing=$listing
814 id=""
815 if [ -n "$rowid" ]; then
816 # Here, id becomes NULL when removal of entries at par2table
817 id=`query "select rowid from blog where rowid=$rowid;"`
818 elif [ -n "$serial" ]; then
819 # If new blog leader created, traverse to its head.
820 id=`query "select rowid from blog where id='$serial';"`
821 ## err new-Leader: "select rowid from blog where id='$serial';" id=$id
822 fi
823 if [ -n "$id" ]; then
824 ## If new aritcle is entered, JUMP to blog_reply
825 blog_reply $id
826 return
827 fi
828 fi
829 echo "${guide}新規話題作成" > $tmpd/title.$$
830 listblog $listing > $tmpd/listblog.$$
831 genform $formdir/blog.def \
832 | _m4 -D_TITLE_="spaste(\`$tmpd/title.$$')" \
833 -D_FORMHEAD_="序文は簡単に詳しくはコメントに" \
834 -D_DUMPHEAD_="これまでの蓄積" \
835 -D_FORM_="syscmd(\`cat')" \
836 -D_DUMPTABLE_="spaste(\`$tmpd/listblog.$$')" \
837 $layout/html.m4.html \
838 $layout/form+dump-whead.m4.html
839 }
841 blog_reply() { # Posting to blog article
842 rowid=$1
844 if [ -z "$rowid" ]; then
845 echo "表示する日記番号が未指定です。" | html p
846 return
847 fi
848 title=`getvalbyid blog title $rowid`
849 owner=`getvalbyid blog owner $rowid`
850 if [ -z "$title" ]; then
851 echo "日記番号指定が無効です。" | html p
852 return
853 fi
854 blog_writable $rowid $user; rc=$?
855 if [ $rc = 0 ]; then
856 iswritable=true
857 else
858 iswritable=false
859 if [ $((rc & $BLOG_FROZEN)) -gt 0 ]; then
860 isfrozen=true
861 frozen_class='frozen"'
862 frozen_flag=$FROZEN_TAG
863 fi
864 fi
865 if isuser "$owner"; then
866 subtitle="`gecos $owner` さんの話題"
867 else
868 grprowid=`query "select rowid from grp where gname=\"$owner\";"`
869 subtitle="グループ
870 <a href=\"?grp+$grprowid\" accesskey=\"h\" title=\"H\">$owner</a> での話題
871 `query \"SELECT printf('(チーム:%s)', val)\
872 FROM blog_s
873 WHERE id=(SELECT id FROM blog WHERE rowid=$rowid)
874 AND key='team';
875 \"|htmlescape`"
876 memclass=`grp_getbodyclass "$owner"`
877 fi
879 text=`getpar text`
880 if [ -n "$text" ]; then
881 if $iswritable; then
882 par2table $formdir/article.def
883 st=$?
884 case $st in
885 0|4)
886 [ "$st" = "4" ] && act="書込削除"
887 blog_notify_reply $rowid $user "$text" $act
888 if [ -n "$grprowid" ]; then
889 qgrp=$(sqlquote "$owner")
890 dbsetbyid grp $owner wtime "`date '+%F %T'`"
891 fi
892 ;;
893 esac
894 else
895 if $isfrozen; then
896 title="$title(凍結板につき書き込み不可)"
897 else
898 title="$title(加入してないので書き込み不可)"
899 fi
900 fi
901 fi
902 def=$formdir/article.def
903 echo "$title" > $tmpd/title.$$
904 echo "$subtitle$frozen_flag" > $tmpd/subtitle.$$
905 ${BLOG_SHOW:-blog_showentry} blog $rowid \
906 | _m4 -D_TITLE_="spaste(\`$tmpd/title.$$')" \
907 -D_BODYCLASS_=general"${memclass:+ $memclass}" \
908 -D_FORMHEAD_="spaste(\`$tmpd/subtitle.$$')" \
909 -D_FORM_='' \
910 -D_DUMPTABLE_="syscmd(cat)" -D_DUMPHEAD_="" \
911 $layout/html.m4.html $layout/form+dump-whead.m4.html
912 }