1<?php 2/** 3 * Info tIndexmenu: Displays the index of a specified namespace. 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Samuele Tognini <samuele@netsons.org> 7 * @author Rene Hadler <rene.hadler@iteas.at> 8 * 9 */ 10 11if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 13if(!defined('INDEXMENU_IMG_ABSDIR')) define('INDEXMENU_IMG_ABSDIR',DOKU_PLUGIN."tindexmenu/images"); 14require_once(DOKU_PLUGIN.'syntax.php'); 15require_once(DOKU_INC.'inc/search.php'); 16 17/** 18* 19* Wrapper around deprecated search_callback. 20* @deprecated 21* 22*/ 23if(!function_exists("search_callback")) { 24 function search_callback($func,&$data,$base,$file,$type,$lvl,$opts) { 25 return call_user_func_array($func, array(&$data,$base,$file,$type,$lvl,$opts)); 26 } 27} 28 29/** 30 * All DokuWiki plugins to extend the parser/rendering mechanism 31 * need to inherit from this class 32 */ 33class syntax_plugin_indexmenu_indexmenu extends DokuWiki_Syntax_Plugin { 34 35 var $sort=false; 36 var $msort=false; 37 var $rsort=false; 38 var $nsort=false; 39 40 /** 41 * return some info 42 */ 43 function getInfo(){ 44 return array( 45 'author' => 'Samuele Tognini mod. by Rene Hadler', 46 'email' => 'samuele@netsons.org, rene.hadler@iteas.at', 47 'date' => rtrim(io_readFile(DOKU_PLUGIN.'tindexmenu/VERSION.txt')), 48 'name' => 'tIndexmenu', 49 'desc' => 'Insert the index of a specified namespace.', 50 'url' => 'http://wiki.splitbrain.org/plugin:tindexmenu' 51 ); 52 } 53 54 /** 55 * What kind of syntax are we? 56 */ 57 function getType(){ 58 return 'substition'; 59 } 60 61 function getPType(){ 62 return 'block'; 63 } 64 65 /** 66 * Where to sort in? 67 */ 68 function getSort(){ 69 return 138; 70 } 71 72 /** 73 * Connect pattern to lexer 74 */ 75 function connectTo($mode) { 76 $this->Lexer->addSpecialPattern('{{indexmenu>.+?}}',$mode,'plugin_indexmenu_indexmenu'); 77 } 78 79 /** 80 * Handle the match 81 */ 82 function handle($match, $state, $pos, &$handler){ 83 $theme="default"; 84 $ns="."; 85 $level = -1; 86 $nons = true; 87 $gen_id='random'; 88 $maxjs=0; 89 $max=0; 90 $jsajax=''; 91 $nss=array(); 92 $match = substr($match,12,-2); 93 //split namespace,level,theme 94 $match = preg_split('/\|/u', $match, 2); 95 //split options 96 $opts=preg_split('/ /u',$match[1]); 97 //Context option 98 $context = in_array('context',$opts); 99 //split optional namespaces 100 $nss_temp=preg_split("/ /u",$match[0],-1,PREG_SPLIT_NO_EMPTY); 101 //Array optional namespace => level 102 for ($i = 1; $i < count($nss_temp); $i++) { 103 $nsss=preg_split("/#/u",$nss_temp[$i]); 104 if (!$context) { 105 $nsss[0] = $this->_parse_ns($nsss[0]); 106 } 107 $nss[]=array($nsss[0],(is_numeric($nsss[1])) ? $nsss[1] : $level); 108 } 109 //split main requested namespace 110 if (preg_match('/(.*)#(\S*)/u',$nss_temp[0],$ns_opt)) { 111 //split level 112 $ns = $ns_opt[1]; 113 if (is_numeric($ns_opt[2])) $level=$ns_opt[2]; 114 } else { 115 $ns = $nss_temp[0]; 116 } 117 if (!$context) { 118 $ns = $this->_parse_ns($ns); 119 } 120 //nocookie option (disable for uncached pages) 121 $nocookie=$context||in_array('nocookie',$opts); 122 //noscroll option 123 $noscroll=in_array('noscroll',$opts); 124 //Open at current namespace option 125 $navbar=in_array('navbar',$opts); 126 //no namespaces options 127 $nons = in_array('nons',$opts); 128 //no pages option 129 $nopg = in_array('nopg',$opts); 130 //disable toc preview 131 $notoc = in_array('notoc',$opts); 132 //Main sort method 133 if (in_array('tsort',$opts)) { 134 $sort='t'; 135 } elseif (in_array('dsort',$opts)) { 136 $sort='d'; 137 } else $sort=0; 138 //Directory sort 139 $nsort=in_array('nsort',$opts); 140 //Metadata sort method 141 if ($msort = in_array('msort',$opts)) { 142 $msort='indexmenu_n'; 143 } elseif (preg_match('/msort#(\S+)/u',$match[1],$msort_tmp) >0) $msort=str_replace(':',' ',$msort_tmp[1]); 144 //reverse sort 145 $rsort=in_array('rsort',$opts); 146 //javascript option 147 if (!$js= in_array('js',$opts)) { 148 //split theme 149 if (preg_match('/js#(\S*)/u',$match[1],$tmp_theme) > 0) { 150 if (is_dir(INDEXMENU_IMG_ABSDIR."/".$tmp_theme[1])) { 151 $theme=$tmp_theme[1]; 152 } 153 $js=true; 154 } 155 } 156 //id generation method 157 if (preg_match('/id#(\S+)/u',$match[1],$id) >0) $gen_id=$id[1]; 158 //max option 159 if (preg_match('/max#(\d+)($|\s+|#(\d+))/u',$match[1],$maxtmp) >0) { 160 $max=$maxtmp[1]; 161 if ($maxtmp[3]) $jsajax = "&max=".$maxtmp[3]; 162 //disable cookie to avoid javascript errors 163 $nocookie=true; 164 } 165 if ($sort) $jsajax .= "&sort=".$sort; 166 if ($msort) $jsajax .= "&msort=".$msort; 167 if ($rsort) $jsajax .= "&rsort=1"; 168 if ($nsort) $jsajax .= "&nsort=1"; 169 if ($nopg) $jsajax .= "&nopg=1"; 170 //max js option 171 if (preg_match('/maxjs#(\d+)/u',$match[1],$maxtmp) >0) $maxjs=$maxtmp[1]; 172 //js options 173 $js_opts=compact('theme','gen_id','nocookie','navbar','noscroll','maxjs','notoc','jsajax','context'); 174 return array($ns, 175 $js_opts, 176 $sort, 177 $msort, 178 $rsort, 179 $nsort, 180 array('level' => $level, 181 'nons' => $nons, 182 'nopg' => $nopg, 183 'nss' => $nss, 184 'max' => $max, 185 'js' => $js, 186 'skip_index' => $this->getConf('skip_index'), 187 'skip_file' => $this->getConf('skip_file'), 188 'headpage' => $this->getConf('headpage'), 189 'hide_headpage' => $this->getConf('hide_headpage') 190 ) 191 ); 192 } 193 194 /** 195 * Render output 196 */ 197 function render($mode, &$renderer, $data) { 198 global $ACT; 199 global $conf; 200 global $INFO; 201 if($mode == 'xhtml'){ 202 if ($ACT == 'preview') { 203 //Check user permission to display indexmenu in a preview page 204 if( $this->getConf('only_admins') && 205 $conf['useacl'] && 206 $INFO['perm'] < AUTH_ADMIN) 207 return false; 208 //disable cookies 209 $data[1]['nocookie']=true; 210 } 211 //Navbar with nojs 212 if ($data[1]['navbar'] && !$data[6]['js']) { 213 if (!isset($data[0])) $data[0]='..'; 214 $data[6]['nss'][]=array(getNS($INFO['id'])); 215 $renderer->info['cache'] = FALSE; 216 } 217 218 if ($data[1]['context']) { 219 //resolve current id relative namespaces 220 $data[0]=$this->_parse_ns($data[0],$INFO['id']); 221 foreach ($data[6]['nss'] as $key=>$value) { 222 $data[6]['nss'][$key][0] = $this->_parse_ns($value[0],$INFO['id']); 223 } 224 $renderer->info['cache'] = FALSE; 225 } 226 $n = $this->_indexmenu($data); 227 if (!@$n) { 228 $n = $this->getConf('empty_msg'); 229 $n = str_replace('{{ns}}',cleanID($data[0]),$n); 230 $n = p_render('xhtml',p_get_instructions($n),$info); 231 } 232 $renderer->doc .= $n; 233 return true; 234 } else if ($mode == 'metadata') { 235 if (!($data[1]['navbar'] && !$data[6]['js']) && !$data[1]['context']) { 236 //this is an indexmenu page that needs the PARSER_CACHE_USE event trigger; 237 $renderer->meta['indexmenu'] = TRUE; 238 } 239 $renderer->doc .= ((empty($data[0])) ? $conf['title'] : nons($data[0])) ." index\n\n"; 240 unset($renderer->persistent['indexmenu']); 241 return true; 242 } else { 243 return false; 244 } 245 } 246 247 /** 248 * Return the index 249 * @author Samuele Tognini <samuele@netsons.org> 250 * 251 * This function is a simple hack of Dokuwiki html_index($ns) 252 * @author Andreas Gohr <andi@splitbrain.org> 253 */ 254 function _indexmenu($myns) { 255 global $conf; 256 $ns = $myns[0]; 257 $js_opts=$myns[1]; 258 $this->sort = $myns[2]; 259 $this->msort = $myns[3]; 260 $this->rsort = $myns[4]; 261 $this->nsort = $myns[5]; 262 $opts = $myns[6]; 263 $output=false; 264 $data = array(); 265 $js_name="indexmenu_"; 266 $fsdir="/".utf8_encodeFN(str_replace(':','/',$ns)); 267 if ($this->sort || $this->msort || $this->rsort) { 268 $custsrch=$this->_search($data,$conf['datadir'],array($this,'_search_index'),$opts,$fsdir); 269 } else { 270 search($data,$conf['datadir'],array($this,'_search_index'),$opts,$fsdir); 271 } 272 if (!$data) return false; 273 274 // Id generation method 275 if (is_numeric($js_opts['gen_id'])) { 276 $js_name .= $js_opts['gen_id']; 277 } elseif ($js_opts['gen_id'] == 'ns') { 278 $js_name .= sprintf("%u",crc32($ns)); 279 } else { 280 $js_name .= uniqid(rand()); 281 } 282 283 //javascript index 284 if ($opts['js']) { 285 $ns = str_replace('/',':',$ns); 286 $output_tmp=$this->_jstree($data,$ns,$js_opts,$js_name,$opts['max']); 287 //remove unwanted nodes from standard index 288 $this->_clean_data($data); 289 } else { 290 //Nojs dokuwiki index 291 $output .= "<script type='text/javascript' charset='utf-8'>\n"; 292 $output .= "<!--//--><![CDATA[//><!--\n"; 293 $output .= "indexmenu_nojsqueue.push(new Array('".$js_name."','".utf8_encodeFN($js_opts['jsajax'])."'));\n"; 294 $output .= "jQuery(function(){indexmenu_loadJs(DOKU_BASE+'lib/plugins/tindexmenu/nojsindex.js');});\n"; 295 $output .= "//--><!]]>\n"; 296 $output .= "</script>\n"; 297 $output.="\n".'<div id="nojs_'.$js_name.'" class="indexmenu_nojs"'; 298 $output.=">\n"; 299 $output.=html_buildlist($data,'idx',array($this,"_html_list_index"),"html_li_index"); 300 $output.="</div>\n"; 301 } 302 $output.=$output_tmp; 303 return $output; 304 } 305 306 /** 307 * Build the browsable index of pages using javascript 308 * 309 * @author Samuele Tognini <samuele@netsons.org> mod. by Rene Hadler 310 */ 311 function _jstree($data,$ns,$js_opts,$js_name,$max) { 312 global $conf; 313 $hns=false; 314 if (empty($data)) return false; 315 //Render requested ns as root 316 $headpage=$this->getConf('headpage'); 317 if (empty($ns) && !empty($headpage)) $headpage.=','.$conf['start']; 318 $title=$this->_getTitle($ns,$headpage,$hns); 319 if (empty($title)) { 320 (empty($ns)) ? $title = htmlspecialchars($conf['title'],ENT_QUOTES) : $title=$ns; 321 } 322 $out = "<script type='text/javascript' charset='utf-8'>\n"; 323 $out .= "<!--//--><![CDATA[//><!--\n"; 324 $out .= "var $js_name = new dTree('".$js_name."','".$js_opts['theme']."');\n"; 325 $sepchar = idfilter(':'); 326 $out .= "$js_name.config.urlbase='".substr(wl(":"), 0, -1)."';\n"; 327 $out .= "$js_name.config.sepchar='".$sepchar."';\n"; 328 if ($js_opts['notoc']) $out .="$js_name.config.toc=false;\n"; 329 if ($js_opts['nocookie']) $out .="$js_name.config.useCookies=false;\n"; 330 if ($js_opts['noscroll']) $out .="$js_name.config.scroll=false;\n"; 331 if ($js_opts['maxjs'] > 0) $out .= "$js_name.config.maxjs=".$js_opts['maxjs'].";\n"; 332 if (!empty($js_opts['jsajax'])) $out .= "$js_name.config.jsajax='".utf8_encodeFN($js_opts['jsajax'])."';\n"; 333 $out .= $js_name.".add('".idfilter(cleanID($ns))."',0,-1,'".$title."'"; 334 if ($hns) $out .= ",'".idfilter(cleanID($hns))."'"; 335 $out .= ");\n"; 336 $anodes = $this->_jsnodes($data,$js_name); 337 $out .= $anodes[0]; 338 $out .= "document.write(".$js_name.");\n"; 339 $out .= $js_name.".init("; 340 $out .= (int) is_file(INDEXMENU_IMG_ABSDIR.'/'.$js_opts['theme'].'/style.css').","; 341 $out .= (int) $js_opts['nocookie'].","; 342 $out .= '"'.$anodes[1].'",'; 343 $out .= (int) $js_opts['navbar'].",$max"; 344 $out .= ");\n"; 345 $out .= "//--><!]]>\n"; 346 $out .= "</script>\n"; 347 return $out; 348 } 349 350 /** 351 * Return array of javascript nodes and nodes to open. 352 * 353 * @author Samuele Tognini <samuele@netsons.org> 354 */ 355 function _jsnodes($data,$js_name,$noajax=1) { 356 if (empty($data)) return false; 357 //Array of nodes to check 358 $q=array('0'); 359 //Current open node 360 $node=0; 361 $out=''; 362 $extra=''; 363 if ($noajax) { 364 $jscmd=$js_name.".add"; 365 $com=";\n"; 366 } else { 367 $jscmd="new Array "; 368 $com=","; 369 } 370 foreach ($data as $i=>$item){ 371 $i++; 372 //Remove already processed nodes (greater level = lower level) 373 while ($item['level'] <= $data[end($q)-1]['level']) { 374 array_pop($q); 375 } 376 377 //till i found its father node 378 if ($item['level']==1) { 379 //root node 380 $father='0'; 381 } else { 382 //Father node 383 $father=end($q); 384 } 385 //add node and its options 386 if ($item['type'] == 'd' ) { 387 //Search the lowest open node of a tree branch in order to open it. 388 if ($item['open']) ($item['level'] < $data[$node]['level']) ? $node=$i : $extra .= "$i "; 389 //insert node in last position 390 array_push($q,$i); 391 } 392 $out .= $jscmd."('".idfilter($item['id'])."',$i,".$father.",'".$item['title']."'"; 393 //hns 394 ($item['hns']) ? $out .= ",'".idfilter($item['hns'])."'" : $out .= ",0"; 395 ($item['type'] == 'd' || $item['type']=='l') ? $out .= ",1" : $out .= ",0"; 396 //MAX option 397 ($item['type']=='l') ? $out .= ",1" : $out .= ",0"; 398 $out .= ")".$com; 399 } 400 $extra=rtrim($extra,' '); 401 return array($out,$extra); 402 } 403 /** 404 * Get page title, checking for headpages 405 * 406 * @author Samuele Tognini <samuele@netsons.org> 407 */ 408 function _getTitle ($ns,$headpage,&$hns) { 409 global $conf; 410 $hns=false; 411 $title=noNS($ns); 412 if (empty($headpage)) return $title; 413 $ahp=explode(",",$headpage); 414 foreach ($ahp as $hp) { 415 switch ($hp) { 416 case ":inside:": 417 $page=$ns.":".noNS($ns); 418 break; 419 case ":same:": 420 $page=$ns; 421 break; 422 //it's an inside start 423 case ":start:": 424 $page=ltrim($ns.":".$conf['start'],":"); 425 break; 426 //inside pages 427 default: 428 $page=$ns.":".$hp; 429 } 430 //check headpage 431 if (@file_exists(wikiFN($page)) && auth_quickaclcheck($page) >= AUTH_READ) { 432 if ($conf['useheading'] && $title_tmp=p_get_first_heading($page,FALSE)) $title=$title_tmp; 433 $title=htmlspecialchars($title,ENT_QUOTES); 434 $hns=$page; 435 //headpage found, exit for 436 break; 437 } 438 } 439 return $title; 440 } 441 442 /** 443 * Parse namespace request 444 * 445 * @author Samuele Tognini <samuele@netsons.org> 446 */ 447 function _parse_ns ($ns,$id=FALSE) { 448 if (!$id) { 449 global $ID; 450 $id = $ID; 451 } 452 //Just for old reelases compatibility 453 if (empty($ns) || $ns == '..') $ns=":.."; 454 return resolve_id(getNS($id),$ns); 455 } 456 457 /** 458 * Clean index data from unwanted nodes in nojs mode. 459 * 460 * @author Samuele Tognini <samuele@netsons.org> 461 */ 462 function _clean_data(&$data) { 463 foreach ($data as $i=>$item) { 464 //closed node 465 if ($item['type'] == "d" && !$item['open']) { 466 $a=$i+1; 467 $level=$data[$i]['level']; 468 //search and remove every lower and closed nodes 469 while ($data[$a]['level'] > $level && !$data[$a]['open']) { 470 unset($data[$a]); 471 $a++; 472 } 473 } 474 $i++; 475 } 476 } 477 478 /** 479 * Build the browsable index of pages 480 * 481 * $opts['ns'] is the current namespace 482 * 483 * @author Andreas Gohr <andi@splitbrain.org> 484 * modified by Samuele Tognini <samuele@netsons.org> 485 */ 486 function _search_index(&$data,$base,$file,$type,$lvl,$opts){ 487 global $conf; 488 $hns=false; 489 $return=false; 490 $isopen=false; 491 $skip_index=$opts['skip_index']; 492 $skip_file=$opts['skip_file']; 493 $headpage=$opts['headpage']; 494 $id = pathID($file); 495 if($type == 'd'){ 496 // Skip folders in plugin conf 497 if (!empty($skip_index) && 498 preg_match($skip_index, $id)) 499 return false; 500 //check ACL (for sneaky_index namespaces too). 501 if ($this->getConf('sneaky_index') && auth_quickaclcheck($id.':') < AUTH_READ) return false; 502 //Open requested level 503 if ($opts['level'] > $lvl || $opts['level'] == -1) $isopen=true; 504 //Search optional namespaces 505 if (!empty($opts['nss'])){ 506 $nss=$opts['nss']; 507 for ($a=0; $a<count($nss);$a++) { 508 if (preg_match("/^".$id."($|:.+)/i",$nss[$a][0],$match)) { 509 //It contains an optional namespace 510 $isopen=true; 511 } elseif (preg_match("/^".$nss[$a][0]."(:.*)/i",$id,$match)) { 512 //It's inside an optional namespace 513 if ($nss[$a][1] == -1 || substr_count($match[1],":") < $nss[$a][1]) { 514 $isopen=true; 515 } else { 516 $isopen=false; 517 } 518 } 519 } 520 } 521 if ($opts['nons']) { 522 return $isopen; 523 } elseif ($opts['max'] >0 && !$isopen && $lvl >= $opts['max']) { 524 $isopen=false; 525 //Stop recursive searching 526 $return=false; 527 //change type 528 $type="l"; 529 } elseif ($opts['js']) { 530 $return=true; 531 } else { 532 $return=$isopen; 533 } 534 //Set title and headpage 535 $title=$this->_getTitle($id,$headpage,$hns); 536 if (!$hns && $opts['nopg']) $hns=$id.":".$conf['start']; 537 } else { 538 //Nopg.Dont show pages 539 if ($opts['nopg']) return false; 540 $return=true; 541 //Nons.Set all pages at first level 542 if ($opts['nons']) $lvl=1; 543 //don't add 544 if (substr($file,-4) != '.txt') return false; 545 //check hiddens and acl 546 if (isHiddenPage($id) || auth_quickaclcheck($id) < AUTH_READ) return false; 547 //Skip files in plugin conf 548 if (!empty($skip_file) && 549 preg_match($skip_file, $id)) 550 return false; 551 //Skip headpages to hide 552 if (!$opts['nons'] && 553 !empty($headpage) && 554 $opts['hide_headpage']) { 555 if ($id==$conf['start']) return false; 556 $ahp=explode(",",$headpage); 557 foreach ($ahp as $hp) { 558 switch ($hp) { 559 case ":inside:": 560 if (noNS($id)==noNS(getNS($id))) return false; 561 break; 562 case ":same:": 563 if (@is_dir(dirname(wikiFN($id))."/".utf8_encodeFN(noNS($id)))) return false; 564 break; 565 //it' s an inside start 566 case ":start:": 567 if (noNS($id)==$conf['start']) return false; 568 break; 569 default: 570 if (noNS($id)==cleanID($hp)) return false; 571 } 572 } 573 } 574 //Set title 575 if (!$conf['useheading'] || !$title=p_get_first_heading($id,FALSE)) $title=noNS($id); 576 $title=htmlspecialchars($title,ENT_QUOTES); 577 } 578 579 $item = array( 'id' => $id, 580 'type' => $type, 581 'level' => $lvl, 582 'open' => $isopen, 583 'title' => $title, 584 'hns' => $hns, 585 'file' => $file, 586 'return' => $return 587 ); 588 $item['sort'] = $this->_setorder($item); 589 $data[] = $item; 590 return $return; 591 } 592 593 594 /** 595 * Index item formatter 596 * 597 * User function for html_buildlist() 598 * 599 * @author Andreas Gohr <andi@splitbrain.org> 600 * modified by Samuele Tognini <samuele@netsons.org> 601 */ 602 function _html_list_index($item){ 603 $ret = ''; 604 //namespace 605 if($item['type']=='d' || $item['type']=='l'){ 606 $link=$item['id']; 607 $more='idx='.$item['id']; 608 //namespace link 609 if ($item['hns']) { 610 $link=$item['hns']; 611 $tagid="indexmenu_idx_head"; 612 $more=''; 613 } else { 614 //namespace with headpage 615 $tagid="indexmenu_idx"; 616 if ($item['open']) $tagid.=' open'; 617 } 618 $ret .= '<a href="'.wl($link,$more).'" class="'.$tagid.'">'; 619 $ret .= $item['title']; 620 $ret .= '</a>'; 621 }else{ 622 //page link 623 $ret .= html_wikilink(':'.$item['id']); 624 } 625 return $ret; 626 } 627 628 629 /** 630 * recurse direcory 631 * 632 * This function recurses into a given base directory 633 * and calls the supplied function for each file and directory 634 * 635 * @param array ref $data The results of the search are stored here 636 * @param string $base Where to start the search 637 * @param callback $func Callback (function name or arayy with object,method) 638 * @param string $dir Current directory beyond $base 639 * @param int $lvl Recursion Level 640 * @author Andreas Gohr <andi@splitbrain.org> 641 * modified by Samuele Tognini <samuele@netsons.org> 642 */ 643 function _search(&$data,$base,$func,$opts,$dir='',$lvl=1){ 644 $dirs = array(); 645 $files = array(); 646 $files_tmp=array(); 647 $dirs_tmp=array(); 648 649 //read in directories and files 650 $dh = @opendir($base.'/'.$dir); 651 if(!$dh) return; 652 while(($file = readdir($dh)) !== false){ 653 //skip hidden files and upper dirs 654 if(preg_match('/^[\._]/',$file)) continue; 655 if(is_dir($base.'/'.$dir.'/'.$file)){ 656 $dirs[] = $dir.'/'.$file; 657 continue; 658 } 659 $files[] = $dir.'/'.$file; 660 } 661 closedir($dh); 662 //Sort dirs 663 if ($this->nsort) { 664 foreach($dirs as $dir){ 665 search_callback($func,$dirs_tmp,$base,$dir,'d',$lvl,$opts); 666 } 667 usort($dirs_tmp,array($this,"_cmp")); 668 foreach ($dirs_tmp as $dir) { 669 $data[]=$dir; 670 if ($dir['return']) $this->_search($data,$base,$func,$opts,$dir['file'],$lvl+1); 671 } 672 } else { 673 sort($dirs); 674 foreach($dirs as $dir){ 675 if (search_callback($func,$data,$base,$dir,'d',$lvl,$opts)) $this->_search($data,$base,$func,$opts,$dir,$lvl+1); 676 } 677 } 678 //Sort files 679 foreach($files as $file){ 680 search_callback($func,$files_tmp,$base,$file,'f',$lvl,$opts); 681 } 682 usort($files_tmp,array($this,"_cmp")); 683 if (empty($dirs) && empty($files_tmp)) { 684 $v=end($data); 685 if (!$v['hns']) array_pop($data); 686 } else { 687 $data=array_merge($data,$files_tmp); 688 } 689 return true; 690 } 691 692 /** 693 * Sort nodes 694 * 695 */ 696 function _cmp($a, $b) { 697 if ($this->rsort) { 698 return strnatcasecmp($b['sort'], $a['sort']); 699 } else { 700 return strnatcasecmp($a['sort'], $b['sort']); 701 } 702 } 703 704 705 /** 706 * Add sort information to item. 707 * 708 * @author Samuele Tognini <samuele@netsons.org> 709 */ 710 function _setorder($item) { 711 $sort=false; 712 if ($item['type']=='d') { 713 //Fake order info when nsort is not requested 714 ($this->nsort) ? $page=$item['hns'] : $sort=0; 715 } 716 if ($item['type']=='f') $page=$item['id']; 717 if ($page) { 718 if ($this->msort) $sort=p_get_metadata($page,$this->msort); 719 if (!$sort && $this->sort) { 720 switch ($this->sort) { 721 case 't': 722 $sort=$item['title']; 723 break; 724 case 'd': 725 $sort=@filectime(wikiFN($page)); 726 break; 727 } 728 } 729 } 730 if ($sort===false) $sort=noNS($item['id']); 731 return $sort; 732 } 733} //Indexmenu class end 734