OSDN Git Service

BugTrack/2484 AutoTicketLink for JIRA: Support underscore key XX_X
[pukiwiki/pukiwiki.git] / plugin / rename.inc.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone
3 // rename.inc.php
4 // Copyright 2002-2018 PukiWiki Development Team
5 // License: GPL v2 or (at your option) any later version
6 //
7 // Rename plugin: Rename page-name and related data
8 //
9 // Usage: http://path/to/index.php?plugin=rename[&refer=page_name]
10
11 define('PLUGIN_RENAME_LOGPAGE', ':RenameLog');
12
13 function plugin_rename_action()
14 {
15         global $whatsnew;
16
17         if (PKWK_READONLY) die_message('PKWK_READONLY prohibits this');
18
19         $method = plugin_rename_getvar('method');
20         if ($method == 'regex') {
21                 $src = plugin_rename_getvar('src');
22                 if ($src == '') return plugin_rename_phase1();
23
24                 $src_pattern = '/' . preg_quote($src, '/') . '/';
25                 $arr0 = preg_grep($src_pattern, plugin_rename_get_existpages());
26                 if (! is_array($arr0) || empty($arr0))
27                         return plugin_rename_phase1('nomatch');
28
29                 $dst = plugin_rename_getvar('dst');
30                 $arr1 = preg_replace($src_pattern, $dst, $arr0);
31                 foreach ($arr1 as $page)
32                         if (! is_pagename($page))
33                                 return plugin_rename_phase1('notvalid');
34
35                 return plugin_rename_regex($arr0, $arr1);
36
37         } else {
38                 // $method == 'page'
39                 $page  = plugin_rename_getvar('page');
40                 $refer = plugin_rename_getvar('refer');
41
42                 if ($refer == '') {
43                         return plugin_rename_phase1();
44
45                 } else if (! plugin_rename_is_page($refer)) {
46                         return plugin_rename_phase1('notpage', $refer);
47
48                 } else if ($refer === $whatsnew) {
49                         return plugin_rename_phase1('norename', $refer);
50
51                 } else if ($page == '' || $page === $refer) {
52                         return plugin_rename_phase2();
53
54                 } else if (! is_pagename($page)) {
55                         return plugin_rename_phase2('notvalid');
56
57                 } else {
58                         return plugin_rename_refer();
59                 }
60         }
61 }
62
63 // 変数を取得する
64 function plugin_rename_getvar($key)
65 {
66         global $vars;
67         return isset($vars[$key]) ? $vars[$key] : '';
68 }
69
70 // エラーメッセージを作る
71 function plugin_rename_err($err, $page = '')
72 {
73         global $_rename_messages;
74
75         if ($err == '') return '';
76
77         $body = $_rename_messages['err_' . $err];
78         if (is_array($page)) {
79                 $tmp = '';
80                 foreach ($page as $_page) $tmp .= '<br />' . $_page;
81                 $page = $tmp;
82         }
83         if ($page != '') $body = sprintf($body, htmlsc($page));
84
85         $msg = sprintf($_rename_messages['err'], $body);
86         return $msg;
87 }
88
89 //第一段階:ページ名または正規表現の入力
90 function plugin_rename_phase1($err = '', $page = '')
91 {
92         global $_rename_messages;
93
94         $script = get_base_uri();
95         $msg    = plugin_rename_err($err, $page);
96         $refer  = plugin_rename_getvar('refer');
97         $method = plugin_rename_getvar('method');
98
99         $radio_regex = $radio_page = '';
100         if ($method == 'regex') {
101                 $radio_regex = ' checked="checked"';
102         } else {
103                 $radio_page  = ' checked="checked"';
104         }
105         $select_refer = plugin_rename_getselecttag($refer);
106
107         $s_src = htmlsc(plugin_rename_getvar('src'));
108         $s_dst = htmlsc(plugin_rename_getvar('dst'));
109
110         $ret = array();
111         $ret['msg']  = $_rename_messages['msg_title'];
112         $ret['body'] = <<<EOD
113 $msg
114 <form action="$script" method="post">
115  <div>
116   <input type="hidden" name="plugin" value="rename" />
117   <input type="radio"  name="method" id="_p_rename_page" value="page"$radio_page />
118   <label for="_p_rename_page">{$_rename_messages['msg_page']}:</label>$select_refer<br />
119   <input type="radio"  name="method" id="_p_rename_regex" value="regex"$radio_regex />
120   <label for="_p_rename_regex">{$_rename_messages['msg_regex']}:</label><br />
121   <label for="_p_rename_from">From:</label><br />
122   <input type="text" name="src" id="_p_rename_from" size="80" value="$s_src" /><br />
123   <label for="_p_rename_to">To:</label><br />
124   <input type="text" name="dst" id="_p_rename_to"   size="80" value="$s_dst" /><br />
125   <input type="submit" value="{$_rename_messages['btn_next']}" /><br />
126  </div>
127 </form>
128 EOD;
129         return $ret;
130 }
131
132 //第二段階:新しい名前の入力
133 function plugin_rename_phase2($err = '')
134 {
135         global $_rename_messages;
136
137         $script = get_base_uri();
138         $msg   = plugin_rename_err($err);
139         $page  = plugin_rename_getvar('page');
140         $refer = plugin_rename_getvar('refer');
141         if ($page == '') $page = $refer;
142
143         $msg_related = '';
144         $related = plugin_rename_getrelated($refer);
145         if (! empty($related))
146                 $msg_related = '<label for="_p_rename_related">' . $_rename_messages['msg_do_related'] . '</label>' .
147                 '<input type="checkbox" name="related" id="_p_rename_related" value="1" checked="checked" /><br />';
148
149         $msg_rename = sprintf($_rename_messages['msg_rename'], make_pagelink($refer));
150         $s_page  = htmlsc($page);
151         $s_refer = htmlsc($refer);
152
153         $ret = array();
154         $ret['msg']  = $_rename_messages['msg_title'];
155         $ret['body'] = <<<EOD
156 $msg
157 <form action="$script" method="post">
158  <div>
159   <input type="hidden" name="plugin" value="rename" />
160   <input type="hidden" name="refer"  value="$s_refer" />
161   $msg_rename<br />
162   <label for="_p_rename_newname">{$_rename_messages['msg_newname']}:</label>
163   <input type="text" name="page" id="_p_rename_newname" size="80" value="$s_page" /><br />
164   $msg_related
165   <input type="submit" value="{$_rename_messages['btn_next']}" /><br />
166  </div>
167 </form>
168 EOD;
169         if (! empty($related)) {
170                 $ret['body'] .= '<hr /><p>' . $_rename_messages['msg_related'] . '</p><ul>';
171                 sort($related, SORT_STRING);
172                 foreach ($related as $name)
173                         $ret['body'] .= '<li>' . make_pagelink($name) . '</li>';
174                 $ret['body'] .= '</ul>';
175         }
176         return $ret;
177 }
178
179 //ページ名と関連するページを列挙し、phase3へ
180 function plugin_rename_refer()
181 {
182         $page  = plugin_rename_getvar('page');
183         $refer = plugin_rename_getvar('refer');
184
185         $pages[encode($refer)] = encode($page);
186         if (plugin_rename_getvar('related') != '') {
187                 $from = strip_bracket($refer);
188                 $to   = strip_bracket($page);
189                 foreach (plugin_rename_getrelated($refer) as $_page)
190                         $pages[encode($_page)] = encode(str_replace($from, $to, $_page));
191         }
192         return plugin_rename_phase3($pages);
193 }
194
195 //正規表現でページを置換
196 function plugin_rename_regex($arr_from, $arr_to)
197 {
198         $exists = array();
199         foreach ($arr_to as $page)
200                 if (is_page($page))
201                         $exists[] = $page;
202
203         if (! empty($exists)) {
204                 return plugin_rename_phase1('already', $exists);
205         } else {
206                 $pages = array();
207                 foreach ($arr_from as $refer)
208                         $pages[encode($refer)] = encode(array_shift($arr_to));
209                 return plugin_rename_phase3($pages);
210         }
211 }
212
213 function plugin_rename_phase3($pages)
214 {
215         global $_rename_messages;
216
217         $script = get_base_uri();
218         $msg = $input = '';
219         $files = plugin_rename_get_files($pages);
220
221         $exists = array();
222         foreach ($files as $_page=>$arr)
223                 foreach ($arr as $old=>$new)
224                         if (file_exists($new))
225                                 $exists[$_page][$old] = $new;
226
227         $pass = plugin_rename_getvar('pass');
228         if ($pass != '' && pkwk_login($pass)) {
229                 return plugin_rename_proceed($pages, $files, $exists);
230         } else if ($pass != '') {
231                 $msg = plugin_rename_err('adminpass');
232         }
233
234         $method = plugin_rename_getvar('method');
235         if ($method == 'regex') {
236                 $s_src = htmlsc(plugin_rename_getvar('src'));
237                 $s_dst = htmlsc(plugin_rename_getvar('dst'));
238                 $msg   .= $_rename_messages['msg_regex'] . '<br />';
239                 $input .= '<input type="hidden" name="method" value="regex" />';
240                 $input .= '<input type="hidden" name="src"    value="' . $s_src . '" />';
241                 $input .= '<input type="hidden" name="dst"    value="' . $s_dst . '" />';
242         } else {
243                 $s_refer   = htmlsc(plugin_rename_getvar('refer'));
244                 $s_page    = htmlsc(plugin_rename_getvar('page'));
245                 $s_related = htmlsc(plugin_rename_getvar('related'));
246                 $msg   .= $_rename_messages['msg_page'] . '<br />';
247                 $input .= '<input type="hidden" name="method"  value="page" />';
248                 $input .= '<input type="hidden" name="refer"   value="' . $s_refer   . '" />';
249                 $input .= '<input type="hidden" name="page"    value="' . $s_page    . '" />';
250                 $input .= '<input type="hidden" name="related" value="' . $s_related . '" />';
251         }
252
253         if (! empty($exists)) {
254                 $msg .= $_rename_messages['err_already_below'] . '<ul>';
255                 foreach ($exists as $page=>$arr) {
256                         $msg .= '<li>' . make_pagelink(decode($page));
257                         $msg .= $_rename_messages['msg_arrow'];
258                         $msg .= htmlsc(decode($pages[$page]));
259                         if (! empty($arr)) {
260                                 $msg .= '<ul>' . "\n";
261                                 foreach ($arr as $ofile=>$nfile)
262                                         $msg .= '<li>' . $ofile .
263                                         $_rename_messages['msg_arrow'] . $nfile . '</li>' . "\n";
264                                 $msg .= '</ul>';
265                         }
266                         $msg .= '</li>' . "\n";
267                 }
268                 $msg .= '</ul><hr />' . "\n";
269
270                 $input .= '<input type="radio" name="exist" value="0" checked="checked" />' .
271                         $_rename_messages['msg_exist_none'] . '<br />';
272                 $input .= '<input type="radio" name="exist" value="1" />' .
273                         $_rename_messages['msg_exist_overwrite'] . '<br />';
274         }
275
276         $ret = array();
277         $ret['msg'] = $_rename_messages['msg_title'];
278         $ret['body'] = <<<EOD
279 <p>$msg</p>
280 <form action="$script" method="post">
281  <div>
282   <input type="hidden" name="plugin" value="rename" />
283   $input
284   <label for="_p_rename_adminpass">{$_rename_messages['msg_adminpass']}</label>
285   <input type="password" name="pass" id="_p_rename_adminpass" value="" />
286   <input type="submit" value="{$_rename_messages['btn_submit']}" />
287  </div>
288 </form>
289 <p>{$_rename_messages['msg_confirm']}</p>
290 EOD;
291
292         ksort($pages, SORT_STRING);
293         $ret['body'] .= '<ul>' . "\n";
294         foreach ($pages as $old=>$new)
295                 $ret['body'] .= '<li>' .  make_pagelink(decode($old)) .
296                         $_rename_messages['msg_arrow'] .
297                         htmlsc(decode($new)) .  '</li>' . "\n";
298         $ret['body'] .= '</ul>' . "\n";
299         return $ret;
300 }
301
302 function plugin_rename_get_files($pages)
303 {
304         $files = array();
305         $dirs  = array(BACKUP_DIR, DIFF_DIR, DATA_DIR);
306         if (exist_plugin_convert('attach'))  $dirs[] = UPLOAD_DIR;
307         if (exist_plugin_convert('counter')) $dirs[] = COUNTER_DIR;
308         // and more ...
309
310         $matches = array();
311         foreach ($dirs as $path) {
312                 $dir = opendir($path);
313                 if (! $dir) continue;   // TODO: !== FALSE or die()?
314                 while (($file = readdir($dir)) !== FALSE) {
315                         if ($file == '.' || $file == '..') continue;
316                         foreach ($pages as $from => $to) {
317                                 // TODO: preg_quote()?
318                                 $pattern = '/^' . str_replace('/', '\/', $from) . '([._].+)$/';
319                                 if (preg_match($pattern, $file, $matches)) {
320                                         $newfile = $to . $matches[1];
321                                         $files[$from][$path . $file] = $path . $newfile;
322                                 }
323                         }
324                 }
325         }
326         return $files;
327 }
328
329 function plugin_rename_proceed($pages, $files, $exists)
330 {
331         global $now, $_rename_messages;
332
333         if (plugin_rename_getvar('exist') == '')
334                 foreach ($exists as $key=>$arr)
335                         unset($files[$key]);
336
337         set_time_limit(0);
338         foreach ($files as $page=>$arr) {
339                 foreach ($arr as $old=>$new) {
340                         if (isset($exists[$page][$old]) && $exists[$page][$old])
341                                 unlink($new);
342                         rename($old, $new);
343                 }
344                 // linkデータベースを更新する BugTrack/327 arino
345                 $new_page = $pages[$page];
346                 links_update(decode($page));
347                 links_update(decode($new_page));
348         }
349         // Rename counter
350         $pages_decoded = array();
351         foreach ($pages as $old=>$new) {
352                 $pages_decoded[decode($old)] = decode($new);
353         }
354         if (exist_plugin('counter')) {
355                 plugin_counter_page_rename($pages_decoded);
356         }
357
358         $postdata = get_source(PLUGIN_RENAME_LOGPAGE);
359         $postdata[] = '*' . $now . "\n";
360         if (plugin_rename_getvar('method') == 'regex') {
361                 $postdata[] = '-' . $_rename_messages['msg_regex'] . "\n";
362                 $postdata[] = '--From:[[' . plugin_rename_getvar('src') . ']]' . "\n";
363                 $postdata[] = '--To:[['   . plugin_rename_getvar('dst') . ']]' . "\n";
364         } else {
365                 $postdata[] = '-' . $_rename_messages['msg_page'] . "\n";
366                 $postdata[] = '--From:[[' . plugin_rename_getvar('refer') . ']]' . "\n";
367                 $postdata[] = '--To:[['   . plugin_rename_getvar('page')  . ']]' . "\n";
368         }
369
370         if (! empty($exists)) {
371                 $postdata[] = "\n" . $_rename_messages['msg_result'] . "\n";
372                 foreach ($exists as $page=>$arr) {
373                         $postdata[] = '-' . decode($page) .
374                                 $_rename_messages['msg_arrow'] . decode($pages[$page]) . "\n";
375                         foreach ($arr as $ofile=>$nfile)
376                                 $postdata[] = '--' . $ofile .
377                                         $_rename_messages['msg_arrow'] . $nfile . "\n";
378                 }
379                 $postdata[] = '----' . "\n";
380         }
381
382         foreach ($pages as $old=>$new)
383                 $postdata[] = '-' . decode($old) .
384                         $_rename_messages['msg_arrow'] . decode($new) . "\n";
385
386         // 更新の衝突はチェックしない。
387
388         // ファイルの書き込み
389         page_write(PLUGIN_RENAME_LOGPAGE, join('', $postdata));
390
391         // Refresh RecentChanges / Delete cache/recent.dat
392         delete_recent_changes_cache();
393
394         //リダイレクト
395         $page = plugin_rename_getvar('page');
396         if ($page == '') $page = PLUGIN_RENAME_LOGPAGE;
397
398         pkwk_headers_sent();
399         header('Location: ' . get_page_uri($page, PKWK_URI_ROOT));
400         exit;
401 }
402
403 function plugin_rename_getrelated($page)
404 {
405         $related = array();
406         $pages = plugin_rename_get_existpages();
407         $pattern = '/(?:^|\/)' . preg_quote(strip_bracket($page), '/') . '(?:\/|$)/';
408         foreach ($pages as $name) {
409                 if ($name === $page) continue;
410                 if (preg_match($pattern, $name)) $related[] = $name;
411         }
412         return $related;
413 }
414
415 function plugin_rename_getselecttag($page)
416 {
417         global $whatsnew;
418
419         $pages = array();
420         foreach (plugin_rename_get_existpages() as $_page) {
421                 if ($_page === $whatsnew) continue;
422
423                 $selected = ($_page === $page) ? ' selected' : '';
424                 $s_page = htmlsc($_page);
425                 $pages[$_page] = '<option value="' . $s_page . '"' . $selected . '>' .
426                         $s_page . '</option>';
427         }
428         ksort($pages, SORT_STRING);
429         $list = join("\n" . ' ', $pages);
430
431         return <<<EOD
432 <select name="refer">
433  <option value=""></option>
434  $list
435 </select>
436 EOD;
437
438 }
439
440 /**
441  * List exist pages and deleted pages
442  */
443 function plugin_rename_get_existpages() {
444         $list1 = array_values(get_existpages());
445         $list2 = array_values(get_existpages(DIFF_DIR, '.txt'));
446         $list3 = array_values(get_existpages(BACKUP_DIR, '.txt'));
447         $list4 = array_values(get_existpages(BACKUP_DIR, '.gz'));
448         $list5 = array_values(get_existpages(COUNTER_DIR, '.count'));
449         $wholelist = array_merge($list1, $list2, $list3, $list4, $list5);
450         $list = array_unique($wholelist);
451         return $list;
452 }
453
454 /**
455  * Return where the page exists or existed
456  */
457 function plugin_rename_is_page($page) {
458         $enc = encode($page);
459         if (is_page($page)) {
460                 return true;
461         }
462         if (file_exists(DIFF_DIR . $enc . '.txt')) {
463                 return true;
464         }
465         if (file_exists(BACKUP_DIR . $enc . '.txt')) {
466                 return true;
467         }
468         if (file_exists(BACKUP_DIR . $enc . '.gz')) {
469                 return true;
470         }
471         if (file_exists(COUNTER_DIR . $enc . '.count')) {
472                 return true;
473         }
474         return false;
475 }
476
477 /**
478  * Setup initial pages (:RenameLog)
479  */
480 function plugin_rename_setup_initial_pages() {
481         if (!is_page(PLUGIN_RENAME_LOGPAGE)) {
482                 // Create :RenameLog
483                 $body = "#freeze\n// :RenameLog (rename plugin)\n";
484                 page_write(PLUGIN_RENAME_LOGPAGE, $body);
485         }
486 }