OSDN Git Service

BugTrack/2424 bugtrack: Define bugtrack list style by CSS
[pukiwiki/pukiwiki.git] / plugin / bugtrack.inc.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone.
3 // bugtrack.inc.php
4 // Copyright
5 //   2002-2017 PukiWiki Development Team
6 //   2002 Y.MASUI GPL2  http://masui.net/pukiwiki/ masui@masui.net
7 //
8 // BugTrack plugin
9
10 // Numbering format
11 define('PLUGIN_BUGTRACK_NUMBER_FORMAT', '%d'); // Like 'page/1'
12 //define('PLUGIN_BUGTRACK_NUMBER_FORMAT', '%03d'); // Like 'page/001'
13
14 function plugin_bugtrack_init()
15 {
16         global $_plugin_bugtrack;
17         static $init;
18
19         if (isset($init)) return; // Already init
20         if (isset($_plugin_bugtrack)) die('Global $_plugin_bugtrack had been init. Why?');
21         $init = TRUE;
22
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'),
30                 'base'     => 'ページ',
31                 'summary'  => 'サマリ',
32                 'nosummary'=> 'ここにサマリを記入して下さい',
33                 'priority' => '優先順位',
34                 'state'    => '状態',
35                 'name'     => '投稿者',
36                 'noname'   => '名無しさん',
37                 'date'     => '投稿日',
38                 'body'     => 'メッセージ',
39                 'category' => 'カテゴリー',
40                 'pagename' => 'ページ名',
41                 'pagename_comment' => '空欄のままだと自動的にページ名が振られます。',
42                 'version_comment'  => '空欄でも構いません',
43                 'version'  => 'バージョン',
44                 'submit'   => '追加'
45                 );
46 }
47
48 // #bugtrack: Show bugtrack form
49 function plugin_bugtrack_convert()
50 {
51         global $vars;
52
53         if (PKWK_READONLY) return ''; // Show nothing
54
55         $base = $vars['page'];
56         $category = array();
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;
61         }
62
63         return plugin_bugtrack_print_form($base, $category);
64 }
65
66 function plugin_bugtrack_print_form($base, $category)
67 {
68         global $_plugin_bugtrack;
69         static $id = 0;
70
71         ++$id;
72
73         $select_priority = "\n";
74         $count = count($_plugin_bugtrack['priority_list']);
75         $selected = '';
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";
81         }
82
83         $select_state = "\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";
88         }
89
90         if (empty($category)) {
91                 $encoded_category = '<input name="category" id="_p_bugtrack_category_' . $id .
92                         '" type="text" />';
93         } else {
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";
99                 }
100                 $encoded_category .= '</select>';
101         }
102
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']);
116         $body = <<<EOD
117 <form action="$script" method="post">
118  <table border="0">
119   <tr>
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>
122   </tr>
123   <tr>
124    <th><label for="_p_bugtrack_category_$id">$s_category</label></th>
125    <td>$encoded_category</td>
126   </tr>
127   <tr>
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>
130   </tr>
131   <tr>
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>
134   </tr>
135   <tr>
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>
139   </tr>
140   <tr>
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>
144   </tr>
145   <tr>
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>
148   </tr>
149   <tr>
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>
152   </tr>
153   <tr>
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" />
159    </td>
160   </tr>
161  </table>
162 </form>
163 EOD;
164
165         return $body;
166 }
167
168 // Add new issue
169 function plugin_bugtrack_action()
170 {
171         global $post;
172
173         if (PKWK_READONLY) die_message('PKWK_READONLY prohibits editing');
174         if ($post['mode'] != 'submit') return FALSE;
175
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']);
179
180         pkwk_headers_sent();
181         header('Location: ' . get_page_uri($page, PKWK_URI_ROOT));
182         exit;
183 }
184
185 function plugin_bugtrack_write($base, $pagename, $summary, $name, $priority, $state, $category, $version, $body)
186 {
187         global $post;
188
189         $base     = strip_bracket($base);
190         $pagename = strip_bracket($pagename);
191
192         $postdata = plugin_bugtrack_template($base, $summary, $name, $priority,
193                 $state, $category, $version, $body);
194
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) {
198                 $id = 1;
199         } else {
200                 $latest_page = $page_list[count($page_list) - 1]['name'];
201                 $id = intval(substr($latest_page, strlen($base) + 1)) + 1;
202         }
203         $page = $base . '/' . sprintf(PLUGIN_BUGTRACK_NUMBER_FORMAT, $id);
204
205         if ($pagename == '') {
206                 page_write($page, $postdata);
207         } else {
208                 $pagename = get_fullname($pagename, $base);
209                 if (is_page($pagename) || ! is_pagename($pagename)) {
210                         $pagename = $page; // Set default
211                 } else {
212                         page_write($page, 'move to [[' . $pagename . ']]');
213                 }
214                 page_write($pagename, $postdata);
215         }
216
217         return $page;
218 }
219
220 // Generate new page contents
221 function plugin_bugtrack_template($base, $summary, $name, $priority, $state, $category, $version, $body)
222 {
223         global $_plugin_bugtrack;
224
225         $base = '[[' . $base . ']]';
226         if ($name != '') $name = '[[' . $name . ']]';
227
228         if ($name    == '') $name    = $_plugin_bugtrack['noname'];
229         if ($summary == '') $summary = $_plugin_bugtrack['nosummary'];
230
231          return <<<EOD
232 * $summary
233
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
241
242 ** ${_plugin_bugtrack['body']}
243 $body
244 --------
245
246 #comment
247 EOD;
248 }
249
250 // ----------------------------------------
251 // BugTrack-List plugin
252
253 function _plugin_bugtrack_list_paganame_compare($a, $b)
254 {
255         return strnatcmp($a['name'], $b['name']);
256 }
257
258
259 /**
260  * Get page list for "$page/"
261  */
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));
270                         } else {
271                                 $page_list[] = array('name'=>$p);
272                         }
273                 }
274         }
275         return $page_list;
276 }
277
278 /**
279  * #bugtrack_list plugin itself
280  *
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
285  */
286 function plugin_bugtrack_list_convert()
287 {
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+
295         }
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;
301         }
302         if (!is_page_readable($page)) {
303                 $body = str_replace('$1', htmlsc($page), $_title_cannotread);
304                 return $body;
305         }
306         $data = array();
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']);
310         $data_map = array();
311         if ($cache_enabled) {
312                 // Cache management
313                 $data_updated = true;
314                 $cache_filepath = CACHE_DIR . encode($page) . '.bugtrack';
315                 $json_cached = pkwk_file_get_contents($cache_filepath);
316                 if ($json_cached) {
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;
325                                         }
326                                 }
327                         }
328                         if (is_array($data) && !empty($data)) {
329                                 $all_ok = true;
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']) {
333                                                 $all_ok = false;
334                                                 break;
335                                         }
336                                 }
337                                 if (!$all_ok) {
338                                         // Clear cache
339                                         foreach ($data as $d) {
340                                                 $page_name = $d[0];
341                                                 $data_map[$page_name] = $d;
342                                         }
343                                         $data = array();
344                                 }
345                         }
346                 }
347         }
348
349         if (empty($data) || !empty($data_map)) {
350                 // No cache
351                 foreach ($page_list as $page_info) {
352                         $page_name = $page_info['name'];
353                         $filled = false;
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']) {
358                                         $data[] = $d;
359                                         $filled = true;
360                                 }
361                         }
362                         if (!$filled) {
363                                 $data[] = plugin_bugtrack_list_pageinfo($page_info['name'], null, true, $page_info['filetime']);
364                         }
365                 }
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)) {
369                                 continue;
370                         }
371                         foreach (array('name', 'priority', 'state', 'category') as $item)
372                                 $$item = htmlsc($$item);
373                         $page_link = make_pagelink($page_name);
374
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]);
378
379                         $row = <<<EOD
380  <tr class="$cssclass">
381   <td>$page_link</td>
382   <td>$state</td>
383   <td>$priority</td>
384   <td>$category</td>
385   <td>$name</td>
386   <td>$summary</td>
387  </tr>
388 EOD;
389                         $row = preg_replace('#(?<=>)\n\s+#', '', $row) . "\n"; // Reduce space size
390                         $rec = &$data[$i];
391                         $rec[] = $state_no; // color number
392                         $rec[] = $row; // HTML content
393                 }
394                 if ($cache_enabled) {
395                         // Save cache
396                         if (isset($cache_refresh_time_prev)) {
397                                 $refreshed_at = $cache_refresh_time_prev;
398                         } else {
399                                 $refreshed_at = time();
400                         }
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);
404                 }
405         }
406         $table = array();
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;
411         }
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]);
420         }
421         return '<table border="1" width="100%">' . "\n" .
422                 $table_html . "\n" .
423                 '</table>';
424 }
425
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)
428 {
429         global $WikiName, $InterWikiName, $BracketName, $_plugin_bugtrack;
430
431         if ($no === NULL)
432                 $no = preg_match('/\/([0-9]+)$/', $page, $matches) ? $matches[1] : 0;
433
434         $source = get_source($page);
435
436         // Check 'moved' page _just once_
437         $regex  = "/move\s*to\s*($WikiName|$InterWikiName|\[\[$BracketName\]\])/";
438         $match  = array();
439         if ($recurse && preg_match($regex, $source[0], $match))
440                 return plugin_bugtrack_list_pageinfo(strip_bracket($match[1]), $no, FALSE, $filetime);
441
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]));
448                         } else {
449                                 $$item = trim($matches[1]);
450                         }
451                 } else {
452                                 $$item = ''; // Data not found
453                 }
454         }
455         if (preg_match("/\*([^\n]*)/", $body, $matches)) {
456                 $summary = $matches[1];
457                 make_heading($summary);
458                 $summary = trim($summary);
459         }
460         return array($page, $no, $summary, $name, $priority, $state, $category, $filetime);
461 }