s4

view s4-blog.sh @ 1039:68a01c699acb

Fix HELP string for minitbl
author HIROSE Yuuji <yuuji@gentei.org>
date Sat, 06 Apr 2024 18:08:10 +0900
parents b8b1b8356b94
children 78e904f9be34
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_getteam() {
31 # $1=rowid of blog
32 blogid="${1%%[!A-Z0-9a-z_]*}"
33 # team cannot get `getvalbyid blog team "$blogid"` because it's not
34 # defined in blog.def. Yes, it is Illegal USE!!
35 query "SELECT val FROM blog_s
36 WHERE id=(SELECT id FROM blog WHERE rowid=$blogid)
37 AND key='team';"
38 }
39 blog_notify_reply() (
40 # $1=blogid $2=ReplyingUser $3=WrittenText $4(optional)=Action
41 blogid="${1%%[!A-Z0-9a-z_]*}"
42 blogowner=`getvalbyid blog owner "$blogid"`
43 blogtitle=`getvalbyid blog title "$blogid"`
44 blogurl="$urlbase?replyblog+$blogid"
45 action=${4:-書き込み}
46 case "$3" in
47 @all*) mode=all ;;
48 *) mode=`getvalbyid blog notify "$blogid"` ;;
49 esac
50 isgroup "$blogowner" && _isgroup=true || _isgroup=false
51 ### EXCEPT=`sqlquote "$user"` ## User should receive to feal some annoyance
52 case $mode in
53 admin)
54 if $_isgroup; then
55 emails=`getgroupadminmails "$blogowner"`
56 else
57 emails=`collectemail "$blogowner"`
58 fi
59 notifyto=`getpar notifyto`
60 if [ -n "$notifyto" ]; then
61 emails=$emails" `email4groupbyuid \"$blogowner\" $notifyto`"
62 fi
63 ;;
64 no|"") emails="" ;; # 2020-0630 Omit email when heavy load...(XXX)
65 *) team=`blog_getteam "$blogid"`
66 # team cannot get by `getvalbyid blog team "$blogid"`
67 emails=`TEAM=$team collectemail "$blogowner"` ;;
68 esac
69 ## 2017-0210 Respond to the direct reply mark such as: >#1234
70 replymark=`echo "$3"|nkf -w -Z0|grep '^ *>#'`
71 authgecos=`gecos $2`
72 ## 2020-1209 If the first line begins with '## ', use it as Subject
73 firstline=`echo "$3"|head -1|nkf -w -Z0`
74 if [ -z "$4" ]; then
75 if [ -n "$replymark" ]; then
76 # If the action is new subscription($4="") and has ">#123" marks...
77 ids=`echo "$replymark"|sed 's/[^#0-9]*#\([0-9]*\)[^#0-9]*/\1 /g'`
78 ids=`echo $ids|tr -dc '[0-9 ]'|tr ' ' ','`
79 # -> 123,345,347
80 unames=`query "SELECT distinct author FROM article \
81 WHERE rowid in ($ids)\
82 AND blogid=(SELECT id FROM blog WHERE rowid=$blogid);"`
83 if [ -n "$unames" ]; then
84 e4g=$(if $_isgroup; then
85 email4group "$blogowner" $unames
86 else
87 for u in $unames; do
88 collectemail $u
89 done
90 fi)
91 emails=$emails" $e4g"
92 for e in $unames; do
93 g=`gecos $e`
94 whom=$whom"${whom:+,}${g:-$e}さん"
95 done
96 action="${whom}への返信"
97 fi
98 elif echo "$firstline" | grep -q "^## "; then
99 subject=${firstline#\#\# }
100 elif echo "$firstline" | grep -q "^@all "; then
101 subject=${firstline#@all }
102 fi
103 else
104 # This else block is not symmetry, check later(2020-1209)
105 [ x"$2" = x"$blogowner" ] && return # If author=blogowner, unnecessary
106 fi
107 test -z "$emails" && return
108 sj=${subject:-${action}通知}
109 err notify: user=$user Admins=`getgroupadmins "$blogowner"` Mode=$mode Emails="[$emails]"
110 quotedowner=`echo $blogowner | nkf -jM | tr -d '\n"'`
111 MAIL_FROM=$noreply_from \
112 SMAIL_TO="\"$quotedowner\" readers <$noreply>" \
113 smail "$emails" "$sj $urlbase"<<EOF
114 [$blogtitle]板に${action}がありました。
115 ※※※このメイルには返信できません(返信は次のURLへ)※※※
116 場所: $blogurl (返信先)
117 所有: $blogowner
118 題目: $blogtitle
119 筆者: $authgecos
120 内容:
121 `echo "$3"|sed 's/^/ /'`
123 ※※このメイルに返信しても通知者には伝わりません。
124 ※※上記URLから${S4NAME:-s4}掲示板に書き込んでください。
125 EOF
126 )
128 blog_showentry() {
129 # $1=table $2=rowid $3(optional)=control-sequence
130 # if [ -n "$2" ]; then
131 # if [ -n "$imgcached" ]; then
132 # bstmpdir=$tmpdir/$imgcached/$thumbxy
133 # else
134 # bstmpdir=$tmpd
135 # # tmpd=`mktempd`
136 # # tmpfiles=$tmpfiles" $tmpd"
137 # fi
138 # fi
139 control=$3
140 td=`getcachedir "article/$2"`
141 [ -d "$td" ] || mkdir -p $td
142 tbl=${1%%[!A-Z0-9a-z_]*} rowid=${2%%[!A-Z0-9a-z_]*}
143 err blog_showentry: rowid=$rowid, '$2'=$2 user=$user
144 ts=${tbl}_s tm=${tbl}_m
145 at=article as=article_s am=article_m
146 serial=$(($(date +%s)-1420038000))s$$
147 cannotread='<div class="relative"><img class="overlap" src="img/key.png" alt="(読み取り不可)"></div>'
148 blog_writable $rowid $user
149 rc=$?
150 if [ $rc = 0 ]; then
151 iswritable=true
152 ismem=true
153 else
154 iswritable=false
155 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ]; then
156 ismem=false
157 else
158 ismem=true
159 fi
160 fi
161 # This function grasps blog entry definiton directly.
162 # blog: id
163 # blog_s: title,ctime,heading
164 # blog_m: *article
166 blogowner=`getvalbyid blog owner "$2"`
167 isgroup "$blogowner" && isgroup=true || isgroup=false
168 isgrpadmin=false
169 isgrpowner "$user" "$blogowner" && isgrpadmin=true
170 [ x"$user" = x"$blogowner" ] && isgrpadmin=true
172 # 2015-10-05 check readable
173 if ! $iswritable; then
174 # err blogowner=$blogowner
175 if $isgroup; then
176 regmode=`getgroupattr "$blogowner" regmode`
177 # err regmode=$regmode
178 if [ x"$regmode" = x"moderated" ]; then
179 # if ! ismember $user $blogowner; then
180 if ! $ismem; then
181 echo "加入してからどうぞ" | html p
182 return
183 fi
184 fi
185 fi
186 fi
187 err "blog_showentry Entered: `gdate +%S.%03N` blogrowid=$rowid"
188 blog_notify=`getvalbyid blog notify "$rowid"`
189 blog_team=`blog_getteam "$rowid"`
190 blog_mode=`getvalbyid blog mode "$rowid"`
191 blog_math=`getvalbyid blog mathjax "$rowid"`
192 case "$blog_notify" in # "all", "admin" or "no" (or NULL)
193 admin) notifyto=adm ;;
194 *) notifyto="" ;;
195 esac
196 case $blog_mode in
197 *quiz*|*close*|"") # When blog_mode is "", fallback to quiz/close
198 f_exclusive=1
199 if $isgroup; then
200 qgrp=`sqlquote "$blogowner"`
201 if $isgrpadmin; then
202 F_UNREADABLE="''"
203 else
204 if [ x"$blog_mode" = x"quiz" ]; then
205 F_UNREADABLE="CASE
206 WHEN author IN (SELECT user FROM grp_adm WHERE gname=$qgrp)
207 THEN ''
208 WHEN author = '$user'
209 THEN ''
210 ELSE 'Unreadable'
211 END"
212 elif [ x"$blog_mode" = x"report-closed" ]; then
213 F_UNREADABLE="CASE
214 WHEN author = '$user'
215 THEN ''
216 ELSE 'Unreadable'
217 END"
218 else
219 F_UNREADABLE="'Unreadable'"
220 fi
221 fi
222 else # User blog
223 if [ x"$blog_mode" = x"quiz" ]; then
224 F_UNREADABLE="CASE
225 WHEN author = '$blogowner'
226 THEN '' ELSE 'Unreadable'
227 END"
228 else
229 F_UNREADABLE="'Unreadable'"
230 fi
231 fi
232 ;;
233 *) f_exclusive=''
234 F_UNREADABLE="''"
235 ;;
236 esac
238 # err "SELECT id from $tbl where rowid=$rowid"
239 id=`query "select id from $tbl where rowid=$rowid;"`
240 #err id=$id
241 #err "select val from $ts where key='title' and id='$id';"
243 ## Parse control sequence
244 nlimit=$listartlimit
245 case "$control" in
246 n:[Aa][Ll][Ll])
247 unset nlimit ;;
248 n:*)
249 nlimit=${control##*:}
250 nlimit=${nlimit%%[!A-Z0-9a-z_]*}
251 ;;
252 f:[Aa][Ll][Ll]) ;;
253 f:2???-??-??*) # f:2020-12-27T08:02:43
254 fetch=${control#f:}
255 fetch_ajax=`echo "'$fetch'"|tr T ' '`
256 esac
257 err control=$control fetch_ajax=$fetch_ajax
258 #(1)Display root article
259 cat<<EOF
260 <form class="replyblog" action="$myname?replyblog+${rowid}#bottom" method="POST" enctype="multipart/form-data">
261 <table class="bloghead">
262 EOF
264 href="<a href=\"?editheading+$rowid\" accesskey=\"e\" title=\"Shortcut: E${nl}Edit\"> 編集 </a>"
265 if $ismem; then
266 case $blog_mode in
267 *report*|*quiz*|*enquete*)
268 href2="<a href=\"?lshandout+$rowid\" accesskey=\"l\" title=\"Shortcut: L${nl}List Handouts\"> 提出状況 </a>"
269 case "$isgrpadmin$blog_mode" in
270 false*closed*|false*quiz|false*enquete*) ;;
271 *)
272 href3="(ファイル取得[<a href=\"?gethandout+$rowid\" accesskey=\"f\" title=\"Shortcut: F${nl}File Retrieval\">記事順</a>|<a href=\"?gethandout+$rowid+by_uname\" accesskey=\"u\" title=\"Shortcut: U${nl}File Retrieval by User\">著者順</a>])"
273 ;;
274 esac
275 ;;
276 esac
277 fi
278 hidebtn='<button type="button" id="hideauth" accesskey="a" title="Shortcut: A
279 Hide/Show Author - Useful for summary printing
280 OFFにするとまとめ印刷に便利。">著者OFF</button>'
281 href4="${blog_math:+<span id=\"mathjax\">Math</span>} $hidebtn <a href=\"#bottom\" accesskey=\"b\" title=\"Shortcut: B${nl}to the Bottom\"> 末尾へ</a>"
282 $isgrpadmin &&
283 href5="<a href=\"?blogseen+$rowid\" accesskey=\"s\" title=\"Shortcut: S${nl}State of Accesses\"> 読刻</a>"
284 quizmodefile=$tmpd/quiz; rm -f "$quizmodefile" # XXX: Global state
285 midfile=$tmpd/midfile
287 query<<-EOF > $midfile
288 SELECT coalesce((SELECT "yes" FROM blog
289 -- GrpAdmin CAN EDIT heading since 2019-08-15
290 WHERE '$isgrpadmin' = 'true'
291 OR (rowid=$rowid AND author='$user')),
292 ''),
293 max(CASE key WHEN 'ctime' THEN val END) ctime,
294 max(CASE key WHEN 'heading' THEN hex(val) END) heading,
295 CASE (SELECT val FROM $ts WHERE key="mode" AND id="$id")
296 WHEN 'report-closed' THEN 'レポート提出用(closed)'
297 WHEN 'report-open' THEN 'レポート提出用(open)'
298 WHEN 'quiz' THEN 'クイズ'
299 WHEN 'enquete' THEN '集計'
300 ELSE ''
301 END
302 FROM $ts WHERE id='$id' GROUP BY id;
303 EOF
304 if test -s $midfile && IFS='|' read edit ctime hexhead blogtype < $midfile
305 then
306 if [ -z "$fetch_ajax" ]; then # UUUUU
308 cat<<-EOF
309 <tr><td>${edit:+$href }$ctime $blogtype $href2${edit:+$href3} $href4 $href5</td></tr>
310 <tr class="preface${frozen_class:+ }$frozen_class">
311 <td>`echo "$hexhead"|unhexize|htmlescape|hreflink|minitbl`</td></tr>
312 </table>
313 EOF
314 case "$blogtype" in
315 "クイズ"|"XXXX集計")
316 echo "${blogtype}モードは本人と管理者の書き込みのみが表示されます"
317 ;;
318 esac | html p 'class="warn"'
320 fi # UUUUU
321 if [ x"$blogtype" = x"クイズ" -o x"$blogtype" = x"XXXX集計" ]; then
322 if $isgroup; then
323 # Failsafe to query timeout
324 qgrp=`sqlquote "$blogowner"`
325 cat<<-EOF > $quizmodefile
326 AND (author IN (SELECT user FROM grp_adm WHERE gname=$qgrp)
327 OR
328 author='$user')
329 EOF
330 if $isgrpadmin; then #
331 : > $quizmodefile
332 fi
333 else # if user-blog
334 if [ x"$user" != x"$blogowner" ]; then
335 cat<<-EOF > $quizmodefile
336 AND author IN ('$blogowner', '$user')
337 EOF
338 fi
339 fi
340 fi
341 else # Cannot read SQL output
342 echo "時間をおいて繋いでください(Please visit later)." | html p
343 return
344 fi
346 lkhome="<a href=\"$myname?home" lke='">'
347 lkedit="<a href=\"$myname?editart"
348 hlink="$myname?home" elink="$myname?editart"
349 catlink="$myname?showattc+article_m"
350 deficon="img/file-icon.png"
351 # 2016-08-15 Newer flag introduced
352 atime=`query "SELECT time FROM acclog
353 WHERE tbl='blog' AND tblrowid=$rowid AND user='$user';"`
354 iconcleaner=$tmpd/iconcleaner.$$
355 [ -s $quizmodefile ] && cond_qz=`cat $quizmodefile`
356 # *** DO NOT USE query(), use "sq $db" instead here ***
357 # because the next block in pipe line uses query() repeatedly.
358 ###### TEST: 2020-04-23 Use intermediate file to shorten duration of db-lock
359 ###### sq $db<<EOF |
360 query <<EOF > $midfile
361 WITH a_s AS (
362 SELECT id,
363 max(CASE key WHEN 'ctime' THEN val END) TIME,
364 max(CASE key WHEN 'text' THEN val END) TEXT
365 FROM article_s
366 GROUP by id
367 )
368 SELECT a.id,
369 CASE author
370 WHEN '$user' THEN a.rowid||'+'||$rowid
371 ELSE ''
372 END edit,
373 CASE -- 「通知送信」ボタンの有無
374 WHEN '$notifyto' = '' THEN '' -- 不要モードならなし
375 WHEN '$user' = author THEN '' -- 筆者自身ならなし
376 ELSE "yes"
377 END notify,
378 (SELECT rowid FROM user WHERE name=author) user_rid,
379 author,
380 coalesce((SELECT val FROM user_s
381 WHERE name=author AND key='gecos'),
382 author) uname,
383 (SELECT val FROM user_s WHERE name=author AND key='$iconcachekey')
384 icon,
385 a.rowid,
386 s.TIME,
387 CASE WHEN s.TIME < '2019-05'
388 THEN printf('平成%d年%d月%d日%s',
389 substr(s.TIME, 1, 4)-1988,
390 substr(s.TIME, 6, 2),
391 substr(s.TIME, 9, 2),
392 substr(s.TIME, 12)
393 )
394 WHEN s.TIME < '2020'
395 THEN printf('令和元年%d月%d日%s',
396 substr(s.TIME, 6, 2),
397 substr(s.TIME, 9, 2),
398 substr(s.TIME, 12))
399 WHEN s.TIME < '2050'
400 THEN printf('令和%d年%d月%d日%s',
401 substr(s.TIME, 1, 4)-2018,
402 substr(s.TIME, 6, 2),
403 substr(s.TIME, 9, 2),
404 substr(s.TIME, 12))
405 ELSE s.TIME
406 END reki,
407 CASE WHEN s.TIME > '$atime' THEN 'new' ELSE '' END newer,
408 hex(s.TEXT),
410 $F_UNREADABLE cannotread,
412 (SELECT group_concat(rowid||':'||length(bin)||':'||hex(val), ' ')
413 FROM article_m
414 WHERE id=a.id AND key='image') imxgids
415 FROM (select rowid,id,author from article
416 where blogid in
417 (select id from blog where rowid=$rowid)
418 $cond_qz) a
419 LEFT JOIN
420 a_s s
421 ON a.id=s.id
422 ${fetch_ajax:+WHERE s.TIME > $fetch_ajax};
423 EOF
424 if [ $? -ne 0 -a ! -s $midfile ]; then
425 echo "時間をおいてください(Visit later please)." | html p
426 return
427 fi
428 printf '%s' "${blog_math:+$mathjax}"
429 echo '<table class="blog_replies"> <!-- blog:blog_showentry() main table -->'
430 # If, nLimit = 50
431 # show article:1, hide(2, 3), show(4, ...)
432 # Therefore hide 2 or more article when narts>53
433 narts=`wc -l < $midfile`
434 if [ -n "$nlimit" -a "$narts" -gt "$((nlimit+2))" ]; then
435 newtop=`cat -n $midfile | grep "|new|" | head -1 | cut -f1`
436 if [ -n "$newtop" ]; then
437 afternew=$((narts-newtop+1))
438 [ $afternew -gt $((nlimit+2)) ] && nlimit=$((afternew+0))
439 err Newtop=$newtop lines=$narts afternew=$afternew nlim=$nilmit
440 fi
441 fi
442 if [ $nlimit -lt $((narts-2)) ]; then
443 n=0
444 omitline=$td/omitline
445 #CAT="tail -n $nlimit"
446 CAT=cat
447 limitedmsg="<span class=\"warn\">※最新${nlimit}件のみの表示※</span>"
448 showalllink="<a title=\"Show All\" href=\"?replyblog+$rowid+n:all\">全件表示</a>"
449 cat<<-EOF > $omitline
450 <tr class="warn">
451 <th>:<br>$limitedmsg<br>($((narts-$nlimit-1))件省略)<br>:</th>
452 <th>$showalllink</th>
453 EOF
454 else
455 CAT=cat
456 fi
457 err "blog_showentry Started: `gdate +%S.%03N` ${fetch_ajax:+ajax}"
458 # Start blog_replies table
459 $CAT $midfile |
460 while IFS='|' read -r id edit notify uid author uname icon aid \
461 tm reki new hte fa imgids
462 do
463 if [ -n "$omitline" ]; then
464 n=$((n+1))
465 if [ $n -eq 1 ]; then
466 :
467 elif [ $n -eq 2 ]; then
468 cat $omitline
469 continue
470 elif [ $n -lt $((narts-nlimit+1)) ]; then
471 continue
472 fi
473 fi
474 mf2=$tmpd/midfile2
475 cachefile="$td/$id.row.html"
476 stampfile="$td/$id.row.stamp"
477 editlink="${edit:+<a href="$elink+$edit">編集</a> }"
478 nt="<label style=\"font-size: 70%;\"><input type=\"checkbox\"\
479 name=\"notifyto\" value=\"$uid\">返信通知送信</label>"
480 # fa is file accessibility flag # err "----r=$aid fa=[$fa]----"
482 # First, check the availability of user-icon.
483 # If not existent, clear and reset row cache by rm $stampfile
484 if [ ! -s "$icon" ]; then
485 rm -f "$stampfile"; unset stampfile
486 fi
487 if test -s "$stampfile" &&
488 test -s "$cachefile" &&
489 { ts=`cat "$stampfile"`; test -n "$ts"; } &&
490 /bin/test "$ts" '>' "$tm" && # Cache timestamp is newer
491 test "$stampfile" -nt "$icon"; then # UserIcon is older
492 : Nothing to do
493 else
494 { ######## New ROW creation begins here ######## >$cachefile
495 cachestamp=$tmpd/cache.$$.stamp
496 touch $cachestamp
497 tdcls="__NEWCLS__repatt"
498 if [ -s "$icon" ]; then
499 icfn=`echo "$icon"|htmlescape`
500 picon="<p class=\"proficon\"><a href=\"$hlink+$uid\" title=\"${author%@*}\"><img src=\"$icfn\"></a></p>"
501 else
502 echo "DELETE FROM user_s WHERE key='$iconcachekey' AND
503 val=`sqlquotestr \"$icon\"`;" >> $iconcleaner
504 picon=""
505 fi
507 cat<<EOF
508 <tr id="$id">
509 <td class="$tdcls">${picon}__EDIT__<a href="#$aid">#$aid</a>
510 <a class="author" href="$hlink+$uid" title="${author%@*}">`echo $uname|htmlescape`</a>
511 <span title="$tm">${reki:-$tm}</span>
512 <__NOTIFY__></td>
513 EOF
514 echo -n "<td id=\"$aid\" class=\"repl\">"
515 echo "$hte"|unhexize|htmlescape|hreflink|minitbl
516 usecache='' tsfile=$td/$id.stamp
517 for i in $imgids; do
518 mrid=${i%%:*}; i=${i#*:}; sz=`size_h ${i%%:*}`
519 _href="href=\"$catlink+$mrid\""
520 _href_raw="href=\"$catlink+$mrid+raw\""
521 fn=`echo "${i#*:}"|unhexize`
522 fnb=$fn"(${sz})"
523 case "$fn" in
524 *.[Pp][Nn][Gg]|*.[Jj][Pp][Gg]|*.[Jj][Pp][Ee][Gg]|*.[GgTt][Ii][Ff])
525 # fmt=${fn##*.} # convert - jpg:- is slow...why
526 case "$fn" in
527 *.[Pp][Nn][Gg]) fmt=png ;;
528 *.[Gg][Ii][Ff]) fmt=gif ;;
529 *) fmt=jpeg ;;
530 esac
531 outfile=$td/$mrid-${fn%.*}.$fmt
532 #err fn=$fn outfile=$outfile
533 #err "usecache=$usecache `ls -l $outfile`"
534 #err tm=$tm
535 #err tsfile=$tsfile=`cat $tsfile`
536 if [ -s "$outfile" ] && # $outfile should be > 0
537 { [ "$usecache" ] || # And usecache flag is true, or...
538 { [ -s "$tsfile" ] && [ x"`cat $tsfile`" = x"$tm" ]
539 };}; then
540 usecache=1 # Set usecache flag on
541 cat<<-EOF
542 <__UNCLICKABLE__><a $_href><img src="$outfile">
543 $fnb</a>
544 EOF
545 # !!NOTE!! Create row stamp ONLY WHEN imgcache is active
546 else
547 query "SELECT hex(bin) FROM article_m WHERE rowid=$mrid;" \
548 > $mf2 # Stop query here 2020-04-23
549 if cat $mf2 | unhexize \
550 | convert -define ${fmt}:size=100x100 -resize 100x100'>' \
551 - ${fmt}:- > $outfile
552 then
553 cat "$outfile" \
554 | hexize \
555 | sed -e 's/\(..\)/%\1/g' \
556 -e "s|^|<__UNCLICKABLE__><a $_href><img src=\"data:image/$fmt,|" \
557 -e "s|\$|\">$fnb</a>|"
558 unset stampfile # img data stream is not suitable to cache
559 echo $tm > $tsfile
560 else # Failed to convert
561 rm -f $outfile
562 echo "<__UNCLICKABLE__><a $_href>$fnb</a>"
563 fi
564 fi
565 ;;
566 *)
567 echo -n "<__UNCLICKABLE__><a $_href><img src=\"$deficon\">$fnb</a>"
568 echo "[<a ${_href_raw}>Direct</a>]"
569 ;;
570 esac
571 done
572 echo "</td></tr>"
573 } > "$cachefile.$$" ######## New ROW Creation Ends here ########
574 # Care about race condition
575 if [ -z "$hte" -a -s $cachefile -a $cachefile -nt $cachestamp ]; then
576 # If other process have created cache, give up to serve our file
577 rm -f $cachefile.$$
578 else
579 mv -f $cachefile.$$ $cachefile
580 fi
581 test -n "$stampfile" && date "+%F %T" > $stampfile
582 fi
583 if [ -n "$fa" ]; then
584 replhref="s/a href=[^>]*>/a>/"
585 else
586 replhref=""
587 fi
588 # Printing a cached row
589 sed -e "/^<td class=/s/__NEWCLS__/$new${new:+ }/" \
590 -e "/^<td class=/s,__EDIT__,$editlink," \
591 -e "/^<__NOTIFY__>/s,,${notify:+$nt}," \
592 ${replhref:+-e "/^<__UNCLICKABLE__>/$replhref"} \
593 ${replhref:+-e "/^<__UNREADABLE__>/$replhref"} \
594 -e "/<__UNCLICKABLE__>/s///" \
595 -e "/<__UNREADABLE__>/s,,${fa:+$cannotread}," \
596 $cachefile
597 done
599 help="=== コメントに使用できる特殊記法(記号は全て半角) ===
600 行頭に href=URL でURLへのリンク
601 行頭に iframe=URL でURL先を開く iframe
602 行頭「* 」で箇条書、次の行頭空白で継続、行頭詰めると箇条書終わり
603 行頭「1. 」で番号付、2行目以降も「1. 」で勝手に番号増える、行頭詰めで終わり
604 [[#記事番号]] でs4内の記事番号に飛ぶリンク
605 [[#検索キーワード]] でs4内の記事検索(記号はいくつか使えない)
606 [[URL]] でURLへのリンク、 [[URL|文字列]]でアンカー文字列指定
607 {{画像URL}} でインライン画像、 {{画像URL|幅}} でピクセル幅指定
608 {{{URL}}} でURL先を開く iframe、 {{{URL|高さ}}} ピクセル高さ指定
609 行頭: ## 大見出し, ### 中見出し, #### 小見出し
610 行末の2連続スペースまたは ;;; で強制改行(<br>)
611 |*見出し列|列2|列3… と行頭から始まる縦棒区切り行を続けて表
612 |*&gt;| でcolspan=2のカラム、&gt;&gt;で3,……、 *なしで td colspan
613 |*^ でrowspan=2のカラム、^^で3,^^^で4……、 *なしで td rowspan
614 「 *語群* 」で強調(両側の空白必要、** でもっと強調。*の代わりに _ でも可)
615 「~~文~~」 で「文」の部分に打ち消し線。
616 - [ ] と - [x] でチェックボックス"
617 touchhelp="${touchpanel:+<p class=\"help\">$help</p>}"
618 filehelp="《添付の注意》
619 $file_accept_help"
620 ntmode="通知モード=$blog_notify${blog_team:+ (team=$blog_team)}
621 記事の1行目を「## 」(半角シャープシャープ空白=大見出し)
622 にするとそれより後ろの部分がSubject(件名)になります。
623 記事先頭に @all で全員(チーム所有の場合はチーム全体)通知になります。
624 If the first line begins with &quot;## &quot;, sent it as Subject of email.
625 If begins with &quot;@all&quot;, notify to all group(or team) members."
626 textform=$(cat<<-EOF
627 <div class="fold">
628 <input type="checkbox" id="cmt" checked><label
629 accesskey="c" title="C" for="cmt">コメントする</label><div>
630 <table class="b">
631 <tr><td><textarea id="text" name="text" cols="72" rows="4" title="$help">
632 </textarea>$touchhelp</td></tr>
633 <tr><td>添付ファイル(${filesize_max_MB}以下):
634 `cgi_file image "" "$file_accept title=\"$filehelp\" multiple"`
635 </td></tr>
636 </table>
637 <input type="hidden" name="fetchtime" value="`date +%FT%T`">
638 <input type="hidden" name="filesize_max" value="$filesize_max">
639 <input type="submit" id="c" value="送信" class="$blog_notify" title="$ntmode">
640 <input type="reset" value="リセット"></div></div>
641 EOF
642 )
643 cat<<-EOF
644 </table> <!-- end of s4-blog:blog_showentry() main table -->
645 <p class="update_link"><a href="?reload/$rowid" accesskey="r"
646 title="Shortcut: R${nl}Get New">
647 <button id="reload">最新取得</button></a> / <a
648 href="#title" id="bottom" accesskey="t"
649 title="Shortcut: T${nl}to the Top">先頭へ</a>
650 ${showalllink:+/ `echo $showalllink|sed 's/n:all/&\#bottom/'`$limitedmsg}</p>
651 EOF
652 $iswritable && cat<<-EOF
653 <div class="blogcomment">
654 <input type="hidden" name="blogid" value="$id">
655 <input type="hidden" name="id" value="`genserial`">
656 <input type="hidden" name="stage" value="replyblog">
657 $textform
658 </div>
659 </form> <!-- End of s4-blog:blog_showentry() main form -->
660 EOF
661 # Clean up orphaned icon cache
662 [ -s $iconcleaner ] && query ".read '$iconcleaner'"
663 # Record access log
664 acclog blog $rowid
665 err "blog_showentry Finished: `gdate +%S.%03N` ${fetch_ajax:+ajax}"
666 }
668 lshandout() {
669 # $1=rowid of blog (numericalized in s4.cgi)
670 blog_writable $1 $user
671 rc=$? # =0: writable, $BLOG_NOTMEM bit set => not member
672 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ] ; then
673 echo "メンバー以外は利用できません。" | html p; return
674 fi
675 time=`getvalbyid blog ctime $1|colrm 11`
676 owner=`getvalbyid blog owner $1`
677 title=`getvalbyid blog title $1`
678 ge=`gecos "$owner"`
679 htmlowner=`printf '%s' "${ge:-$owner}"|htmlescape`
680 fh=$tmpd/formhead
681 echo "$time [$title]@$htmlowner" > $fh
682 lshandoutsub "$owner" "$@" \
683 |_m4 -D_TITLE_="提出状況" \
684 -D_H1_="提出状況" \
685 -D_FORMHEAD_="syscmd(cat $fh)" \
686 -D_FORM_="syscmd(cat)" -D_DUMPHEAD_= -D_DUMPTABLE_= \
687 $layout/html.m4.html $layout/form+dump-whead.m4.html
688 gn=`printf '%s' "$owner"|htmlescape`
689 echo "<p><a href=\"?lshandoutall+$1\">グループ $gn すべてのレポート板集計</a></p>"
690 }
691 gethandoutcsv() {
692 # contenttype; echo
693 CATCSV=1 lshandoutall "$1"
694 }
695 gethandoutcsv2() {
696 # contenttype; echo
697 SQL=$(cat<<-EOF
698 WITH this_blog_articles AS (
699 SELECT rtb.id bid, rtb.brid, a.id aid, author, title, ctime
700 FROM report_type_blogs rtb JOIN article a ON rtb.id=a.blogid
701 ), text_or_file AS (
702 SELECT bid, author, title, ctime, 'text' shu, count(val) cnt
703 FROM this_blog_articles tba, article_s s
704 ON tba.aid=s.id
705 WHERE key='text'
706 GROUP by bid, author
707 UNION
708 SELECT bid, author, title, ctime, 'file' shu, count(val) cnt
709 FROM this_blog_articles tba, article_m m
710 ON tba.aid=m.id
711 WHERE key='image'
712 GROUP by bid, author
713 ), count_list AS (
714 SELECT author,
715 substr(ctime, 1, 10)||upper(substr(shu, 1, 1)) unit,
716 cnt
717 FROM text_or_file
718 )
719 SELECT gecos "名前",
720 substr(author, 1, instr(author, '@')-1) "uname",
721 unit,
722 cnt "post"
723 FROM count_list cl JOIN gecoses g ON cl.author=g.name;
724 EOF
725 ) gethandoutcsv "$1"
726 }
727 lshandout_ulink_table() {
728 # NO Args. Read stdin as SQL
729 echo '<table class="b td3rr td3evw">'
730 hrb="<a href=\"?home+"
731 # echo "$sql" | sq -header -html $db \ # Formerly, this is called via sq()
733 printf ".mode html\n.header ON\n" | query
734 cat | query \
735 | sed -e "s,\(<TR><TD>\)\([^ ]*\) \(.*\)</TD>,\1$hrb\2\">\3</TD>," -e 's,<TD>0</TD>,<TD class="warn">0</TD>,'
736 echo '</table>'
737 printf ".mode list\n.header OFF\n" | query
738 }
739 lshandoutall() {
740 # $1=rowid of blog
741 blog_writable $1 $user
742 rc=$? # =0: writable, $BLOG_NOTMEM bit set => not member
743 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ] ; then
744 echo "メンバー以外は利用できません。" | html p; return
745 fi
746 rowid=$(($1 + 0))
747 owner=`getvalbyid blog owner $1`
748 qowner=`sqlquotestr "$owner"`
750 query<<-EOF
751 CREATE TEMPORARY TABLE IF NOT EXISTS report_type_blogs AS
752 WITH blog_owner_mode AS (
753 SELECT id,
754 blog.rowid brid,
755 max(CASE key WHEN 'owner' THEN val END) owner,
756 max(CASE key WHEN 'mode' THEN val END) mode,
757 max(CASE key WHEN 'title' THEN val END) title,
758 max(CASE key WHEN 'ctime' THEN val END) ctime
759 FROM blog NATURAL JOIN blog_s
760 GROUP BY id
761 )
762 SELECT id, brid, title, ctime FROM blog_owner_mode
763 /* WHERE owner=$qowner AND mode LIKE '%report%'; */
764 WHERE owner=$qowner
765 AND
766 (mode LIKE '%report%' OR mode LIKE '%quiz%'
767 OR mode LIKE '%enquete%');
768 /* ↑これでレポート形式の blogid 一覧を得る */
769 EOF
770 if [ -z "$CATCSV" ]; then
771 _m4 -D_TITLE_="提出状況" $layout/html.m4.html
772 ge=`gecos "$owner"`
773 tbls=""
774 grptxt=`printf '%s' "${ge:-$owner}"|htmlescape`
775 echo "<h1>$grptxt 書き込み状況一覧</h1>"
776 fi
777 if [ -z "$SQL" ]; then
778 bridlist=`query "SELECT brid FROM report_type_blogs;"`
779 for brid in $bridlist; do # Skip this loop if $SQL set
780 brid=$(($brid + 0)) # Ensure to be a number
781 [ $brid = 0 ] && continue
782 time=`getvalbyid blog ctime $brid|colrm 11`
783 title=`getvalbyid blog title $brid`
784 titleH=`printf '%s' "$title"|htmlescape`
785 state=`getvalbyid blog state $brid|htmlescape`
786 tt="handout_$brid"
787 [ "$state" = "frozen" ] && frozen=" $FROZEN_TAG" || frozen=""
788 if [ -z "$CATCSV" ]; then
789 echo "<h2>$time - <a href=\"?replyblog+$brid\">$titleH</a>$frozen</h2>"
790 lshandoutsub "$owner" $brid "$tt"
791 else
792 lshandoutsub "$owner" $brid "$tt" >/dev/null # Only create temp.table
793 fi
794 tbls="$tbls${tbls:+ LEFT NATURAL JOIN }$tt"
795 done
796 fi
797 sql=${SQL:-"SELECT * FROM $tbls;"}
798 if [ -z "$CATCSV" ]; then
799 echo "<hr><h2>総合</h2>"
800 echo "$sql" | lshandout_ulink_table
801 echo "<h2>総合(<a id=\"gethandoutcsv\" href=\"?gethandoutcsv+$rowid\">CSV</a>)</h2>"
802 echo '<p id="bommsg"></p>'
803 printf ".mode csv\n.header ON\n" | query
804 echo '<pre id="totalcsv" class="list">'
805 echo "$sql" | query | sed 's/^"[0-9]* /"/'
806 echo "</pre>"
807 echo "<pre><a href=\"?gethandoutcsv2+$rowid\">縦持ちCSV</a></pre>"
808 else
809 contenttype "Application/CSV"
810 printf ".mode csv\n.header ON\n" | query >/dev/null
811 fn=report-count.csv
812 printf 'Content-Disposition: filename="%s"\n' "$fn"
813 outfile=$tmpd/out-$$.csv
814 echo "$sql" | query | sed 's/^"[0-9]* /"/' > $outfile
815 echo "Content-Length: " `cat $outfile | wc -c`; echo
817 cat $outfile
818 exit 0
819 fi
820 printf ".mode list\n.header OFF\n.separator |\n" | query
821 }
822 lshandoutsub() {
823 # $1=owner $2=rowid of blog &optional $3=temp_table name
824 qgname=`sqlquote "$1"`
825 if isgroup "$1"; then
826 sample="(select user from grp_mem where gname=$qgname)"
827 else
828 sample="(select distinct author as user from arts)"
829 echo "(集計は板への投稿者のみ)" | html p
830 fi
831 tmpname="${3:-handout_$2}"
832 sql="CREATE TEMPORARY TABLE IF NOT EXISTS $tmpname AS
833 with arts as (select id,author from article \
834 where blogid=(select id from blog where rowid=$2))\
835 select (select rowid from user where name=c0.user)||' '|| \
836 (select gecos from gecoses where name=c0.user) as 'メンバー',\
837 substr(c0.user, 1, instr(c0.user, '@')-1) 'uname',\
838 sum(case when c1.key is not null then 1 else 0 end)\
839 as '[$title] コメント記入',\
840 sum(case when c2.key is not null then 1 else 0 end)\
841 as '[$title] ファイルの提出'\
842 from $sample c0 \
843 left join (select id,author from arts) a\
844 on c0.user=a.author\
845 left join (select id,key from article_s where key='text') c1\
846 on a.id=c1.id left join (select id,key from article_m ) c2\
847 on c1.id=c2.id group by c0.user order by c0.user;\
848 \
849 SELECT * FROM $tmpname;"
850 # err ishandoutsub: sql="$sql"
851 echo "$sql" | lshandout_ulink_table
852 }
853 gethandout() {
854 # $1=rowid of blog
855 rid=`numericalize "$1"`
856 test x"$2" = x"by_uname" && by_uname="$2"
857 blog_writable $rid $user
858 rc=$? # =0: writable, $BLOG_NOTMEM bit set => not member
859 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ] ; then
860 contenttype; echo
861 echo "メンバー以外は利用できません。" | html p; return
862 fi
863 # Here, this blog is writable by $user
864 mode=`getvalbyid blog mode $1`
865 owner=`getvalbyid blog owner $1`
866 blogauthor=`getvalbyid blog author $1`
867 isopenblogauthor=false
868 if [ x"$user" = x"$owner" ]; then
869 : OK
870 elif isgrpowner "$user" "$owner"; then
871 : OK
872 elif [ x"$blogauthor" = x"$user" ]; then
873 # Non-admin Author of blog cannot do gethandout() in report-closed mode
874 # for avoidance the risk of fake report-closed blog.
875 case "$mode" in # Only report-open can be handled by blog author
876 *open*) isopenblogauthor=true ;;
877 esac
878 else
879 contenttype; echo
880 echo "グループ管理者のみ取得できます。" | html p; return
881 fi
882 copy2csv=false
883 blogid=`getvalbyid blog id $1`
884 isgroup "$owner" && isgroup=true || isgroup=false
885 isgrpowner "$user" "$owner" && isgrpadmin=true || isgrpadmin=false
887 i=0
888 midfile=$tmpd/midfile
889 bd=$tmpd/archive.$$
890 mkdir $bd
891 case "$mode" in
892 *quiz*)
893 copy2csv=true ;;
894 *enquete*)
895 copy2csv=true
896 csvline=`getvalbyid blog heading $1 | grep "..*,." | head -1`
897 # Create CSV-base table for questionnaire
898 # If heading in blog_s has at least 1 CSV line,
899 # we take the line as column list.
900 # Otherwise we produce two column CSV as below:
901 # USER,ANSWER
902 query "DROP TABLE IF EXISTS tmp_q;"
903 if [ -n "$csvline" ]; then
904 query <<-EOF
905 CREATE TEMPORARY TABLE tmp_q("user", $csvline);
906 EOF
907 if [ $? != 0 ]; then
908 contenttype; echo
909 cat <<-EOF | html p; exit
910 掲示板のヘッダにあるCSV定義が不正でCSV出力できません。
911 $csvline
912 空白なしの項目名を半角カンマ区切りで1行で書いてください。
913 EOF
914 fi
915 else
916 query <<-EOF
917 CREATE TEMPORARY TABLE tmp_q(user text PRIMARY KEY, answer);
918 EOF
919 fi
920 esac
921 if $copy2csv; then
922 mkdir $bd/$rid
923 outcsv=$bd/$rid/migrate-$rid.csv
924 fullcsv=$bd/$rid/all-text-full-$rid.csv
925 sq "$db" <<-EOF | tr '|' ',' > $outcsv
926 SELECT author as "USER",
927 replace(val, x'0a', ',') as "${csvline:-ANSWER}"
928 FROM article a JOIN article_s s ON a.id=s.id
929 AND blogid=(SELECT id FROM blog WHERE rowid=$rid)
930 AND s.key='text';
931 EOF
932 sq "$db" <<-EOF > $fullcsv
933 .mode csv
934 .head 1
935 SELECT author as "ユーザ",
936 (SELECT gecos FROM gecoses g WHERE author=g.name) as "表示名",
937 val as "テキスト"
938 FROM article a JOIN article_s s ON a.id=s.id
939 AND blogid=(SELECT id FROM blog WHERE rowid=$rid)
940 AND s.key='text';
941 EOF
942 fi
943 query <<-EOF > $midfile # Using tempfile for quick db-unlock
944 SELECT a.rowid, a.id artid, a.author,
945 max(CASE key WHEN 'ctime' THEN hex(s.val) END) ctime,
946 max(CASE key WHEN 'text' THEN hex(s.val) END) text
947 FROM article a JOIN article_s s ON a.id=s.id
948 WHERE blogid=(SELECT id FROM blog WHERE rowid=$rowid)
949 GROUP BY artid
950 ORDER BY a.rowid;
951 EOF
952 cat $midfile | while IFS='|' read rowid artid author ctime text; do
953 $isgrpowner || $isopenblogauthor \
954 || isfilereadable $user article_s $rowid || continue
955 if [ "$by_uname" ]; then
956 dir=`printf $bd/%d/%s/%s "$rid" "$author" "$rowid"`
957 else
958 dir=`printf $bd/%d/%06d "$rid" "$rowid"`
959 fi
960 test -d "$dir" || mkdir -p "$dir"
961 i=0
962 query "SELECT m.rowid, m.val FROM article_m m \
963 WHERE id='$artid' AND m.key IN ('image', 'document', 'binary');" \
964 | while IFS='|' read mrowid filename; do
965 i=$((i+1))
966 if [ "$by_uname" ]; then
967 outfile=`printf "%s/%s" "$dir" "$filename"`
968 else
969 outfile=`printf "%s/%s" "$dir" "$filename"`
970 fi
971 query "SELECT quote(bin) FROM article_m WHERE rowid=$mrowid;" \
972 | unhexize > "$outfile"
973 done
974 art=00-Article.txt
975 while [ -s "$dir"/$art ]; do
976 art=0$art
977 done
978 cat<<-EOF > "$dir/$art"
979 -----------------------------------------
980 Author: $author
981 Date: `echo "$ctime" | unhexize`
982 -----------------------------------------
983 `echo "$text" | unhexize`
984 EOF
985 done
986 if [ ! -d $bd/$rid ]; then
987 contenttype; echo
988 echo "取得できるファイルがありませんでした。" | html p
989 return
990 fi
992 if $copy2csv; then
993 query <<-EOF > $bd/$rid/all-text-1stline-$rid.csv
994 .mode csv
995 .head 1
996 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_q("user", "TEXT");
997 .import $outcsv tmp_q
998 SELECT * FROM tmp_q;
999 .mode list
1000 .head 0
1001 EOF
1002 fi
1003 err "BDLIST: `ls -l $bd`"
1004 rm "$outcsv"
1005 arcname=archive-$rid.tar.gz
1006 ### outstdout=true
1007 (cd $bd
1008 # query() CANNOT BE used in this subshell
1009 if [ "$outstdout" ]; then
1010 cat <<-EOF
1011 Content-type: application/x-gzip
1012 Content-Disposition: filename="$arcname"
1014 EOF
1015 tar zcf - $rid
1016 return
1017 else
1018 tar zcf .archive.tar.gz $rid && mv .archive.tar.gz "$arcname"
1019 err Creating tar archive "`ls -l "$arcname"`"
1020 fi
1022 arcfile=$bd/$arcname
1023 echo "Content-type: application/x-gzip"
1024 echo "Content-Length: `cat $arcfile|wc -c`"
1025 echo "Content-Disposition: filename=\"$arcname\""
1026 echo
1027 cat $arcfile
1029 blogseen() { # $1 = blogid
1030 blogid=${1%%[!0-9]*}
1031 if [ -z "$blogid" ]; then
1032 echo "Invalid blog id" | html p; exit
1033 fi
1034 blog_writable "$blogid" "$user"
1035 rc=$? # =0: writable, $BLOG_NOTMEM bit set => not member
1036 if [ $((rc & $BLOG_NOTMEM)) -gt 0 ] ; then
1037 echo "メンバー以外は利用できません。" | html p; return
1038 fi
1039 owner=`getvalbyid blog owner $rowid`
1040 qowner=`sqlquotestr "$owner"`
1041 grprowid=`query "SELECT rowid FROM grp WHERE gname=$qowner;"`
1042 ge=`gecos "$owner" | htmlescape`
1043 title=`getvalbyid blog title $rowid | htmlescape`
1044 h1="アクセス時刻"
1045 link2board="<a href=\"?replyblog+$rowid\">$title</a>"
1046 link2group="<a href=\"?grp+$grprowid\">$ge</a>"
1047 _m4 -D_TITLE_="$h1" $layout/html.m4.html
1048 echo "$h1" | html h1
1049 echo "[$link2board]@$link2group" | html h2
1050 warn=' class="warn"'
1051 cat <<-EOF
1052 <table class="b">
1053 <tr><th>メンバー</th><th>uname</th><th>最終閲覧時刻</th></tr>
1054 EOF
1055 query <<-EOF |
1056 WITH grpmem as (
1057 SELECT user, (SELECT gecos FROM gecoses WHERE name=user) gecos
1058 FROM grp_mem
1059 WHERE gname=(SELECT val FROM blog_s
1060 WHERE id=(select id from blog where rowid=$blogid)
1061 AND key='owner')
1062 ), acctime AS (
1063 SELECT user, max(time) atime
1064 FROM tblaccesses
1065 WHERE tbl='blog' AND tblrowid=$blogid
1066 GROUP BY user
1068 SELECT g.user,
1069 (SELECT rowid FROM user u WHERE u.name=g.user),
1070 hex(gecos),
1071 atime
1072 FROM grpmem g LEFT JOIN acctime t
1073 ON g.user = t.user
1074 GROUP BY g.user
1075 ORDER BY atime DESC;
1076 EOF
1077 while IFS='|' read u uid hexge time; do
1078 td=${time:+"<td>"} # If the variable time is set, td=<td>
1079 td=${td:-"<td$warn>"} # else td=<td class="warn">
1080 cat <<-EOF
1081 <tr>
1082 <td><a href="?home+$uid">`echo "$hexge"|unhexize|htmlescape`</a></td>
1083 <td>`printf '%s' "${u%%@*}"|htmlescape`</td>
1084 $td${time:----}</td></tr>
1085 EOF
1086 done
1087 cat <<-EOF
1088 </table>
1089 <p><a href="?replyblog+$rowid">[$title]に戻る</a></p>
1090 </html>
1091 EOF
1093 lsmyfile() { # $1(optional)=SortBy
1094 case "$1" in
1095 ""|CTIME-DESC)
1096 by="CTIME" ord="DESC" ;;
1097 CTIME*) by="CTIME" ;;
1098 FILE*) by="FILE" ;;
1099 OWNER*) by="OWNER" ;;
1100 TITLE*) by="TITLE" ;;
1101 esac
1102 case "$1" in
1103 *DESC) ord="DESC" ;;
1104 esac
1105 case "$ord" in
1106 DESC) lkod="" jord="降順" ;;
1107 *) lkod="-DESC" jord="昇順" ;;
1108 esac
1109 sql="select m.val||'/'||m.rowid FILE,
1110 coalesce(
1111 case when (select name from user where name=bs.owner)
1112 is not null
1113 then (select val from user_s where name=bs.owner
1114 and key='gecos')
1115 when (select gname from grp where gname=bs.owner)
1116 is not null
1117 then (select val from grp_s where gname=bs.owner
1118 and key='gecos')
1119 else
1120 null
1121 end,
1122 bs.owner
1123 ) OWNER,
1124 a_s.val CTIME,
1125 ',t,'||bs.title||':'||b.rowid||'#'||a.id TITLE
1126 from (select rowid,id,val from article_m where id
1127 in (select id from article where author='$user')
1128 and type like 'file:%')
1129 m left join article a on m.id=a.id
1130 left join article_s a_s on a.id=a_s.id and a_s.key='ctime'
1131 left join (select id,
1132 max(case key when 'owner' then val end) as owner,
1133 max(case key when 'title' then val end) as title
1134 from blog_s group by id)
1135 bs on a.blogid=bs.id
1136 left join blog b on bs.id=b.id
1137 where m.val is not null order by $by $ord;"
1138 err lshandoutbyauthor: sql=`echo "$sql"`
1139 title="個人提出ファイル"
1140 _m4 -D_TITLE_=$title $layout/html.m4.html
1141 hra="<a href=\"?lsmyfile+"
1142 hrb="<a href=\"?showattc+article_m+"
1143 hrc="<a href=\"?replyblog+"
1144 (echo '<table class="b">'
1145 echo "$sql"|sq -html -header $db ) \
1146 | sed -e "s|\(<TR><TD>\)\([^/]*\)/\([0-9]*\)|\1$hrb\3\">\2</a>|" \
1147 -e "s|,t,\(.*\):\([^<]*\)\(</TD>\)|$hrc\2\">\1</a>\3|" \
1148 -e "s|\(<TH>\)\([A-Z]*\)\(</TH>\)|\1$hra\2$lkod\">\2</a>|" \
1149 | _m4 -D_TITLE_=$title -D_FORM_="<p>($by$jord)</p>" \
1150 -D_DUMPTABLE_="syscmd(cat)" $layout/form+dump.m4.html
1151 echo '</table>'
1153 getteamcsv() {
1154 gid=`numericalize "$1"`
1155 grp=`getgroupbyid "$gid"`
1156 extra="$2"
1157 err "gid=$gid grp=$grp extra=$extra"
1158 if ! isgrpowner "$user" "$grp"; then
1159 contentytpe 'text/plain; charset="utf-8"'; echo
1160 echo "管理者メンバー以外は利用できません。" | html p; return
1161 fi
1162 fn="Team-$gid"
1163 case "$extra" in
1164 "") ;;
1165 name)
1166 fn=${fn}-with-name
1167 xSQL=",
1168 substr(user, 1, instr(user, '@')-1) \"uname\",
1169 coalesce((SELECT gecos FROM gecoses WHERE name=user), user) gecos" ;;
1170 esac
1171 fn="$fn.csv"
1172 csv="$tmpd/$fn.csv"
1173 err csv=$csv
1174 # We can leave csv mode here because this scripts will exit soon
1175 query <<-EOF > "$csv"
1176 .mode csv
1177 .head 1
1178 SELECT val "ルーム名を事前割り当て", user "メールアドレス" $xSQL
1179 FROM grp_mem_m
1180 WHERE key='team'
1181 AND gname=(SELECT gname FROM grp WHERE rowid=$gid)
1182 ORDER BY val;
1183 EOF
1184 contenttype 'text/plain; charset="utf-8"'
1185 echo "Content-Disposition: filename=\"$fn\""
1186 echo "Content-Length: " `cat $csv | wc -c`; echo
1187 cat $csv
1188 exit
1190 searchart() {
1191 _m4 -D_TITLE_="検索結果" $layout/html.m4.html
1192 kwd=`getpar kwd|nkf -wZ1` # Convert Zenkaku-SPC to ASCII-SPC
1193 bloglist=`getpar bloglist|sed 's/[^0-9,]//g'`
1194 kwdgrp=""
1195 authcond=""
1196 if [ -z "$kwd" ]; then
1197 echo "検索語を指定してください" | html p; return
1198 fi
1199 if logstart "$searchlog"; then
1200 { echo "kwd=$kwd"
1201 test -n "$bloglist" && echo "bloglist=$bloglist"
1202 } >> $searchlog
1203 logend "$searchlog"
1204 fi
1205 if expr x"$kwd" : 'x#[1-9][0-9]*$' >/dev/null 1>&2; then
1206 # Like '#1234', assume as artID
1207 rowid=$((${kwd#\#} + 0)) # Force to be a number
1208 kc="ar.rowid = $rowid"
1209 else
1210 for k in `printf '%s' "$kwd" | sed "s/'/''/g"`; do # With wrap quotes
1211 ctime=""
1212 if expr x"$k" : 'x@[><= ]*[1-9][][0-9]*-[][0-9:-]*$' >/dev/null >&2; then
1213 # '@<2016-10-10' -> ctime < '2016-10-10'
1214 # '@>=2016-10-10' -> ctime >= '2016-10-10'
1215 # '@2016-10-10' -> ctime GLOB '@2016-10-10'
1216 k=${k#@}
1217 case "$k" in
1218 [\<\>]*) op=${k%%[!<>=]*}; ctime=${k##*[><= ]} ;;
1219 *) op='GLOB'; ctime="${k##*[><= ]}*" ;;
1220 esac
1221 kc=$kc${kc:+" AND "}"ctime $op '${ctime}'"
1222 # Not sure GROUP BY a.blogid is comfortable for searchers...?
1223 ##### kwdgrp=" GROUP BY a.blogid" ## Add this to lessen results
1224 elif [ x"$k" = x"@today" -o x"$k" = x"@今日" ]; then
1225 ctime=`date +%F`
1226 elif n=`expr x"$k" : 'x@\([0-9]*\)days*'` >/dev/null >&2; then
1227 ctime=`query "SELECT datetime('now', 'localtime', '-$n days');"`
1228 elif [ x"$k" = x"@week" ]; then
1229 ctime=`query "SELECT datetime('now', 'localtime', '-7 days');"`
1230 elif n=`expr x"$k" : 'x@\([0-9]*\)weeks*'` >/dev/null >&2; then
1231 n=$((n * 7))
1232 ctime=`query "SELECT datetime('now', 'localtime', '-$n days');"`
1233 elif [ x"$k" = x"@month" ]; then
1234 ctime=`query "SELECT datetime('now', 'localtime', '-1 month');"`
1235 elif n=`expr x"$k" : 'x@\([0-9]*\)months*'` >/dev/null >&2; then
1236 ctime=`query "SELECT datetime('now', 'localtime', '-$n month');"`
1237 elif [ x"$k" = x"@year" ]; then
1238 ctime=`query "SELECT datetime('now', 'localtime', '-1 year');"`
1239 elif n=`expr x"$k" : 'x@\([0-9]*\)years*'` >/dev/null >&2; then
1240 ctime=`query "SELECT datetime('now', 'localtime', '-$n year');"`
1241 fi
1242 if [ -n "$ctime" ]; then
1243 kc=$kc${kc:+" AND "}"ctime > '${ctime}'"
1244 else
1245 e=""
1246 case "$k" in
1247 *${likeesc}*) e="" ;; # Giving up char-escaping
1248 *%*|*_*) k=`printf '%s' "$k"|sed "s/\([%_]\)/${likeesc}\1/g"`
1249 e=" ESCAPE '$likeesc'" ;;
1250 esac
1251 kc=$kc${kc:+" AND "}"content LIKE '%$k%'$e"
1252 fi
1253 done
1254 fi
1255 kwd=`printf '%s' "$kwd"|htmlescape`
1256 owner=`getpar owner`
1257 owner=${owner:-$1}
1258 grid=`getpar grid`
1259 msg=""
1260 if [ -n "$grid" ]; then
1261 grp=`getgroupbyid "$grid"`
1262 qgrp=`sqlquote "$grp"`
1263 cond="WHERE key='owner' AND val=$qgrp"
1264 msg="(`linkhome $grid` グループから)"
1265 elif [ -n "$owner" ]; then
1266 cond="where key='owner' and val='$owner'"
1267 msg="(`linkhome $owner` さんの記録から)"
1268 elif { author=`getpar author`; test -n "$author"; }; then
1269 atptn=`sqlquotestr $author`
1270 #kc="$kc${kc:+ AND }author=$atptn"
1271 authcond="WHERE author=$atptn"
1272 if isuser $author; then
1273 msg="(`linkhome $author` さんの書き込みから)"
1274 fi
1275 fi
1276 if [ -n "$bloglist" ]; then
1277 blogcond="AND bl.rid IN ($bloglist)"
1278 fi
1280 sf=`search_form "$search_form_args" "$kwd" | sed '1d;$d'` # rm <div></div>
1281 echo "$sf" | sed -e "/POST SENTENCE/s/.*/__PS__/" -e "/EOF/q" \
1282 | _m4 -D__PS__="による検索結果$msg"
1283 echo "(上記入力窓で再検索すると下記の掲示板のみに絞って再検索します)" \
1284 | html p 'class="small"'
1285 # article_s: id=article-id, key='text', val='TEXT'
1286 # article: id=article-id, blogid=blogkd
1287 # blog: id=blog-id, author=LeaderAuthor
1288 # blog_s: id=blog-id, key='title', val='BLOG-TITLE'
1289 # WANT: blog-ROWid,article-id,val(TEXT)
1290 sql2="`sql4readableblogs` -- Extract user-readable blogs
1291 -- 0.3sec
1292 WITH artsm AS (
1293 SELECT a.id,ctime, text || ' ' || coalesce(files, '') content
1294 FROM article a
1295 LEFT JOIN
1296 (SELECT ars.id, ctime, text, coalesce(files, '') files
1297 FROM (SELECT id,
1298 max(CASE key WHEN 'ctime' THEN val END) ctime,
1299 max(CASE key WHEN 'text' THEN val END) text
1300 FROM article_s
1301 GROUP BY id) ars
1302 LEFT JOIN
1303 (SELECT id, group_concat(val) files
1304 FROM article_m
1305 WHERE type LIKE 'file:%'
1306 GROUP BY id) arm
1307 ON ars.id=arm.id
1308 ) ar
1309 ON a.id=ar.id
1310 ), ar AS (
1311 SELECT a.rowid, a.blogid, a.id, a.author, ctime, content
1312 FROM article a JOIN artsm ON a.id=artsm.id
1313 $authcond
1314 ), bl AS (
1315 SELECT blg.rid, blg.*, blog_s.val TITLE
1316 FROM readableblogs blg JOIN blog_s ON blg.id=blog_s.id AND blog_s.key='title'
1318 SELECT bl.rid||'+n:all#'||ar.id '',
1319 bl.title TITLE,
1320 (SELECT gecos FROM gecoses WHERE name=ar.author) AUTHOR,
1321 substr(ctime, 0, 11) DATE,
1322 substr(content, 0, 78) TEXT
1323 FROM ar JOIN bl
1324 ON ar.blogid=bl.id
1325 WHERE $kc AND bl.id IN (SELECT id FROM blog_s $cond) $blogcond
1326 ORDER by DATE DESC, TITLE, ctime;"
1327 sedopt="s,<TR><TD>\([^<]*\)</TD>,<TR><TD><a\
1328 href=\"?replyblog+\1\">VIEW</a></TD>,"
1329 # echo "$sql2" > tmp/sql.out
1330 result=$tmpd/result.$$
1331 cat<<EOF
1332 <table class="b searchart">
1333 `sq -header -html $db "$sql2"|sed "$sedopt"|tee $result`
1334 </table>
1335 EOF
1336 if [ -s "$result" ]; then
1337 found=$((`grep "^<TR><TD>" $result | wc -l` + 0)) # Cast to INT
1338 one=${found%1}
1339 echo "$found match${one:+es} found"
1340 # <a href="?replyblog+39#12345">VIEW</a>
1341 # -> 39,49,55, -> 39,49,55
1342 # -> <input type="hidden" name="bloglist" value="39,49,55">
1343 sed -n "/.*href=.*replyblog\+\([0-9][0-9]*\).*/s//\1/p" "$result" \
1344 | sort | uniq | tr '\n' ',' \
1345 | sed -e 's/,$//' \
1346 -e 's/^/<input type="hidden" name="bloglist" value="/' \
1347 -e 's/$/">/'
1348 else
1349 echo orz...
1350 fi
1351 echo "$sf" | sed "1,/-- EOF/d" # Close <form>
1353 listblog() (
1354 # $1={user,group}
1355 qow=`sqlquote "$1"`
1356 cond="where a.id in (select id from blog_s where key='owner' and val=$qow) order by ctime desc"
1357 cgi_form searchart<<EOF
1358 <label>`cgi_text kwd`という語を含む記事をこの一覧から検索</label>
1359 `cgi_hidden owner $user`
1360 EOF
1361 DT_CHLD=article:blogid DT_QOWNER=$qow \
1362 dumptable html blog 'ctime title heading' "$cond"
1365 blog_setval() {
1366 # $1=GRProwID $2=key $3=value
1367 # RETURN VALUE(JSON):
1368 # {code: EXIT_CODE, message: MESSAGE}
1369 # This function will be called via ajax control of fetch() suite,
1370 # so we need to return JSON text string and exit directly.
1371 rid=`numericalize $1`
1372 blogowner=`getvalbyid blog owner "$rid"`
1373 contenttype "application/json; charset=utf-8"; echo
1374 if [ -z "$blogowner" ]; then
1375 msg="不当な掲示板です"; code=1
1376 elif ! isgroup "$blogowner"; then
1377 msg="グループのみの操作です"; code=2
1378 elif ! isgrpowner "$user" "$blogowner"; then
1379 msg="グループ管理者のみの操作です"; code=3
1380 else # With full permission
1381 blogid=`query "SELECT id FROM blog WHERE rowid=$rid;"`
1382 dbsetbyid blog "$blogid" "$2" "$3"
1383 code=0
1384 fi
1385 # echo "{\"code\": $code, \"message\": \"foo\"}"; exit
1386 newval=`getvalbyid blog "$2" "$1"`
1387 alert="${msg:+, \"alert\": \"$msg\"}"
1388 json=$(cat <<-EOF
1389 {"code": $code, "$2": "`printf '%s' "$newval"|sed 's/"/\\\\"/g'`"$alert}
1390 EOF
1392 err blog_setval: returning JSON: "$json"
1393 printf '%s\n' "$json"
1394 exit
1397 blog_setfrozen() {
1398 # $1=GRProwID $2=val={ "frozen" | "" }
1399 err blog_setfrozen: getvalbyid-blog-$1=`getvalbyid blog state "$1"`
1400 case `getvalbyid blog state "$1"` in
1401 [Ff][Rr]*) newval="" ;;
1402 *) newval="frozen" ;;
1403 esac
1404 blog_setval "$1" state $newval
1407 blog_addentry() {
1408 # $1=GRProwID(if it is a group)
1409 grprowid=`numericalize $1`
1410 rowid=`getpar rowid`
1411 ## err blog_addentry0: rowid=$rowid
1412 if [ -n "$grprowid" ]; then
1413 owner=`getgroupbyid $grprowid`
1414 else
1415 owner=`getpar owner`
1416 fi
1417 htmlowner=`printf '%s' $owner|htmlescape`
1418 err blog-add: \$1=$grprowid rowid=$rowid owner=$owner
1419 if isgroup "$owner"; then
1420 if [ -z "$grprowid" ]; then
1421 qgrp=`sqlquote "$owner"` # Inefficient...
1422 grprowid=`query "SELECT rowid FROM grp WHERE gname=$qgrp;"`
1423 fi
1424 groupmode=1 listing=$owner GF_OWNER=$owner
1425 titleguide="[$owner]" guide="[`linkhome $grprowid`]"
1426 GF_ARGS=$(mvteamform "$owner")
1427 else
1428 usermode=1 listing=$user guide="[個人]" titleguide=$guide
1429 fi
1431 title=`getpar title`
1432 if [ -n "$title" ]; then
1433 if [ "$usermode" ]; then
1434 err usermode: user=$user owner=$owner
1435 if [ x"$user" != x"$owner" ]; then
1436 echo "他人の日記は書けません" | html p
1437 return 2
1438 fi
1439 elif [ "$groupmode" ]; then # if write to group log
1440 grp=$owner #\`getpar grp\`
1441 err ismember: $user $grp
1442 if ! ismember "$user" "$grp"; then
1443 echo "(話題作成はこのグループに加入してから)" | html p
1444 return 3
1445 fi
1446 fi
1447 par2table $formdir/blog.def
1448 serial=`getpar serial`
1449 ## err SERIAL: $serial ROWID=$rowid listing=$listing
1450 id=""
1451 if [ -n "$rowid" ]; then
1452 # Here, id becomes NULL when removal of entries at par2table
1453 id=`query "select rowid from blog where rowid=$rowid;"`
1454 elif [ -n "$serial" ]; then
1455 # If new blog leader created, traverse to its head.
1456 id=`query "select rowid from blog where id='$serial';"`
1457 ## err new-Leader: "select rowid from blog where id='$serial';" id=$id
1458 fi
1459 if [ -n "$id" ]; then
1460 ## If modifying existing blog, JUMP to blog_reply
1461 blog_reply $id
1462 return
1463 fi
1464 # Newly created blog comes here:
1465 mv2team=`getpar mv2team`
1466 if [ -n "$mv2team" -a -n "$groupmode" ]; then
1467 # For newly created BLOG, assign team-name if necessary and correct
1468 qmt=`sqlquote "$mv2team"`
1469 qowner=`sqlquote "$owner"`
1470 team=$(query "SELECT val FROM grp_mem_m
1471 WHERE key='team' AND val=$qmt AND gname=$qowner;")
1472 if [ -n "$team" ]; then # If it is valid team name
1473 qtt=`sqlquote "$title"`
1474 # We should acquire newly created blog id from title step by step
1475 thisblog=$(query \
1476 "SELECT id FROM blog_s
1477 WHERE id IN (SELECT id FROM blog_s
1478 WHERE key='owner' AND val=$qowner)
1479 AND key='title' AND val=$qtt;")
1480 if [ -n "$thisblog" ]; then
1481 query "REPLACE INTO blog_s(id, key, type, val)
1482 VALUES('$thisblog', 'team', 'string', $qmt);"
1483 fi
1484 fi
1485 fi
1486 fi
1487 echo "${titleguide}新規話題作成" > $tmpd/title.$$
1488 echo "${guide}新規話題作成" > $tmpd/h1.$$
1489 listblog "$listing" > $tmpd/listblog.$$
1490 genform $formdir/blog.def \
1491 | _m4 -D_TITLE_="spaste(\`$tmpd/title.$$')" \
1492 -D_H1_="spaste(\`$tmpd/h1.$$')" \
1493 -D_FORMHEAD_="序文は簡単に詳しくはコメントに" \
1494 -D_DUMPHEAD_="これまでの蓄積" \
1495 -D_FORM_="syscmd(\`cat')" \
1496 -D_DUMPTABLE_="spaste(\`$tmpd/listblog.$$')" \
1497 $layout/html.m4.html \
1498 $layout/form+dump-whead.m4.html
1501 blog_reply() { # Posting to blog article
1502 # $1=rowid $2=control-sequence
1503 rowid=`numericalize $1` # Ensure (already purified in s4.cgi)
1505 if [ -z "$rowid" ]; then
1506 echo "表示する日記番号が未指定です。" | html p
1507 return
1508 fi
1509 title=`getvalbyid blog title $rowid`
1510 owner=`getvalbyid blog owner $rowid`
1511 htmlowner=`printf '%s' $owner|htmlescape`
1512 qowner=`sqlquotestr "$owner"`
1513 if [ -z "$title" ]; then
1514 echo "日記番号指定が無効です。" | html p
1515 return
1516 fi
1517 err "blog_reply Started: `gdate +%S.%03N` blogrowid=$rowid"
1518 blog_writable $rowid $user; rc=$?
1519 if [ $rc = 0 ]; then
1520 iswritable=true
1521 else
1522 iswritable=false
1523 if [ $((rc & $BLOG_FROZEN)) -gt 0 ]; then
1524 isfrozen=true
1525 frozen_class='frozen"'
1526 frozen_flag=$FROZEN_TAG
1527 fi
1528 fi
1529 if isuser "$owner"; then
1530 subtitle="`gecos $owner` さんの話題"
1531 else
1532 grprowid=`query "select rowid from grp where gname=$qowner;"`
1533 subtitle="グループ
1534 <a href=\"?grp+$grprowid\" accesskey=\"h\" title=\"H\">$htmlowner</a> での話題
1535 `query \"SELECT printf('(チーム:%s)', val)\
1536 FROM blog_s
1537 WHERE id=(SELECT id FROM blog WHERE rowid=$rowid)
1538 AND key='team';
1539 \"|htmlescape`"
1540 memclass=`grp_getbodyclass "$owner"`
1541 fi
1543 text=`getpar text`
1544 if [ -n "$text" ]; then
1545 if $iswritable; then
1546 ## BEGIN: 2020-06-11 - Post Integrity Check. Disable if it slows down..
1547 blogid=`getpar blogid | tr -c -d 'a-z0-9'`
1548 brid=`query "SELECT rowid FROM blog WHERE id='$blogid';"`
1549 if [ x"$rowid" != x"$brid" ]; then
1550 _id=`getpar id | tr -c -d 'a-z0-9'`
1551 _aid=`query "SELECT rowid FROM article WHERE id='$_id';"`
1552 if [ -z "$_aid" ]; then
1553 echo "掲示板から書き込んで下さい。" | html p
1554 return
1555 fi
1556 fi
1557 ## END:
1558 par2table $formdir/article.def
1559 st=$?
1560 err "blog_reply: POSTdone `gdate +%S.%03N` - st=$st title=$title owner=$owner user=$user, blogid=$blogid"
1561 case $st in
1562 0|4)
1563 [ "$st" = "4" ] && act="書込削除"
1564 blog_notify_reply $rowid $user "$text" $act
1565 if [ -n "$grprowid" ]; then
1566 qgrp=$(sqlquote "$owner")
1567 dbsetbyid grp "$owner" wtime "`date '+%F %T'`"
1568 else
1569 dbsetbyid user "$user" wtime "`date '+%F %T'`"
1570 fi
1571 ;;
1572 esac
1573 else
1574 if $isfrozen; then
1575 title="$title(凍結板につき書き込み不可)"
1576 else
1577 title="$title(加入してないので書き込み不可)"
1578 fi
1579 fi
1580 fi
1581 def=$formdir/article.def
1582 echo "$title" | htmlescape > $tmpd/title.$$
1583 echo "$subtitle$frozen_flag" > $tmpd/subtitle.$$
1584 ${BLOG_SHOW:-blog_showentry} blog $rowid "$2" \
1585 | _m4 -D_TITLE_="spaste(\`$tmpd/title.$$')" \
1586 -D_H1_="spaste(\`$tmpd/title.$$')" \
1587 -D_BODYCLASS_=general"${memclass:+ $memclass}" \
1588 -D_FORMHEAD_="spaste(\`$tmpd/subtitle.$$')" \
1589 -D_FORM_='' \
1590 -D_DUMPTABLE_="syscmd(cat)" -D_DUMPHEAD_="" \
1591 $layout/html.m4.html $layout/form+dump-whead.m4.html
1592 err "blog_reply Finished: `gdate +%S.%03N` user=$user owner=[$owner] title=[$title]"
1595 blog_fetch() {
1596 contenttype "text/plain; charset=utf-8"; echo
1597 err blog_fetch: blog "$@"
1598 blog_reply "$@"
1599 # blog_showentry blog "$@"
1602 blog_reply_article() { # Direct link to article in some blog
1603 arid=${1:-0} # Already sanitized to digits
1604 brid=`query "SELECT rowid FROM blog WHERE \
1605 id=(SELECT blogid FROM article WHERE rowid=$arid);"`
1606 if [ -n "$brid" ]; then
1607 newurl="?replyblog+$brid#$arid"
1608 echo "Refresh: 0; $newurl"; echo
1609 exit 0
1610 else
1611 contenttype; echo
1612 echo "無効な記事番号です." | html p
1613 fi