1<?php 2if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../../').'/'); 3 if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 4 require_once(DOKU_PLUGIN.'action.php'); 5 6/** Clickable index. 7 Based on Thierry Legras's idea. 8 Overrides the index command to provide a clickable index. 9 Based on aq3tree (http://www.kryogenix.org/code/browser/aqlists/) 10 License: GPL 11 */ 12 13 14class action_plugin_fullindex extends DokuWiki_Action_Plugin { 15 //store the namespaces for sorting 16 var $sortIndex = array(); 17 18 /** 19 * Constructor 20 */ 21 function action_plugin_fullindex(){ 22 $this->setupLocale(); 23 } 24 25 function getInfo() { 26 return array('author' => 'Martin Tschofen', 27 'email' => 'mtbrains@comcast.net', 28 'date' => '2007-02-04', 29 'name' => 'fullindex', 30 'desc' => 'Collapsable Index with alternate page names and numbers', 31 'url' => 'https://www.dokuwiki.org/plugin:fullindex'); 32 } 33 34 function register(&$controller) { 35 $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'html_index_clickable'); 36 } 37 38 function html_index_clickable(&$event){ 39 global $conf; 40 global $ID; 41 if ($event->data != 'index') return; 42 require_once(DOKU_INC.'inc/search.php'); 43 $dir = $conf['datadir']; 44 $ns = cleanID($ns); 45 #fixme use appropriate function 46 if(empty($ns)){ 47 $ns = dirname(str_replace(':','/',$ID)); 48 if($ns == '.') $ns =''; 49 } 50 $ns = utf8_encodeFN(str_replace(':','/',$ns)); 51 print $this->plugin_locale_xhtml('intro'); 52 53 $data = array(); 54 search($data,$conf['datadir'],array(&$this,'search_fullindex'),array('ns' => $ns)); 55 usort($data, "_strnatSort"); 56 57 print '<span class="aqLbl">'.$this->getLang('collapse_header').'</span><ul id="aqNav">'; 58 foreach ($this->getLang('collapse') as $key => $item) { 59 print '<li id="aqli'.$key.'"><a href="#" onclick="aq_show('.$key.', this)">'.$item.'</a></li>'; 60 } 61 print '</ul>'; 62 //if conf is not set to titles 63 if($conf['plugin']['fullindex']['link_names'] == 0){ 64 print $this->html_build_full_list($data,'idx','html_list_index','html_li_index'); 65 } else { //alternative titles 66 print $this->html_build_full_list($data,'idx',array(&$this,'_html_title_index'),'html_li_index'); 67 } 68 69 // prevent Dokuwiki normal processing of $ACT (it would clean the variable and destroy our 'index' value. 70 $event->preventDefault(); 71 // index command belongs to us, there is no need to hold up Dokuwiki letting other plugins see if its for them 72 $event->stopPropagation(); 73 } 74 75 /** 76 * Build an unordered list 77 * Based on inc/search.php - @author Andreas Gohr <andi@splitbrain.org> 78 */ 79 function html_build_full_list($data,$class,$func,$lifunc='html_li_default'){ 80 $level = 0; 81 $opens = 0; 82 $ret = ''; 83 84 foreach ($data as $item){ 85 if( $item['level'] > $level ){ 86 //open new list 87 for($i=0; $i<($item['level'] - $level); $i++){ 88 if ($i) $ret .= "<li class=\"clear\">\n"; 89 if ($level > 0) 90 $ret .= "\n<ul class=\"$class\">\n"; 91 else 92 $ret .= "\n<ul class=\"aqtree3clickable\">\n"; 93 } 94 }elseif( $item['level'] < $level ){ 95 //close last item 96 $ret .= "</li>\n"; 97 for ($i=0; $i<($level - $item['level']); $i++){ 98 //close higher lists 99 $ret .= "</ul>\n</li>\n"; 100 } 101 }else{ 102 //close last item 103 $ret .= "</li>\n"; 104 } 105 106 //remember current level 107 $level = $item['level']; 108 109 //print item 110 if(is_array($lifunc)){ 111 $ret .= $lifunc[0]->$lifunc[1]($item); //user object method 112 }else{ 113 $ret .= $lifunc($item); //user function 114 } 115 116 if(is_array($func)){ 117 $ret .= $func[0]->$func[1]($item); //user object method 118 } else { 119 $ret .= $func($item); //user function 120 } 121 } 122 123 //close remaining items and lists 124 for ($i=0; $i < $level; $i++){ 125 $ret .= "</li></ul>\n"; 126 } 127 128 return $ret; 129 } 130 131 /** 132 * find all items and collect necessary information 133 */ 134 135 function search_fullindex(&$data,$base,$file,$type,$lvl,$opts){ 136 global $conf; 137 138 $return = true; 139 140 $item = array(); 141 142 if($type == 'd' && !preg_match('#^'.$file.'(/|$)#','/'.$opts['ns'])){ 143 //always true - only difference to the inc/search.php function 144 $return = true; 145 }elseif($type == 'f' && !preg_match('#\.txt$#',$file)){ 146 //don't add 147 return false; 148 } 149 150 $id = pathID($file); 151 152 //check hidden 153 if(isHiddenPage($id)){ 154 return false; 155 } 156 157 //check ACL 158 if($type=='f' && auth_quickaclcheck($id) < AUTH_READ){ 159 return false; 160 } 161 162 //don't display any namespace's index file -- displayed as the namespace instead 163 if($type == 'f' && preg_match('#\:'.$conf['start'].'$#', $id)) { return false;} 164 165 //setup the sort string 166 if($type=='d'){ 167 $num = $this->_getMetaTag($id.":".$conf['start'], 'identifier'); 168 $title = $this->_getMetaTag($id.":".$conf['start'], 'alternative'); 169 } else { 170 $num = $this->_getMetaTag($id, 'identifier'); 171 $title = $this->_getMetaTag($id, 'alternative'); 172 } 173 174 175 $data[]=array('id' => $id, 176 'type' => $type, 177 'level' => $lvl, 178 'num' => $num, 179 'title' => $title, 180 'open' => $return, 181 'sort' => $this->_setSortIndex($id, $lvl, $type, $num, $title)); 182 return $return; 183 } 184 185 /* 186 * Create sort index for each item 187 */ 188 189 function _setSortIndex($id, $lvl, $type, $num, $title) { 190 global $conf; 191 192 //directories 193 if ($type == 'd') { 194 //if same add dir to array 195 if(count($this->sortIndex) + 1 == $lvl) { 196 //add to the sortIndex 197 $this->_addToSortIndex($id, $num, $title); 198 } else if (count($this->sortIndex) + 1 > $lvl) { 199 $this->_removeFromSortIndex($lvl); 200 //and add new index 201 $this->_addToSortIndex($id, $num, $title); 202 } else { 203 //remove from index 204 array_pop($this->sortIndex); 205 } 206 $sortIndex = ""; 207 //files 208 } else { 209 if(count($this->sortIndex) + 1 > $lvl) { 210 $this->_removeFromSortIndex($lvl); 211 } 212 $temp = trim($num.$title); 213 if(empty($temp)) { 214 $sortIdx = $id; 215 } else { 216 $sortIdx = $this->_cleanForSort($num)." ".$title; //space required for natsearch fix to work! 217 } 218 } 219 /*debug 220 print_r($type); 221 print_r(": count="); 222 print_r(count($this->sortIndex) + 1); 223 print_r('---lvl='.$lvl.'<br>'); 224 print_r("id="); 225 print_r($id); 226 print_r(" <i>"); 227 print_r($this->sortIndex); 228 print_r("</i><br>"); 229 */ 230 return implode("",$this->sortIndex).$sortIdx; 231 } 232 233 /** 234 * add an namespace to the sortIndex array 235 */ 236 function _addToSortIndex($id, $num, $title){ 237 $newIndex = trim($num.$title); 238 if ($newIndex == "") { 239 $newIndex = strrchr($id, ":"); 240 //what if it was a root ns? 241 if ($newIndex == "") { 242 $newIndex = $id; 243 } 244 } 245 $this->sortIndex[] = $newIndex; 246 } 247 /** 248 * remove any number of namespaces from the sortIndex array 249 */ 250 251 function _removeFromSortIndex($lvl) { 252 $diff = count($this->sortIndex) + 1 - $lvl; 253 while($diff > 0){ 254 //backed out of namespace 255 array_pop($this->sortIndex); 256 $diff = $diff - 1; 257 } 258 } 259 /** 260 * build the individual items 261 */ 262 263 function _html_title_index($item){ 264 $ret = ''; 265 $base = ':'.$item['id']; 266 $base = substr($base,strrpos($base,':')+1); 267 if($item['type']=='d'){ 268 $name = $item['num']." ".$item['title']; 269 $name = trim($name); 270 if($name == ''){ 271 $name = $item['id'];} 272 $ret .= html_wikilink(':'.$item['id'].":", $name); 273 274 //remove link if namespace/globalstart page doesn't exist 275 if(preg_match('#wikilink2#', $ret)) { 276 //should return somekind of meta info for the namespace instead -- only if NS sorting? 277 $ret = '<span>'.noNS($item['id']).'</span>'; 278 } 279 } else { 280 $name = $item['num']." ".$item['title']; 281 $name = trim($name); 282 if($name == ''){ 283 $ret .= html_wikilink(':'.$item['id']); 284 } else { 285 $ret .= html_wikilink(':'.$item['id'], $name); 286 } 287 } 288 return $ret; 289 } 290 291 function _getMetaTag($page, $term) { 292 /*if(!$page){ 293 return ""; 294 }*/ 295 //return the found meta tag 296 $data = p_get_metadata($page); 297 if (array_key_exists($term, $data)){ 298 return $data[$term]; 299 } else { 300 return ""; 301 } 302 } 303 304 /** 305 * fix the natural sort, if a number has characters imbedded 306 */ 307 308 function _cleanForSort($num) { 309 return preg_replace('/([a-zA-Z])/', '*$1', $num); 310 } 311 312} //end of action class 313 314//utilities: natural sort 315function _strnatSort($a, $b) { 316 return strnatcasecmp($a['sort'], $b['sort']); 317}