修訂 | 334d505c8ce364ff615e533dab4d8fe4e54a0b07 (tree) |
---|---|
時間 | 2022-01-24 01:57:59 |
作者 | hai-fun <haifun129@gmai...> |
Commiter | hai-fun |
rev.310, author: sonots
@@ -0,0 +1,1183 @@ | ||
1 | +<?php | |
2 | +/** | |
3 | + * Table of Contents Plugin eXtension | |
4 | + * | |
5 | + * @author sonots | |
6 | + * @license http://www.gnu.org/licenses/gpl.html GPL v2 | |
7 | + * @link http://lsx.sourceforge.jp/?Plugin%2Fcontentsx.inc.php | |
8 | + * @version $Id: contentsx.inc.php,v 1.11 2007-08-03 07:23:17Z sonots $ | |
9 | + * @package plugin | |
10 | + */ | |
11 | + | |
12 | +class PluginContentsx | |
13 | +{ | |
14 | + function PluginContentsx() | |
15 | + { | |
16 | + // Message | |
17 | + static $msg = array(); | |
18 | + if (empty($msg)) $msg = array( | |
19 | + 'toctitle' => _('Table of Contents'), | |
20 | + ); | |
21 | + // Modify here for default option values | |
22 | + static $default_options = array( | |
23 | + 'page' => array('string', ''), | |
24 | + 'fromhere' => array('bool', true), | |
25 | + 'hierarchy' => array('bool', true), | |
26 | + 'compact' => array('bool', true), | |
27 | + 'num' => array('number', ''), | |
28 | + 'depth' => array('number', ''), | |
29 | + 'except' => array('string', ''), | |
30 | + 'filter' => array('string', ''), | |
31 | + 'include' => array('bool', true), | |
32 | + 'cache' => array('enum', 'on', array('on', 'off', 'reset')), | |
33 | + 'link' => array('enum', 'on', array('on', 'off', 'anchor', 'page')), | |
34 | + ); | |
35 | + // Definitions | |
36 | + static $conf = array( | |
37 | + 'def_headline' => '/^(\*{1,3})/', | |
38 | + 'max_depth' => 3, | |
39 | + 'def_include' => '/^#include.*\((.+)\)/', | |
40 | + 'def_title' => '/^TITLE:(.+)/', | |
41 | + 'use_session' => TRUE, // action | |
42 | + 'through_if_admin' => TRUE, // action | |
43 | + ); | |
44 | + $this->msg = &$msg; | |
45 | + $this->default_options = &$default_options; | |
46 | + $this->conf = &$conf; | |
47 | + | |
48 | + // init | |
49 | + $this->options = $this->default_options; | |
50 | + } | |
51 | + | |
52 | + // static | |
53 | + var $msg; | |
54 | + var $default_options; | |
55 | + var $conf; | |
56 | + // var | |
57 | + var $args; | |
58 | + var $options; | |
59 | + var $error = ""; | |
60 | + var $plugin = "contentsx"; | |
61 | + var $metalines; | |
62 | + var $visited = array(); // page => title(alias) | |
63 | + | |
64 | + function action() // clean cache | |
65 | + { | |
66 | + global $vars; | |
67 | + | |
68 | + if (is_admin($vars['pass'], $this->conf['use_session'], $this->conf['through_if_admin']) && | |
69 | + $vars['pcmd'] == 'clean') { | |
70 | + $body = $this->clean_cache(); | |
71 | + } else { | |
72 | + $body = $this->display_password_form(); | |
73 | + } | |
74 | + return array('msg'=>'Clean Contentsx Caches', 'body'=>$body); | |
75 | + } | |
76 | + | |
77 | + function clean_cache() | |
78 | + { | |
79 | + set_time_limit(0); | |
80 | + global $vars; | |
81 | + | |
82 | + $page = isset($vars['page']) ? $vars['page'] : ''; | |
83 | + if ($page != '') { | |
84 | + $file = $this->get_cache_filename($page); | |
85 | + @unlink($file); | |
86 | + if (exec_page($page, '/^#contentsx/')) { | |
87 | + $body = 'Recreated a cache of '; | |
88 | + } else { | |
89 | + $body = 'No #contentsx in '; | |
90 | + } | |
91 | + $body .= make_pagelink($page); | |
92 | + } else { | |
93 | + // remove all files | |
94 | + $files = $this->get_cache_filenames(); | |
95 | + foreach ($files as $file) { | |
96 | + unlink($file); | |
97 | + } | |
98 | + // execute all pages | |
99 | + $exec_pages = exec_existpages('/^#contentsx/'); | |
100 | + if (empty($exec_pages)) { | |
101 | + $body = ''; | |
102 | + } else { | |
103 | + $links = array_map('make_pagelink', $exec_pages); | |
104 | + $body = '<p>Following pages were executed to assure:</p>' | |
105 | + . '<p>' . implode("<br />\n", $links) . '</p>'; | |
106 | + } | |
107 | + } | |
108 | + return $body; | |
109 | + } | |
110 | + | |
111 | + /** | |
112 | + * Display a password form | |
113 | + * | |
114 | + * @param $msg error message or some messages | |
115 | + * @return string form html | |
116 | + */ | |
117 | + function display_password_form($message = "") | |
118 | + { | |
119 | + $cmd = $this->plugin; | |
120 | + $pcmd = 'clean'; | |
121 | + $form = array(); | |
122 | + $form[] = '<form action="' . get_script_uri() . '?cmd=' . $cmd . '" method="post">'; | |
123 | + $form[] = '<div>'; | |
124 | + $form[] = ' <input type="hidden" name="pcmd" value="' . $pcmd . '" />'; | |
125 | + $form[] = ' <input type="text" name="page" size="24" value="" /> ' . _('A Page (Blank if All)') . '<br />'; | |
126 | + if (! is_admin(null, $this->conf['use_session'], $this->conf['through_if_admin'])) { | |
127 | + $form[] = ' <input type="password" name="pass" size="24" value="" /> ' . _('Admin Password') . '<br />'; | |
128 | + } else { | |
129 | + $form[] = ' <input type="password" name="pass" size="24" value="" style="background-color:#ddd;" disabled="disabled" /> ' . _('Admin Password') . '<br />'; | |
130 | + } | |
131 | + $form[] = ' <input type="submit" name="submit" value="Submit" /><br />'; | |
132 | + $form[] = '</div>'; | |
133 | + $form[] = '</form>'; | |
134 | + $form = implode("\n", $form); | |
135 | + | |
136 | + if ($message != '') { | |
137 | + $message = '<p><b>' . htmlspecialchars($message) . '</b></p>'; | |
138 | + } | |
139 | + return $message . $form; | |
140 | + } | |
141 | + | |
142 | + function convert() | |
143 | + { | |
144 | + $args = func_get_args(); | |
145 | + if ($GLOBALS['vars']['cmd'] != 'read') { | |
146 | + return ''; | |
147 | + } | |
148 | + $body = $this->body($args); | |
149 | + if ($body != '') { | |
150 | + $body = '<table border="0" class="toc"><tbody>' . "\n" | |
151 | + . '<tr><td class="toctitle">' . "\n" | |
152 | + . '<span>' . $this->msg['toctitle'] . "</span>\n" | |
153 | + . "</td></tr>\n" | |
154 | + . '<tr><td class="toclist">' . "\n" | |
155 | + . $body | |
156 | + . "</td></tr>\n" | |
157 | + . "</tbody></table>\n"; | |
158 | + } | |
159 | + if ($this->error != "" ) { | |
160 | + return "<p>#$this->plugin(): $this->error</p>"; | |
161 | + } | |
162 | + return $body; | |
163 | + } | |
164 | + | |
165 | + function body($args) | |
166 | + { | |
167 | + global $vars; | |
168 | + | |
169 | + $parser = new PluginContentsxOptionParser(); | |
170 | + $this->options = $parser->parse_options($args, $this->options); | |
171 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
172 | + | |
173 | + $this->options['page'][1] = $this->check_page($this->options['page'][1]); | |
174 | + if ($this->error !== "") { return; } | |
175 | + | |
176 | + $this->init_metalines($this->options['page'][1]); | |
177 | + if ($this->error !== "") { return; } | |
178 | + | |
179 | + $this->narrow_metalines(); | |
180 | + if ($this->error !== "") { return; } | |
181 | + | |
182 | + $body = $this->frontend(); | |
183 | + if ($this->error !== "") { return; } | |
184 | + return $body; | |
185 | + } | |
186 | + | |
187 | + function get_title($page) | |
188 | + { | |
189 | + $page = $this->check_page($page); | |
190 | + $this->init_metalines($page); | |
191 | + $title = $this->visited[$page]; | |
192 | + // FYI: $title = strip_htmltag(make_link($title)); | |
193 | + // $link = make_pagelink($page, $title); | |
194 | + return $title; | |
195 | + } | |
196 | + | |
197 | + function get_visited($page) | |
198 | + { | |
199 | + $page = $this->check_page($page); | |
200 | + $this->init_metalines($page); | |
201 | + return array_keys($this->visited); | |
202 | + } | |
203 | + | |
204 | + function get_metalines($page) | |
205 | + { | |
206 | + $page = $this->check_page($page); | |
207 | + $this->init_metalines($page); | |
208 | + return $this->metalines; | |
209 | + } | |
210 | + | |
211 | + function narrow_metalines() | |
212 | + { | |
213 | + $this->fromhere_metalines(); | |
214 | + $this->include_metalines(); | |
215 | + $this->filter_metalines(); | |
216 | + $this->except_metalines(); | |
217 | + | |
218 | + $parser = new PluginContentsxOptionParser(); | |
219 | + $this->options['depth'][1] = $parser->parse_numoption($this->options['depth'][1], 0, $this->conf['max_depth']); | |
220 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
221 | + $this->depth_filter_metalines(); | |
222 | + | |
223 | + $num = sizeof($this->metalines); | |
224 | + $this->options['num'][1] = $parser->parse_numoption($this->options['num'][1], 1, $num); | |
225 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
226 | + $this->num_filter_metalines(); | |
227 | + } | |
228 | + | |
229 | + function frontend() | |
230 | + { | |
231 | + $this->hierarchy_metalines(); | |
232 | + $this->compact_metalines(); | |
233 | + $this->makelink_metalines(); | |
234 | + | |
235 | + return $this->list_metalines(); | |
236 | + } | |
237 | + | |
238 | + function list_metalines() | |
239 | + { | |
240 | + if (sizeof($this->metalines) == 0) { | |
241 | + return; | |
242 | + } | |
243 | + | |
244 | + /* HTML validate (without <ul><li style="list-type:none"><ul><li>, we have to do as | |
245 | + <ul><li style="padding-left:16*2px;margin-left:16*2px"> as pukiwiki standard. I did not like it) | |
246 | + | |
247 | + <ul> <ul><li>1 | |
248 | + <li>1</li> </li><li>1 | |
249 | + <li>1 <ul><li>2 | |
250 | + <ul> </li></ul></li><li>1 | |
251 | + <li>2</li> </li><li>1 | |
252 | + </ul> => <ul><li style="list-type:none"><ul><li>3 | |
253 | + </li> </li></ul></li></ul></li></ul> | |
254 | + <li>1</li> | |
255 | + <li>1</li> | |
256 | + <ul><li style="list-type:none"><ul> | |
257 | + <li>3</li> | |
258 | + </ul></li></ul> | |
259 | + </li> | |
260 | + </ul> | |
261 | + */ | |
262 | + | |
263 | + $ul = $pdepth = 0; | |
264 | + foreach ($this->metalines as $metaline) { | |
265 | + $display = $metaline['display']; | |
266 | + $depth = $metaline['listdepth']; | |
267 | + if ($depth > $pdepth) { | |
268 | + $diff = $depth - $pdepth; | |
269 | + $html .= str_repeat('<ul><li style="list-style:none">', $diff - 1); | |
270 | + if ($depth == 1) { // or $first flag | |
271 | + $html .= '<ul class="' . $this->plugin . '"><li>'; | |
272 | + } else { | |
273 | + $html .= '<ul><li>'; | |
274 | + } | |
275 | + $ul += $diff; | |
276 | + } elseif ($depth == $pdepth) { | |
277 | + $html .= '</li><li>'; | |
278 | + } elseif ($depth < $pdepth) { | |
279 | + $diff = $pdepth - $depth; | |
280 | + $html .= str_repeat('</li></ul>', $diff); | |
281 | + $html .= '</li><li>'; | |
282 | + $ul -= $diff; | |
283 | + } | |
284 | + $html .= $display; | |
285 | + $html .= "\n"; | |
286 | + $pdepth = $depth; | |
287 | + } | |
288 | + $html .= str_repeat('</li></ul>', $ul); | |
289 | + return $html; | |
290 | + } | |
291 | + | |
292 | + function makelink_metalines() | |
293 | + { | |
294 | + $metalines = array(); | |
295 | + foreach ($this->metalines as $metaline) { | |
296 | + $anchor = $metaline['anchor']; | |
297 | + $headline = $metaline['headline']; | |
298 | + $headline = strip_htmltag(make_link($headline)); // convert inline plugin | |
299 | + $metaline['display'] = $this->make_pagelink($this->options['page'][1], $headline, $anchor); | |
300 | + $metalines[] = $metaline; | |
301 | + } | |
302 | + $this->metalines = $metalines; | |
303 | + } | |
304 | + | |
305 | + function make_pagelink($page, $alias, $anchor) | |
306 | + { | |
307 | + global $vars; | |
308 | + if ($this->options['link'][1] == 'off' || $anchor =='') { | |
309 | + return htmlspecialchars($alias); | |
310 | + } | |
311 | + if (($this->options['link'][1] == 'on' && $page == $vars['page']) | |
312 | + || $this->options['link'][1] == 'anchor') { | |
313 | + $page = ''; | |
314 | + } | |
315 | + global $show_passage; | |
316 | + $tmp = $show_passage; $show_passage = 0; | |
317 | + $link = make_pagelink($page, $alias, $anchor); | |
318 | + $show_passage = $tmp; | |
319 | + return $link; | |
320 | + } | |
321 | + | |
322 | + function compact_metalines() | |
323 | + { | |
324 | + // Hmmmmm, complex | |
325 | + if (!$this->options['compact'][1]) { | |
326 | + return; | |
327 | + } | |
328 | + if (! $this->options['hierarchy'][1]) { | |
329 | + return; | |
330 | + } | |
331 | + // 1) fill in list spaces for each page | |
332 | + // 1 3 1 1 3 3 1 => 1 2 1 1 2 2 1 (2 was none, move 3 to 2) | |
333 | + // 2 2 2 => 1 1 1 | |
334 | + $listdepthstack = array(); | |
335 | + foreach ($this->metalines as $metaline) { | |
336 | + $page = $metaline['page']; | |
337 | + $listdepth = $metaline['listdepth']; | |
338 | + if(! in_array($listdepth, $listdepthstack[$page])) { | |
339 | + $listdepthstack[$page][] = $listdepth; | |
340 | + } | |
341 | + } | |
342 | + foreach (array_keys($listdepthstack) as $page) { | |
343 | + sort($listdepthstack[$page]); | |
344 | + } | |
345 | + // 1 2 4 == (0=>1, 1=>2, 2=>4) -> (1=>1, 1=>2, 3=>4) -exchange keys and values-> (1=>1, 2=>1, 4=>3) | |
346 | + $listdepthfill = array(); | |
347 | + foreach ($listdepthstack as $page => $stack) { | |
348 | + foreach($stack as $i => $listdepth) { | |
349 | + $listdepthfill[$page][$listdepth] = $i + 1; | |
350 | + } | |
351 | + } | |
352 | + $metalines = array(); | |
353 | + foreach ($this->metalines as $metaline) { | |
354 | + $page = $metaline['page']; | |
355 | + $listdepth = $metaline['listdepth']; | |
356 | + $metaline['listdepth'] = $listdepthfill[$page][$listdepth]; | |
357 | + $metalines[] = $metaline; | |
358 | + } | |
359 | + $this->metalines = $metalines; | |
360 | + | |
361 | + // 2) fill in previous list space, seperately for each page | |
362 | + // 1 3 2 => 1 2 2 | |
363 | + $pdepth = array(); $plistdepth = array(); | |
364 | + foreach (array_keys($listdepthstack) as $page) { | |
365 | + $pdepth[$page] = -1; | |
366 | + $plistdepth[$page] = 0; | |
367 | + } | |
368 | + $metalines = array(); | |
369 | + $this->hoge = array(); | |
370 | + foreach ($this->metalines as $metaline) { | |
371 | + $page = $metaline['page']; | |
372 | + if ($metaline['depth'] > $pdepth[$page]) { | |
373 | + $metaline['listdepth'] = $plistdepth[$page] + 1; | |
374 | + } elseif($metaline['depth'] == $pdepth[$page]) { | |
375 | + $metaline['listdepth'] = $plistdepth[$page]; | |
376 | + } else { | |
377 | + $metaline['listdepth'] = ($plistdepth[$page] < $metaline['listdepth']) ? $plistdepth[$page]: $metaline['listdepth']; | |
378 | + } | |
379 | + $pdepth[$page] = $metaline['depth']; | |
380 | + $plistdepth[$page] = $metaline['listdepth']; | |
381 | + $metalines[] = $metaline; | |
382 | + } | |
383 | + $this->metalines = $metalines; | |
384 | + } | |
385 | + | |
386 | + function hierarchy_metalines() | |
387 | + { | |
388 | + $include = 0; | |
389 | + if ($this->options['include'][1] && sizeof($this->visited) >= 2) { // include (0,1,2,3...) -> (1,2,3,4...) | |
390 | + $include = 1; | |
391 | + } | |
392 | + $metalines = array(); | |
393 | + foreach($this->metalines as $metaline) { | |
394 | + if ($this->options['hierarchy'][1]) { | |
395 | + $metaline['listdepth'] = $metaline['depth'] + $include; | |
396 | + } else { | |
397 | + $metaline['listdepth'] = 1; | |
398 | + } | |
399 | + $metalines[] = $metaline; | |
400 | + } | |
401 | + $this->metalines = $metalines; | |
402 | + } | |
403 | + | |
404 | + function num_filter_metalines() | |
405 | + { | |
406 | + if ($this->options['num'][1] === '') { | |
407 | + return; | |
408 | + } | |
409 | + $metalines = array(); | |
410 | + foreach ($this->options['num'][1] as $num) { | |
411 | + $metalines[] = $this->metalines[$num - 1]; | |
412 | + } | |
413 | + $this->metalines = $metalines; | |
414 | + } | |
415 | + | |
416 | + function depth_filter_metalines() | |
417 | + { | |
418 | + if ($this->options['depth'][1] === '') { | |
419 | + return; | |
420 | + } | |
421 | + $metalines = array(); | |
422 | + foreach ($this->metalines as $metaline) { | |
423 | + $depth = $metaline['depth']; | |
424 | + if (in_array($depth, $this->options['depth'][1])) { | |
425 | + $metalines[] = $metaline; | |
426 | + } | |
427 | + } | |
428 | + $this->metalines = $metalines; | |
429 | + } | |
430 | + | |
431 | + function filter_metalines() | |
432 | + { | |
433 | + if ($this->options['filter'][1] === "") { | |
434 | + return; | |
435 | + } | |
436 | + $metalines = array(); | |
437 | + foreach ($this->metalines as $metaline) { | |
438 | + $headline = $metaline['headline']; | |
439 | + if (ereg($this->options['filter'][1], $headline)) { | |
440 | + $metalines[] = $metaline; | |
441 | + } | |
442 | + } | |
443 | + $this->metalines = $metalines; | |
444 | + } | |
445 | + | |
446 | + function except_metalines() | |
447 | + { | |
448 | + if ($this->options['except'][1] === "") { | |
449 | + return; | |
450 | + } | |
451 | + $metalines = array(); | |
452 | + foreach ($this->metalines as $metaline) { | |
453 | + $headline = $metaline['headline']; | |
454 | + if (!ereg($this->options['except'][1], $headline)) { | |
455 | + $metalines[] = $metaline; | |
456 | + } | |
457 | + } | |
458 | + $this->metalines = $metalines; | |
459 | + } | |
460 | + | |
461 | + function include_metalines() | |
462 | + { | |
463 | + if ($this->options['include'][1]) { | |
464 | + return; | |
465 | + } | |
466 | + $metalines = array(); | |
467 | + foreach ($this->metalines as $metaline) { | |
468 | + if ($metaline['page'] == $this->options['page'][1]) { | |
469 | + $metalines[] = $metaline; | |
470 | + } | |
471 | + } | |
472 | + $this->metalines = $metalines; | |
473 | + } | |
474 | + | |
475 | + function fromhere_metalines() | |
476 | + { | |
477 | + if (! $this->options['fromhere'][1]) { | |
478 | + return; | |
479 | + } | |
480 | + $metalines = array(); | |
481 | + foreach ($this->metalines as $metaline) { | |
482 | + if ($metaline['fromhere']) { | |
483 | + $metalines[] = $metaline; | |
484 | + } | |
485 | + } | |
486 | + $this->metalines = $metalines; | |
487 | + } | |
488 | + | |
489 | + function init_metalines($page) | |
490 | + { | |
491 | + $this->metalines = array(); | |
492 | + $this->visited = array(); | |
493 | + if ($this->read_cache($page) !== false) { return; } | |
494 | + $this->metalines = $this->r_metalines($page); | |
495 | + $this->write_cache($page); | |
496 | + } | |
497 | + | |
498 | + function get_cache_filename($page) | |
499 | + { | |
500 | + return CACHE_DIR . encode($page) . ".$this->plugin"; | |
501 | + } | |
502 | + | |
503 | + function get_cache_filenames() | |
504 | + { | |
505 | + return get_existfiles(CACHE_DIR, ".$this->plugin"); | |
506 | + } | |
507 | + | |
508 | + /** | |
509 | + * Read cache | |
510 | + * | |
511 | + * @param $apage pagename | |
512 | + * @return mixed contents or FALSE if cache should be renewed | |
513 | + */ | |
514 | + function read_cache($apage) | |
515 | + { | |
516 | + if ($this->options['cache'][1] == 'off' || $this->options['cache'][1] == 'reset') { | |
517 | + return false; | |
518 | + } | |
519 | + if (! is_page($apage)) { | |
520 | + return false; | |
521 | + } | |
522 | + $cache = $this->get_cache_filename($apage); | |
523 | + if (! $this->file_exists($cache)) { | |
524 | + return false; | |
525 | + } | |
526 | + if (! $this->is_readable($cache)) { | |
527 | + $this->error = "Cache file, $cache is not readable. "; | |
528 | + return; | |
529 | + } | |
530 | + | |
531 | + $lines = file($cache); | |
532 | + | |
533 | + $pages = csv_explode(',', rtrim(array_shift($lines))); | |
534 | + foreach ($pages as $page) { | |
535 | + list($page, $title) = csv_explode('=', $page); | |
536 | + $visited[$page] = $title; | |
537 | + } | |
538 | + | |
539 | + // renew cache if preview mode | |
540 | + if (isset($vars['preview']) || isset($vars['realview'])) { | |
541 | + return false; | |
542 | + } | |
543 | + // renew cache if page is newer than cache | |
544 | + foreach ($visited as $page => $title) { | |
545 | + if (is_page_newer($page, $cache)) { | |
546 | + return false; | |
547 | + } | |
548 | + } | |
549 | + | |
550 | + $this->visited = $visited; | |
551 | + $metalines = array(); | |
552 | + foreach ($lines as $line) { | |
553 | + $metas = csv_explode(',', rtrim($line)); | |
554 | + $metaline = array(); | |
555 | + foreach ($metas as $meta) { | |
556 | + list($key, $val) = explode('=', $meta, 2); | |
557 | + $metaline[$key] = $val; | |
558 | + } | |
559 | + $metalines[] = $metaline; | |
560 | + } | |
561 | + $this->metalines = $metalines; | |
562 | + } | |
563 | + | |
564 | + function write_cache($apage) | |
565 | + { | |
566 | + if ($this->options['cache'][1] == 'off') { | |
567 | + return; | |
568 | + } | |
569 | + if (! is_page($apage)) { | |
570 | + return; | |
571 | + } | |
572 | + $cache = $this->get_cache_filename($apage); | |
573 | + if ($this->file_exists($cache) && ! $this->is_writable($cache)) { | |
574 | + $this->error = "Cache file, $cache is not writable. "; | |
575 | + return; | |
576 | + } | |
577 | + | |
578 | + $pages = array(); | |
579 | + foreach ($this->visited as $page => $title) { | |
580 | + $pages[] = csv_implode('=', array($page, $title)); | |
581 | + } | |
582 | + $contents = ''; | |
583 | + $contents .= csv_implode(',', $pages) . "\n"; | |
584 | + foreach ($this->metalines as $metaline) { | |
585 | + $metas = array(); | |
586 | + foreach ($metaline as $key => $val) { | |
587 | + $metas[] = "$key=$val"; | |
588 | + } | |
589 | + $contents .= csv_implode(',', $metas) . "\n"; | |
590 | + } | |
591 | + // file_put_contents($cache, $contents); // PHP5 | |
592 | + if (! $fp = fopen($cache, "w")) { | |
593 | + $this->error = "Can not open $cache. "; | |
594 | + return; | |
595 | + } | |
596 | + if (! fwrite($fp, $contents)) { | |
597 | + $this->error = "Can not write to $cache. "; | |
598 | + return; | |
599 | + } | |
600 | + fclose($fp); | |
601 | + } | |
602 | + | |
603 | + function r_metalines($page, $detected = false) | |
604 | + { | |
605 | + if (array_key_exists($page, $this->visited)) { | |
606 | + return array(); | |
607 | + } | |
608 | + if (! is_page($page)) { | |
609 | + return array(); | |
610 | + } | |
611 | + $this->visited[$page] = ''; | |
612 | + $lines = $this->get_source($page); | |
613 | + $multiline = 0; | |
614 | + $metalines = array(); | |
615 | + foreach ($lines as $i => $line) { | |
616 | + // multiline plugin. refer lib/convert_html | |
617 | + if(defined('PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK') && PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK === 0) { | |
618 | + $matches = array(); | |
619 | + if ($multiline < 2) { | |
620 | + if(preg_match('/^#([^\(\{]+)(?:\(([^\r]*)\))?(\{*)/', $line, $matches)) { | |
621 | + $multiline = strlen($matches[3]); | |
622 | + } | |
623 | + } else { | |
624 | + if (preg_match('/^\}{' . $multiline . '}$/', $line, $matches)) { | |
625 | + $multiline = 0; | |
626 | + } | |
627 | + continue; | |
628 | + } | |
629 | + } | |
630 | + | |
631 | + // fromhere | |
632 | + if ($this->options['page'][1] == $page && !$detected) { | |
633 | + if (preg_match('/^#' . $this->plugin . '/', $line, $matches)) { | |
634 | + $detected = true; | |
635 | + continue; | |
636 | + } | |
637 | + } | |
638 | + | |
639 | + if (preg_match($this->conf['def_headline'], $line, $matches)) { | |
640 | + $depth = strlen($matches[1]); | |
641 | + $anchor = '#' . $this->make_heading($line); // *** [id] is removed from $line | |
642 | + $headline = trim($line); | |
643 | + $metalines[] = array(page=>$page, headline=>$headline, anchor=>$anchor, depth=>$depth, linenum=>$i, fromhere=>$detected); | |
644 | + continue; | |
645 | + } | |
646 | + | |
647 | + if (preg_match($this->conf['def_include'], $line, $matches)) { | |
648 | + $args = csv_explode(',', $matches[1]); | |
649 | + $inclpage = array_shift($args); | |
650 | + $options = array(); | |
651 | + foreach ($args as $arg) { | |
652 | + list($key, $val) = array_pad(explode('=', $arg, 2), 2, true); | |
653 | + $options[$key] = $val; | |
654 | + } | |
655 | + $inclpage = get_fullname($inclpage, $page); | |
656 | + if (! $this->is_page($inclpage)) { | |
657 | + continue; | |
658 | + } | |
659 | + // $anchor = PluginIncludex::get_page_anchor($inclpage) | |
660 | + $anchor = 'z' . md5($inclpage); | |
661 | + $anchor = '#' . htmlspecialchars($anchor); | |
662 | + if (exist_plugin('includex') & is_callable(array('PluginIncludex', 'get_titlestr'))) { | |
663 | + $titlestr = PluginIncludex::get_titlestr($inclpage, $options['titlestr']); | |
664 | + } else { | |
665 | + $titlestr = $inclpage; | |
666 | + } | |
667 | + $metalines[] = array(page=>$inclpage, headline=>$titlestr, anchor=>$anchor, depth=>0, linenum=>$i, fromhere=>$detected); | |
668 | + $metalines = array_merge($metalines, $this->r_metalines($inclpage, $detected)); | |
669 | + continue; | |
670 | + } | |
671 | + | |
672 | + if (preg_match($this->conf['def_title'], $line, $matches)) { | |
673 | + $title = $matches[1]; | |
674 | + $this->visited[$page] = $title; | |
675 | + continue; | |
676 | + } | |
677 | + } | |
678 | + return $metalines; | |
679 | + } | |
680 | + | |
681 | + // copy from lib/html.php | |
682 | + function make_heading(& $str, $strip = TRUE) | |
683 | + { | |
684 | + global $NotePattern; | |
685 | + | |
686 | + // Cut fixed-heading anchors | |
687 | + $id = ''; | |
688 | + $matches = array(); | |
689 | + if (preg_match('/^(\*{0,3})(.*?)\[#([A-Za-z][\w-]+)\](.*?)$/m', $str, $matches)) { | |
690 | + $str = $matches[2] . $matches[4]; | |
691 | + $id = & $matches[3]; | |
692 | + } else { | |
693 | + $str = preg_replace('/^\*{0,3}/', '', $str); | |
694 | + } | |
695 | + | |
696 | + // Cut footnotes and tags | |
697 | + if ($strip === TRUE) { | |
698 | + // $str = strip_htmltag(make_link(preg_replace($NotePattern, '', $str))); // sonots | |
699 | + $str = preg_replace($NotePattern, '', $str); // sonots | |
700 | + } | |
701 | + | |
702 | + return $id; | |
703 | + } | |
704 | + | |
705 | + function check_page($page) | |
706 | + { | |
707 | + global $vars, $defaultpage; | |
708 | + if ($page == "") { | |
709 | + $page = isset($vars['page']) ? $vars['page'] : $defaultpage; | |
710 | + } else { | |
711 | + $page = get_fullname($page, $vars['page']); | |
712 | + $this->options['fromhere'][1] = false; | |
713 | + } | |
714 | + if (! $this->is_page($page)) { | |
715 | + $this->error = "No such a page, " . $page; | |
716 | + return; | |
717 | + } | |
718 | + if (! $this->check_readable($page, FALSE, FALSE)) { | |
719 | + $this->error = "Page, " . $page . ", is not readable."; | |
720 | + } | |
721 | + return $page; | |
722 | + } | |
723 | + | |
724 | + // PukiWiki API | |
725 | + function get_source($page) | |
726 | + { | |
727 | + return get_source($page); | |
728 | + } | |
729 | + | |
730 | + function is_page($page) | |
731 | + { | |
732 | + return is_page($page); | |
733 | + } | |
734 | + | |
735 | + function check_readable($page, $flag, $flag) | |
736 | + { | |
737 | + return check_readable($page, $flag, $flag); | |
738 | + } | |
739 | + | |
740 | + // PHP API | |
741 | + function file_exists($file) | |
742 | + { | |
743 | + return file_exists($file); | |
744 | + } | |
745 | + | |
746 | + function is_readable($file) | |
747 | + { | |
748 | + return is_readable($file); | |
749 | + } | |
750 | + | |
751 | + function is_writable($file) | |
752 | + { | |
753 | + return is_writable($file); | |
754 | + } | |
755 | +} | |
756 | + | |
757 | +/////////////////////////////////////// | |
758 | +class PluginContentsxOptionParser | |
759 | +{ | |
760 | + var $error = ""; | |
761 | + | |
762 | + function parse_options($args, $options) | |
763 | + { | |
764 | + if (! is_associative_array($args)) { | |
765 | + $args = $this->associative_args($args, $options); | |
766 | + if ($this->error != "") { return; } | |
767 | + } | |
768 | + | |
769 | + foreach ($args as $key => $val) { | |
770 | + if ( !isset($options[$key]) ) { continue; } // for action ($vars) | |
771 | + $type = $options[$key][0]; | |
772 | + | |
773 | + switch ($type) { | |
774 | + case 'bool': | |
775 | + if($val == "" || $val == "on" || $val == "true") { | |
776 | + $options[$key][1] = true; | |
777 | + } elseif ($val == "off" || $val == "false" ) { | |
778 | + $options[$key][1] = false; | |
779 | + } else { | |
780 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
781 | + $this->error .= "The option, $key, accepts only a boolean value."; | |
782 | + $this->error .= "#$this->plugin($key) or #$this->plugin($key=on) or #$this->plugin($key=true) for true. "; | |
783 | + $this->error .= "#$this->plugin($key=off) or #$this->plugin($key=false) for false. "; | |
784 | + return; | |
785 | + } | |
786 | + break; | |
787 | + case 'string': | |
788 | + $options[$key][1] = $val; | |
789 | + break; | |
790 | + case 'sanitize': | |
791 | + $options[$key][1] = htmlspecialchars($val); | |
792 | + break; | |
793 | + case 'number': | |
794 | + // Do not parse yet, parse after getting min and max. Here, just format checking | |
795 | + if ($val === '') { | |
796 | + $options[$key][1] = ''; | |
797 | + break; | |
798 | + } | |
799 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
800 | + $val = substr($val, 1, strlen($val) - 2); | |
801 | + } | |
802 | + foreach (explode(",", $val) as $range) { | |
803 | + if (preg_match('/^-?\d+$/', $range)) { | |
804 | + } elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) { | |
805 | + } elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) { | |
806 | + } else { | |
807 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
808 | + $this->error .= "The option, " . $key . ", accepts number values such as 1, 1:3, 1+3, 1,2,4. "; | |
809 | + $this->error .= "Specify options as \"$key=1,2,4\" or $key=(1,2,3) when you want to use \",\". "; | |
810 | + $this->error .= "In more details, a style like (1:3,5:7,9:) is also possible. 9: means from 9 to the last. "; | |
811 | + $this->error .= "Furtermore, - means backward. -1:-3 means 1,2,3 from the tail. "; | |
812 | + return; | |
813 | + } | |
814 | + } | |
815 | + $options[$key][1] = $val; | |
816 | + break; | |
817 | + case 'enum': | |
818 | + if($val == "") { | |
819 | + $options[$key][1] = $options[$key][2][0]; | |
820 | + } elseif (in_array($val, $options[$key][2])) { | |
821 | + $options[$key][1] = $val; | |
822 | + } else { | |
823 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
824 | + $this->error .= "The option, " . $key . ", accepts values from one of (" . join(",", $options[$key][2]) . "). "; | |
825 | + $this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=" . $options[$key][2][0] . "). "; | |
826 | + return; | |
827 | + } | |
828 | + break; | |
829 | + case 'array': | |
830 | + if ($val == '') { | |
831 | + $options[$key][1] = array(); | |
832 | + break; | |
833 | + } | |
834 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
835 | + $val = substr($val, 1, strlen($val) - 2); | |
836 | + } | |
837 | + $val = explode(',', $val); | |
838 | + //$val = $this->support_paren($val); | |
839 | + $options[$key][1] = $val; | |
840 | + break; | |
841 | + case 'enumarray': | |
842 | + if ($val == '') { | |
843 | + $options[$key][1] = $options[$key][2]; | |
844 | + break; | |
845 | + } | |
846 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
847 | + $val = substr($val, 1, strlen($val) - 2); | |
848 | + } | |
849 | + $val = explode(',', $val); | |
850 | + //$val = $this->support_paren($val); | |
851 | + $options[$key][1] = $val; | |
852 | + foreach ($options[$key][1] as $each) { | |
853 | + if (! in_array($each, $options[$key][2])) { | |
854 | + $this->error = "$key=" . htmlspecialchars(join(",", $options[$key][1])) . " is invalid. "; | |
855 | + $this->error .= "The option, " . $key . ", accepts sets of values from (" . join(",", $options[$key][2]) . "). "; | |
856 | + $this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=(" . join(',',$options[$key][2]) . ")). "; | |
857 | + return; | |
858 | + } | |
859 | + } | |
860 | + break; | |
861 | + default: | |
862 | + } | |
863 | + } | |
864 | + | |
865 | + return $options; | |
866 | + } | |
867 | + | |
868 | + /** | |
869 | + * Handle associative type option arguments as | |
870 | + * ["prefix=Hoge/", "contents=(hoge", "hoge", "hoge)"] => ["prefix"=>"hoge/", "contents"=>"(hoge,hoge,hoge)"] | |
871 | + * This has special supports for parentheses type arguments (number, array, enumarray) | |
872 | + * Check option in along with. | |
873 | + * @access public | |
874 | + * @param Array $args Original option arguments | |
875 | + * @return Array $result Converted associative option arguments | |
876 | + */ | |
877 | + function associative_args($args, $options) | |
878 | + { | |
879 | + $result = array(); | |
880 | + while (($arg = current($args)) !== false) { | |
881 | + list($key, $val) = array_pad(explode("=", $arg, 2), 2, ''); | |
882 | + if (! isset($options[$key])) { | |
883 | + $this->error = 'No such a option, ' . htmlspecialchars($key); | |
884 | + return; | |
885 | + } | |
886 | + // paren support | |
887 | + if ($val[0] === '(' && ($options[$key][0] == 'number' || | |
888 | + $options[$key][0] == 'array' || $options[$key][0] == 'enumarray')) { | |
889 | + while(true) { | |
890 | + if ($val[strlen($val)-1] === ')' && substr_count($val, '(') == substr_count($val, ')')) { | |
891 | + break; | |
892 | + } | |
893 | + $arg = next($args); | |
894 | + if ($arg === false) { | |
895 | + $this->error = "The # of open and close parentheses of one of your arguments did not match. "; | |
896 | + return; | |
897 | + } | |
898 | + $val .= ',' . $arg; | |
899 | + } | |
900 | + } | |
901 | + $result[$key] = $val; | |
902 | + next($args); | |
903 | + } | |
904 | + return $result; | |
905 | + } | |
906 | + | |
907 | + function parse_numoption($optionval, $min, $max) | |
908 | + { | |
909 | + if ($optionval === '') { | |
910 | + return ''; | |
911 | + } | |
912 | + $result = array(); | |
913 | + foreach (explode(",", $optionval) as $range) { | |
914 | + if (preg_match('/^-?\d+$/', $range)) { | |
915 | + $left = $right = $range; | |
916 | + } elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) { | |
917 | + list($left, $right) = explode(":", $range, 2); | |
918 | + if ($left == "" && $right == "") { | |
919 | + $left = $min; | |
920 | + $right = $max; | |
921 | + } elseif($left == "") { | |
922 | + $left = $min; | |
923 | + } elseif ($right == "") { | |
924 | + $right = $max; | |
925 | + } | |
926 | + } elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) { | |
927 | + list($left, $right) = explode("+", $range, 2); | |
928 | + $right += $left; | |
929 | + } | |
930 | + if ($left < 0) { | |
931 | + $left += $max + 1; | |
932 | + } | |
933 | + if ($right < 0) { | |
934 | + $right += $max + 1; | |
935 | + } | |
936 | + $result = array_merge($result, range($left, $right)); | |
937 | + // range allows like range(5, 3) also | |
938 | + } | |
939 | + // filter | |
940 | + foreach (array_keys($result) as $i) { | |
941 | + if ($result[$i] < $min || $result[$i] > $max) { | |
942 | + unset($result[$i]); | |
943 | + } | |
944 | + } | |
945 | + sort($result); | |
946 | + $result = array_unique($result); | |
947 | + | |
948 | + return $result; | |
949 | + } | |
950 | + | |
951 | + function option_debug_print($options) { | |
952 | + foreach ($options as $key => $val) { | |
953 | + $type = $val[0]; | |
954 | + $val = $val[1]; | |
955 | + if(is_array($val)) { | |
956 | + $val=join(',', $val); | |
957 | + } | |
958 | + $body .= "$key=>($type, $val),"; | |
959 | + } | |
960 | + return $body; | |
961 | + } | |
962 | + | |
963 | + // php extension | |
964 | + function is_associative_array($array) | |
965 | + { | |
966 | + if (!is_array($array) || empty($array)) | |
967 | + return false; | |
968 | + $keys = array_keys($array); | |
969 | + return array_keys($keys) !== $keys; | |
970 | + // or | |
971 | + //return is_array($array) && !is_numeric(implode(array_keys($array))); | |
972 | + } | |
973 | +} | |
974 | + | |
975 | +//////////////// PukiWiki API Extension | |
976 | +if (! function_exists('is_admin')) { | |
977 | + /** | |
978 | + * PukiWiki admin login with session | |
979 | + * | |
980 | + * @param string $pass | |
981 | + * @param boolean $use_session Use Session log | |
982 | + * @param boolean $use_basicauth Use BasicAuth log | |
983 | + * @return boolean | |
984 | + */ | |
985 | + function is_admin($pass = null, $use_session = false, $use_basicauth = false) | |
986 | + { | |
987 | + $is_admin = FALSE; | |
988 | + if ($use_basicauth) { | |
989 | + if (is_callable(array('auth', 'check_role'))) { // Plus! | |
990 | + $is_admin = ! auth::check_role('role_adm_contents'); | |
991 | + } | |
992 | + } | |
993 | + if (! $is_admin && isset($pass)) { | |
994 | + $is_admin = function_exists('pkwk_login') ? pkwk_login($pass) : | |
995 | + md5($pass) === $GLOBALS['adminpass']; // 1.4.3 | |
996 | + } | |
997 | + if ($use_session) { | |
998 | + session_start(); | |
999 | + if ($is_admin) $_SESSION['is_admin'] = TRUE; | |
1000 | + return isset($_SESSION['is_admin']) && $_SESSION['is_admin']; | |
1001 | + } else { | |
1002 | + return $is_admin; | |
1003 | + } | |
1004 | + } | |
1005 | +} | |
1006 | + | |
1007 | +if (! function_exists('is_page_newer')) { | |
1008 | + /** | |
1009 | + * Check if the page timestamp is newer than the file timestamp | |
1010 | + * | |
1011 | + * PukiWiki API Extension | |
1012 | + * | |
1013 | + * @param string $page pagename | |
1014 | + * @param string $file filename | |
1015 | + * @param bool $ignore_notimestamp Ignore notimestamp edit and see the real time editted | |
1016 | + * @return boolean | |
1017 | + */ | |
1018 | + function is_page_newer($page, $file, $ignore_notimestamp = TRUE) | |
1019 | + { | |
1020 | + $filestamp = file_exists($file) ? filemtime($file) : 0; | |
1021 | + if ($ignore_notimestamp) { // See the diff file. PukiWiki Trick. | |
1022 | + $pagestamp = is_page($page) ? filemtime(DIFF_DIR . encode($page) . '.txt') : 0; | |
1023 | + } else { | |
1024 | + $pagestamp = is_page($page) ? filemtime(get_filename($page)) : 0; | |
1025 | + } | |
1026 | + return $pagestamp > $filestamp; | |
1027 | + } | |
1028 | +} | |
1029 | + | |
1030 | +if (! function_exists('exec_page')) { | |
1031 | + /** | |
1032 | + * Execute (convert_html) this page | |
1033 | + * | |
1034 | + * PukiWiki API Extension | |
1035 | + * | |
1036 | + * @param string $page | |
1037 | + * @param string $regexp execute only matched lines (preg_grep) | |
1038 | + * @return boolean executed | |
1039 | + */ | |
1040 | + function exec_page($page, $regexp = null) | |
1041 | + { | |
1042 | + global $vars, $get, $post; | |
1043 | + $lines = get_source($page); | |
1044 | + if (isset($regexp)) { | |
1045 | + $lines = preg_grep($regexp, $lines); | |
1046 | + } | |
1047 | + if (empty($lines)) return FALSE; | |
1048 | + $tmp_page = $vars['page']; | |
1049 | + $tmp_cmd = $vars['cmd']; | |
1050 | + $vars['cmd'] = $get['cmd'] = $post['cmd'] = 'read'; | |
1051 | + $vars['page'] = $get['page'] = $post['page'] = $page; | |
1052 | + convert_html($lines); | |
1053 | + $vars['page'] = $get['page'] = $post['page'] = $tmp_page; | |
1054 | + $vars['cmd'] = $get['cmd'] = $post['cmd'] = $tmp_cmd; | |
1055 | + return TRUE; | |
1056 | + } | |
1057 | +} | |
1058 | + | |
1059 | +if (! function_exists('exec_existpages')) { | |
1060 | + /** | |
1061 | + * Execute (convert_html) all pages | |
1062 | + * | |
1063 | + * PukiWiki API Extension | |
1064 | + * | |
1065 | + * @param string $regexp execute only matched lines (preg_grep) | |
1066 | + * @return array executed pages | |
1067 | + */ | |
1068 | + function exec_existpages($regexp = null) | |
1069 | + { | |
1070 | + global $vars, $get, $post; | |
1071 | + $pages = get_existpages(); | |
1072 | + $exec_pages = array(); | |
1073 | + $tmp_page = $vars['page']; | |
1074 | + $tmp_cmd = $vars['cmd']; | |
1075 | + $vars['cmd'] = $get['cmd'] = $post['cmd'] = 'read'; | |
1076 | + foreach ($pages as $page) { | |
1077 | + $vars['page'] = $get['page'] = $post['page'] = $page; | |
1078 | + $lines = get_source($page); | |
1079 | + if (isset($regexp)) { | |
1080 | + $lines = preg_grep($regexp, $lines); | |
1081 | + } | |
1082 | + if (empty($lines)) continue; | |
1083 | + convert_html($lines); | |
1084 | + $exec_pages[] = $page; | |
1085 | + } | |
1086 | + $vars['page'] = $get['page'] = $post['page'] = $tmp_page; | |
1087 | + $vars['cmd'] = $get['cmd'] = $post['cmd'] = $tmp_cmd; | |
1088 | + return $exec_pages; | |
1089 | + } | |
1090 | +} | |
1091 | + | |
1092 | +////////////// PHP API Extension | |
1093 | +if (! function_exists('get_existfiles')) { | |
1094 | + /** | |
1095 | + * Get list of files in a directory | |
1096 | + * | |
1097 | + * PHP Extension | |
1098 | + * | |
1099 | + * @access public | |
1100 | + * @param string $dir Directory Name | |
1101 | + * @param string $ext File Extension | |
1102 | + * @param bool $recursive Traverse Recursively | |
1103 | + * @return array array of filenames | |
1104 | + * @uses is_dir() | |
1105 | + * @uses opendir() | |
1106 | + * @uses readdir() | |
1107 | + */ | |
1108 | + function &get_existfiles($dir, $ext = '', $recursive = FALSE) | |
1109 | + { | |
1110 | + if (($dp = @opendir($dir)) == FALSE) | |
1111 | + return FALSE; | |
1112 | + $pattern = '/' . preg_quote($ext, '/') . '$/'; | |
1113 | + $dir = ($dir[strlen($dir)-1] == '/') ? $dir : $dir . '/'; | |
1114 | + $dir = ($dir == '.' . '/') ? '' : $dir; | |
1115 | + $files = array(); | |
1116 | + while (($file = readdir($dp)) !== false ) { | |
1117 | + if($file != '.' && $file != '..' && is_dir($dir . $file) && $recursive) { | |
1118 | + $files = array_merge($files, get_existfiles($dir . $file, $ext, $recursive)); | |
1119 | + } else { | |
1120 | + $matches = array(); | |
1121 | + if (preg_match($pattern, $file, $matches)) { | |
1122 | + $files[] = $dir . $file; | |
1123 | + } | |
1124 | + } | |
1125 | + } | |
1126 | + closedir($dp); | |
1127 | + return $files; | |
1128 | + } | |
1129 | +} | |
1130 | + | |
1131 | +if (! function_exists('is_associative_array')) { | |
1132 | + /** | |
1133 | + * Check if an array is an associative array | |
1134 | + * | |
1135 | + * PHP Extension | |
1136 | + * | |
1137 | + * @param array $array | |
1138 | + * @return boolean | |
1139 | + */ | |
1140 | + function is_associative_array($array) | |
1141 | + { | |
1142 | + if (!is_array($array) || empty($array)) | |
1143 | + return false; | |
1144 | + $keys = array_keys($array); | |
1145 | + return array_keys($keys) !== $keys; | |
1146 | + // or | |
1147 | + //return is_array($array) && !is_numeric(implode(array_keys($array))); | |
1148 | + } | |
1149 | +} | |
1150 | + | |
1151 | +if (! function_exists('_')) { | |
1152 | + function &_($str) | |
1153 | + { | |
1154 | + return $str; | |
1155 | + } | |
1156 | +} | |
1157 | + | |
1158 | +/////////////////////////////////// | |
1159 | +function plugin_contentsx_common_init() | |
1160 | +{ | |
1161 | + global $plugin_contentsx; | |
1162 | + if (class_exists('PluginContentsxUnitTest')) { | |
1163 | + $plugin_contentsx = new PluginContentsxUnitTest(); | |
1164 | + } elseif (class_exists('PluginContentsxUser')) { | |
1165 | + $plugin_contentsx = new PluginContentsxUser(); | |
1166 | + } else { | |
1167 | + $plugin_contentsx = new PluginContentsx(); | |
1168 | + } | |
1169 | +} | |
1170 | + | |
1171 | +function plugin_contentsx_convert() | |
1172 | +{ | |
1173 | + global $plugin_contentsx; plugin_contentsx_common_init(); | |
1174 | + $args = func_get_args(); | |
1175 | + return call_user_func_array(array(&$plugin_contentsx, 'convert'), $args); | |
1176 | +} | |
1177 | +function plugin_contentsx_action() | |
1178 | +{ | |
1179 | + global $plugin_contentsx; plugin_contentsx_common_init(); | |
1180 | + return call_user_func(array(&$plugin_contentsx, 'action')); | |
1181 | +} | |
1182 | + | |
1183 | +?> |
@@ -0,0 +1,681 @@ | ||
1 | +<?php | |
2 | +/** | |
3 | + * Page Include Plugin | |
4 | + * | |
5 | + * @author sonots | |
6 | + * @license http://www.gnu.org/licenses/gpl.html GPL v2 | |
7 | + * @link http://lsx.sourceforge.jp/?Plugin%2Fincludex.inc.php | |
8 | + * @version $Id: includex.inc.php,v 1.5 2007-06-05 07:23:17Z sonots $ | |
9 | + * @package plugin | |
10 | + */ | |
11 | + | |
12 | +class PluginIncludex | |
13 | +{ | |
14 | + function PluginIncludex() | |
15 | + { | |
16 | + // Modify here for default values (type, default, choices) | |
17 | + static $default_options = array( | |
18 | + 'num' => array('number', ''), | |
19 | + 'except' => array('string', ''), | |
20 | + 'filter' => array('string', ''), | |
21 | + 'title' => array('enum', 'on', array('on', 'off', 'nolink', 'basename')), // obsolete | |
22 | + 'titlestr' => array('enum', 'title', array('name', 'off', 'basename', 'title', 'relname')), | |
23 | + 'titlelink' => array('bool', true), | |
24 | + 'section' => array('array', array()), | |
25 | + 'permalink' => array('string', false), | |
26 | + 'head' => array('bool', true), | |
27 | + ); | |
28 | + static $default_section_options = array( | |
29 | + 'num' => array('number', ''), | |
30 | + 'depth' => array('number', ''), | |
31 | + 'except' => array('string', ''), | |
32 | + 'filter' => array('string', ''), | |
33 | + 'cache' => array('enum', 'on', array('on', 'off', 'reset')), | |
34 | + 'inclsub' => array('bool', false), // not yet | |
35 | + ); | |
36 | + $this->default_options = &$default_options; | |
37 | + $this->default_section_options = &$default_section_options; | |
38 | + | |
39 | + // init | |
40 | + static $visited = array(); | |
41 | + $this->visited = &$visited; | |
42 | + $this->options = $this->default_options; | |
43 | + $this->section_options = $this->default_section_options; | |
44 | + } | |
45 | + | |
46 | + // static | |
47 | + var $default_options; | |
48 | + var $default_section_options; | |
49 | + var $visited; | |
50 | + // var | |
51 | + var $error = ""; | |
52 | + var $plugin = "includex"; | |
53 | + var $options; | |
54 | + var $section_options; | |
55 | + var $inclpage; | |
56 | + var $lines; | |
57 | + var $headlines; | |
58 | + var $narrowed_headlines; | |
59 | + | |
60 | + function convert() | |
61 | + { | |
62 | + $args = func_get_args(); | |
63 | + $body = $this->body($args); | |
64 | + if ($this->error != "" ) { return "<p>#$this->plugin(): $this->error</p>"; } | |
65 | + return $body; | |
66 | + } | |
67 | + | |
68 | + function body($args) | |
69 | + { | |
70 | + global $vars, $get, $post; | |
71 | + | |
72 | + $this->visited[$vars['page']] = TRUE; | |
73 | + $this->inclpage = array_shift($args); | |
74 | + $this->check_page(); | |
75 | + if ($this->error != "") { return; } | |
76 | + | |
77 | + $parser = new PluginIncludexOptionParser(); | |
78 | + $this->options = $parser->parse_options($args, $this->options); | |
79 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
80 | + | |
81 | + $this->check_options(); | |
82 | + if ($this->error != "") { return; } | |
83 | + | |
84 | + $this->init_lines(); | |
85 | + if ($this->error !== "") { return; } | |
86 | + | |
87 | + $this->narrow_lines(); | |
88 | + if ($this->error !== "") { return; } | |
89 | + | |
90 | + $body = $this->frontend(); | |
91 | + if ($this->error !== "") { return; } | |
92 | + | |
93 | + $this->visited[$this->inclpage] = TRUE; | |
94 | + return $body; | |
95 | + } | |
96 | + | |
97 | + function check_options() | |
98 | + { | |
99 | + // support lower version | |
100 | + if ($this->options['title'][1] != 'on') { | |
101 | + if ($this->options['title'][1] == 'nolink') { | |
102 | + $this->options['titlelink'][1] = false; | |
103 | + $this->options['titlestr'][1] = 'name'; | |
104 | + } else { | |
105 | + $this->options['titlestr'][1] = $this->options['title'][1]; | |
106 | + } | |
107 | + } | |
108 | + if ($this->options['permalink'][1] === '') { | |
109 | + $this->options['permalink'][1] = _('Permalink'); | |
110 | + } | |
111 | + } | |
112 | + | |
113 | + function check_page() | |
114 | + { | |
115 | + global $vars; | |
116 | + | |
117 | + if (empty($this->inclpage)) { | |
118 | + $this->error = "No page is specified."; | |
119 | + return; | |
120 | + } | |
121 | + $current = $vars['page']; | |
122 | + $this->inclpage = get_fullname($this->inclpage, $current); | |
123 | + | |
124 | + if (! $this->is_page($this->inclpage)) { | |
125 | + $this->error = "$this->inclpage does not eixst."; | |
126 | + return; | |
127 | + } | |
128 | + if (! $this->check_readable($this->inclpage, false, false)) { | |
129 | + $this->error = "$this->inclpage is not readable."; | |
130 | + return; | |
131 | + } | |
132 | + if (isset($this->visited[$this->inclpage])) { | |
133 | + $this->error = "$this->inclpage is already included."; | |
134 | + return; | |
135 | + } | |
136 | + } | |
137 | + | |
138 | + function frontend() | |
139 | + { | |
140 | + global $vars, $get, $post; | |
141 | + | |
142 | + $titlestr = PluginIncludex::get_titlestr($this->inclpage, $this->options['titlestr'][1]); | |
143 | + $title = PluginIncludex::get_title($this->inclpage, $titlestr, $this->options['title'][1]); | |
144 | + if ($this->error != "") { return; } | |
145 | + | |
146 | + // because included page would use these variables. | |
147 | + $tmp = $vars['page']; | |
148 | + $get['page'] = $post['page'] = $vars['page'] = $this->inclpage; | |
149 | + if (function_exists('convert_filter')) { | |
150 | + $this->lines = convert_filter($this->lines); // plus | |
151 | + } | |
152 | + $body = convert_html($this->lines); | |
153 | + $get['page'] = $post['page'] = $vars['page'] = $tmp; | |
154 | + if ($this->error != "") { return; } | |
155 | + | |
156 | + $footer = ''; | |
157 | + if ($this->options['permalink'][1] !== false) { | |
158 | + $linkstr = $this->make_inline($this->options['permalink'][1]); | |
159 | + $footer = '<p class="permalink">' . | |
160 | + make_pagelink($this->inclpage, $linkstr) . '</p>'; | |
161 | + } | |
162 | + | |
163 | + return $title . "\n" . $body . $footer; | |
164 | + } | |
165 | + | |
166 | + // static | |
167 | + function get_titlestr($inclpage, $option = null, $current = null) | |
168 | + { | |
169 | + switch ($option) { | |
170 | + case 'off': | |
171 | + $titlestr = ''; | |
172 | + break; | |
173 | + case 'name': | |
174 | + $titlestr = htmlspecialchars($inclpage); | |
175 | + break; | |
176 | + case 'basename': | |
177 | + $titlestr = htmlspecialchars(basename($inclpage)); | |
178 | + break; | |
179 | + case 'relname': | |
180 | + if (! isset($current)) $current = $GLOBALS['vars']['page']; | |
181 | + if (($i = strpos($inclpage, $current . '/')) === 0) { | |
182 | + $titlestr = htmlspecialchars(substr($inclpage, strlen($current)+1)); | |
183 | + } else { | |
184 | + $titlestr = htmlspecialchars($inclpage); | |
185 | + } | |
186 | + break; | |
187 | + case 'on': | |
188 | + case 'title': | |
189 | + default: | |
190 | + if (exist_plugin('contentsx')) { | |
191 | + $contentsx = new PluginContentsx(); | |
192 | + if (method_exists($contentsx, 'get_title')) { | |
193 | + $titlestr = $contentsx->get_title($inclpage); | |
194 | + $titlestr = strip_htmltag(make_link($titlestr)); | |
195 | + } | |
196 | + } | |
197 | + if ($titlestr == '') $titlestr = htmlspecialchars($inclpage); | |
198 | + break; | |
199 | + } | |
200 | + return $titlestr; | |
201 | + } | |
202 | + | |
203 | + // static | |
204 | + function get_title($inclpage, $titlestr, $option = true) | |
205 | + { | |
206 | + global $fixed_heading_edited; | |
207 | + $anchorlink = ' ' . PluginIncludex::get_page_anchorlink($inclpage); | |
208 | + $editlink = $fixed_heading_edited ? ' ' . PluginIncludex::get_page_editlink($inclpage) : ''; | |
209 | + | |
210 | + if ($titlestr == '') { | |
211 | + //return $ret = '<div class="' .$this->plugin . '">' . $anchorlink . '</div>'; | |
212 | + return ''; | |
213 | + } | |
214 | + switch ($option) { | |
215 | + case false: | |
216 | + $ret = '<h1 class="includex">' . $titlestr . $editlink . $anchorlink . '</h1>'; | |
217 | + break; | |
218 | + case true: | |
219 | + default: | |
220 | + $link = make_pagelink($inclpage, $titlestr); | |
221 | + $ret = '<h1 class="includex">' . $link . $editlink . $anchorlink . '</h1>'; | |
222 | + break; | |
223 | + } | |
224 | + return $ret; | |
225 | + } | |
226 | + | |
227 | + function narrow_lines() | |
228 | + { | |
229 | + $parser = new PluginIncludexOptionParser(); | |
230 | + | |
231 | + if (! empty($this->options['section'][1]) && exist_plugin('contentsx')) { | |
232 | + $this->section_lines(); | |
233 | + } | |
234 | + | |
235 | + $this->filter_lines(); | |
236 | + $this->except_lines(); | |
237 | + | |
238 | + $num = sizeof($this->lines); | |
239 | + $this->options['num'][1] = $parser->parse_numoption($this->options['num'][1], 1, $num); | |
240 | + if ($parser->error !== "") { $this->error = $parser->error; return; } | |
241 | + $this->num_filter_lines(); | |
242 | + | |
243 | + $this->cut_head_lines(); | |
244 | + } | |
245 | + | |
246 | + function cut_head_lines() | |
247 | + { | |
248 | + // cut the headline on the first line | |
249 | + if ($this->options['head'][1] === FALSE) { | |
250 | + $def_headline = '/^(\*{1,3})/'; | |
251 | + if (preg_match($def_headline, $this->lines[0])) { | |
252 | + unset($this->lines[0]); | |
253 | + } | |
254 | + } | |
255 | + } | |
256 | + | |
257 | + | |
258 | + function section_lines() | |
259 | + { | |
260 | + if (empty($this->options['section'][1])) { | |
261 | + return; | |
262 | + } | |
263 | + $parser = new PluginIncludexOptionParser(); | |
264 | + $this->section_options = $parser->parse_options($this->options['section'][1], $this->section_options); | |
265 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
266 | + | |
267 | + // what a public class! hehehe | |
268 | + $contentsx = new PluginContentsx(); | |
269 | + $contentsx->options['include'][1] = false; | |
270 | + $contentsx->options['fromhere'][1] = false; | |
271 | + $contentsx->options['page'][1] = $contentsx->check_page($this->inclpage); | |
272 | + $this->headlines = $contentsx->get_metalines($this->inclpage); | |
273 | + if ($contentsx->error != "") { $this->error = $contentsx->error; return; } | |
274 | + foreach ($this->section_options as $key => $val) { | |
275 | + $contentsx->options[$key] = $val; | |
276 | + } | |
277 | + | |
278 | + $contentsx->narrow_metalines(); | |
279 | + if ($contentsx->error != "") { $this->error = $contentsx->error; return; } | |
280 | + $this->narrowed_headlines = $contentsx->metalines; | |
281 | + | |
282 | + $size = sizeof($this->headlines); | |
283 | + $this->section_options['num'][1] = $parser->parse_numoption($this->section_options['num'][1], 0, $size); | |
284 | + $lines = array(); | |
285 | + if (in_array(0, $this->section_options['num'][1])) { | |
286 | + $linenum = $this->headlines[0]['linenum']; | |
287 | + $lines = array_merge($lines, array_splice($this->lines, 0, $linenum)); | |
288 | + } | |
289 | + // FutureWork: Do no rely on contentsx's cache as much as possible. | |
290 | + $i = 0; $size = sizeof($this->headlines); | |
291 | + foreach ($this->narrowed_headlines as $narrowed_headline) { | |
292 | + $linenum = $narrowed_headline['linenum']; | |
293 | + for (; $i < $size; $i++ ) { | |
294 | + $current = $i; | |
295 | + if ($linenum != $this->headlines[$current]['linenum']) { | |
296 | + continue; | |
297 | + } | |
298 | + $next = $i + 1; | |
299 | + if ($next < $size) { | |
300 | + $len = $this->headlines[$next]['linenum'] - $linenum; | |
301 | + $lines = array_merge($lines, array_slice($this->lines, $linenum, $len)); | |
302 | + } else { | |
303 | + $lines = array_merge($lines, array_slice($this->lines, $linenum)); | |
304 | + } | |
305 | + break; | |
306 | + } | |
307 | + } | |
308 | + $this->lines = $lines; | |
309 | + } | |
310 | + | |
311 | + function num_filter_lines() | |
312 | + { | |
313 | + if ($this->options['num'][1] === '') { | |
314 | + return; | |
315 | + } | |
316 | + $lines = array(); | |
317 | + foreach ($this->options['num'][1] as $num) { | |
318 | + $lines[] = $this->lines[$num - 1]; | |
319 | + } | |
320 | + $this->lines = $lines; | |
321 | + } | |
322 | + | |
323 | + function filter_lines() | |
324 | + { | |
325 | + if ($this->options['filter'][1] === "") { | |
326 | + return; | |
327 | + } | |
328 | + $lines = array(); | |
329 | + foreach ($this->lines as $line) { | |
330 | + if (ereg($this->options['filter'][1], $line)) { | |
331 | + $lines[] = $line; | |
332 | + } | |
333 | + } | |
334 | + $this->lines = $lines; | |
335 | + } | |
336 | + | |
337 | + function except_lines() | |
338 | + { | |
339 | + if ($this->options['except'][1] === "") { | |
340 | + return; | |
341 | + } | |
342 | + $lines = array(); | |
343 | + foreach ($this->lines as $line) { | |
344 | + if (! ereg($this->options['except'][1], $line)) { | |
345 | + $lines[] = $line; | |
346 | + } | |
347 | + } | |
348 | + $this->lines = $lines; | |
349 | + } | |
350 | + | |
351 | + function init_lines() | |
352 | + { | |
353 | + $this->lines = $this->get_source($this->inclpage); | |
354 | + } | |
355 | + | |
356 | + // static | |
357 | + function get_page_anchor($page) { | |
358 | + // anchor must be '^[A-Za-z][A-Za-z0-9_-]*' | |
359 | + return 'z' . md5($page); | |
360 | + } | |
361 | + | |
362 | + // PukiWiki API Extension | |
363 | + | |
364 | + // convert inline plugins | |
365 | + // PukiWiki API InlineConverter does htmlspecialchars, too. | |
366 | + // refer plugin/make_link.php#make_link | |
367 | + // static | |
368 | + function make_inline($string, $page = '') | |
369 | + { | |
370 | + global $vars; | |
371 | + static $converter; | |
372 | + | |
373 | + if (! isset($converter)) $converter = new InlineConverter(array('plugin')); | |
374 | + | |
375 | + $clone = $converter->get_clone($converter); | |
376 | + return $clone->convert($string, ($page != '') ? $page : $vars['page']); | |
377 | + } | |
378 | + | |
379 | + // refer plugin/aname.inc.php | |
380 | + // static | |
381 | + function get_page_anchorlink($page) { | |
382 | + global $_symbol_anchor; | |
383 | + global $pkwk_dtd; | |
384 | + | |
385 | + $id = $this->get_page_anchor($page); | |
386 | + $id = htmlspecialchars($id); | |
387 | + | |
388 | + // aname allows only fiexed_anchors such as x83dvkd8 | |
389 | + //if (exist_plugin_inline('aname')) { | |
390 | + //$link = do_plugin_inline('aname', "$anchor,super,$_symbol_anchor"); | |
391 | + //} | |
392 | + | |
393 | + if (isset($pkwk_dtd) && $pkwk_dtd < PKWK_DTD_XHTML_1_1) { | |
394 | + $attr_id = ' id="' . $id . '" name="' . $id . '"'; | |
395 | + } else { | |
396 | + $attr_id = ' id="' . $id . '"'; | |
397 | + } | |
398 | + $attr_href = ' href="#' . $id . '"'; | |
399 | + $attr_title = ' title="' . $id . '"'; | |
400 | + $attr_class = ' class="anchor_super"'; | |
401 | + $link = '<a' . $attr_class . $attr_id . $attr_href . $attr_title . '>' . $_symbol_anchor . '</a>'; | |
402 | + return $link; | |
403 | + } | |
404 | + | |
405 | + // static | |
406 | + function get_page_editlink($page) { | |
407 | + $r_page = rawurlencode($page); | |
408 | + $link = '<a class="anchor_super" href="' . get_script_uri() . '?cmd=edit&page=' . $r_page . '">'; | |
409 | + $link .= '<img class="paraedit" src="' . IMAGE_DIR . 'edit.png" alt="Edit" title="Edit" />'; | |
410 | + $link .= '</a>'; | |
411 | + return $link; | |
412 | + } | |
413 | + | |
414 | + function get_source($page) | |
415 | + { | |
416 | + return get_source($page); | |
417 | + } | |
418 | + | |
419 | + function is_page($page) | |
420 | + { | |
421 | + return is_page($page); | |
422 | + } | |
423 | + | |
424 | + function check_readable($page, $flag, $flag) | |
425 | + { | |
426 | + return check_readable($page, $flag, $flag); | |
427 | + } | |
428 | + | |
429 | +} | |
430 | + | |
431 | +/////////////////////////////////////// | |
432 | +class PluginIncludexOptionParser | |
433 | +{ | |
434 | + var $error = ""; | |
435 | + | |
436 | + function parse_options($args, $options) | |
437 | + { | |
438 | + if (! $this->is_associative_array($args)) { | |
439 | + $args = $this->associative_args($args, $options); | |
440 | + if ($this->error != "") { return; } | |
441 | + } | |
442 | + | |
443 | + foreach ($args as $key => $val) { | |
444 | + if ( !isset($options[$key]) ) { continue; } // for action ($vars) | |
445 | + $type = $options[$key][0]; | |
446 | + | |
447 | + switch ($type) { | |
448 | + case 'bool': | |
449 | + if($val == "" || $val == "on" || $val == "true") { | |
450 | + $options[$key][1] = true; | |
451 | + } elseif ($val == "off" || $val == "false" ) { | |
452 | + $options[$key][1] = false; | |
453 | + } else { | |
454 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
455 | + $this->error .= "The option, $key, accepts only a boolean value."; | |
456 | + $this->error .= "#$this->plugin($key) or #$this->plugin($key=on) or #$this->plugin($key=true) for true. "; | |
457 | + $this->error .= "#$this->plugin($key=off) or #$this->plugin($key=false) for false. "; | |
458 | + return; | |
459 | + } | |
460 | + break; | |
461 | + case 'string': | |
462 | + $options[$key][1] = $val; | |
463 | + break; | |
464 | + case 'sanitize': | |
465 | + $options[$key][1] = htmlspecialchars($val); | |
466 | + break; | |
467 | + case 'number': | |
468 | + // Do not parse yet, parse after getting min and max. Here, just format checking | |
469 | + if ($val === '') { | |
470 | + $options[$key][1] = ''; | |
471 | + break; | |
472 | + } | |
473 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
474 | + $val = substr($val, 1, strlen($val) - 2); | |
475 | + } | |
476 | + foreach (explode(",", $val) as $range) { | |
477 | + if (preg_match('/^-?\d+$/', $range)) { | |
478 | + } elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) { | |
479 | + } elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) { | |
480 | + } else { | |
481 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
482 | + $this->error .= "The option, " . $key . ", accepts number values such as 1, 1:3, 1+3, 1,2,4. "; | |
483 | + $this->error .= "Specify options as \"$key=1,2,4\" or $key=(1,2,3) when you want to use \",\". "; | |
484 | + $this->error .= "In more details, a style like (1:3,5:7,9:) is also possible. 9: means from 9 to the last. "; | |
485 | + $this->error .= "Furtermore, - means backward. -1:-3 means 1,2,3 from the tail. "; | |
486 | + return; | |
487 | + } | |
488 | + } | |
489 | + $options[$key][1] = $val; | |
490 | + break; | |
491 | + case 'enum': | |
492 | + if($val == "") { | |
493 | + $options[$key][1] = $options[$key][2][0]; | |
494 | + } elseif (in_array($val, $options[$key][2])) { | |
495 | + $options[$key][1] = $val; | |
496 | + } else { | |
497 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
498 | + $this->error .= "The option, " . $key . ", accepts values from one of (" . join(",", $options[$key][2]) . "). "; | |
499 | + $this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=" . $options[$key][2][0] . "). "; | |
500 | + return; | |
501 | + } | |
502 | + break; | |
503 | + case 'array': | |
504 | + if ($val == '') { | |
505 | + $options[$key][1] = array(); | |
506 | + break; | |
507 | + } | |
508 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
509 | + $val = substr($val, 1, strlen($val) - 2); | |
510 | + } | |
511 | + $val = explode(',', $val); | |
512 | + //$val = $this->support_paren($val); | |
513 | + $options[$key][1] = $val; | |
514 | + break; | |
515 | + case 'enumarray': | |
516 | + if ($val == '') { | |
517 | + $options[$key][1] = $options[$key][2]; | |
518 | + break; | |
519 | + } | |
520 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
521 | + $val = substr($val, 1, strlen($val) - 2); | |
522 | + } | |
523 | + $val = explode(',', $val); | |
524 | + //$val = $this->support_paren($val); | |
525 | + $options[$key][1] = $val; | |
526 | + foreach ($options[$key][1] as $each) { | |
527 | + if (! in_array($each, $options[$key][2])) { | |
528 | + $this->error = "$key=" . htmlspecialchars(join(",", $options[$key][1])) . " is invalid. "; | |
529 | + $this->error .= "The option, " . $key . ", accepts sets of values from (" . join(",", $options[$key][2]) . "). "; | |
530 | + $this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=(" . join(',',$options[$key][2]) . ")). "; | |
531 | + return; | |
532 | + } | |
533 | + } | |
534 | + break; | |
535 | + default: | |
536 | + } | |
537 | + } | |
538 | + | |
539 | + return $options; | |
540 | + } | |
541 | + | |
542 | + /** | |
543 | + * Handle associative type option arguments as | |
544 | + * ["prefix=Hoge/", "contents=(hoge", "hoge", "hoge)"] => ["prefix"=>"hoge/", "contents"=>"(hoge,hoge,hoge)"] | |
545 | + * This has special supports for parentheses type arguments (number, array, enumarray) | |
546 | + * Check option in along with. | |
547 | + * @access public | |
548 | + * @param Array $args Original option arguments | |
549 | + * @return Array $result Converted associative option arguments | |
550 | + */ | |
551 | + function associative_args($args, $options) | |
552 | + { | |
553 | + $result = array(); | |
554 | + while (($arg = current($args)) !== false) { | |
555 | + list($key, $val) = array_pad(explode("=", $arg, 2), 2, ''); | |
556 | + if (! isset($options[$key])) { | |
557 | + $this->error = 'No such a option, ' . htmlspecialchars($key); | |
558 | + return; | |
559 | + } | |
560 | + // paren support | |
561 | + if ($val[0] === '(' && ($options[$key][0] == 'number' || | |
562 | + $options[$key][0] == 'array' || $options[$key][0] == 'enumarray')) { | |
563 | + while(true) { | |
564 | + if ($val[strlen($val)-1] === ')' && substr_count($val, '(') == substr_count($val, ')')) { | |
565 | + break; | |
566 | + } | |
567 | + $arg = next($args); | |
568 | + if ($arg === false) { | |
569 | + $this->error = "The # of open and close parentheses of one of your arguments did not match. "; | |
570 | + return; | |
571 | + } | |
572 | + $val .= ',' . $arg; | |
573 | + } | |
574 | + } | |
575 | + $result[$key] = $val; | |
576 | + next($args); | |
577 | + } | |
578 | + return $result; | |
579 | + } | |
580 | + | |
581 | + function parse_numoption($optionval, $min, $max) | |
582 | + { | |
583 | + if ($optionval === '') { | |
584 | + return ''; | |
585 | + } | |
586 | + $result = array(); | |
587 | + foreach (explode(",", $optionval) as $range) { | |
588 | + if (preg_match('/^-?\d+$/', $range)) { | |
589 | + $left = $right = $range; | |
590 | + } elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) { | |
591 | + list($left, $right) = explode(":", $range, 2); | |
592 | + if ($left == "" && $right == "") { | |
593 | + $left = $min; | |
594 | + $right = $max; | |
595 | + } elseif($left == "") { | |
596 | + $left = $min; | |
597 | + } elseif ($right == "") { | |
598 | + $right = $max; | |
599 | + } | |
600 | + } elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) { | |
601 | + list($left, $right) = explode("+", $range, 2); | |
602 | + $right += $left; | |
603 | + } | |
604 | + if ($left < 0) { | |
605 | + $left += $max + 1; | |
606 | + } | |
607 | + if ($right < 0) { | |
608 | + $right += $max + 1; | |
609 | + } | |
610 | + $result = array_merge($result, range($left, $right)); | |
611 | + // range allows like range(5, 3) also | |
612 | + } | |
613 | + // filter | |
614 | + foreach (array_keys($result) as $i) { | |
615 | + if ($result[$i] < $min || $result[$i] > $max) { | |
616 | + unset($result[$i]); | |
617 | + } | |
618 | + } | |
619 | + sort($result); | |
620 | + $result = array_unique($result); | |
621 | + | |
622 | + return $result; | |
623 | + } | |
624 | + | |
625 | + function option_debug_print($options) { | |
626 | + foreach ($options as $key => $val) { | |
627 | + $type = $val[0]; | |
628 | + $val = $val[1]; | |
629 | + if(is_array($val)) { | |
630 | + $val=join(',', $val); | |
631 | + } | |
632 | + $body .= "$key=>($type, $val),"; | |
633 | + } | |
634 | + return $body; | |
635 | + } | |
636 | + | |
637 | + // php extension | |
638 | + function is_associative_array($array) | |
639 | + { | |
640 | + if (!is_array($array) || empty($array)) | |
641 | + return false; | |
642 | + $keys = array_keys($array); | |
643 | + return array_keys($keys) !== $keys; | |
644 | + // or | |
645 | + //return is_array($array) && !is_numeric(implode(array_keys($array))); | |
646 | + } | |
647 | +} | |
648 | + | |
649 | +// php extension | |
650 | +if (! function_exists('_')) { | |
651 | + function &_($str) | |
652 | + { | |
653 | + return $str; | |
654 | + } | |
655 | +} | |
656 | + | |
657 | +//////////////////////////////////////////// | |
658 | +function plugin_includex_common_init() | |
659 | +{ | |
660 | + global $plugin_includex; | |
661 | + if (class_exists('PluginIncludexUnitTest')) { | |
662 | + $plugin_includex = new PluginIncludexUnitTest(); | |
663 | + } elseif (class_exists('PluginIncludexUser')) { | |
664 | + $plugin_includex = new PluginIncludexUser(); | |
665 | + } else { | |
666 | + $plugin_includex = new PluginIncludex(); | |
667 | + } | |
668 | +} | |
669 | + | |
670 | +function plugin_includex_convert() | |
671 | +{ | |
672 | + global $plugin_includex; plugin_includex_common_init(); | |
673 | + $args = func_get_args(); | |
674 | + return call_user_func_array(array(&$plugin_includex, 'convert'), $args); | |
675 | +} | |
676 | + | |
677 | +if (! defined('INIT_DIR')) // if not Plus! | |
678 | + if (file_exists(DATA_HOME . 'init/includex.ini.php')) | |
679 | + include_once(DATA_HOME . 'init/includex.ini.php'); | |
680 | + | |
681 | +?> |
@@ -0,0 +1,1279 @@ | ||
1 | +<?php | |
2 | +/** | |
3 | + * Page List (ls) Plugin | |
4 | + * | |
5 | + * @author sonots <http://note.sonots.com> | |
6 | + * @license http://www.gnu.org/licenses/gpl.html GPL | |
7 | + * @link http://lsx.sourceforge.jp/?Plugin%2Flsx.inc.php | |
8 | + * @version $Id: lsx.inc.php,v 1.17 2007-06-16 11:14:46 sonots $ | |
9 | + * @package plugin | |
10 | + */ | |
11 | + | |
12 | +class PluginLsx | |
13 | +{ | |
14 | + function PluginLsx() | |
15 | + { | |
16 | + // Configure external plugins | |
17 | + static $conf = array( | |
18 | + 'plugin_contents' => 'contentsx', | |
19 | + 'plugin_include' => 'includex', | |
20 | + 'plugin_new' => 'new', | |
21 | + 'plugin_tag' => 'tag', | |
22 | + ); | |
23 | + // Modify here for default option values | |
24 | + static $default_options = array( | |
25 | + 'hierarchy' => array('bool', true), | |
26 | + 'non_list' => array('bool', true), | |
27 | + 'reverse' => array('bool', false), | |
28 | + 'basename' => array('bool', false), // obsolete | |
29 | + 'sort' => array('enum', 'name', array('name', 'reading', 'date')), | |
30 | + 'tree' => array('enum', false, array(false, 'leaf', 'dir')), | |
31 | + 'depth' => array('number', ''), | |
32 | + 'num' => array('number', ''), | |
33 | + 'next' => array('bool', false), | |
34 | + 'except' => array('string', ''), | |
35 | + 'filter' => array('string', ''), | |
36 | + 'prefix' => array('string', ''), | |
37 | + 'contents' => array('array', ''), | |
38 | + 'include' => array('array', ''), | |
39 | + 'info' => array('enumarray', array(), array('date', 'new')), | |
40 | + 'date' => array('bool', false), // will be obsolete | |
41 | + 'new' => array('bool', false), | |
42 | + 'tag' => array('string', ''), | |
43 | + 'linkstr' => array('enum', 'relative', array('relative', 'absolute', 'basename', 'title', 'headline')), | |
44 | + 'link' => array('enum', 'page', array('page', 'anchor', 'off')), | |
45 | + 'newpage' => array('enum', false, array('on', 'except')), | |
46 | + 'popular' => array('enum', false, array('total', 'today', 'yesterday', 'recent')), // alpha | |
47 | + ); | |
48 | + $this->conf = &$conf; | |
49 | + $this->default_options = &$default_options; | |
50 | + | |
51 | + // init | |
52 | + $this->options = $this->default_options; | |
53 | + if (function_exists('mb_ereg')) { // extension_loaded('mbstring') | |
54 | + mb_regex_encoding(SOURCE_ENCODING); | |
55 | + $this->ereg = 'mb_ereg'; | |
56 | + } else { | |
57 | + $this->ereg = 'ereg'; | |
58 | + } | |
59 | + } | |
60 | + | |
61 | + // static | |
62 | + var $conf; | |
63 | + var $default_options; | |
64 | + // var | |
65 | + var $options; | |
66 | + var $error = ""; | |
67 | + var $plugin = "lsx"; | |
68 | + var $metapages; | |
69 | + | |
70 | + function convert() | |
71 | + { | |
72 | + $args = func_get_args(); | |
73 | + $body = $this->body($args); | |
74 | + if ($this->error != "") { | |
75 | + $body = "<p>$this->plugin(): $this->error</p>"; | |
76 | + } | |
77 | + return $body; | |
78 | + } | |
79 | + | |
80 | + function action() | |
81 | + { | |
82 | + global $vars; | |
83 | + | |
84 | + $args = $vars; | |
85 | + $body = $this->body($args); | |
86 | + if ($this->error != "") { | |
87 | + $body = "<p>$this->plugin(): $this->error</p>"; | |
88 | + } | |
89 | + if (! isset($body)) $body = '<p>no result.</p>'; | |
90 | + | |
91 | + if ($this->options['tag'][1] != '') { | |
92 | + $msg = htmlspecialchars($this->options['tag'][1]); | |
93 | + } elseif ($this->options['prefix'][1] != '') { | |
94 | + $msg = htmlspecialchars($this->options['prefix'][1]); | |
95 | + } else { | |
96 | + $msg = $this->plugin; | |
97 | + } | |
98 | + return array('msg'=>$msg, 'body'=>$body); | |
99 | + } | |
100 | + | |
101 | + function body($args) | |
102 | + { | |
103 | + $parser = new PluginLsxOptionParser(); | |
104 | + $this->options = $parser->parse_options($args, $this->options); | |
105 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
106 | + | |
107 | + $this->validate_options(); | |
108 | + if ($this->error !== "") { return $this->error; } | |
109 | + | |
110 | + $this->init_metapages(); | |
111 | + if ($this->error !== "") { return $this->error; } | |
112 | + $this->prefix_filter_metapages(); | |
113 | + if ($this->error !== "") { return $this->error; } | |
114 | + $this->nonlist_filter_metapages(); | |
115 | + if ($this->error !== "") { return $this->error; } | |
116 | + $this->relative_metapages(); // before filter, except | |
117 | + if ($this->error !== "") { return $this->error; } | |
118 | + $this->filter_filter_metapages(); | |
119 | + if ($this->error !== "") { return $this->error; } | |
120 | + $this->except_filter_metapages(); | |
121 | + if ($this->error !== "") { return $this->error; } | |
122 | + | |
123 | + $this->newpage_filter_metapages(); | |
124 | + if ($this->error !== "") { return $this->error; } | |
125 | + | |
126 | + $parser = new PluginLsxOptionParser(); | |
127 | + $this->maxdepth = $this->depth_metapages(); | |
128 | + $this->options['depth'][1] = $parser->parse_numoption($this->options['depth'][1], 1, $this->maxdepth); | |
129 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
130 | + $this->depth_filter_metapages(); | |
131 | + if ($this->error !== "") { return $this->error; } | |
132 | + | |
133 | + $this->tree_filter_metapages(); | |
134 | + if ($this->error !== "") { return $this->error; } | |
135 | + $this->popular_metapages(); // before sort | |
136 | + if ($this->error !== "") { return $this->error; } | |
137 | + $this->timestamp_metapages(); // before sort | |
138 | + if ($this->error !== "") { return $this->error; } | |
139 | + $this->sort_metapages(); // before num_filter | |
140 | + if ($this->error !== "") { return $this->error; } | |
141 | + | |
142 | + $this->maxnum = sizeof($this->metapages); // after all filters | |
143 | + $this->options['num'][1] = $parser->parse_numoption($this->options['num'][1], 1, $this->maxnum); | |
144 | + if ($parser->error != "") { $this->error = $parser->error; return; } | |
145 | + $this->num_filter_metapages(); | |
146 | + if ($this->error !== "") { return $this->error; } | |
147 | + | |
148 | + $this->hierarchy_metapages(); | |
149 | + if ($this->error !== "") { return $this->error; } | |
150 | + | |
151 | + $this->info_metapages(); | |
152 | + if ($this->error !== "") { return $this->error; } | |
153 | + $this->linkstr_metapages(); | |
154 | + if ($this->error !== "") { return $this->error; } | |
155 | + $this->link_metapages(); | |
156 | + if ($this->error !== "") { return $this->error; } | |
157 | + | |
158 | + $body = $this->list_pages(); | |
159 | + $body .= $this->next_pages(); | |
160 | + | |
161 | + return $body; | |
162 | + } | |
163 | + | |
164 | + function validate_options() | |
165 | + { | |
166 | + global $vars; | |
167 | + if ($this->options['tag'][1] != '') { | |
168 | + if(! exist_plugin($this->conf['plugin_tag'])) { | |
169 | + $this->error .= "The option, tag, requires #{$this->conf['plugin_tag']} plugin, but it does not exist. "; | |
170 | + return; | |
171 | + } | |
172 | + $this->options['hierarchy'][1] = false; | |
173 | + // best is to turn off the default only so that 'hierarchy' can be configured by option. | |
174 | + } else { | |
175 | + if ($this->options['prefix'][1] == '') { | |
176 | + $this->options['prefix'][1] = $vars['page'] != '' ? $vars['page'] . '/' : ''; | |
177 | + } | |
178 | + } | |
179 | + if ($this->options['prefix'][1] == '/') { | |
180 | + $this->options['prefix'][1] = ''; | |
181 | + } elseif ($this->options['prefix'][1] != '') { | |
182 | + $this->options['prefix'][1] = $this->get_fullname($this->options['prefix'][1], $vars['page']); | |
183 | + } | |
184 | + $this->options['prefix'][4] = $this->options['prefix'][1]; | |
185 | + | |
186 | + if ($this->options['sort'][1] == 'date') { | |
187 | + $this->options['hierarchy'][1] = false; | |
188 | + } | |
189 | + | |
190 | + // alpha func | |
191 | + if ($this->options['popular'][1] != false) { | |
192 | + $this->options['sort'][1] = 'popular'; | |
193 | + $this->options['hierarchy'][1] = false; | |
194 | + // Future Work: info_popular. hmmm | |
195 | + } | |
196 | + // Another Idea | |
197 | + // sort=popular>today,popular>total,popular>yesterday,popular>recent | |
198 | + // if (strpos($this->options['sort'][1], 'popular>') !== false) { | |
199 | + // list($this->optiions['sort'][1], $this->options['popular'][1]) = explode('>', $this->options['sort'][1]); | |
200 | + // $this->options['hierarchy'][1] = false; | |
201 | + // } | |
202 | + | |
203 | + if ($this->options['contents'][1] != '') { | |
204 | + if(! exist_plugin_convert($this->conf['plugin_contents'])) { | |
205 | + $this->error .= "The option, contents, requires {$this->conf['plugin_contents']} plugin, but it does not exist. "; | |
206 | + return; | |
207 | + } | |
208 | + } | |
209 | + | |
210 | + if ($this->options['include'][1] != '') { | |
211 | + if(! exist_plugin_convert($this->conf['plugin_include'])) { | |
212 | + $this->error .= "The option, include, requires {$this->conf['plugin_include']} plugin, but it does not exist. "; | |
213 | + return; | |
214 | + } | |
215 | + $this->options['hierarchy'][1] = false; // hierarchy + include => XHTML invalid | |
216 | + $this->options['date'][1] = false; // include does not use definitely | |
217 | + $this->options['new'][1] = false; // include does not use definitely | |
218 | + $this->options['contents'][1] = ''; // include does not use definitely | |
219 | + } | |
220 | + | |
221 | + if ($this->options['linkstr'][1] === 'title' || $this->options['linkstr'][1] === 'headline') { | |
222 | + if(! exist_plugin_convert($this->conf['plugin_contents'])) { | |
223 | + $this->error .= "The option, linkstr, requires {$this->conf['plugin_contents']} plugin, but it does not exist. "; | |
224 | + return; | |
225 | + } | |
226 | + } | |
227 | + | |
228 | + // to support lower versions | |
229 | + // basename -> linkstr | |
230 | + if ($this->options['basename'][1] === true) { | |
231 | + $this->options['linkstr'][1] = 'basename'; | |
232 | + } | |
233 | + | |
234 | + // new,date -> info | |
235 | + foreach ($this->options['info'][2] as $key) { | |
236 | + if ($this->options[$key][1]) { | |
237 | + array_push($this->options['info'][1], $key); | |
238 | + } | |
239 | + } | |
240 | + $this->options['info'][1] = array_unique($this->options['info'][1]); | |
241 | + // to save time (to avoid in_array everytime) | |
242 | + foreach ($this->options['info'][1] as $key) { | |
243 | + $this->options[$key][1] = true; | |
244 | + } | |
245 | + if ($this->options['new'][1] && ! exist_plugin_inline($this->conf['plugin_new'])) { | |
246 | + $this->error .= "The option, new, requires {$this->conf['plugin_new']} plugin, but it does not exist. "; | |
247 | + return; | |
248 | + } | |
249 | + } | |
250 | + | |
251 | + function next_pages() | |
252 | + { | |
253 | + if (! $this->options['next'][1] || $this->options['num'][1] == '') return; | |
254 | + | |
255 | + $options = $this->options; | |
256 | + unset($options['num']); | |
257 | + $href = get_script_uri() . '?' . 'cmd=lsx'; | |
258 | + foreach ($options as $key => $val) { | |
259 | + if (isset($val[4])) { | |
260 | + $href .= '&' . htmlspecialchars($key) . '=' . htmlspecialchars(rawurlencode($val[4])); | |
261 | + } | |
262 | + } | |
263 | + $count = count($this->options['num'][1]); | |
264 | + $min = reset($this->options['num'][1]); | |
265 | + $max = end($this->options['num'][1]); | |
266 | + $maxnum = $this->maxnum; | |
267 | + $prevmin = max($min - $count, 0); | |
268 | + $prevmax = min($min - 1, $maxnum); | |
269 | + $prevlink = ''; | |
270 | + if ($prevmax > 0) { | |
271 | + $prevhref = $href . '&num=' . $prevmin . ':' . $prevmax; | |
272 | + $prevlink = '<span class="prev" style="float:left;"><a href="' . $prevhref . '">' . _('Prev ') . $count . '</a></span>'; | |
273 | + } | |
274 | + $nextmin = max($max + 1, 0); | |
275 | + $nextmax = min($max + $count, $maxnum); | |
276 | + $nextlink = ''; | |
277 | + if ($nextmin < $maxnum) { | |
278 | + $nexthref = $href . '&num=' . $nextmin . ':' . $nextmax; | |
279 | + $nextlink = '<span class="next" style="float:right;"><a href="' . $nexthref . '">' . _('Next ') . $count . '</a></span>'; | |
280 | + } | |
281 | + $ret = ''; | |
282 | + $ret .= '<div class="lsx">' . $prevlink . $nextlink . '</div><div style="clear:both;"></div>'; | |
283 | + return $ret; | |
284 | + } | |
285 | + | |
286 | + function list_pages() | |
287 | + { | |
288 | + global $script; | |
289 | + | |
290 | + if (sizeof($this->metapages) == 0) { | |
291 | + return; | |
292 | + } | |
293 | + | |
294 | + /* HTML validate (without <ul><li style="list-type:none"><ul><li>, we have to do as | |
295 | + <ul><li style="padding-left:16*2px;margin-left:16*2px"> as pukiwiki standard. I did not like it) | |
296 | + | |
297 | + <ul> <ul><li>1 | |
298 | + <li>1</li> </li><li>1 | |
299 | + <li>1 <ul><li>2 | |
300 | + <ul> </li></ul></li><li>1 | |
301 | + <li>2</li> </li><li>1 | |
302 | + </ul> => <ul><li style="list-type:none"><ul><li>3 | |
303 | + </li> </li></ul></li></ul></li></ul> | |
304 | + <li>1</li> | |
305 | + <li>1</li> | |
306 | + <ul><li style="list-type:none"><ul> | |
307 | + <li>3</li> | |
308 | + </ul></li></ul> | |
309 | + </li> | |
310 | + </ul> | |
311 | + */ | |
312 | + $ul = $pdepth = 0; | |
313 | + foreach ($this->metapages as $i => $metapage) { | |
314 | + $page = $metapage['page']; | |
315 | + $exist = $metapage['exist']; | |
316 | + $depth = $metapage['listdepth']; | |
317 | + $info = $metapage['info']; | |
318 | + $link = $metapage['link']; | |
319 | + if ($exist && $this->options['include'][1] != '') { | |
320 | + $option = '"' . $page . '"'; | |
321 | + if (! empty($this->options['include'][1])) { | |
322 | + $option .= ',' . csv_implode(',', $this->options['include'][1]); | |
323 | + } | |
324 | + $html .= do_plugin_convert($this->conf['plugin_include'], $option); | |
325 | + continue; | |
326 | + } | |
327 | + if ($depth > $pdepth) { | |
328 | + $diff = $depth - $pdepth; | |
329 | + $html .= str_repeat('<ul><li style="list-style:none">', $diff - 1); | |
330 | + if ($depth == 1) { // or $first flag | |
331 | + $html .= '<ul class="' . $this->plugin . '"><li>'; | |
332 | + } else { | |
333 | + $html .= '<ul><li>'; | |
334 | + } | |
335 | + $ul += $diff; | |
336 | + } elseif ($depth == $pdepth) { | |
337 | + $html .= '</li><li>'; | |
338 | + } elseif ($depth < $pdepth) { | |
339 | + $diff = $pdepth - $depth; | |
340 | + $html .= str_repeat('</li></ul>', $diff); | |
341 | + $html .= '</li><li>'; | |
342 | + $ul -= $diff; | |
343 | + } | |
344 | + $pdepth = $depth; | |
345 | + | |
346 | + $html .= $link; | |
347 | + if (isset($info) && $info != '') { | |
348 | + $html .= '<span class="lsx_info">' . $info . '</span>' . "\n"; | |
349 | + } | |
350 | + | |
351 | + if ($exist && $this->options['contents'][1] != '') { | |
352 | + $args = $this->options['contents'][1]; | |
353 | + $pagearg = 'page=' . $page ; | |
354 | + array_unshift($args, $pagearg); | |
355 | + $contentsx = new PluginContentsx(); | |
356 | + $html .= call_user_func(array($contentsx, 'body'), $args); | |
357 | + } | |
358 | + } | |
359 | + $html .= str_repeat('</li></ul>', $ul); | |
360 | + return $html; | |
361 | + } | |
362 | + | |
363 | + function link_metapages() | |
364 | + { | |
365 | + switch ($this->options['link'][1]) { | |
366 | + case 'page': | |
367 | + foreach ($this->metapages as $i => $metapage) { | |
368 | + if ($metapage['exist']) { | |
369 | + $this->metapages[$i]['link'] = | |
370 | + $this->make_pagelink($metapage['page'], $metapage['linkstr']); | |
371 | + } else { | |
372 | + $this->metapages[$i]['link'] = $metapage['linkstr']; | |
373 | + } | |
374 | + } | |
375 | + break; | |
376 | + case 'anchor': | |
377 | + foreach ($this->metapages as $i => $metapage) { | |
378 | + // PluginIncludex::get_page_anchor($metapage['page']) | |
379 | + $anchor = 'z' . md5($metapage['page']); | |
380 | + $anchor = '#' . htmlspecialchars($anchor); | |
381 | + if ($metapage['exist']) { | |
382 | + $this->metapages[$i]['link'] = | |
383 | + $this->make_pagelink('', $metapage['linkstr'], $anchor); | |
384 | + } else { | |
385 | + $this->metapages[$i]['link'] = $metapage['linkstr']; | |
386 | + } | |
387 | + } | |
388 | + break; | |
389 | + case 'off': | |
390 | + foreach ($this->metapages as $i => $metapage) { | |
391 | + $this->metapages[$i]['link'] = $metapage['linkstr']; | |
392 | + } | |
393 | + break; | |
394 | + } | |
395 | + } | |
396 | + | |
397 | + function linkstr_metapages() | |
398 | + { | |
399 | + switch ($this->options['linkstr'][1]) { | |
400 | + case 'absolute': | |
401 | + foreach ($this->metapages as $i => $metapage) { | |
402 | + $this->metapages[$i]['linkstr'] = | |
403 | + htmlspecialchars($metapage['page']); | |
404 | + } | |
405 | + break; | |
406 | + case 'basename': | |
407 | + foreach ($this->metapages as $i => $metapage) { | |
408 | + $this->metapages[$i]['linkstr'] = | |
409 | + htmlspecialchars($this->my_basename($metapage['page'])); | |
410 | + } | |
411 | + break; | |
412 | + case 'title': | |
413 | + $contentsx = new PluginContentsx(); | |
414 | + foreach ($this->metapages as $i => $metapage) { | |
415 | + $title = $contentsx->get_title($metapage['page']); | |
416 | + $title = strip_htmltag(make_link($title)); | |
417 | + $this->metapages[$i]['linkstr'] = $title; | |
418 | + } | |
419 | + break; | |
420 | + case 'headline': | |
421 | + $contentsx = new PluginContentsx(); | |
422 | + foreach ($this->metapages as $i => $metapage) { | |
423 | + $metalines = $contentsx->get_metalines($metapage['page']); | |
424 | + $title = $metalines[0]['headline']; | |
425 | + $title = strip_htmltag(make_link($title)); | |
426 | + $this->metapages[$i]['linkstr'] = $title; | |
427 | + } | |
428 | + break; | |
429 | + } | |
430 | + // default: relative | |
431 | + if ($this->options['hierarchy'][1] === true) { | |
432 | + foreach ($this->metapages as $i => $metapage) { | |
433 | + if (! isset($metapage['linkstr']) || $metapage['linkstr'] === '') { | |
434 | + $this->metapages[$i]['linkstr'] = | |
435 | + htmlspecialchars($this->my_basename($metapage['page'])); | |
436 | + } | |
437 | + } | |
438 | + } else { | |
439 | + foreach ($this->metapages as $i => $metapage) { | |
440 | + if (! isset($metapage['linkstr']) || $metapage['linkstr'] === '') { | |
441 | + $this->metapages[$i]['linkstr'] = | |
442 | + htmlspecialchars($metapage['relative']); | |
443 | + } | |
444 | + } | |
445 | + } | |
446 | + } | |
447 | + | |
448 | + function popular_metapages() | |
449 | + { | |
450 | + if ($this->options['popular'][1] === false) { | |
451 | + return; | |
452 | + } | |
453 | + | |
454 | + if (function_exists('set_timezone')) { // plus | |
455 | + list($zone, $zonetime) = set_timezone(DEFAULT_LANG); | |
456 | + $localtime = UTIME + $zonetime; | |
457 | + $today = gmdate('Y/m/d', $localtime); | |
458 | + $yesterday = gmdate('Y/m/d',gmmktime(0,0,0, gmdate('m',$localtime), gmdate('d',$localtime)-1, gmdate('Y',$localtime))); | |
459 | + } else { | |
460 | + $localtime = ZONETIME + UTIME; | |
461 | + $today = get_date('Y/m/d'); // == get_date('Y/m/d', UTIME) == date('Y/m/d, ZONETIME + UTIME); | |
462 | + $yesterday = get_date('Y/m/d', mktime(0,0,0, date('m',$localtime), date('d',$localtime)-1, date('Y',$localtime))); | |
463 | + } | |
464 | + | |
465 | + foreach ($this->metapages as $i => $metapage) { | |
466 | + $page = $metapage['page']; | |
467 | + $lines = file(COUNTER_DIR . encode($page) . '.count'); | |
468 | + $lines = array_map('rtrim', $lines); | |
469 | + list($total_count, $date, $today_count, $yesterday_count, $ip) = $lines; | |
470 | + | |
471 | + $popular = 0; | |
472 | + switch ($this->options['popular'][1]) { | |
473 | + case 'total': | |
474 | + $popular = $total_count; | |
475 | + break; | |
476 | + case 'today': | |
477 | + if ($date == $today) { | |
478 | + $popular = $today_count; | |
479 | + } | |
480 | + break; | |
481 | + case 'yesterday': | |
482 | + if ($date == $today) { | |
483 | + $popular = $yesterday_count; | |
484 | + } elseif ($date == $yesterday) { | |
485 | + $popular = $today_count; | |
486 | + } | |
487 | + break; | |
488 | + case 'recent': | |
489 | + if ($date == $today) { | |
490 | + $popular = $today_count + $yesterday_count; | |
491 | + } elseif ($date == $yesterday) { | |
492 | + $popular = $today_count; | |
493 | + } | |
494 | + break; | |
495 | + } | |
496 | + if ($popular > 0) { | |
497 | + $this->metapages[$i]['popular'] = $popular; | |
498 | + } else { | |
499 | + unset($this->metapages[$i]); // like popular plugin | |
500 | + } | |
501 | + } | |
502 | + } | |
503 | + | |
504 | + function timestamp_metapages() | |
505 | + { | |
506 | + if (! $this->options['date'][1] && ! $this->options['new'][1] && | |
507 | + $this->options['sort'][1] !== 'date') { | |
508 | + return; | |
509 | + } | |
510 | + foreach ($this->metapages as $i => $metapage) { | |
511 | + $page = $metapage['page']; | |
512 | + $timestamp = $this->get_filetime($page); | |
513 | + $this->metapages[$i]['timestamp'] = $timestamp; | |
514 | + } | |
515 | + } | |
516 | + | |
517 | + function date_metapages() | |
518 | + { | |
519 | + if (! $this->options['date'][1] && ! $this->options['new'][1]) { | |
520 | + return; | |
521 | + } | |
522 | + foreach ($this->metapages as $i => $metapage) { | |
523 | + $timestamp = $metapage['timestamp']; | |
524 | + $date = format_date($timestamp); | |
525 | + $this->metapages[$i]['date'] = $date; | |
526 | + } | |
527 | + } | |
528 | + | |
529 | + function info_date_metapages() | |
530 | + { | |
531 | + if (! $this->options['date'][1]) { | |
532 | + return; | |
533 | + } | |
534 | + foreach ($this->metapages as $i => $metapage) { | |
535 | + $this->metapages[$i]['info_date'] = | |
536 | + '<span class="comment_date">' . $metapage['date'] . '</span>'; | |
537 | + } | |
538 | + } | |
539 | + | |
540 | + function info_new_metapages() | |
541 | + { | |
542 | + if (! $this->options['new'][1]) { | |
543 | + return; | |
544 | + } | |
545 | + foreach ($this->metapages as $i => $metapage) { | |
546 | + $date = $this->metapages[$i]['date']; | |
547 | + // burdonsome, but to use configuration of new plugin | |
548 | + $new = do_plugin_inline($this->conf['plugin_new'], 'nodate', $date); | |
549 | + $this->metapages[$i]['info_new'] = $new; | |
550 | + } | |
551 | + } | |
552 | + | |
553 | + function info_metapages() | |
554 | + { | |
555 | + if (empty($this->options['info'][1])) { | |
556 | + return; | |
557 | + } | |
558 | + | |
559 | + $this->date_metapages(); | |
560 | + $this->info_date_metapages(); | |
561 | + $this->info_new_metapages(); | |
562 | + | |
563 | + //foreach ($this->options['info'][2] as $key) { | |
564 | + // call_user_func(array($this, $key . '_metapages')); | |
565 | + //} | |
566 | + foreach ($this->metapages as $i => $metapage) { | |
567 | + $info = ''; | |
568 | + foreach ($this->options['info'][1] as $key) { | |
569 | + $info .= ' ' . $metapage['info_' . $key]; | |
570 | + } | |
571 | + $this->metapages[$i]['info'] = $info; | |
572 | + } | |
573 | + } | |
574 | + | |
575 | + function tree_filter_metapages() | |
576 | + { | |
577 | + if ($this->options['tree'][1] === false) { | |
578 | + return; | |
579 | + } | |
580 | + $allpages = get_existpages(); | |
581 | + $this->sort_pages($allpages); | |
582 | + $current = current($allpages); | |
583 | + while ($next = next($allpages)) { | |
584 | + if (strpos($next, $current . '/') === FALSE) { | |
585 | + $leafs[$current] = TRUE; | |
586 | + } else { | |
587 | + $leafs[$current] = FALSE; | |
588 | + } | |
589 | + $current = $next; | |
590 | + } | |
591 | + $leafs[$current] = TRUE; | |
592 | + | |
593 | + switch ($this->options['tree'][1]) { | |
594 | + case 'dir': | |
595 | + foreach ($this->metapages as $i => $metapage) { | |
596 | + $page = $metapage['page']; | |
597 | + if ($leafs[$page]) { | |
598 | + unset($this->metapages[$i]); | |
599 | + } | |
600 | + } | |
601 | + break; | |
602 | + case 'leaf': | |
603 | + foreach ($this->metapages as $i => $metapage) { | |
604 | + $page = $metapage['page']; | |
605 | + if (! $leafs[$page]) { | |
606 | + unset($this->metapages[$i]); | |
607 | + } | |
608 | + } | |
609 | + break; | |
610 | + } | |
611 | + } | |
612 | + | |
613 | + function hierarchy_metapages() | |
614 | + { | |
615 | + if ($this->options['hierarchy'][1] === false) { | |
616 | + return; | |
617 | + } | |
618 | + $pdepth = substr_count($this->options['prefix'][1], '/') - 1; | |
619 | + $pdir = $this->my_dirname($this->options['prefix'][1]); | |
620 | + $pdirlen = ($pdir == '') ? 0 : strlen($pdir) + 1; // Add '/' | |
621 | + $num = count($this->metapages); | |
622 | + foreach ($this->metapages as $i => $metapage) { | |
623 | + $page = $metapage['page']; | |
624 | + $depth = $metapage['depth']; // depth_metapages() | |
625 | + if ($this->options['hierarchy'][1] === true) { | |
626 | + $this->metapages[$i]['listdepth'] = $depth; | |
627 | + } | |
628 | + while ($depth > 1) { | |
629 | + $page = $this->my_dirname($page); | |
630 | + if ($page == '') break; | |
631 | + $depth = substr_count($page, '/') - $pdepth; | |
632 | + | |
633 | + // if parent dir does not exist, complement | |
634 | + if (($j = $this->array_search_by($page, $this->metapages, 'page')) === false) { | |
635 | + if ($this->options['hierarchy'][1] === true) { | |
636 | + $relative = substr($page, $pdirlen); | |
637 | + $listdepth = $depth; | |
638 | + $this->metapages[] = array('reading'=>$page,'page'=>$page, 'relative'=>$relative, 'exist'=>false, 'depth'=>$depth, 'listdepth'=>$listdepth, 'timestamp'=>1, 'date'=>'', 'leaf'=>false); | |
639 | + // PHP: new item is ignored on this loop | |
640 | + } | |
641 | + } | |
642 | + } | |
643 | + } | |
644 | + if (count($this->metapages) != $num) { | |
645 | + $this->sort_metapages(); | |
646 | + } | |
647 | + } | |
648 | + | |
649 | + function sort_metapages($sort = 'natcasesort', $sortflag = SORT_REGULAR) | |
650 | + { | |
651 | + switch ($this->options['sort'][1]) { | |
652 | + case 'name': | |
653 | + $this->sort_by($this->metapages, 'page', 'sort', SORT_STRING); | |
654 | + break; | |
655 | + case 'date': | |
656 | + $this->sort_by($this->metapages, 'timestamp', 'rsort', SORT_NUMERIC); | |
657 | + break; | |
658 | + case 'reading': | |
659 | + $this->sort_by($this->metapages, 'reading', 'sort', SORT_STRING); | |
660 | + break; | |
661 | + case 'popular': | |
662 | + $this->sort_by($this->metapages, 'popular', 'rsort', SORT_NUMERIC); | |
663 | + break; | |
664 | + default: | |
665 | + $this->sort_by($this->metapages, $this->options['sort'][1], $sort, $sortflag); | |
666 | + break; | |
667 | + } | |
668 | + | |
669 | + if ($this->options['reverse'][1]) { | |
670 | + $this->metapages = array_reverse($this->metapages); | |
671 | + } | |
672 | + } | |
673 | + | |
674 | + function depth_metapages() | |
675 | + { | |
676 | + if ($this->options['depth'][1] === '' && $this->options['hierarchy'][1] === false && | |
677 | + $this->options['tree'][1] === false ) { | |
678 | + return; | |
679 | + } | |
680 | + $pdepth = substr_count($this->options['prefix'][1], '/') - 1; | |
681 | + | |
682 | + foreach ($this->metapages as $i => $metapage) { | |
683 | + $page = $metapage['page']; | |
684 | + $depth = substr_count($page, '/'); | |
685 | + $this->metapages[$i]['depth'] = $depth - $pdepth; | |
686 | + } | |
687 | + | |
688 | + return $this->max_by($this->metapages, 'depth'); | |
689 | + } | |
690 | + | |
691 | + function relative_metapages() | |
692 | + { | |
693 | + $pdir = $this->my_dirname($this->options['prefix'][1]); | |
694 | + if ($pdir == '') { | |
695 | + foreach ($this->metapages as $i => $metapage) { | |
696 | + $this->metapages[$i]['relative'] = $metapage['page']; | |
697 | + } | |
698 | + } else { | |
699 | + $pdirlen = strlen($pdir) + 1; // Add strlen('/') | |
700 | + foreach ($this->metapages as $i => $metapage) { | |
701 | + $this->metapages[$i]['relative'] = substr($metapage['page'], $pdirlen); | |
702 | + } | |
703 | + } | |
704 | + } | |
705 | + | |
706 | + function init_metapages() | |
707 | + { | |
708 | + if ($this->options['sort'][1] === 'reading') { | |
709 | + // Beta Function | |
710 | + if ($this->options['tag'][1] == '') { | |
711 | + $readings = $this->get_readings(); | |
712 | + } else { | |
713 | + $plugin_tag = new PluginTag(); | |
714 | + $pages = $plugin_tag->get_taggedpages($this->options['tag'][1]); | |
715 | + if ($pages === FALSE) { | |
716 | + $this->error = 'The tag token, ' . htmlspecialchars($this->options['tag'][1]) . ', is invalid. '; | |
717 | + $this->error .= 'Perhaps, the tag does not exist. '; | |
718 | + } | |
719 | + $readings = $this->get_readings(); // why can not set pages... | |
720 | + foreach ($pages as $page) | |
721 | + $tagged_readings[$page] = ''; | |
722 | + // array_intersect_key >= PHP 5.1.0 RC1 | |
723 | + // $readings = array_intersect_key($readings, $tagged_readings); | |
724 | + foreach ($readings as $page => $reading) { | |
725 | + if (! isset($tagged_readings[$page])) unset($readings[$page]); | |
726 | + } | |
727 | + } | |
728 | + $metapages = array(); | |
729 | + foreach ($readings as $page => $reading) { | |
730 | + unset($readings[$page]); | |
731 | + $metapages[] = array('reading'=>$reading,'page'=>$page, 'exist'=>true, 'depth'=>1, 'listdepth'=>1, 'timestamp'=>1, 'date'=>''); | |
732 | + } | |
733 | + $this->metapages = $metapages; | |
734 | + } else { | |
735 | + if ($this->options['tag'][1] == '') { | |
736 | + $pages = get_existpages(); | |
737 | + } else { | |
738 | + $plugin_tag = new PluginTag(); | |
739 | + $pages = $plugin_tag->get_taggedpages($this->options['tag'][1]); | |
740 | + if ($pages === FALSE) { | |
741 | + $this->error = 'The tag token, ' . htmlspecialchars($this->options['tag'][1]) . ', is invalid. '; | |
742 | + $this->error .= 'Perhaps, the tag does not exist. '; | |
743 | + } | |
744 | + } | |
745 | + $metapages = array(); | |
746 | + foreach ($pages as $i => $page) { | |
747 | + unset($pages[$i]); | |
748 | + $metapages[] = array('page'=>$page, 'exist'=>true, 'depth'=>1, 'listdepth'=>1, 'timestamp'=>1, 'date'=>''); | |
749 | + } | |
750 | + $this->metapages = $metapages; | |
751 | + } | |
752 | + } | |
753 | + | |
754 | + function depth_filter_metapages() | |
755 | + { | |
756 | + if ($this->options['depth'][1] === '') { | |
757 | + return; | |
758 | + } | |
759 | + $metapages = array(); | |
760 | + foreach ($this->metapages as $i => $metapage) { | |
761 | + unset($this->metapages[$i]); | |
762 | + if (in_array($metapage['depth'], $this->options['depth'][1])) { | |
763 | + $metapages[] = $metapage; | |
764 | + } | |
765 | + } | |
766 | + $this->metapages = $metapages; | |
767 | + } | |
768 | + | |
769 | + // sort before this ($this->sort_by) | |
770 | + function num_filter_metapages() | |
771 | + { | |
772 | + if ($this->options['num'][1] === '') { | |
773 | + return; | |
774 | + } | |
775 | + $metapages = array(); | |
776 | + // $num < count($this->metapages) is assured. | |
777 | + foreach ($this->options['num'][1] as $num) { | |
778 | + $metapages[] = $this->metapages[$num - 1]; | |
779 | + } | |
780 | + $this->metapages = $metapages; | |
781 | + } | |
782 | + | |
783 | + function newpage_filter_metapages() | |
784 | + { | |
785 | + if ($this->options['newpage'][1] === false) { | |
786 | + return; | |
787 | + } | |
788 | + if ($this->options['newpage'][1] == 'on') { | |
789 | + $new = true; | |
790 | + } elseif ($this->options['newpage'][1] == 'except') { | |
791 | + $new = false; | |
792 | + } | |
793 | + $metapages = array(); | |
794 | + foreach ($this->metapages as $i => $metapage) { | |
795 | + unset($this->metapages[$i]); | |
796 | + if ($new == $this->is_newpage($metapage['page'])) { | |
797 | + $metapages[] = $metapage; | |
798 | + } | |
799 | + } | |
800 | + $this->metapages = $metapages; | |
801 | + } | |
802 | + | |
803 | + function prefix_filter_metapages() | |
804 | + { | |
805 | + if ($this->options['prefix'][1] === "") { | |
806 | + return; | |
807 | + } | |
808 | + $metapages = array(); | |
809 | + foreach ($this->metapages as $i => $metapage) { | |
810 | + unset($this->metapages[$i]); | |
811 | + if (strpos($metapage['page'], $this->options['prefix'][1]) !== 0) { | |
812 | + continue; | |
813 | + } | |
814 | + $metapages[] = $metapage; | |
815 | + } | |
816 | + $this->metapages = $metapages; | |
817 | + } | |
818 | + | |
819 | + function nonlist_filter_metapages() | |
820 | + { | |
821 | + if ($this->options['non_list'][1] === false) { | |
822 | + return; | |
823 | + } | |
824 | + global $non_list; | |
825 | + $metapages = array(); | |
826 | + foreach ($this->metapages as $i => $metapage) { | |
827 | + unset($this->metapages[$i]); | |
828 | + if (preg_match("/$non_list/", $metapage['page'])) { | |
829 | + continue; | |
830 | + } | |
831 | + $metapages[] = $metapage; | |
832 | + } | |
833 | + $this->metapages = $metapages; | |
834 | + } | |
835 | + | |
836 | + function except_filter_metapages() | |
837 | + { | |
838 | + if ($this->options['except'][1] === "") { | |
839 | + return; | |
840 | + } | |
841 | + $metapages = array(); | |
842 | + foreach ($this->metapages as $i => $metapage) { | |
843 | + unset($this->metapages[$i]); | |
844 | + if (call_user_func($this->ereg, $this->options['except'][1], $metapage['relative'])) { | |
845 | + continue; | |
846 | + } | |
847 | + $metapages[] = $metapage; | |
848 | + } | |
849 | + $this->metapages = $metapages; | |
850 | + } | |
851 | + | |
852 | + function filter_filter_metapages() | |
853 | + { | |
854 | + if ($this->options['filter'][1] === "") { | |
855 | + return; | |
856 | + } | |
857 | + $metapages = array(); | |
858 | + foreach ($this->metapages as $i => $metapage) { | |
859 | + unset($this->metapages[$i]); | |
860 | + if (! call_user_func($this->ereg, $this->options['filter'][1], $metapage['relative'])) { | |
861 | + continue; | |
862 | + } | |
863 | + $metapages[] = $metapage; | |
864 | + } | |
865 | + $this->metapages = $metapages; | |
866 | + } | |
867 | + | |
868 | + // PukiWiki API Extension | |
869 | + | |
870 | + function sort_pages(&$pages) | |
871 | + { | |
872 | + $pages = str_replace('/', "\0", $pages); | |
873 | + sort($pages, SORT_STRING); | |
874 | + $pages = str_replace("\0", '/', $pages); | |
875 | + } | |
876 | + | |
877 | + // No PREG_SPLIT_NO_EMPTY version | |
878 | + // copy from lib/make_link.php#get_fullname | |
879 | + function get_fullname($name, $refer) | |
880 | + { | |
881 | + global $defaultpage; | |
882 | + | |
883 | + // 'Here' | |
884 | + if ($name == '' || $name == './') return $refer; | |
885 | + | |
886 | + // Absolute path | |
887 | + if ($name{0} == '/') { | |
888 | + $name = substr($name, 1); | |
889 | + return ($name == '') ? $defaultpage : $name; | |
890 | + } | |
891 | + | |
892 | + // Relative path from 'Here' | |
893 | + if (substr($name, 0, 2) == './') { | |
894 | + $arrn = preg_split('#/#', $name, -1); //, PREG_SPLIT_NO_EMPTY); | |
895 | + $arrn[0] = $refer; | |
896 | + return join('/', $arrn); | |
897 | + } | |
898 | + | |
899 | + // Relative path from dirname() | |
900 | + if (substr($name, 0, 3) == '../') { | |
901 | + $arrn = preg_split('#/#', $name, -1); //, PREG_SPLIT_NO_EMPTY); | |
902 | + $arrp = preg_split('#/#', $refer, -1, PREG_SPLIT_NO_EMPTY); | |
903 | + | |
904 | + while (! empty($arrn) && $arrn[0] == '..') { | |
905 | + array_shift($arrn); | |
906 | + array_pop($arrp); | |
907 | + } | |
908 | + $name = ! empty($arrp) ? join('/', array_merge($arrp, $arrn)) : | |
909 | + (! empty($arrn) ? $defaultpage . '/' . join('/', $arrn) : $defaultpage); | |
910 | + } | |
911 | + | |
912 | + return $name; | |
913 | + } | |
914 | + | |
915 | + function is_newpage($page) | |
916 | + { | |
917 | + // pukiwiki trick | |
918 | + return ! _backup_file_exists($page); | |
919 | + } | |
920 | + | |
921 | + function make_pagelink($page, $alias = '', $anchor = '', $refer = '', $isautolink = FALSE) | |
922 | + { | |
923 | + // no passage | |
924 | + global $show_passage; | |
925 | + $tmp = $show_passage; $show_passage = 0; | |
926 | + $link = make_pagelink($page, $alias, $anchor, $refer, $isautolink); | |
927 | + $show_passage = $tmp; | |
928 | + return $link; | |
929 | + } | |
930 | + | |
931 | + function get_readings() | |
932 | + { | |
933 | + return get_readings(); | |
934 | + } | |
935 | + | |
936 | + function get_filetime($page) | |
937 | + { | |
938 | + return get_filetime($page); | |
939 | + } | |
940 | + | |
941 | + // PHP Extension | |
942 | + | |
943 | + // dirname(Page/) => '.' , dirname(Page/a) => Page, dirname(Page) => '.' | |
944 | + // But, want Page/ => Page, Page/a => Page, Page => '' | |
945 | + function my_dirname($page) | |
946 | + { | |
947 | + if (($pos = strrpos($page, '/')) !== false) { | |
948 | + return substr($page, 0, $pos); | |
949 | + } else { | |
950 | + return ''; | |
951 | + } | |
952 | + } | |
953 | + | |
954 | + // basename(Page/) => Page , basename(Page/a) => a, basename(Page) => Page | |
955 | + // But, want Page/ => '', Page/a => a, Page => Page | |
956 | + function my_basename($page) | |
957 | + { | |
958 | + if (($pos = strrpos($page, '/')) !== false) { | |
959 | + return substr($page, $pos + 1); | |
960 | + } else { | |
961 | + return $page; | |
962 | + } | |
963 | + } | |
964 | + | |
965 | + function array_search_by($value, $array, $fieldname = null) | |
966 | + { | |
967 | + foreach ($array as $i => $val) { | |
968 | + if ($value == $val[$fieldname]) { | |
969 | + return $i; | |
970 | + } | |
971 | + } | |
972 | + return false; | |
973 | + } | |
974 | + | |
975 | + function in_array_by($value, $array, $fieldname = null) | |
976 | + { | |
977 | + //foreach ($array as $i => $befree) { | |
978 | + // $field_array[$i] = $array[$i][$fieldname]; | |
979 | + //} | |
980 | + //return in_array($value, $field_array); | |
981 | + | |
982 | + foreach ($array as $i => $val) { | |
983 | + if ($value == $val[$fieldname]) { | |
984 | + return true; | |
985 | + } | |
986 | + } | |
987 | + return false; | |
988 | + } | |
989 | + | |
990 | + # sort arrays by a specific field without maintaining key association | |
991 | + function sort_by(&$array, $fieldname = null, $sort, $sortflag = SORT_REGULAR) | |
992 | + { | |
993 | + $field_array = $inarray = array(); | |
994 | + # store the keyvalues in a seperate array | |
995 | + foreach ($array as $i => $befree) { | |
996 | + $field_array[$i] = $array[$i][$fieldname]; | |
997 | + } | |
998 | + $field_array = str_replace('/', "\0", $field_array); // must not be here. Refactor me. | |
999 | + switch ($sort) { | |
1000 | + case 'sort': | |
1001 | + # sort an array and maintain index association... | |
1002 | + asort($field_array, $sortflag); | |
1003 | + break; | |
1004 | + case 'rsort': | |
1005 | + # sort an array in reverse order and maintain index association | |
1006 | + arsort($field_array, $sortflag); | |
1007 | + break; | |
1008 | + case 'natsort': | |
1009 | + natsort($field_array); | |
1010 | + case 'natcasesort': | |
1011 | + # sort an array using a case insensitive "natural order" algorithm | |
1012 | + natcasesort($field_array); | |
1013 | + break; | |
1014 | + } | |
1015 | + # rebuild the array | |
1016 | + $outarray = array(); | |
1017 | + foreach ( $field_array as $i=> $befree) { | |
1018 | + $outarray[] = $array[$i]; | |
1019 | + unset($array[$i]); | |
1020 | + } | |
1021 | + $array = $outarray; | |
1022 | + } | |
1023 | + | |
1024 | + function max_by($array, $fieldname = null) | |
1025 | + { | |
1026 | + $field_array = $inarray = array(); | |
1027 | + # store the keyvalues in a seperate array | |
1028 | + foreach ($array as $i => $befree) { | |
1029 | + $field_array[$i] = $array[$i][$fieldname]; | |
1030 | + } | |
1031 | + return max($field_array); | |
1032 | + } | |
1033 | +} | |
1034 | +/////////////////////////////////////// | |
1035 | +class PluginLsxOptionParser | |
1036 | +{ | |
1037 | + var $error = ""; | |
1038 | + | |
1039 | + function parse_options($args, $options) | |
1040 | + { | |
1041 | + if (! $this->is_associative_array($args)) { | |
1042 | + $args = $this->associative_args($args, $options); | |
1043 | + if ($this->error != "") { return; } | |
1044 | + } | |
1045 | + | |
1046 | + foreach ($args as $key => $val) { | |
1047 | + if ( !isset($options[$key]) ) { continue; } // for action ($vars) | |
1048 | + $type = $options[$key][0]; | |
1049 | + $options[$key][4] = $val; | |
1050 | + | |
1051 | + switch ($type) { | |
1052 | + case 'bool': | |
1053 | + if($val == "" || $val == "on" || $val == "true") { | |
1054 | + $options[$key][1] = true; | |
1055 | + } elseif ($val == "off" || $val == "false" ) { | |
1056 | + $options[$key][1] = false; | |
1057 | + } else { | |
1058 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
1059 | + $this->error .= "The option, $key, accepts only a boolean value."; | |
1060 | + $this->error .= "#$this->plugin($key) or #$this->plugin($key=on) or #$this->plugin($key=true) for true. "; | |
1061 | + $this->error .= "#$this->plugin($key=off) or #$this->plugin($key=false) for false. "; | |
1062 | + return; | |
1063 | + } | |
1064 | + break; | |
1065 | + case 'string': | |
1066 | + $options[$key][1] = $val; | |
1067 | + break; | |
1068 | + case 'sanitize': | |
1069 | + $options[$key][1] = htmlspecialchars($val); | |
1070 | + break; | |
1071 | + case 'number': | |
1072 | + // Do not parse yet, parse after getting min and max. Here, just format checking | |
1073 | + if ($val === '') { | |
1074 | + $options[$key][1] = ''; | |
1075 | + break; | |
1076 | + } | |
1077 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
1078 | + $val = substr($val, 1, strlen($val) - 2); | |
1079 | + } | |
1080 | + foreach (explode(",", $val) as $range) { | |
1081 | + if (preg_match('/^-?\d+$/', $range)) { | |
1082 | + } elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) { | |
1083 | + } elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) { | |
1084 | + } else { | |
1085 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
1086 | + $this->error .= "The option, " . $key . ", accepts number values such as 1, 1:3, 1+3, 1,2,4. "; | |
1087 | + $this->error .= "Specify options as \"$key=1,2,4\" or $key=(1,2,3) when you want to use \",\". "; | |
1088 | + $this->error .= "In more details, a style like (1:3,5:7,9:) is also possible. 9: means from 9 to the last. "; | |
1089 | + $this->error .= "Furtermore, - means backward. -1:-3 means 1,2,3 from the tail. "; | |
1090 | + return; | |
1091 | + } | |
1092 | + } | |
1093 | + $options[$key][1] = $val; | |
1094 | + break; | |
1095 | + case 'enum': | |
1096 | + if($val == "") { | |
1097 | + $options[$key][1] = $options[$key][2][0]; | |
1098 | + } elseif (in_array($val, $options[$key][2])) { | |
1099 | + $options[$key][1] = $val; | |
1100 | + } else { | |
1101 | + $this->error = htmlspecialchars("$key=$val") . " is invalid. "; | |
1102 | + $this->error .= "The option, " . $key . ", accepts values from one of (" . join(",", $options[$key][2]) . "). "; | |
1103 | + $this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=" . $options[$key][2][0] . "). "; | |
1104 | + return; | |
1105 | + } | |
1106 | + break; | |
1107 | + case 'array': | |
1108 | + if ($val == '') { | |
1109 | + $options[$key][1] = array(); | |
1110 | + break; | |
1111 | + } | |
1112 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
1113 | + $val = substr($val, 1, strlen($val) - 2); | |
1114 | + } | |
1115 | + $val = explode(',', $val); | |
1116 | + //$val = $this->support_paren($val); | |
1117 | + $options[$key][1] = $val; | |
1118 | + break; | |
1119 | + case 'enumarray': | |
1120 | + if ($val == '') { | |
1121 | + $options[$key][1] = $options[$key][2]; | |
1122 | + break; | |
1123 | + } | |
1124 | + if ($val[0] === '(' && $val[strlen($val) - 1] == ')') { | |
1125 | + $val = substr($val, 1, strlen($val) - 2); | |
1126 | + } | |
1127 | + $val = explode(',', $val); | |
1128 | + //$val = $this->support_paren($val); | |
1129 | + $options[$key][1] = $val; | |
1130 | + foreach ($options[$key][1] as $each) { | |
1131 | + if (! in_array($each, $options[$key][2])) { | |
1132 | + $this->error = "$key=" . htmlspecialchars(join(",", $options[$key][1])) . " is invalid. "; | |
1133 | + $this->error .= "The option, " . $key . ", accepts sets of values from (" . join(",", $options[$key][2]) . "). "; | |
1134 | + $this->error .= "By the way, #$this->plugin($key) equals to #$this->plugin($key=(" . join(',',$options[$key][2]) . ")). "; | |
1135 | + return; | |
1136 | + } | |
1137 | + } | |
1138 | + break; | |
1139 | + default: | |
1140 | + } | |
1141 | + } | |
1142 | + | |
1143 | + return $options; | |
1144 | + } | |
1145 | + | |
1146 | + /** | |
1147 | + * Handle associative type option arguments as | |
1148 | + * ["prefix=Hoge/", "contents=(hoge", "hoge", "hoge)"] => ["prefix"=>"hoge/", "contents"=>"(hoge,hoge,hoge)"] | |
1149 | + * This has special supports for parentheses type arguments (number, array, enumarray) | |
1150 | + * Check option in along with. | |
1151 | + * @access public | |
1152 | + * @param Array $args Original option arguments | |
1153 | + * @return Array $result Converted associative option arguments | |
1154 | + */ | |
1155 | + function associative_args($args, $options) | |
1156 | + { | |
1157 | + $result = array(); | |
1158 | + while (($arg = current($args)) !== false) { | |
1159 | + list($key, $val) = array_pad(explode("=", $arg, 2), 2, ''); | |
1160 | + if (! isset($options[$key])) { | |
1161 | + $this->error = 'No such a option, ' . htmlspecialchars($key); | |
1162 | + return; | |
1163 | + } | |
1164 | + // paren support | |
1165 | + if ($val[0] === '(' && ($options[$key][0] == 'number' || | |
1166 | + $options[$key][0] == 'array' || $options[$key][0] == 'enumarray')) { | |
1167 | + while(true) { | |
1168 | + if ($val[strlen($val)-1] === ')' && substr_count($val, '(') == substr_count($val, ')')) { | |
1169 | + break; | |
1170 | + } | |
1171 | + $arg = next($args); | |
1172 | + if ($arg === false) { | |
1173 | + $this->error = "The # of open and close parentheses of one of your arguments did not match. "; | |
1174 | + return; | |
1175 | + } | |
1176 | + $val .= ',' . $arg; | |
1177 | + } | |
1178 | + } | |
1179 | + $result[$key] = $val; | |
1180 | + next($args); | |
1181 | + } | |
1182 | + return $result; | |
1183 | + } | |
1184 | + | |
1185 | + function parse_numoption($optionval, $min, $max) | |
1186 | + { | |
1187 | + if ($optionval === '') { | |
1188 | + return ''; | |
1189 | + } | |
1190 | + $result = array(); | |
1191 | + foreach (explode(",", $optionval) as $range) { | |
1192 | + if (preg_match('/^-?\d+$/', $range)) { | |
1193 | + $left = $right = $range; | |
1194 | + } elseif (preg_match('/^-?\d*\:-?\d*$/', $range)) { | |
1195 | + list($left, $right) = explode(":", $range, 2); | |
1196 | + if ($left == "" && $right == "") { | |
1197 | + $left = $min; | |
1198 | + $right = $max; | |
1199 | + } elseif($left == "") { | |
1200 | + $left = $min; | |
1201 | + } elseif ($right == "") { | |
1202 | + $right = $max; | |
1203 | + } | |
1204 | + } elseif (preg_match('/^-?\d+\+-?\d+$/', $range)) { | |
1205 | + list($left, $right) = explode("+", $range, 2); | |
1206 | + $right += $left; | |
1207 | + } | |
1208 | + if ($left < 0) { | |
1209 | + $left += $max + 1; | |
1210 | + } | |
1211 | + if ($right < 0) { | |
1212 | + $right += $max + 1; | |
1213 | + } | |
1214 | + $result = array_merge($result, range($left, $right)); | |
1215 | + // range allows like range(5, 3) also | |
1216 | + } | |
1217 | + // filter | |
1218 | + foreach (array_keys($result) as $i) { | |
1219 | + if ($result[$i] < $min || $result[$i] > $max) { | |
1220 | + unset($result[$i]); | |
1221 | + } | |
1222 | + } | |
1223 | + sort($result); | |
1224 | + $result = array_unique($result); | |
1225 | + | |
1226 | + return $result; | |
1227 | + } | |
1228 | + | |
1229 | + function option_debug_print($options) { | |
1230 | + foreach ($options as $key => $val) { | |
1231 | + $type = $val[0]; | |
1232 | + $val = $val[1]; | |
1233 | + if(is_array($val)) { | |
1234 | + $val=join(',', $val); | |
1235 | + } | |
1236 | + $body .= "$key=>($type, $val),"; | |
1237 | + } | |
1238 | + return $body; | |
1239 | + } | |
1240 | + | |
1241 | + // php extension | |
1242 | + function is_associative_array($array) | |
1243 | + { | |
1244 | + if (!is_array($array) || empty($array)) | |
1245 | + return false; | |
1246 | + $keys = array_keys($array); | |
1247 | + return array_keys($keys) !== $keys; | |
1248 | + // or | |
1249 | + //return is_array($array) && !is_numeric(implode(array_keys($array))); | |
1250 | + } | |
1251 | +} | |
1252 | + | |
1253 | +////////////////////////////////// | |
1254 | +function plugin_lsx_common_init() | |
1255 | +{ | |
1256 | + global $plugin_lsx; | |
1257 | + if (class_exists('PluginLsxUnitTest')) { | |
1258 | + $plugin_lsx = new PluginLsxUnitTest(); | |
1259 | + } elseif (class_exists('PluginLsxUser')) { | |
1260 | + $plugin_lsx = new PluginLsxUser(); | |
1261 | + } else { | |
1262 | + $plugin_lsx = new PluginLsx(); | |
1263 | + } | |
1264 | +} | |
1265 | + | |
1266 | +function plugin_lsx_convert() | |
1267 | +{ | |
1268 | + global $plugin_lsx; plugin_lsx_common_init(); | |
1269 | + $args = func_get_args(); | |
1270 | + return call_user_func_array(array(&$plugin_lsx, 'convert'), $args); | |
1271 | +} | |
1272 | + | |
1273 | +function plugin_lsx_action() | |
1274 | +{ | |
1275 | + global $plugin_lsx; plugin_lsx_common_init(); | |
1276 | + return call_user_func(array(&$plugin_lsx, 'action')); | |
1277 | +} | |
1278 | + | |
1279 | +?> |