• 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

Revision4a24baeea36ba75c6bb4963919c87614f956d792 (tree)
Time2022-01-24 01:58:20
Authorhai-fun <haifun129@gmai...>
Commiterhai-fun

Log Message

PHP8

Change Summary

Incremental Difference

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