2 // PukiWiki - Yet another WikiWikiWeb clone.
5 // 2002-2017 PukiWiki Development Team
6 // 2002 Y.MASUI GPL2 http://masui.net/pukiwiki/ masui@masui.net
11 define('PLUGIN_BUGTRACK_NUMBER_FORMAT', '%d'); // Like 'page/1'
12 //define('PLUGIN_BUGTRACK_NUMBER_FORMAT', '%03d'); // Like 'page/001'
14 function plugin_bugtrack_init()
16 global $_plugin_bugtrack;
19 if (isset($init)) return; // Already init
20 if (isset($_plugin_bugtrack)) die('Global $_plugin_bugtrack had been init. Why?');
23 $_plugin_bugtrack = array(
24 'priority_list' => array('緊急', '重要', '普通', '低'),
25 'state_list' => array('提案', '着手', '完了', '保留', '却下'),
26 'state_sort' => array('着手', '保留', '完了', '提案', '却下'),
27 'state_class' => array('bugtrack_state_proposal', 'bugtrack_state_accept',
28 'bugrack_state_resolved', 'bugtrack_state_pending', 'bugtrack_state_cancel',
29 'bugtrack_state_undef'),
32 'nosummary'=> 'ここにサマリを記入して下さい',
39 'category' => 'カテゴリー',
41 'pagename_comment' => '空欄のままだと自動的にページ名が振られます。',
42 'version_comment' => '空欄でも構いません',
48 // #bugtrack: Show bugtrack form
49 function plugin_bugtrack_convert()
53 if (PKWK_READONLY) return ''; // Show nothing
55 $base = $vars['page'];
57 if (func_num_args()) {
58 $category = func_get_args();
59 $_base = get_fullname(strip_bracket(array_shift($category)), $base);
60 if (is_pagename($_base)) $base = $_base;
63 return plugin_bugtrack_print_form($base, $category);
66 function plugin_bugtrack_print_form($base, $category)
68 global $_plugin_bugtrack;
73 $select_priority = "\n";
74 $count = count($_plugin_bugtrack['priority_list']);
76 for ($i = 0; $i < $count; ++$i) {
77 if ($i == ($count - 1)) $selected = ' selected="selected"'; // The last one
78 $priority_list = htmlsc($_plugin_bugtrack['priority_list'][$i]);
79 $select_priority .= ' <option value="' . $priority_list . '"' .
80 $selected . '>' . $priority_list . '</option>' . "\n";
84 for ($i = 0; $i < count($_plugin_bugtrack['state_list']); ++$i) {
85 $state_list = htmlsc($_plugin_bugtrack['state_list'][$i]);
86 $select_state .= ' <option value="' . $state_list . '">' .
87 $state_list . '</option>' . "\n";
90 if (empty($category)) {
91 $encoded_category = '<input name="category" id="_p_bugtrack_category_' . $id .
94 $encoded_category = '<select name="category" id="_p_bugtrack_category_' . $id . '">';
95 foreach ($category as $_category) {
96 $s_category = htmlsc($_category);
97 $encoded_category .= '<option value="' . $s_category . '">' .
98 $s_category . '</option>' . "\n";
100 $encoded_category .= '</select>';
103 $script = get_base_uri();
104 $s_base = htmlsc($base);
105 $s_name = htmlsc($_plugin_bugtrack['name']);
106 $s_category = htmlsc($_plugin_bugtrack['category']);
107 $s_priority = htmlsc($_plugin_bugtrack['priority']);
108 $s_state = htmlsc($_plugin_bugtrack['state']);
109 $s_pname = htmlsc($_plugin_bugtrack['pagename']);
110 $s_pnamec = htmlsc($_plugin_bugtrack['pagename_comment']);
111 $s_version = htmlsc($_plugin_bugtrack['version']);
112 $s_versionc = htmlsc($_plugin_bugtrack['version_comment']);
113 $s_summary = htmlsc($_plugin_bugtrack['summary']);
114 $s_body = htmlsc($_plugin_bugtrack['body']);
115 $s_submit = htmlsc($_plugin_bugtrack['submit']);
117 <form action="$script" method="post">
120 <th><label for="_p_bugtrack_name_$id">$s_name</label></th>
121 <td><input id="_p_bugtrack_name_$id" name="name" size="20" type="text" /></td>
124 <th><label for="_p_bugtrack_category_$id">$s_category</label></th>
125 <td>$encoded_category</td>
128 <th><label for="_p_bugtrack_priority_$id">$s_priority</label></th>
129 <td><select id="_p_bugtrack_priority_$id" name="priority">$select_priority </select></td>
132 <th><label for="_p_bugtrack_state_$id">$s_state</label></th>
133 <td><select id="_p_bugtrack_state_$id" name="state">$select_state </select></td>
136 <th><label for="_p_bugtrack_pagename_$id">$s_pname</label></th>
137 <td><input id="_p_bugtrack_pagename_$id" name="pagename" size="20" type="text" />
138 <small>$s_pnamec</small></td>
141 <th><label for="_p_bugtrack_version_$id">$s_version</label></th>
142 <td><input id="_p_bugtrack_version_$id" name="version" size="10" type="text" />
143 <small>$s_versionc</small></td>
146 <th><label for="_p_bugtrack_summary_$id">$s_summary</label></th>
147 <td><input id="_p_bugtrack_summary_$id" name="summary" size="60" type="text" /></td>
150 <th><label for="_p_bugtrack_body_$id">$s_body</label></th>
151 <td><textarea id="_p_bugtrack_body_$id" name="body" cols="60" rows="6"></textarea></td>
154 <td colspan="2" align="center">
155 <input type="submit" value="$s_submit" />
156 <input type="hidden" name="plugin" value="bugtrack" />
157 <input type="hidden" name="mode" value="submit" />
158 <input type="hidden" name="base" value="$s_base" />
169 function plugin_bugtrack_action()
173 if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
174 if ($post['mode'] != 'submit') return FALSE;
176 $page = plugin_bugtrack_write($post['base'], $post['pagename'], $post['summary'],
177 $post['name'], $post['priority'], $post['state'], $post['category'],
178 $post['version'], $post['body']);
181 header('Location: ' . get_page_uri($page, PKWK_URI_ROOT));
185 function plugin_bugtrack_write($base, $pagename, $summary, $name, $priority, $state, $category, $version, $body)
189 $base = strip_bracket($base);
190 $pagename = strip_bracket($pagename);
192 $postdata = plugin_bugtrack_template($base, $summary, $name, $priority,
193 $state, $category, $version, $body);
195 $page_list = plugin_bugtrack_get_page_list($base, false);
196 usort($page_list, '_plugin_bugtrack_list_paganame_compare');
197 if (count($page_list) == 0) {
200 $latest_page = $page_list[count($page_list) - 1]['name'];
201 $id = intval(substr($latest_page, strlen($base) + 1)) + 1;
203 $page = $base . '/' . sprintf(PLUGIN_BUGTRACK_NUMBER_FORMAT, $id);
205 if ($pagename == '') {
206 page_write($page, $postdata);
208 $pagename = get_fullname($pagename, $base);
209 if (is_page($pagename) || ! is_pagename($pagename)) {
210 $pagename = $page; // Set default
212 page_write($page, 'move to [[' . $pagename . ']]');
214 page_write($pagename, $postdata);
220 // Generate new page contents
221 function plugin_bugtrack_template($base, $summary, $name, $priority, $state, $category, $version, $body)
223 global $_plugin_bugtrack;
225 $base = '[[' . $base . ']]';
226 if ($name != '') $name = '[[' . $name . ']]';
228 if ($name == '') $name = $_plugin_bugtrack['noname'];
229 if ($summary == '') $summary = $_plugin_bugtrack['nosummary'];
234 - ${_plugin_bugtrack['base' ]}: $base
235 - ${_plugin_bugtrack['name' ]}: $name
236 - ${_plugin_bugtrack['priority']}: $priority
237 - ${_plugin_bugtrack['state' ]}: $state
238 - ${_plugin_bugtrack['category']}: $category
239 - ${_plugin_bugtrack['date' ]}: &now;
240 - ${_plugin_bugtrack['version' ]}: $version
242 ** ${_plugin_bugtrack['body']}
250 // ----------------------------------------
251 // BugTrack-List plugin
253 function _plugin_bugtrack_list_paganame_compare($a, $b)
255 return strnatcmp($a['name'], $b['name']);
260 * Get page list for "$page/"
262 function plugin_bugtrack_get_page_list($page, $needs_filetime) {
263 $page_list = array();
264 $pattern = $page . '/';
265 $pattern_len = strlen($pattern);
266 foreach (get_existpages() as $p) {
267 if (strncmp($p, $pattern, $pattern_len) === 0 && pkwk_ctype_digit(substr($p, $pattern_len))) {
268 if ($needs_filetime) {
269 $page_list[] = array('name'=>$p,'filetime'=>get_filetime($p));
271 $page_list[] = array('name'=>$p);
279 * #bugtrack_list plugin itself
281 * Cache specification
282 * * Enable only for PHP5.4+ (Because of JSON_UNESCAPE_UNICODE)
283 * * Use cached values for articles that have the unchaged filemtime
284 * * Invalidate all cache data everyday
286 function plugin_bugtrack_list_convert()
288 global $vars, $_plugin_bugtrack, $_title_cannotread;
289 $cache_format_version = 1;
290 $cache_expire_time = 60 * 60 * 24;
291 $cache_refresh_time_prev;
292 static $cache_enabled;
293 if (!isset($cache_enabled)) {
294 $cache_enabled = defined('JSON_UNESCAPED_UNICODE'); // PHP 5.4+
296 $page = $vars['page'];
297 if (func_num_args()) {
298 list($_page) = func_get_args();
299 $_page = get_fullname(strip_bracket($_page), $page);
300 if (is_pagename($_page)) $page = $_page;
302 if (!is_page_readable($page)) {
303 $body = str_replace('$1', htmlsc($page), $_title_cannotread);
307 $page_list = plugin_bugtrack_get_page_list($page, true);
308 usort($page_list, '_plugin_bugtrack_list_paganame_compare');
309 $count_list = count($_plugin_bugtrack['state_list']);
311 if ($cache_enabled) {
313 $data_updated = true;
314 $cache_filepath = CACHE_DIR . encode($page) . '.bugtrack';
315 $json_cached = pkwk_file_get_contents($cache_filepath);
317 $wrapdata = json_decode($json_cached);
318 if (is_object($wrapdata)) {
319 if (isset($wrapdata->version, $wrapdata->pages, $wrapdata->refreshed_at)) {
320 $cache_time_prev = $wrapdata->refreshed_at;
321 if ($cache_format_version == $wrapdata->version
322 && time() < $cache_time_prev + $cache_expire_time) {
323 $data = $wrapdata->pages;
324 $cache_refresh_time_prev = $cache_time_prev;
328 if (is_array($data) && !empty($data)) {
330 foreach ($page_list as $i=>$page_info) {
331 list($page_name, $no, $summary, $name, $priority, $state, $category, $filetime) = $data[$i];
332 if ($filetime !== $page_info['filetime'] || $page_name !== $page_info['name']) {
339 foreach ($data as $d) {
341 $data_map[$page_name] = $d;
349 if (empty($data) || !empty($data_map)) {
351 foreach ($page_list as $page_info) {
352 $page_name = $page_info['name'];
354 if (isset($data_map[$page_name])) {
355 $d = $data_map[$page_name];
356 list($page_name, $no, $summary, $name, $priority, $state, $category, $filetime) = $d;
357 if ($filetime == $page_info['filetime']) {
363 $data[] = plugin_bugtrack_list_pageinfo($page_info['name'], null, true, $page_info['filetime']);
366 foreach ($data as $i=>$line) {
367 list($page_name, $no, $summary, $name, $priority, $state, $category, $filetime, $state_no_cached, $html_cached) = $line;
368 if (isset($state_no_cached) && isset($html_cached)) {
371 foreach (array('name', 'priority', 'state', 'category') as $item)
372 $$item = htmlsc($$item);
373 $page_link = make_pagelink($page_name);
375 $state_no = array_search($state, $_plugin_bugtrack['state_sort']);
376 if ($state_no === NULL || $state_no === FALSE) $state_no = $count_list;
377 $cssclass = htmlsc($_plugin_bugtrack['state_class'][$state_no]);
380 <tr class="$cssclass">
389 $row = preg_replace('#(?<=>)\n\s+#', '', $row) . "\n"; // Reduce space size
391 $rec[] = $state_no; // color number
392 $rec[] = $row; // HTML content
394 if ($cache_enabled) {
396 if (isset($cache_refresh_time_prev)) {
397 $refreshed_at = $cache_refresh_time_prev;
399 $refreshed_at = time();
401 $json = array('refreshed_at'=>$refreshed_at, 'pages'=>$data, 'version'=>$cache_format_version);
402 $cache_body = json_encode($json, JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES);
403 file_put_contents($cache_filepath, $cache_body, LOCK_EX);
407 for ($i = 0; $i <= $count_list + 1; ++$i) $table[$i] = array();
408 foreach ($data as $line) {
409 list($page_name, $no, $summary, $name, $priority, $state, $category, $filetime, $state_no, $html) = $line;
410 $table[$state_no][$no] = $html;
412 $table_html = ' <tr class="bugtrack_list_header">' . "\n";
413 foreach (array('pagename', 'state', 'priority', 'category', 'name', 'summary') as $item)
414 $table_html .= ' <th>' .
415 htmlsc($_plugin_bugtrack[$item]) . '</th>' . "\n";
416 $table_html .= ' </tr>' . "\n";
417 for ($i = 0; $i <= $count_list; ++$i) {
418 ksort($table[$i], SORT_NUMERIC);
419 $table_html .= join("\n", $table[$i]);
421 return '<table border="1" width="100%">' . "\n" .
426 // Get one set of data from a page (or a page moved to $page)
427 function plugin_bugtrack_list_pageinfo($page, $no = NULL, $recurse = TRUE, $filetime = NULL)
429 global $WikiName, $InterWikiName, $BracketName, $_plugin_bugtrack;
432 $no = preg_match('/\/([0-9]+)$/', $page, $matches) ? $matches[1] : 0;
434 $source = get_source($page);
436 // Check 'moved' page _just once_
437 $regex = "/move\s*to\s*($WikiName|$InterWikiName|\[\[$BracketName\]\])/";
439 if ($recurse && preg_match($regex, $source[0], $match))
440 return plugin_bugtrack_list_pageinfo(strip_bracket($match[1]), $no, FALSE, $filetime);
442 $body = join("\n", $source);
443 foreach(array('summary', 'name', 'priority', 'state', 'category') as $item) {
444 $regex = '/-\s*' . preg_quote($_plugin_bugtrack[$item], '/') . '\s*:(.*)/';
445 if (preg_match($regex, $body, $matches)) {
446 if ($item == 'name') {
447 $$item = strip_bracket(trim($matches[1]));
449 $$item = trim($matches[1]);
452 $$item = ''; // Data not found
455 if (preg_match("/\*([^\n]*)/", $body, $matches)) {
456 $summary = $matches[1];
457 make_heading($summary);
458 $summary = trim($summary);
460 return array($page, $no, $summary, $name, $priority, $state, $category, $filetime);