• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

Revisión334d505c8ce364ff615e533dab4d8fe4e54a0b07 (tree)
Tiempo2022-01-24 01:57:59
Autorhai-fun <haifun129@gmai...>
Commiterhai-fun

Log Message

rev.310, author: sonots

Cambiar Resumen

Diferencia incremental

--- /dev/null
+++ b/contentsx.inc.php
@@ -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+?>
--- /dev/null
+++ b/includex.inc.php
@@ -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&amp;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+?>
--- /dev/null
+++ b/lsx.inc.php
@@ -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 .= '&amp;' . 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 . '&amp;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 . '&amp;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+?>