s4

view s4-blog.sh @ 421:d3bf2e95c0cc

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