s4

view s4-blog.sh @ 534:ce6d0f04f520

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