2 // PukiWiki - Yet another WikiWikiWeb clone
4 // Copyright 2002-2020 PukiWiki Development Team
5 // License: GPL v2 or (at your option) any later version
7 // pcomment plugin - Show/Insert comments into specified (another) page
9 // Usage: #pcomment([page][,max][,options])
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)
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
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
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);
31 define('PLUGIN_PCOMMENT_AUTO_LOG', 0); // 0:off 1-N:number of comments per page
33 // Update recording page's timestamp instead of parent's page itself
34 define('PLUGIN_PCOMMENT_TIMESTAMP', 0);
37 define('PLUGIN_PCOMMENT_FORMAT_NAME', '[[$name]]');
38 define('PLUGIN_PCOMMENT_FORMAT_MSG', '$msg');
39 define('PLUGIN_PCOMMENT_FORMAT_NOW', '&new{$now};');
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");
45 function plugin_pcomment_action()
49 if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
51 if (! isset($vars['msg']) || $vars['msg'] == '') return array();
52 $refer = isset($vars['refer']) ? $vars['refer'] : '';
54 $retval = plugin_pcomment_insert();
55 if ($retval['collided']) {
56 $vars['page'] = $refer;
61 header('Location: ' . get_page_uri($refer, PKWK_URI_ROOT));
65 function plugin_pcomment_convert()
68 global $_pcmt_messages;
79 foreach(func_get_args() as $arg)
80 plugin_pcomment_check_arg($arg, $params);
82 $vars_page = isset($vars['page']) ? $vars['page'] : '';
83 if (isset($params['_args'][0]) && $params['_args'][0] != '') {
84 $page = $params['_args'][0];
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))) {
98 $count = isset($params['_args'][1]) ? intval($params['_args'][1]) : 0;
99 if ($count == 0) $count = PLUGIN_PCOMMENT_NUM_COMMENTS;
101 $_page = get_fullname(strip_bracket($page), $vars_page);
102 if (!is_pagename($_page))
103 return sprintf($_pcmt_messages['err_pagename'], htmlsc($_page));
105 $dir = PLUGIN_PCOMMENT_DIRECTION_DEFAULT;
106 if ($params['below']) {
108 } elseif ($params['above']) {
112 list($comments, $digest) = plugin_pcomment_get_comments($_page, $count, $dir, $params['reply']);
115 $form_start = $form = $form_end = '';
119 if ($params['noname']) {
120 $title = $_pcmt_messages['msg_comment'];
123 $title = $_pcmt_messages['btn_name'];
124 $name = '<input type="text" name="name" size="' . PLUGIN_PCOMMENT_SIZE_NAME . '" />';
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 />';
132 $s_page = htmlsc($page);
133 $s_refer = htmlsc($vars_page);
134 $s_nodate = htmlsc($params['nodate']);
135 $s_count = htmlsc($count);
137 $form_start = '<form action="' . get_base_uri() .
138 '" method="post" class="_p_pcomment_form">' . "\n";
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']}" />
152 $form_end = '</form>' . "\n";
155 if (! is_page($_page)) {
156 $link = make_pagelink($_page);
157 $recent = $_pcmt_messages['msg_none'];
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) : '';
166 '<p>' . $recent . ' ' . $link . '</p>' . "\n" .
178 '<p>' . $recent . ' ' . $link . '</p>' . "\n" .
183 function plugin_pcomment_insert()
185 global $vars, $now, $_title_updated, $_no_name, $_pcmt_messages;
187 $refer = isset($vars['refer']) ? $vars['refer'] : '';
188 $page = isset($vars['page']) ? $vars['page'] : '';
189 $page = get_fullname($page, $refer);
191 if (! is_pagename($page))
193 'msg' =>'Invalid page name',
194 'body'=>'Cannot add comment' ,
198 check_editable($page, true, true);
200 $ret = array('msg' => $_title_updated, 'collided' => FALSE);
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);
213 $reply_hash = isset($vars['reply']) ? $vars['reply'] : '';
214 if ($reply_hash || ! is_page($page)) {
215 $msg = preg_replace('/^\-+/', '', $msg);
219 if (! is_page($page)) {
220 $postdata = '[[' . htmlsc(strip_bracket($refer)) . ']]' . "\n\n" .
223 $postdata = get_source($page);
224 $count = count($postdata);
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'];
233 while ($start_position < $count) {
234 if (preg_match('/^\-/', $postdata[$start_position])) break;
237 $end_position = $start_position;
239 $dir = isset($vars['dir']) ? $vars['dir'] : '';
241 // Find the comment to reply
244 if ($reply_hash != '') {
245 while ($end_position < $count) {
247 if (preg_match('/^(\-{1,2})(?!\-)(.*)$/', $postdata[$end_position++], $matches)
248 && md5($matches[2]) === $reply_hash)
251 $level = strlen($matches[1]) + 1;
253 while ($end_position < $count) {
254 if (preg_match('/^(\-{1,3})(?!\-)/', $postdata[$end_position], $matches)
255 && strlen($matches[1]) < $level) break;
263 if ($b_reply == FALSE)
264 $end_position = ($dir == '0') ? $start_position : $count;
266 // Insert new comment
267 array_splice($postdata, $end_position, 0, str_repeat('-', $level) . $msg . "\n");
269 if (PLUGIN_PCOMMENT_AUTO_LOG) {
270 $_count = isset($vars['count']) ? $vars['count'] : '';
271 plugin_pcomment_auto_log($page, $dir, $_count, $postdata);
274 $postdata = join('', $postdata);
276 page_write($page, $postdata, PLUGIN_PCOMMENT_TIMESTAMP);
278 if (PLUGIN_PCOMMENT_TIMESTAMP) {
279 if ($refer != '') pkwk_touch_file(get_filename($refer));
287 function plugin_pcomment_auto_log($page, $dir, $count, & $postdata)
289 if (! PLUGIN_PCOMMENT_AUTO_LOG) return;
291 $keys = array_keys(preg_grep('/(?:^-(?!-).*$)/m', $postdata));
292 if (count($keys) < (PLUGIN_PCOMMENT_AUTO_LOG + $count)) return;
295 // Top N comments (N = PLUGIN_PCOMMENT_AUTO_LOG)
296 $old = array_splice($postdata, $keys[0], $keys[PLUGIN_PCOMMENT_AUTO_LOG] - $keys[0]);
299 $old = array_splice($postdata, $keys[count($keys) - PLUGIN_PCOMMENT_AUTO_LOG]);
302 // Decide new page name
306 $_page = $page . '/' . $i;
307 } while (is_page($_page));
309 page_write($_page, '[[' . $page . ']]' . "\n\n" . join('', $old));
312 plugin_pcomment_auto_log($page, $dir, $count, $postdata);
316 function plugin_pcomment_check_arg($val, & $params)
319 $l_val = strtolower($val);
320 foreach (array_keys($params) as $key) {
321 if (strpos($key, $l_val) === 0) {
322 $params[$key] = TRUE;
328 $params['_args'][] = $val;
331 function plugin_pcomment_get_comments($page, $count, $dir, $reply)
333 global $_msg_pcomment_restrict;
335 if (! check_readable($page, false, false))
336 return array(str_replace('$1', $page, $_msg_pcomment_restrict));
338 $reply = (! PKWK_READONLY && $reply); // Suprress radio-buttons
340 $data = get_source($page);
341 $data = preg_replace('/^#pcomment\(?.*/i', '', $data); // Avoid eternal recurse
343 if (! is_array($data)) return array('', 0);
345 $digest = md5(join('', $data));
347 // Get latest N comments
349 $cmts = $matches = array();
350 if ($dir) $data = array_reverse($data);
351 foreach ($data as $line) {
352 if ($count > 0 && $dir && $cnt == $count) break;
354 if (preg_match('/^(\-{1,2})(?!\-)(.+)$/', $line, $matches)) {
355 if ($count > 0 && strlen($matches[1]) == 1 && ++$cnt > $count) break;
357 // Ready for radio-buttons
360 $cmts[] = $matches[1] . "\x01" . $num . "\x02" .
361 md5($matches[2]) . "\x03" . $matches[2] . "\n";
368 if ($dir) $data = array_reverse($data);
369 unset($cmts, $matches);
371 // Remove lines before comments
372 while (! empty($data) && substr($data[0], 0, 1) != '-')
375 $comments = convert_html($data);
380 $comments = preg_replace('/<li>' . "\x01" . '(\d+)' . "\x02" . '(.*)' . "\x03" . '/',
381 '<li class="pcmt"><input class="pcmt" type="radio" name="reply" value="$2" tabindex="$1" />',
384 return array($comments, $digest);