OSDN Git Service

BugTrack/2500 comment, pcomment plugin: Require msg on submit
[pukiwiki/pukiwiki.git] / plugin / pcomment.inc.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone
3 // pcomment.inc.php
4 // Copyright 2002-2020 PukiWiki Development Team
5 // License: GPL v2 or (at your option) any later version
6 //
7 // pcomment plugin - Show/Insert comments into specified (another) page
8
9 // Usage: #pcomment([page][,max][,options])
10 //
11 //   page -- An another page-name that holds comments
12 //           (default:PLUGIN_PCOMMENT_PAGE)
13 //   max  -- Max number of recent comments to show
14 //           (0:Show all, default:PLUGIN_PCOMMENT_NUM_COMMENTS)
15 //
16 // Options:
17 //   above -- Comments are listed above the #pcomment (added by chronological order)
18 //   below -- Comments are listed below the #pcomment (by reverse order)
19 //   reply -- Show radio buttons allow to specify where to reply
20
21 // Default recording page name (%s = $vars['page'] = original page name)
22 define('PLUGIN_PCOMMENT_PAGE', '[[Comments/%s]]');
23 define('PLUGIN_PCOMMENT_PAGE_COMPATIBLE', '[[コメント/%s]]'); // for backword compatible of 'ja' pcomment
24
25 define('PLUGIN_PCOMMENT_NUM_COMMENTS',     10); // Default 'latest N posts'
26 define('PLUGIN_PCOMMENT_DIRECTION_DEFAULT', 1); // 1: above 0: below
27 define('PLUGIN_PCOMMENT_SIZE_MSG',  70);
28 define('PLUGIN_PCOMMENT_SIZE_NAME', 15);
29
30 // Auto log rotation
31 define('PLUGIN_PCOMMENT_AUTO_LOG', 0); // 0:off 1-N:number of comments per page
32
33 // Update recording page's timestamp instead of parent's page itself
34 define('PLUGIN_PCOMMENT_TIMESTAMP', 0);
35
36 // ----
37 define('PLUGIN_PCOMMENT_FORMAT_NAME',   '[[$name]]');
38 define('PLUGIN_PCOMMENT_FORMAT_MSG',    '$msg');
39 define('PLUGIN_PCOMMENT_FORMAT_NOW',    '&new{$now};');
40
41 // "\x01", "\x02", "\x03", and "\x08" are used just as markers
42 define('PLUGIN_PCOMMENT_FORMAT_STRING',
43         "\x08" . 'MSG' . "\x08" . ' -- ' . "\x08" . 'NAME' . "\x08" . ' ' . "\x08" . 'DATE' . "\x08");
44
45 function plugin_pcomment_action()
46 {
47         global $vars;
48
49         if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
50
51         if (! isset($vars['msg']) || $vars['msg'] == '') return array();
52         $refer = isset($vars['refer']) ? $vars['refer'] : '';
53
54         $retval = plugin_pcomment_insert();
55         if ($retval['collided']) {
56                 $vars['page'] = $refer;
57                 return $retval;
58         }
59
60         pkwk_headers_sent();
61         header('Location: ' . get_page_uri($refer, PKWK_URI_ROOT));
62         exit;
63 }
64
65 function plugin_pcomment_convert()
66 {
67         global $vars;
68         global $_pcmt_messages;
69
70         $params = array(
71                 'noname'=>FALSE,
72                 'nodate'=>FALSE,
73                 'below' =>FALSE,
74                 'above' =>FALSE,
75                 'reply' =>FALSE,
76                 '_args' =>array()
77         );
78
79         foreach(func_get_args() as $arg)
80                 plugin_pcomment_check_arg($arg, $params);
81
82         $vars_page = isset($vars['page']) ? $vars['page'] : '';
83         if (isset($params['_args'][0]) && $params['_args'][0] != '') {
84                 $page = $params['_args'][0];
85         } else {
86                 $raw_vars_page = strip_bracket($vars_page);
87                 $page = sprintf(PLUGIN_PCOMMENT_PAGE, $raw_vars_page);
88                 $raw_page = strip_bracket($page);
89                 if (!is_page($raw_page)) {
90                         // If the page doesn't exist, search backward-compatible page
91                         // If only compatible page exists, set the page as comment target
92                         $page_compat = sprintf(PLUGIN_PCOMMENT_PAGE_COMPATIBLE, $raw_vars_page);
93                         if (is_page(strip_bracket($page_compat))) {
94                                 $page = $page_compat;
95                         }
96                 }
97         }
98         $count = isset($params['_args'][1]) ? intval($params['_args'][1]) : 0;
99         if ($count == 0) $count = PLUGIN_PCOMMENT_NUM_COMMENTS;
100
101         $_page = get_fullname(strip_bracket($page), $vars_page);
102         if (!is_pagename($_page))
103                 return sprintf($_pcmt_messages['err_pagename'], htmlsc($_page));
104
105         $dir = PLUGIN_PCOMMENT_DIRECTION_DEFAULT;
106         if ($params['below']) {
107                 $dir = 0;
108         } elseif ($params['above']) {
109                 $dir = 1;
110         }
111
112         list($comments, $digest) = plugin_pcomment_get_comments($_page, $count, $dir, $params['reply']);
113
114         if (PKWK_READONLY) {
115                 $form_start = $form = $form_end = '';
116         } else {
117                 // Show a form
118
119                 if ($params['noname']) {
120                         $title = $_pcmt_messages['msg_comment'];
121                         $name = '';
122                 } else {
123                         $title = $_pcmt_messages['btn_name'];
124                         $name = '<input type="text" name="name" size="' . PLUGIN_PCOMMENT_SIZE_NAME . '" />';
125                 }
126
127                 $radio   = $params['reply'] ?
128                         '<input type="radio" name="reply" value="0" tabindex="0" checked="checked" />' : '';
129                 $comment = '<input type="text" name="msg" size="' .
130                         PLUGIN_PCOMMENT_SIZE_MSG . '" required />';
131
132                 $s_page   = htmlsc($page);
133                 $s_refer  = htmlsc($vars_page);
134                 $s_nodate = htmlsc($params['nodate']);
135                 $s_count  = htmlsc($count);
136
137                 $form_start = '<form action="' . get_base_uri() .
138                         '" method="post" class="_p_pcomment_form">' . "\n";
139                 $form = <<<EOD
140   <div>
141   <input type="hidden" name="digest" value="$digest" />
142   <input type="hidden" name="plugin" value="pcomment" />
143   <input type="hidden" name="refer"  value="$s_refer" />
144   <input type="hidden" name="page"   value="$s_page" />
145   <input type="hidden" name="nodate" value="$s_nodate" />
146   <input type="hidden" name="dir"    value="$dir" />
147   <input type="hidden" name="count"  value="$count" />
148   $radio $title $name $comment
149   <input type="submit" value="{$_pcmt_messages['btn_comment']}" />
150   </div>
151 EOD;
152                 $form_end = '</form>' . "\n";
153         }
154
155         if (! is_page($_page)) {
156                 $link   = make_pagelink($_page);
157                 $recent = $_pcmt_messages['msg_none'];
158         } else {
159                 $msg    = ($_pcmt_messages['msg_all'] != '') ? $_pcmt_messages['msg_all'] : $_page;
160                 $link   = make_pagelink($_page, $msg);
161                 $recent = ! empty($count) ? sprintf($_pcmt_messages['msg_recent'], $count) : '';
162         }
163
164         if ($dir) {
165                 return '<div>' .
166                         '<p>' . $recent . ' ' . $link . '</p>' . "\n" .
167                         $form_start .
168                                 $comments . "\n" .
169                                 $form .
170                         $form_end .
171                         '</div>' . "\n";
172         } else {
173                 return '<div>' .
174                         $form_start .
175                                 $form .
176                                 $comments. "\n" .
177                         $form_end .
178                         '<p>' . $recent . ' ' . $link . '</p>' . "\n" .
179                         '</div>' . "\n";
180         }
181 }
182
183 function plugin_pcomment_insert()
184 {
185         global $vars, $now, $_title_updated, $_no_name, $_pcmt_messages;
186
187         $refer = isset($vars['refer']) ? $vars['refer'] : '';
188         $page  = isset($vars['page'])  ? $vars['page']  : '';
189         $page  = get_fullname($page, $refer);
190
191         if (! is_pagename($page))
192                 return array(
193                         'msg' =>'Invalid page name',
194                         'body'=>'Cannot add comment' ,
195                         'collided'=>TRUE
196                 );
197
198         check_editable($page, true, true);
199
200         $ret = array('msg' => $_title_updated, 'collided' => FALSE);
201
202         $msg = str_replace('$msg', rtrim($vars['msg']), PLUGIN_PCOMMENT_FORMAT_MSG);
203         $name = (! isset($vars['name']) || $vars['name'] == '') ? $_no_name : $vars['name'];
204         $name = ($name == '') ? '' : str_replace('$name', $name, PLUGIN_PCOMMENT_FORMAT_NAME);
205         $date = (! isset($vars['nodate']) || $vars['nodate'] != '1') ?
206                 str_replace('$now', $now, PLUGIN_PCOMMENT_FORMAT_NOW) : '';
207         if ($date != '' || $name != '') {
208                 $msg = str_replace("\x08" . 'MSG'  . "\x08", $msg,  PLUGIN_PCOMMENT_FORMAT_STRING);
209                 $msg = str_replace("\x08" . 'NAME' . "\x08", $name, $msg);
210                 $msg = str_replace("\x08" . 'DATE' . "\x08", $date, $msg);
211         }
212
213         $reply_hash = isset($vars['reply']) ? $vars['reply'] : '';
214         if ($reply_hash || ! is_page($page)) {
215                 $msg = preg_replace('/^\-+/', '', $msg);
216         }
217         $msg = rtrim($msg);
218
219         if (! is_page($page)) {
220                 $postdata = '[[' . htmlsc(strip_bracket($refer)) . ']]' . "\n\n" .
221                         '-' . $msg . "\n";
222         } else {
223                 $postdata = get_source($page);
224                 $count    = count($postdata);
225
226                 $digest = isset($vars['digest']) ? $vars['digest'] : '';
227                 if (md5(join('', $postdata)) !== $digest) {
228                         $ret['msg']  = $_pcmt_messages['title_collided'];
229                         $ret['body'] = $_pcmt_messages['msg_collided'];
230                 }
231
232                 $start_position = 0;
233                 while ($start_position < $count) {
234                         if (preg_match('/^\-/', $postdata[$start_position])) break;
235                         ++$start_position;
236                 }
237                 $end_position = $start_position;
238
239                 $dir = isset($vars['dir']) ? $vars['dir'] : '';
240
241                 // Find the comment to reply
242                 $level   = 1;
243                 $b_reply = FALSE;
244                 if ($reply_hash != '') {
245                         while ($end_position < $count) {
246                                 $matches = array();
247                                 if (preg_match('/^(\-{1,2})(?!\-)(.*)$/', $postdata[$end_position++], $matches)
248                                         && md5($matches[2]) === $reply_hash)
249                                 {
250                                         $b_reply = TRUE;
251                                         $level   = strlen($matches[1]) + 1;
252
253                                         while ($end_position < $count) {
254                                                 if (preg_match('/^(\-{1,3})(?!\-)/', $postdata[$end_position], $matches)
255                                                         && strlen($matches[1]) < $level) break;
256                                                 ++$end_position;
257                                         }
258                                         break;
259                                 }
260                         }
261                 }
262
263                 if ($b_reply == FALSE)
264                         $end_position = ($dir == '0') ? $start_position : $count;
265
266                 // Insert new comment
267                 array_splice($postdata, $end_position, 0, str_repeat('-', $level) . $msg . "\n");
268
269                 if (PLUGIN_PCOMMENT_AUTO_LOG) {
270                         $_count = isset($vars['count']) ? $vars['count'] : '';
271                         plugin_pcomment_auto_log($page, $dir, $_count, $postdata);
272                 }
273
274                 $postdata = join('', $postdata);
275         }
276         page_write($page, $postdata, PLUGIN_PCOMMENT_TIMESTAMP);
277
278         if (PLUGIN_PCOMMENT_TIMESTAMP) {
279                 if ($refer != '') pkwk_touch_file(get_filename($refer));
280                 put_lastmodified();
281         }
282
283         return $ret;
284 }
285
286 // Auto log rotation
287 function plugin_pcomment_auto_log($page, $dir, $count, & $postdata)
288 {
289         if (! PLUGIN_PCOMMENT_AUTO_LOG) return;
290
291         $keys = array_keys(preg_grep('/(?:^-(?!-).*$)/m', $postdata));
292         if (count($keys) < (PLUGIN_PCOMMENT_AUTO_LOG + $count)) return;
293
294         if ($dir) {
295                 // Top N comments (N = PLUGIN_PCOMMENT_AUTO_LOG)
296                 $old = array_splice($postdata, $keys[0], $keys[PLUGIN_PCOMMENT_AUTO_LOG] - $keys[0]);
297         } else {
298                 // Bottom N comments
299                 $old = array_splice($postdata, $keys[count($keys) - PLUGIN_PCOMMENT_AUTO_LOG]);
300         }
301
302         // Decide new page name
303         $i = 0;
304         do {
305                 ++$i;
306                 $_page = $page . '/' . $i;
307         } while (is_page($_page));
308
309         page_write($_page, '[[' . $page . ']]' . "\n\n" . join('', $old));
310
311         // Recurse :)
312         plugin_pcomment_auto_log($page, $dir, $count, $postdata);
313 }
314
315 // Check arguments
316 function plugin_pcomment_check_arg($val, & $params)
317 {
318         if ($val != '') {
319                 $l_val = strtolower($val);
320                 foreach (array_keys($params) as $key) {
321                         if (strpos($key, $l_val) === 0) {
322                                 $params[$key] = TRUE;
323                                 return;
324                         }
325                 }
326         }
327
328         $params['_args'][] = $val;
329 }
330
331 function plugin_pcomment_get_comments($page, $count, $dir, $reply)
332 {
333         global $_msg_pcomment_restrict;
334
335         if (! check_readable($page, false, false))
336                 return array(str_replace('$1', $page, $_msg_pcomment_restrict));
337
338         $reply = (! PKWK_READONLY && $reply); // Suprress radio-buttons
339
340         $data = get_source($page);
341         $data = preg_replace('/^#pcomment\(?.*/i', '', $data);  // Avoid eternal recurse
342
343         if (! is_array($data)) return array('', 0);
344
345         $digest = md5(join('', $data));
346
347         // Get latest N comments
348         $num  = $cnt     = 0;
349         $cmts = $matches = array();
350         if ($dir) $data = array_reverse($data);
351         foreach ($data as $line) {
352                 if ($count > 0 && $dir && $cnt == $count) break;
353
354                 if (preg_match('/^(\-{1,2})(?!\-)(.+)$/', $line, $matches)) {
355                         if ($count > 0 && strlen($matches[1]) == 1 && ++$cnt > $count) break;
356
357                         // Ready for radio-buttons
358                         if ($reply) {
359                                 ++$num;
360                                 $cmts[] = $matches[1] . "\x01" . $num . "\x02" .
361                                         md5($matches[2]) . "\x03" . $matches[2] . "\n";
362                                 continue;
363                         }
364                 }
365                 $cmts[] = $line;
366         }
367         $data = $cmts;
368         if ($dir) $data = array_reverse($data);
369         unset($cmts, $matches);
370
371         // Remove lines before comments
372         while (! empty($data) && substr($data[0], 0, 1) != '-')
373                 array_shift($data);
374
375         $comments = convert_html($data);
376         unset($data);
377
378         // Add radio buttons
379         if ($reply)
380                 $comments = preg_replace('/<li>' . "\x01" . '(\d+)' . "\x02" . '(.*)' . "\x03" . '/',
381                         '<li class="pcmt"><input class="pcmt" type="radio" name="reply" value="$2" tabindex="$1" />',
382                         $comments);
383
384         return array($comments, $digest);
385 }