|
- <?php
-
- /**
- * Info Indexmenu: Show a customizable and sortable index for a namespace.
- *
- * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
- * @author Samuele Tognini <samuele@samuele.netsons.org>
- *
- */
-
- use dokuwiki\Extension\SyntaxPlugin;
- use dokuwiki\File\PageResolver;
- use dokuwiki\plugin\indexmenu\Search;
- use dokuwiki\Ui\Index;
-
- /**
- * All DokuWiki plugins to extend the parser/rendering mechanism
- * need to inherit from this class
- */
- class syntax_plugin_indexmenu_indexmenu extends SyntaxPlugin
- {
- /**
- * What kind of syntax are we?
- */
- public function getType()
- {
- return 'substition';
- }
-
- /**
- * Behavior regarding the paragraph
- */
- public function getPType()
- {
- return 'block';
- }
-
- /**
- * Where to sort in?
- */
- public function getSort()
- {
- return 138;
- }
-
- /**
- * Connect pattern to lexer
- *
- * @param string $mode
- */
- public function connectTo($mode)
- {
- $this->Lexer->addSpecialPattern('{{indexmenu>.+?}}', $mode, 'plugin_indexmenu_indexmenu');
- }
-
- /**
- * Handler to prepare matched data for the rendering process
- *
- * @param string $match The text matched by the patterns
- * @param int $state The lexer state for the match
- * @param int $pos The character position of the matched text
- * @param Doku_Handler $handler The Doku_Handler object
- * @return array Return an array with all data you want to use in render
- *
- * @throws Exception
- */
- public function handle($match, $state, $pos, Doku_Handler $handler)
- {
- $theme = 'default'; // name of theme for images and additional css
- $level = -1; // requested depth of initial opened nodes, -1:all
- $max = 0; // number of levels loaded initially, rest should be loaded with ajax. (TODO actual default is 1)
- $maxAjax = 1; // number of levels loaded per ajax request
- $subNSs = [];
- $skipNsCombined = [];
- $skipFileCombined = [];
- $skipNs = '';
- $skipFile = '';
- /* @deprecated 2022-04-15 dTree only */
- $maxJs = 1;
- /* @deprecated 2022-04-15 dTree only. Fancytree always random id */
- $gen_id = 'random';
- /* @deprecated 2021-07-01 -- allow (temporary) switching between versions of the js treemenu */
- $jsVersion = 1; // 0:both, 1:dTree, 2:Fancytree
- /* @deprecated 2022-04-15 dTree only */
- $jsAjax = '';
-
- $defaultsStr = $this->getConf('defaultoptions');
- $defaults = explode(' ', $defaultsStr);
-
- $match = substr($match, 12, -2);
- //split namespace,level,theme
- [$nsStr, $optsStr] = array_pad(explode('|', $match, 2), 2, '');
- //split options
- $opts = explode(' ', $optsStr);
-
- //Context option
- $context = $this->hasOption($defaults, $opts, 'context');
-
- //split subnamespaces with their level of open/closed nodes
- // PREG_SPLIT_NO_EMPTY flag filters empty pieces e.g. due to multiple spaces
- $nsStrs = preg_split("/ /u", $nsStr, -1, PREG_SPLIT_NO_EMPTY);
- //skips i=0 because that becomes main $ns
- $counter = count($nsStrs);
- //skips i=0 because that becomes main $ns
- for ($i = 1; $i < $counter; $i++) {
- $subns_lvl = explode("#", $nsStrs[$i]);
- //context should parse this later in correct context
- if (!$context) {
- $subns_lvl[0] = $this->parseNs($subns_lvl[0]);
- }
- $subNSs[] = [
- $subns_lvl[0], //subns
- isset($subns_lvl[1]) && is_numeric($subns_lvl[1]) ? $subns_lvl[1] : -1 // level
- ];
- }
- //empty pieces were filtered
- if ($nsStrs === []) {
- $nsStrs[0] = '';
- }
- //split main requested namespace
- if (preg_match('/(.*)#(\S*)/u', $nsStrs[0], $matched_ns_lvl)) {
- //split level
- $ns = $matched_ns_lvl[1];
- if (is_numeric($matched_ns_lvl[2])) {
- $level = (int)$matched_ns_lvl[2];
- }
- } else {
- $ns = $nsStrs[0];
- }
- //context needs to be resolved later
- if (!$context) {
- $ns = $this->parseNs($ns);
- }
-
- //nocookie option (disable for uncached pages)
- /* @deprecated 2023-11 dTree only?, too complex */
- $nocookie = $context || $this->hasOption($defaults, $opts, 'nocookie');
- //noscroll option
- /** @deprecated 2023-11 dTree only and too complex */
- $noscroll = $this->hasOption($defaults, $opts, 'noscroll');
- //Open at current namespace option
- $navbar = $this->hasOption($defaults, $opts, 'navbar');
- //no namespaces options
- $nons = $this->hasOption($defaults, $opts, 'nons');
- //no pages option
- $nopg = $this->hasOption($defaults, $opts, 'nopg');
- //disable toc preview
- $notoc = $this->hasOption($defaults, $opts, 'notoc');
- //disable the right context menu
- $nomenu = $this->hasOption($defaults, $opts, 'nomenu');
- //Main sort method
- $tsort = $this->hasOption($defaults, $opts, 'tsort');
- $dsort = $this->hasOption($defaults, $opts, 'dsort');
- if ($tsort) {
- $sort = 't';
- } elseif ($dsort) {
- $sort = 'd';
- } else {
- $sort = 0;
- }
- //sort directories in the same way as files
- $nsort = $this->hasOption($defaults, $opts, 'nsort');
- //group namespaces and pages both sorted separately with same sorting, or nogroup: mix them and sort together
- $group = !$this->hasOption($defaults, $opts, 'nogroup');
-
- //sort headpages up
- $hsort = $this->hasOption($defaults, $opts, 'hsort');
- //Metadata sort method
- if ($msort = $this->hasOption($defaults, $opts, 'msort')) {
- $msort = 'indexmenu_n';
- } elseif ($value = $this->getOption($defaultsStr, $optsStr, '/msort#(\S+)/u')) {
- $msort = str_replace(':', ' ', $value);
- }
- //reverse sort
- $rsort = $this->hasOption($defaults, $opts, 'rsort');
-
- if ($sort) $jsAjax .= "&sort=" . $sort;
- if ($msort) $jsAjax .= "&msort=" . $msort;
- if ($rsort) $jsAjax .= "&rsort=1";
- if ($nsort) $jsAjax .= "&nsort=1";
- if ($group) $jsAjax .= "&group=1";
- if ($hsort) $jsAjax .= "&hsort=1";
- if ($nopg) $jsAjax .= "&nopg=1";
-
- //javascript option
- $dir = '';
- //check defaults for js,js#theme, #theme
- if (!$js = in_array('js', $defaults)) {
- if (preg_match('/(?:^|\s)(js)?#(\S*)/u', $defaultsStr, $matched_js_theme) > 0) {
- if (!empty($matched_js_theme[1])) {
- $js = true;
- }
- if (isset($matched_js_theme[2])) {
- $dir = $matched_js_theme[2];
- }
- }
- }
- //check opts for nojs,#theme or js,js#theme
- if ($js) {
- if (in_array('nojs', $opts)) {
- $js = false;
- } elseif (preg_match('/(?:^|\s)(?:js)?#(\S*)/u', $optsStr, $matched_theme) > 0) {
- if (isset($matched_theme[1])) {
- $dir = $matched_theme[1];
- }
- }
- } elseif ($js = in_array('js', $opts)) {
- //use theme from the defaults
- } elseif (preg_match('/(?:^|\s)js#(\S*)/u', $optsStr, $matched_theme) > 0) {
- $js = true;
- if (isset($matched_theme[1])) {
- $dir = $matched_theme[1];
- }
- }
-
- if ($js) {
- //exist theme?
- if (!empty($dir) && is_dir(DOKU_PLUGIN . "indexmenu/images/" . $dir)) {
- $theme = $dir;
- }
-
- //id generation method
- /* @deprecated 2023-11 not needed anymore */
- $gen_id = $this->getOption($defaultsStr, $optsStr, '/id#(\S+)/u');
-
- //max option: #n is no of lvls during initialization , #m levels retrieved per ajax request
- $matchPattern = '/max#(\d+)(?:$|\s+|#(\d+))/u';
- if ($matched_lvl_sublvl = $this->getOption($defaultsStr, $optsStr, $matchPattern, true)) {
- $max = $matched_lvl_sublvl[1];
- if (!empty($matched_lvl_sublvl[2])) {
- $jsAjax .= "&max=" . $matched_lvl_sublvl[2];
- $maxAjax = (int)$matched_lvl_sublvl[2];
- }
- //disable cookie to avoid javascript errors
- $nocookie = true;
- } else {
- $max = 0; //todo current default seems 1.
- }
-
- //max js option
- if ($maxjs_lvl = $this->getOption($defaultsStr, $optsStr, '/maxjs#(\d+)/u')) {
- $maxJs = $maxjs_lvl;
- }
- /* @deprecated 2021-07-01 -- allow (temporary) switching between versions of the js treemenu */
- $treeNew = $this->hasOption($defaults, $opts, 'treenew'); //overrides old and both
- /* @deprecated 2021-07-01 -- allow (temporary) switching between versions of the js treemenu */
- $treeOld = $this->hasOption($defaults, $opts, 'treeold'); //overrides both
- /* @deprecated 2021-07-01 -- allow (temporary) switching between versions of the js treemenu */
- $treeBoth = $this->hasOption($defaults, $opts, 'treeboth');
- // $jsVersion = $treeNew ? 2 : ($treeOld ? 1 : ($treeBoth ? 0 : $jsVersion));
- $jsVersion = $treeOld ? 1 : ($treeNew ? 2 : ($treeBoth ? 0 : $jsVersion));
- // error_log('$treeOld:'.$treeOld.'$treeNew:'.$treeNew.'$treeBoth:'.$treeBoth);
-
- if ($jsVersion !== 1) {
- //check for theme of fancytree (overrides old dTree theme eventually?)
- if (!empty($dir) && is_dir(DOKU_PLUGIN . 'indexmenu/scripts/fancytree/skin-' . $dir)) {
- $theme = $dir;
- }
- // $theme='default' is later overwritten by 'win7'
- }
- }
- if (is_numeric($gen_id)) {
- /* @deprecated 2023-11 not needed anymore */
- $identifier = $gen_id;
- } elseif ($gen_id == 'ns') {
- $identifier = sprintf("%u", crc32($ns));
- } else {
- $identifier = uniqid(random_int(0, mt_getrandmax()));
- }
-
- //skip namespaces in index
- $skipNsCombined[] = $this->getConf('skip_index');
- if (preg_match('/skipns[+=](\S+)/u', $optsStr, $matched_skipns) > 0) {
- //first sign is: '+' (parallel to conf) or '=' (replace conf)
- $action = $matched_skipns[0][6];
- $index = 0;
- if ($action == '+') {
- $index = 1;
- }
- //directly used in search
- $skipNsCombined[$index] = $matched_skipns[1];
- //fancytree
- $skipNs = ($action == '+' ? '+' : '=') . $matched_skipns[1];
- //dTree
- $jsAjax .= "&skipns=" . utf8_encodeFN(($action == '+' ? '+' : '=') . $matched_skipns[1]);
- }
- //skip file
- $skipFileCombined[] = $this->getConf('skip_file');
- if (preg_match('/skipfile[+=](\S+)/u', $optsStr, $matched_skipfile) > 0) {
- //first sign is: '+' (parallel to conf) or '=' (replace conf)
- $action = $matched_skipfile[0][8];
- $index = 0;
- if ($action == '+') {
- $index = 1;
- }
- //directly used in search
- $skipFileCombined[$index] = $matched_skipfile[1];
- //fancytree
- $skipFile = ($action == '+' ? '+' : '=') . $matched_skipfile[1];
- //dTree
- $jsAjax .= "&skipfile=" . utf8_encodeFN(($action == '+' ? '+' : '=') . $matched_skipfile[1]);
- }
-
- //js options
- return [
- $ns, //0
- [ //1=js_dTreeOpts
- 'theme' => $theme,
- 'identifier' => $identifier, //deprecated
- 'nocookie' => $nocookie, //deprecated
- 'navbar' => $navbar,
- 'noscroll' => $noscroll, //deprecated
- 'maxJs' => $maxJs, //deprecated
- 'notoc' => $notoc, //will be changed to default notoc
- 'jsAjax' => $jsAjax, //deprecated
- 'context' => $context, //only in handler()?
- 'nomenu' => $nomenu //will be changed to default nomenu
- ],
- [ //2=sort
- 'sort' => $sort,
- 'msort' => $msort,
- 'rsort' => $rsort,
- 'nsort' => $nsort,
- 'group' => $group,
- 'hsort' => $hsort
- ],
- [ //3=opts
- 'level' => $level, // requested depth of initial opened nodes, -1:all
- 'nons' => $nons,
- 'nopg' => $nopg,
- 'subnss' => $subNSs, //only used for initial load
- 'navbar' => $navbar, //add current ns to subNSs, for initial load
- 'max' => $max, //number of levels loaded initially, rest should be loaded with ajax
- 'maxajax' => $maxAjax, //number of levels loaded per ajax request
- 'js' => $js,
- 'skipnscombined' => $skipNsCombined,
- 'skipfilecombined' => $skipFileCombined,
- 'skipns' => $skipNs,
- 'skipfile' => $skipFile,
- 'headpage' => $this->getConf('headpage'),
- 'hide_headpage' => $this->getConf('hide_headpage'),
- 'theme' => $theme
- ],
- $jsVersion //4
- ];
- }
-
- /**
- * Looks if the default options and syntax options has the requested option
- *
- * @param array $defaultsOpts array of default options
- * @param array $opts array of options provided via syntax
- * @param string $optionName name of requested option
- * @return bool has $optionName?
- */
- private function hasOption($defaultsOpts, $opts, $optionName)
- {
- $name = $optionName;
- if (substr($optionName, 0, 2) == 'no') {
- $inverseName = substr($optionName, 2);
- } else {
- $inverseName = 'no' . $optionName;
- }
-
- if (in_array($name, $defaultsOpts)) {
- return !in_array($inverseName, $opts);
- } else {
- return in_array($name, $opts);
- }
- }
-
- /**
- * Looks for the value of the requested option in the default options and syntax options
- *
- * @param string $defaultsString default options string
- * @param string $optsString syntax options string
- * @param string $matchPattern pattern to search for
- * @param bool $multipleMatches if multiple returns array, otherwise the first match
- * @return string|array
- */
- private function getOption($defaultsString, $optsString, $matchPattern, $multipleMatches = false)
- {
- if (preg_match($matchPattern, $optsString, $match_o) > 0) {
- if ($multipleMatches) {
- return $match_o;
- } else {
- return $match_o[1];
- }
- } elseif (preg_match($matchPattern, $defaultsString, $match_d) > 0) {
- if ($multipleMatches) {
- return $match_d;
- } else {
- return $match_d[1];
- }
- }
- return false;
- }
-
- /**
- * Handles the actual output creation.
- *
- * @param string $format output format being rendered
- * @param Doku_Renderer $renderer the current renderer object
- * @param array $data data created by handler()
- * @return boolean rendered correctly?
- */
- public function render($format, Doku_Renderer $renderer, $data)
- {
- global $ACT;
- global $conf;
- global $INFO;
-
- $ns = $data[0];
- //theme, identifier, nocookie, navbar, noscroll, maxJs, notoc, jsAjax, context, nomenu
- $js_dTreeOpts = $data[1];
- //sort, msort, rsort, nsort, group, hsort
- $sort = $data[2];
- //opts for search(): level, nons, nopg, subnss, max, maxajax, js, skipns, skipfile, skipnscombined,
- //skipfilecombined, headpage, hide_headpage
- $opts = $data[3];
- /* @deprecated 2021-07-01 temporary */
- $jsVersion = $data[4];
-
- if ($format == 'xhtml') {
- if ($ACT == 'preview') {
- //Check user permission to display indexmenu in a preview page
- if (
- $this->getConf('only_admins') &&
- $conf['useacl'] &&
- $INFO['perm'] < AUTH_ADMIN
- ) {
- return false;
- }
- //disable cookies
- $js_dTreeOpts['nocookie'] = true;
- }
- if ($opts['js'] & $conf['defer_js']) {
- msg(
- 'Indexmenu Plugin: If you use the \'js\'-option of the indexmenu plugin, you have to '
- . 'disable the <a href="https://www.dokuwiki.org/config:defer_js">\'defer_js\'</a>-setting. '
- . 'This setting is temporary, in the future the indexmenu plugin will be improved.',
- -1
- );
- }
- //Navbar with nojs
- if ($js_dTreeOpts['navbar'] && !$opts['js']) {
- if (!isset($ns)) {
- $ns = ':';
- }
- //add ns of current page to let open these nodes (within the $ns), open only 1 level.
- $currentNS = getNS($INFO['id']);
- if ($currentNS !== false) {
- $opts['subnss'][] = [$currentNS, 1];
- }
- $renderer->info['cache'] = false;
- }
- if ($js_dTreeOpts['context']) {
- //resolve ns and subns's relative to current wiki page (instead of sidebar)
- $ns = $this->parseNs($ns, $INFO['id']);
- foreach ($opts['subnss'] as $key => $value) {
- $opts['subnss'][$key][0] = $this->parseNs($value[0], $INFO['id']);
- }
- $renderer->info['cache'] = false;
- }
- //build index
- $html = $this->buildHtmlIndexmenu($ns, $js_dTreeOpts, $sort, $opts, $jsVersion);
- //alternative if empty
- if (!@$html) {
- $html = $this->getConf('empty_msg');
- $html = str_replace('{{ns}}', cleanID($ns), $html);
- $html = p_render('xhtml', p_get_instructions($html), $info);
- }
- $renderer->doc .= $html;
- return true;
- } elseif ($format == 'metadata') {
- /** @var Doku_Renderer_metadata $renderer */
- if (!($js_dTreeOpts['navbar'] && !$opts['js']) && !$js_dTreeOpts['context']) {
- //this is an indexmenu page that needs the PARSER_CACHE_USE event trigger;
- $renderer->meta['indexmenu']['hasindexmenu'] = true;
- }
- //summary
- $renderer->doc .= (empty($ns) ? $conf['title'] : nons($ns)) . " index\n\n";
- unset($renderer->persistent['indexmenu']);
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Return the index
- *
- * @param string $ns
- * @param array $js_dTreeOpts entries: theme, identifier, nocookie, navbar, noscroll, maxJs, notoc, jsAjax, context,
- * nomenu
- * @param array $sort entries: sort, msort, rsort, nsort, group, hsort
- * @param array $opts entries of opts for search(): level, nons, nopg, nss, max, maxajax, js, skipns, skipfile,
- * skipnscombined, skipfilecombined, headpage, hide_headpage
- * @param int $jsVersion
- * @return bool|string return html for a nojs index and when enabled the js rendered index, otherwise false
- *
- * @author Samuele Tognini <samuele@samuele.netsons.org>
- */
- private function buildHtmlIndexmenu($ns, $js_dTreeOpts, $sort, $opts, $jsVersion)
- {
- $js_name = "indexmenu_" . $js_dTreeOpts['identifier'];
- //TODO temporary hack, to switch in Search between searchIndexmenuItemsNew() and searchIndexmenuItems()
- $opts['tempNew'] = false;
- $search = new Search($sort);
- $nodes = $search->search($ns, $opts);
-
- if (!$nodes) return false;
-
- // javascript index
- $output_js = '';
- if ($opts['js']) {
- $ns = str_replace('/', ':', $ns);
-
- // $jsversion: 0:both, 1:dTree, 2:Fancytree
- if ($jsVersion < 2) {
- $output_js .= $this->builddTree($nodes, $ns, $js_dTreeOpts, $js_name, $opts['max']);
- }
- if ($jsVersion !== 1) {
- $output_js .= $this->buildFancyTree($js_name, $ns, $opts, $sort);
- }
-
- //remove unwanted nodes from standard index
- $this->cleanNojsData($nodes);
- }
- $output = "\n";
- $output .= $this->buildNoJSTree($nodes, $js_name, $js_dTreeOpts['jsAjax']);
- $output .= $output_js;
- return $output;
- }
-
- private function buildNoJSTree($nodes, $js_name, $jsAjax)
- {
- // Nojs dokuwiki index
- // extra div needed when index is first element in sidebar of dokuwiki template, template uses this to
- // toggle sidebar the toggle interacts with hide needed for js option.
- $idx = new Index();
- return '<div>'
- . '<div id="nojs_' . $js_name . '" data-jsajax="' . utf8_encodeFN($jsAjax) . '" class="indexmenu_nojs">'
- . html_buildlist($nodes, 'idx', [$this, 'formatIndexmenuItem'], [$idx, 'tagListItem'])
- . '</div>'
- . '</div>';
- }
-
- private function buildFancyTree($js_name, $ns, $opts, $sort)
- {
- global $conf;
- //not needed, because directly retrieved from config
- unset($opts['headpage']);
- unset($opts['hide_headpage']);
- unset($opts['js']); //always true
- unset($opts['skipnscombined']);
- unset($opts['skipfilecombined']);
-
- /* @deprecated 2023-08-14 remove later */
- if ($opts['theme'] == 'default') {
- $opts['theme'] = 'win7';
- }
- $options = [
- 'ns' => $ns,
- 'opts' => $opts,
- 'sort' => $sort,
- 'contextmenu' => false,
- 'startpage' => $conf['start'] //needed? or for contextmenu?
- ];
- return '<div id="tree2_' . $js_name . '" class="indexmenu_js2 skin-' . $opts['theme'] . '"'
- . 'data-options=\'' . json_encode($options) . '\'></div>';
- }
-
- /**
- * Build the browsable index of pages using javascript
- *
- * @param array $nodes array with items of the tree
- * @param string $ns requested namespace
- * @param array $js_dTreeOpts options for javascript renderer
- * @param string $js_name identifier for this index
- * @param int $max the node at $max level will retrieve all its child nodes through the AJAX mechanism
- * @return bool|string returns inline javascript or false
- *
- * @author Samuele Tognini <samuele@samuele.netsons.org>
- * @author Rene Hadler
- *
- * @deprecated 2023-11 will be replace by Fancytree
- */
- private function builddTree($nodes, $ns, $js_dTreeOpts, $js_name, $max)
- {
- global $conf;
- $hns = false;
- if (empty($nodes)) {
- return false;
- }
-
- //TODO jsAjax is empty?? while max is set to 1
- // Render requested ns as root
- $headpage = $this->getConf('headpage');
- // if rootnamespace and headpage, then add startpage as headpage
- // TODO seems not logic, when desired use $conf[headpage]=:start: ??
- if (empty($ns) && !empty($headpage)) {
- $headpage .= ',' . $conf['start'];
- }
- $title = Search::getNamespaceTitle($ns, $headpage, $hns);
- if (empty($title)) {
- if (empty($ns)) {
- $title = hsc($conf['title']);
- } else {
- $title = $ns;
- }
- }
- // inline javascript
- $out = "<script type='text/javascript'>\n";
- $out .= "<!--//--><![CDATA[//><!--\n";
- $out .= "var $js_name = new dTree('" . $js_name . "','" . $js_dTreeOpts['theme'] . "');\n";
- //javascript config options
- $sepchar = idfilter(':', false);
- $out .= "$js_name.config.urlbase='" . substr(wl(":"), 0, -1) . "';\n";
- $out .= "$js_name.config.sepchar='" . $sepchar . "';\n";
- if ($js_dTreeOpts['notoc']) {
- $out .= "$js_name.config.toc=false;\n";
- }
- if ($js_dTreeOpts['nocookie']) {
- $out .= "$js_name.config.useCookies=false;\n";
- }
- if ($js_dTreeOpts['noscroll']) {
- $out .= "$js_name.config.scroll=false;\n";
- }
- //1 is default in dTree
- if ($js_dTreeOpts['maxJs'] > 1) {
- $out .= "$js_name.config.maxjs=" . $js_dTreeOpts['maxJs'] . ";\n";
- }
- if (!empty($js_dTreeOpts['jsAjax'])) {
- $out .= "$js_name.config.jsajax='" . utf8_encodeFN($js_dTreeOpts['jsAjax']) . "';\n";
- }
-
- //add root node
- $out .= $js_name . ".add('" . idfilter(cleanID($ns), false) . "',0,-1," . json_encode($title);
- if ($hns) {
- $out .= ",'" . idfilter(cleanID($hns), false) . "'";
- }
- $out .= ");\n";
- //add nodes
- [$nodesArray, $openNodes] = $this->builddTreeNodes($nodes, $js_name);
- $out .= $nodesArray;
- //write to document
- $out .= "document.write(" . $js_name . ");\n";
- //initialize index
- $out .= "jQuery(function(){" . $js_name . ".init(";
- $out .= (int)is_file(DOKU_PLUGIN . 'indexmenu/images/' . $js_dTreeOpts['theme'] . '/style.css') . ",";
- $out .= (int)$js_dTreeOpts['nocookie'] . ",";
- $out .= '"' . $openNodes . '",';
- $out .= (int)$js_dTreeOpts['navbar'] . ",";
- $out .= (int)$max;
- if ($js_dTreeOpts['nomenu']) {
- $out .= ",1";
- }
- $out .= ");});\n";
-
- $out .= "//--><!]]>\n";
- $out .= "</script>\n";
- return $out;
- }
-
- /**
- * Return array of javascript nodes and nodes to open.
- *
- * @param array $nodes array with items of the tree
- * @param string $js_name identifier for this index
- * @param boolean $noajax return as inline js (=true) or array for ajax response (=false)
- * @return array|bool returns array with
- * - a string of the javascript nodes
- * - and a string of space separated numbers of the opened nodes
- * or false when no data provided
- *
- * @author Samuele Tognini <samuele@samuele.netsons.org>
- *
- * @deprecated 2023-11 will be replace by Fancytree
- */
- public function builddTreeNodes($nodes, $js_name, $noajax = true)
- {
- if (empty($nodes)) {
- return false;
- }
- //Array of nodes to check
- $q = ['0'];
- //Current open node
- $currentOpenNode = 0;
- $out = '';
- $openNodes = '';
- if ($noajax) {
- $jscmd = $js_name . ".add";
- $separator = ";\n";
- } else {
- $jscmd = "new Array ";
- $separator = ",";
- }
-
- foreach ($nodes as $i => $node) {
- $i++;
- //Remove already processed nodes (greater level = lower level)
- while (isset($nodes[end($q) - 1]) && $node['level'] <= $nodes[end($q) - 1]['level']) {
- array_pop($q);
- }
-
- //till i found its father node
- if ($node['level'] == 1) {
- //root node
- $father = '0';
- } else {
- //Father node
- $father = end($q);
- }
- //add node and its options
- if ($node['type'] == 'd') {
- //Search the lowest open node of a tree branch in order to open it.
- if ($node['open']) {
- if ($node['level'] < $nodes[$currentOpenNode]['level']) {
- $currentOpenNode = $i;
- } else {
- $openNodes .= "$i ";
- }
- }
- //insert node in last position
- $q[] = $i;
- }
- $out .= $jscmd . "('" . idfilter($node['id'], false) . "',$i," . $father
- . "," . json_encode($node['title']);
- //hns
- if ($node['hns']) {
- $out .= ",'" . idfilter($node['hns'], false) . "'";
- } else {
- $out .= ",0";
- }
- if ($node['type'] == 'd' || $node['type'] == 'l') {
- $out .= ",1";
- } else {
- $out .= ",0";
- }
- //MAX option
- if ($node['type'] == 'l') {
- $out .= ",1";
- } else {
- $out .= ",0";
- }
- $out .= ")" . $separator;
- }
- $openNodes = rtrim($openNodes, ' ');
- return [$out, $openNodes];
- }
-
- /**
- * Parse namespace request
- *
- * @param string $ns namespaceid
- * @param bool $id page id to resolve $ns relative to.
- * @return string id of namespace
- *
- * @author Samuele Tognini <samuele@samuele.netsons.org>
- */
- public function parseNs($ns, $id = false)
- {
- if ($id === false) {
- global $ID;
- $id = $ID;
- }
- //Just for old releases compatibility, .. was an old version for : in the docs of indexmenu
- if ($ns == '..') {
- $ns = ":";
- }
- $ns = "$ns:arandompagehere";
- $resolver = new PageResolver($id);
- $ns = getNs($resolver->resolveId($ns));
- return $ns === false ? '' : $ns;
- }
-
- /**
- * Clean index data from unwanted nodes in nojs mode.
- *
- * @param array $nodes nodes of the tree
- * @return void
- *
- * @author Samuele Tognini <samuele@samuele.netsons.org>
- */
- private function cleanNojsData(&$nodes)
- {
- $a = 0;
- foreach ($nodes as $i => $node) {
- //all entries before $a are unset
- if ($i < $a) {
- continue;
- }
- //closed node
- if ($node['type'] == "d" && !$node['open']) {
- $a = $i + 1;
- $level = $node['level'];
- //search and remove every lower and closed nodes
- while (isset($nodes[$a]) && $nodes[$a]['level'] > $level && !$nodes[$a]['open']) {
- unset($nodes[$a]);
- $a++;
- }
- }
- }
- }
-
-
- /**
- * Callback to print a Indexmenu item
- *
- * User function for @param array $item item described by array with at least the entries
- * - id page id/namespace id
- * - type 'd', 'l'(directory which is not yet opened) or 'f'
- * - open is node open
- * - title title of link
- * - hns page id of headpage of the namespace or false
- * @return string html of the content of a list item
- *
- * @author Samuele Tognini <samuele@samuele.netsons.org>
- * @author Rik Blok
- * @author Andreas Gohr <andi@splitbrain.org>
- *
- * @see html_buildlist()
- */
- public function formatIndexmenuItem($item)
- {
- global $INFO;
- $ret = '';
-
- //namespace
- if ($item['type'] == 'd' || $item['type'] == 'l') {
- $markCurrentPage = false;
-
- $link = $item['id'];
- $more = 'idx=' . $item['id'];
- //namespace link
- if ($item['hns']) {
- $link = $item['hns'];
- $tagid = "indexmenu_idx_head";
- $more = '';
- //current page is shown?
- $markCurrentPage = $this->getConf('hide_headpage') && $item['hns'] == $INFO['id'];
- } else {
- //namespace without headpage
- $tagid = "indexmenu_idx";
- if ($item['open']) {
- $tagid .= ' open';
- }
- }
-
- if ($markCurrentPage) {
- $ret .= '<span class="curid">';
- }
- $ret .= '<a href="' . wl($link, $more) . '" class="' . $tagid . '" data-wiki-id="'. $item['hns'] . '">'
- . $item['title']
- . '</a>';
- if ($markCurrentPage) {
- $ret .= '</span>';
- }
- return $ret;
- } else {
- //page link
- return html_wikilink(':' . $item['id']);
- }
- }
- }
|