OSDN Git Service

BugTrack/84 Limit page name length: 115 bytes(soft); 125 bytes(hard)
[pukiwiki/pukiwiki.git] / lib / link.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone
3 // link.php
4 // Copyright 2003-2022 PukiWiki Development Team
5 // License: GPL v2 or (at your option) any later version
6 //
7 // Backlinks / AutoLinks related functions
8
9 // ------------------------------------------------------------
10 // DATA STRUCTURE of *.ref and *.rel files
11
12 // CACHE_DIR/encode('foobar').ref
13 // ---------------------------------
14 // Page-name1<tab>0<\n>
15 // Page-name2<tab>1<\n>
16 // ...
17 // Page-nameN<tab>0<\n>
18 //
19 //      0 = Added when link(s) to 'foobar' added clearly at this page
20 //      1 = Added when the sentence 'foobar' found from the page
21 //          by AutoLink feature
22
23 // CACHE_DIR/encode('foobar').rel
24 // ---------------------------------
25 // Page-name1<tab>Page-name2<tab> ... <tab>Page-nameN
26 //
27 //      List of page-names linked from 'foobar'
28
29 // ------------------------------------------------------------
30
31
32 // データベースから関連ページを得る
33 function links_get_related_db($page)
34 {
35         $ref_name = CACHE_DIR . encode($page) . '.ref';
36         if (! file_exists($ref_name)) return array();
37
38         $times = array();
39         foreach (file($ref_name) as $line) {
40                 list($_page) = explode("\t", rtrim($line));
41                 $time = get_filetime($_page);   
42                 if($time != 0) $times[$_page] = $time;
43         }
44         return $times;
45 }
46
47 //ページの関連を更新する
48 function links_update($page)
49 {
50         if (PKWK_READONLY) return; // Do nothing
51
52         if (ini_get('safe_mode') == '0') set_time_limit(0);
53
54         $time = is_page($page, TRUE) ? get_filetime($page) : 0;
55
56         $rel_old        = array();
57         $rel_file       = CACHE_DIR . encode($page) . '.rel';
58         $rel_file_exist = file_exists($rel_file);
59         if ($rel_file_exist === TRUE) {
60                 $lines = file($rel_file);
61                 unlink($rel_file);
62                 if (isset($lines[0]))
63                         $rel_old = explode("\t", rtrim($lines[0]));
64         }
65         $rel_new  = array(); // 参照先
66         $rel_auto = array(); // オートリンクしている参照先
67         $links    = links_get_objects($page, TRUE);
68         foreach ($links as $_obj) {
69                 if (! isset($_obj->type) || $_obj->type != 'pagename' ||
70                     $_obj->name === $page || $_obj->name == '')
71                         continue;
72
73                 if (is_a($_obj, 'Link_autolink')) { // 行儀が悪い
74                         $rel_auto[] = $_obj->name;
75                 } else if (is_a($_obj, 'Link_autoalias')) {
76                         $_alias = get_autoalias_right_link($_obj->name);
77                         if (is_pagename($_alias)) {
78                                 $rel_auto[] = $_alias;
79                         }
80                 } else {
81                         $rel_new[]  = $_obj->name;
82                 }
83         }
84         $rel_new = array_unique($rel_new);
85         
86         // autolinkしか向いていないページ
87         $rel_auto = array_diff(array_unique($rel_auto), $rel_new);
88
89         // 全ての参照先ページ
90         $rel_new = array_merge($rel_new, $rel_auto);
91
92         // .rel:$pageが参照しているページの一覧
93         if ($time) {
94                 // ページが存在している
95                 if (! empty($rel_new)) {
96                         $fp = fopen($rel_file, 'w')
97                                 or die_message('cannot write ' . htmlsc($rel_file));
98                         fputs($fp, join("\t", $rel_new));
99                         fclose($fp);
100                 }
101         }
102
103         // .ref:$_pageを参照しているページの一覧
104         links_add($page, array_diff($rel_new, $rel_old), $rel_auto);
105         links_delete($page, array_diff($rel_old, $rel_new));
106
107         global $WikiName, $autolink, $nowikiname;
108
109         // $pageが新規作成されたページで、AutoLinkの対象となり得る場合
110         if ($time && ! $rel_file_exist && $autolink
111                 && (preg_match("/^$WikiName$/", $page) ? $nowikiname : strlen($page) >= $autolink))
112         {
113                 // $pageを参照していそうなページを一斉更新する(おい)
114                 $pages = links_do_search_page($page);
115                 foreach ($pages as $_page) {
116                         if ($_page !== $page)
117                                 links_update($_page);
118                 }
119         }
120         $ref_file = CACHE_DIR . encode($page) . '.ref';
121
122         // $pageが削除されたときに、
123         if (! $time && file_exists($ref_file)) {
124                 foreach (file($ref_file) as $line) {
125                         list($ref_page, $ref_auto) = explode("\t", rtrim($line));
126
127                         // $pageをAutoLinkでしか参照していないページを一斉更新する(おいおい)
128                         if ($ref_auto)
129                                 links_delete($ref_page, array($page));
130                 }
131         }
132 }
133
134 // Init link cache (Called from link plugin)
135 function links_init()
136 {
137         global $whatsnew;
138
139         if (PKWK_READONLY) return; // Do nothing
140
141         if (ini_get('safe_mode') == '0') set_time_limit(0);
142
143         // Init database
144         foreach (get_existfiles(CACHE_DIR, '.ref') as $cache)
145                 unlink($cache);
146         foreach (get_existfiles(CACHE_DIR, '.rel') as $cache)
147                 unlink($cache);
148
149         $ref   = array(); // 参照元
150         foreach (get_existpages() as $page) {
151                 if ($page == $whatsnew) continue;
152
153                 $rel   = array(); // 参照先
154                 $links = links_get_objects($page);
155                 foreach ($links as $_obj) {
156                         if (! isset($_obj->type) || $_obj->type != 'pagename' ||
157                             $_obj->name == $page || $_obj->name == '') {
158                                 continue;
159                         }
160                         $_name = $_obj->name;
161                         if (is_a($_obj, 'Link_autoalias')) {
162                                 $_alias = get_autoalias_right_link($_name);
163                                 if (! is_pagename($_alias)) {
164                                         continue;       // not PageName
165                                 }
166                                 $_name = $_alias;
167                         }
168                         $rel[] = $_name;
169                         if (! isset($ref[$_name][$page])) {
170                                 $ref[$_name][$page] = 1;
171                         }
172                         if (! is_a($_obj, 'Link_autolink')) {
173                                 $ref[$_name][$page] = 0;
174                         }
175                 }
176                 $rel = array_unique($rel);
177                 if (! empty($rel)) {
178                         $fp = fopen(CACHE_DIR . encode($page) . '.rel', 'w')
179                                 or die_message('cannot write ' . htmlsc(CACHE_DIR . encode($page) . '.rel'));
180                         fputs($fp, join("\t", $rel));
181                         fclose($fp);
182                 }
183         }
184
185         foreach ($ref as $page=>$arr) {
186                 $fp  = fopen(CACHE_DIR . encode($page) . '.ref', 'w')
187                         or die_message('cannot write ' . htmlsc(CACHE_DIR . encode($page) . '.ref'));
188                 foreach ($arr as $ref_page=>$ref_auto)
189                         fputs($fp, $ref_page . "\t" . $ref_auto . "\n");
190                 fclose($fp);
191         }
192 }
193
194 function links_add($page, $add, $rel_auto)
195 {
196         if (PKWK_READONLY) return; // Do nothing
197
198         $rel_auto = array_flip($rel_auto);
199         
200         foreach ($add as $_page) {
201                 $all_auto = isset($rel_auto[$_page]);
202                 $is_page  = is_page($_page);
203                 $ref      = $page . "\t" . ($all_auto ? 1 : 0) . "\n";
204
205                 $ref_file = CACHE_DIR . encode($_page) . '.ref';
206                 if (file_exists($ref_file)) {
207                         foreach (file($ref_file) as $line) {
208                                 list($ref_page, $ref_auto) = explode("\t", rtrim($line));
209                                 if (! $ref_auto) $all_auto = FALSE;
210                                 if ($ref_page !== $page) $ref .= $line;
211                         }
212                         unlink($ref_file);
213                 }
214                 if (! $is_page) {
215                         if (! is_pagename_bytes_within_soft_limit($_page)) {
216                                 continue;
217                         }
218                 }
219                 if ($is_page || ! $all_auto) {
220                         $fp = fopen($ref_file, 'w')
221                                  or die_message('cannot write ' . htmlsc($ref_file));
222                         fputs($fp, $ref);
223                         fclose($fp);
224                 }
225         }
226 }
227
228 function links_delete($page, $del)
229 {
230         if (PKWK_READONLY) return; // Do nothing
231
232         foreach ($del as $_page) {
233                 $ref_file = CACHE_DIR . encode($_page) . '.ref';
234                 if (! file_exists($ref_file)) continue;
235
236                 $all_auto = TRUE;
237                 $is_page = is_page($_page);
238
239                 $ref = '';
240                 foreach (file($ref_file) as $line) {
241                         list($ref_page, $ref_auto) = explode("\t", rtrim($line));
242                         if ($ref_page !== $page) {
243                                 if (! $ref_auto) $all_auto = FALSE;
244                                 $ref .= $line;
245                         }
246                 }
247                 unlink($ref_file);
248                 if (($is_page || ! $all_auto) && $ref != '') {
249                         $fp = fopen($ref_file, 'w')
250                                 or die_message('cannot write ' . htmlsc($ref_file));
251                         fputs($fp, $ref);
252                         fclose($fp);
253                 }
254         }
255 }
256
257 function & links_get_objects($page, $refresh = FALSE)
258 {
259         static $obj;
260
261         if (! isset($obj) || $refresh)
262                 $obj = new InlineConverter(NULL, array('note'));
263
264         $result = $obj->get_objects(join('', preg_grep('/^(?!\/\/|\s)./', get_source($page))), $page);
265         return $result;
266 }
267
268 /**
269  * Search function for AutoLink updating
270  *
271  * @param $word page name
272  * @return list of page name that contains $word
273  */
274 function links_do_search_page($word)
275 {
276         global $whatsnew;
277
278         $keys = get_search_words(preg_split('/\s+/', $word, -1, PREG_SPLIT_NO_EMPTY));
279         foreach ($keys as $key=>$value)
280                 $keys[$key] = '/' . $value . '/S';
281         $pages = get_existpages();
282         $pages = array_flip($pages);
283         unset($pages[$whatsnew]);
284         $count = count($pages);
285         foreach (array_keys($pages) as $page) {
286                 $b_match = FALSE;
287                 // Search for page contents
288                 foreach ($keys as $key) {
289                         $body = get_source($page, TRUE, TRUE, TRUE);
290                         $b_match = preg_match($key, remove_author_header($body));
291                         if (! $b_match) break; // OR
292                 }
293                 if ($b_match) continue;
294                 unset($pages[$page]); // Miss
295         }
296         return array_keys($pages);
297 }