1<?php 2 3// must be run within Dokuwiki 4if (!defined('DOKU_INC')) die(); 5 6if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 7 8class helper_plugin_explorertree extends DokuWiki_Plugin { 9 10 private $routes = array(); // array of routes (registered classes) 11 private $memcache = false; // memcache 12 13 private $options = array( 14 'callbacks' =>array( 15 'page_selected_cb' => null, 16 'ns_selected_cb' => null, 17 'page_selected_js' => null, 18 'ns_selected_js' => null, 19 ), 20 'vars'=>array( 21 'id'=> null, 22 'class' => 'explorertree', 23 ), 24 'init_plugin' => null, 25 ); 26 27 function cache(){ 28 if ($this->memcache === false){ 29 // we will use memcache even it's emulated, as we building a tree requires to search filesystem (multiple fs I/O) and emulated cache only graps one compressed wile from disk (one optimized fs I/O) 30 $this->memcache = plugin_load('helper','memcache'); 31 } 32 return $this->memcahce; 33 } 34 35 36 function getMethods() { 37 $result = array(); 38 $result[] = array( 39 'name' => 'registerRoute', 40 'desc' => 'registers a route: the tree will be created by the options and ajax will be routed to the caller class.', 41 'parameters' => array( 42 'name' => "string unique name, usually the registerer plugin's name (with suffix, if the plugin uses more trees).", 43 'options' => "array of options, that replace the original options (see getOptions)", 44 ) 45 ); 46 $result[] = array( 47 'name' => 'getOptions', 48 'desc' => 'returns a registered route options or the default options', 49 'parameters' => array( 50 'name' => "string unique name for the registered options. if omitted, the default options are returned." 51 ), 52 'return' => array('options'=>'array of options or null if the name does not exists.'), 53 ); 54 $result[] = array( 55 'name' => 'getTree', 56 'desc' => 'gets the tree of NS and pages ', 57 'parameters' => array( 58 'folder' => 'string a folder of the current namespace', 59 ), 60 'return' => array('tree'=>'array'), 61 ); 62 $result[] = array( 63 'name' => 'htmlExplorer', 64 'desc' => 'gets html explorer of the whole wiki.', 65 'parameters' => array( 66 'name' => 'string unique name of a callback/data store.', 67 'base' => 'string ID of the root node', 68 'current' => 'string ID of the current node', 69 ), 70 'return' => array('tree'=>'array'), 71 ); 72 return $result; 73 } 74 75 function registerRoute($name,array $options){ 76 $this->routes[$name] = array_replace_recursive ($this->options,$options); 77 } 78 function getOptions($name = null){ 79 if (!$name) return $this->options; 80 return @$this->routes[$name][$options]; 81 } 82 83 function loadRoute($name,array $reg = null){ 84 if (!$name) return $this->options; 85 if ((! @$this->routes[$name]) && $reg){ 86 if (($p = plugin_load($reg['type'],$reg['plugin'])) && $met = $reg['method']){ 87 call_user_func(array($p,$met),array()); 88 } 89 } 90 return @$this->routes[$name]; 91 } 92 93 94 /** 95 * get a list of namespace / page files 96 * 97 * @param string $folder an already converted filesystem folder of the current namespace 98 */ 99 function getTree($folder=':'){ 100 global $conf; 101 global $ID; 102 // read tree structure from pages and media 103 $ofolder = $folder; 104 if ($folder == '*' || $folder == '') $folder = ':'; 105 if ($folder[0] != ':') $folder = resolve_id($folder,$ID); 106 $dir = strtr(cleanID($folder),':','/'); 107 if (!($this->cache() && is_array($data = $this->cache()->get('explorertree_cache_'.$dir)))){ 108 $data = array(); 109 search($data,$conf['datadir'],'search_index',array('ns'=>getNS($ID)),$dir,$dir == '' ? 1 : count(explode('/',$dir))+1); 110 $count = count($data); 111 if($count>0) for($i=1; $i<$count; $i++){ 112 if($data[$i-1]['id'] == $data[$i]['id'] && $data[$i-1]['type'] == $data[$i]['type']) { 113 unset($data[$i]); 114 $i++; // duplicate found, next $i can't be a duplicate, so skip forward one 115 } 116 } 117 if ($this->cache()) { 118 $this->cache()->set($cache_id = 'explorertree_cache_'.$dir,$data,60); // store the data itself (cache for one minute) 119 } 120 } 121 return $data; 122 } 123 124 /** 125 * Display a tree menu to select a page or namespace 126 * 127 */ 128 function htmlExplorer($name,$base = '',$current = null){ 129 global $lang; 130 if ($base == '' || $base == '*') $base = ':'; 131 if (!($o = $this->loadRoute($name))){ 132 return "<div>Invalid explorertree route!</div>"; //TODO: replace with lang... 133 } 134 $data = $this->getTree($base); 135 // wrap a list with the root level around the other namespaces 136 if ($base == ':'){ 137 array_unshift($data, array( 'level' => 0, 'id' => ':', 'type' => 'd', 138 'open' =>'true', 'label' => '['.$lang['mediaroot'].']')); 139 } 140 $list = html_buildlist($data, 141 $class = $o['vars']['class'], 142 array($this,'_html_list_tree'), 143 array($this,'_html_li_tree') 144 ); 145 if (strncasecmp(trim($list),'<ul ',4)){ 146 $list = "<ul class='{$class}' >".$list."</ul>"; 147 } 148 if (!($id = $o['vars']['id'])){ 149 $id = "explorertree_{$name}"; 150 } 151 if ($base == ':'){ 152 return "<div class='{$class}_root' id='{$id}'>".$list."</div>" 153 ."<script type='text/javascript'>jQuery(document).ready(function(){jQuery('#{$id}').explorerTree(".$this->_treeOpts($name,$current === null ? $base : $current).")});</script>"; 154 } 155 return $list; 156 157 } 158 159 function _treeOpts($name,$current){ 160 $opts = $this->loadRoute($name); 161 $o = array( 162 'route' => $name, 163 'classname' => $opts['vars']['class'], 164 'loader' => $opts['init_plugin'], 165 'current' => ':'.ltrim(strtr($current,'/',':'),':'), 166 'onselectpage' => (bool)$opts['callbacks']['page_selected_cb'], 167 'onselectns' => (bool)$opts['callbacks']['ns_selected_cb'], 168 'onselectnsjs' => null, 169 'onselectpagejs' => null, 170 'token' => getSecurityToken(), 171 ); 172 $json = json_encode($o); 173 $json = preg_replace_callback('~("onselect(ns|page)js"\s*:\s*)null\s*,~',function($m) use ($opts){ 174 if (is_string($x = $opts['callbacks'][$m[2].'_selected_js']) && strlen($x) > 0){ 175 return $m[1].$x.'||null,'; 176 } 177 return $m[0]; 178 },$json); 179 return $json; 180 181 } 182 183 184 185 /** 186 * Item formatter for the tree view 187 * 188 * User function for html_buildlist() 189 * 190 * @author Andreas Gohr <andi@splitbrain.org> 191 */ 192 function _html_list_tree($item){ 193 $ret = ''; 194 // what to display 195 if(!empty($item['label'])){ 196 $base = $item['label']; 197 }else{ 198 $base = ':'.$item['id']; 199 $base = substr($base,strrpos($base,':')+1); 200 } 201 202 // highlight? 203 if( ($item['type']== $this->current_item['type'] && $item['id'] == $this->current_item['id'])) { 204 $cl = ' cur'; 205 } else { 206 $cl = ''; 207 } 208 209 // namespace or page? 210 if($item['type']=='d'){ 211 $ret .= '<a href="#" class="idx_dir'.$cl.' " data-itemid="'.$item["id"].'">'; 212 $ret .= $base; 213 $ret .= '</a>'; 214 }else{ 215 $ret .= '<a href="#" class="wikilink1'.$cl.' " data-itemid="'.$item["id"].'">'; 216 $ret .= noNS($item['id']); 217 $ret .= '</a>'; 218 } 219 return $ret; 220 } 221 222 223 function _html_li_tree($item){ 224 return '<li class="level' . $item['level'] . ' ' .($item["type"] == 'd' ? 'folder ':' '). 225 ($item['open'] ? 'open' : 'closed') . '" data-itemid="'.$item["id"].'">'; 226 } 227 228 229} 230// vim:ts=4:sw=4:et: 231