DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:navilevel

navilevel Plugin

Compatible with DokuWiki

2012-10-13

plugin A navigation tree that adapts to the page that hosts it

Last updated on
2007-06-15
Provides
Syntax

This extension has not been updated in over 2 years. It may no longer be maintained or supported and may have compatibility issues.

Similar to navi, pagequery

Tagged with menu, namespace, navigation

This plugin takes as input a list of internal links and, according to the page that hosts it, selectively displays a subset of them as an unordered list (UL). The order of the original input is kept but only 'low level' links, 'ancestors', 'siblings' and 'children' of the current page will be displayed. The selection is based on comparing the path of the hosting page to the paths of the links in the data list. This should be helpful for sites that have a rather deep, tree-like structure.

The problem I tried to solve is how to provide easy navigation for a site with lots of pages arranged in a namespace-based tree. Before that I experimented with per-namespace sidebar pages but I'd need more than a dozen of them. Now I only have one sidebar page, but depending on the current main page, a different subset of the navigation tree gets displayed.

I use

$conf['useheading'] = 1;

and in the page containing the plugin data, I also use

~~NOCACHE~~

I tried it with DokuWiki versions DokuWiki-2006-03-09 up to DokuWiki-2012-10-13 “Adora Belle”.

In Action

You can see this plugin in action at http://www.acsys1.gr/products/index (the page is in Greek - sorry, but I currently have no publicly available example in English). As you might guess, from the above URL, it is about a product line of some company. Feel free to navigate up and down this URL and see how the sidebar changes. There is only one sidebar file (at the root namespace) and the navigation tree adapts to the current page.

Releases

The current release is #5 from 2007-06-14.

New features of ver. 2007-06-14

Release ver. 2007-06-14 supports a

:a:b:c|some_text_before|some_link_text|some_text_after

syntax.

In this way you may have some text before (i.e. 'some_text_before') and after (i.e. 'some_text_after') of the link and also change the link text (i.e. 'some_link_text').

No formatting/markup-expansion of the text takes place - it is displayed as-is.

Note that, there was a change from version #4 on how this works.

Release version 2007-06-14 also has a new feature. If the page that calls the plugin (or the “main” page if it is called from a sidebar page) is named 'sitemap', all nodes will expand. You can see this in action at http://www.acsys1.gr/sitemap and I even use it as my link rel=“contents”.

Issues with older releases

  • release #1 created invalid HTML
  • release #2 was targeted to sites using start pages (with the same name) on each namespace.
  • release #3 needed better configuration options.
  • release #4 had no serious issues but lacked some functionality.

Usage

You must first design your sitemap.

There must be a 1st level link (located in the root namespace) as the 1st argument of the plugin. This typical will be your start page (for example index). However, there may be more 1st level links present. Thus we have:

<navilevel>
:index  <- 1st level link
.
.
</navilevel>
~~NOCACHE~~

The rest of the links must depict your navigation tree and for each link a parent (lower level) link must exist.


This is right (use $lowlevel = 2;):

<navilevel>
:index
:a:index
:a:b:index
:a:b:c:index
:a:b:d:index
</navilevel>
~~NOCACHE~~

This is wrong (:a:b:index is missing):

<navilevel>
:index
:a:index
:a:b:c:index
:a:b:d:index
</navilevel>
~~NOCACHE~~

This is right (use $lowlevel = 1;):

<navilevel>
:a
:a:b
:c
:c:d
:c:d:e
</navilevel>
~~NOCACHE~~

This is wrong (:c is missing):

<navilevel>
:a
:a:b
:c:d
:c:d:e
</navilevel>
~~NOCACHE~~

If your site is using start pages (with the same name) on each namespace then then you should edit syntax.php and set

$lowlevel = 2;

If you don't base your site on a scheme where you use the same start page name under each namespace, like:

<navilevel>
:a
:a:b
:a:b:c
:d
:d:e
:d:e:f
:d:e:g
:d:e:g:h
</navilevel>
~~NOCACHE~~

Then the default setting

$lowlevel = 1;

will work.

Example

Insert something like this:

<navilevel>
:index
:i:index
:i:a:index
:i:b:index
:h:index
:h:c:index
:h:d:index
:h:d:j:index
:h:e:index
:h:f:index
:h:f:o:index
:h:f:q:index
:h:f:p:index
:h:g:index
:h:m:index
:h:n:index
:h:r:index
:j:index
:j:c:index
:j:d:index
:j:e:index
:j:r:index
:k:index
:k:l:index
:k:l:s:index
:k:l:s:j:index
:k:l:s:j:t:index
:k:l:s:j:u:index
:k:l:s:v:index
:k:l:s:z:index
:k:l:w:index
:k:l:w:x:index
:k:l:w:y:index
</navilevel>
~~NOCACHE~~

in the pages:

:h:index
:h:f:index
:h:f:q:index

and see the different results, depending on the page that hosts it.

The plugin will try to create a reasonable navigation tree based on the current page.

Installation

Search and install the plugin using the Extension Manager. Refer to Plugins on how to install plugins manually.

After installing it you may have to edit lib/plugins/navilevel/syntax.php and change $lowlevel as per the above instructions.

Alternatively, to install manually, create a folder navilevel in lib/plugins and put the below code in lib/plugins/navilevel/syntax.php:

<?php
/**
 *  Add smart navigation capability to DokuWiki
 *
  *  Roland Hellebart's tree plugin ( his http://www.dokuwiki.org/plugin:tree )
 *  was used as a base for this.
 *
 *  Documentation is located at http://www.dokuwiki.org/plugin:navilevel
 *
 *  @license    GNU_GPL_v2
 *  @author     Thanos Massias
 */
 
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
 
/**
 *  All DokuWiki plugins to extend the parser/rendering mechanism
 *  need to inherit from this class
 */
 class syntax_plugin_navilevel extends DokuWiki_Syntax_Plugin {
 
 
    function getInfo(){
        return array(
            'author' => 'Thanos Massias',
            'email'  => 'thanos.massias@gmail.com',
            'date'   => '2007-06-14',
            'name'   => 'navilevel Plugin',
            'desc'   => 'Add smart Navigation Capability',
            'url'    => 'http://www.acsys.gr/plugins/navilevel/',
        );
    }
 
    /**
     *  What kind of syntax are we?
     */
    function getType(){
        return 'protected';
    }
 
    /**
     *  What kind of syntax do we allow (optional)
     */
    function getAllowedTypes() {
        return array('protected');
    }
 
    /**
     *  What about paragraphs? (optional)
     */
    function getPType(){
        return 'block';
    }
 
    /**
     *  Where to sort in?
     */
    function getSort(){
        return 203;
    }
 
    /**
     *  Connect pattern to lexer
     */
    function connectTo($mode) {
        $this->Lexer->addEntryPattern('<navilevel.*?>(?=.*?</navilevel>)',$mode,'plugin_navilevel');
    }
 
    function postConnect() {
        $this->Lexer->addExitPattern('</navilevel>','plugin_navilevel');
    }
 
    /**
     *  Handle the match
     */
    function handle($match, $state, $pos, &$handler){
        switch ($state) {
          case DOKU_LEXER_ENTER :
            break;
          case DOKU_LEXER_MATCHED :
            break;
          case DOKU_LEXER_UNMATCHED :
            break;
          case DOKU_LEXER_EXIT :
            break;
          case DOKU_LEXER_SPECIAL :
            break;
        }
        return array($match, $state);
    }
 
    /**
     *  Create output
     */
    function render($mode, &$renderer, $data) {
        // You may have to configure $rellevel to suite your site
        //
        // $lowlevel = 1;  --> sites not based on per-namespace start pages (default)
        // $lowlevel = 2;  --> sites     based on per-namespace start pages
        //
        $lowlevel = 1;
 
        global $ID;
        global $ullevellast; 
        $linklineID = ':'.$ID;
        //  In case you want the plugin to work correctly within a sidebar add the 
        //  following two lines in '/dokuwiki_home/lib/tpl/template_name/main.php':
        //  ---------------------
        //  global $PPID;
        //  $PPID = $ID;
        //  ---------------------
        global $PPID;
        if ('X'.$PPID != 'X'){
            if ($ID != $PPID){
                $linklineID = ':'.$PPID;
            }
        }
        $partsID = explode(':', $linklineID);
        $countpartsID = count($partsID);
 
        $showall = 0;
        if (($countpartsID == 2) && ($partsID[1] == 'sitemap')){
          $showall = 1;
        } 
 
        if($mode == 'xhtml'){
            switch ($data[1]) {
                case DOKU_LEXER_ENTER :
                  $renderer->doc .= "\n";
                  break;
 
                case DOKU_LEXER_MATCHED :
                  break;
 
                case DOKU_LEXER_UNMATCHED :
                    $content = $data[0];
 
                    //  clean up the input data
                    //  clear any trailing or leading empty lines from the data set
                    $content = preg_replace("/[\r\n]*$/","",$content);
                    $content = preg_replace("/^\s*[\r\n]*/","",$content);
 
                    //  Not sure if PHP handles the DOS \r\n or Mac \r, so being paranoid
                    //  and converting them if they exist to \n
                    $content = preg_replace("/\r\n/","\n",$content);
                    $content = preg_replace("/\r/","\n",$content);
                    $result = $this->tree_explode_node($content);
 
                    $ullevellast = 0;
 
                    foreach ($result as $input){
                        $arrinput = explode('|', $input);
                        $linkline = $arrinput[0];
                        $linkline = trim($linkline);
                        $linktext_before = $arrinput[1];
                        $linktext_before = trim($linktext_before);
                        $linkname = $arrinput[2];
                        $linkname = trim($linkname);
                        $linktext_after = $arrinput[3];
                        $linktext_after = trim($linktext_after);
                        if (strlen($linkline) > 0) {
                            $linkline = cleanID($linkline);
                            if (substr($linkline,0,1) != ':'){
                                $linkline = ':'.$linkline;
                            }
 
                            $parts = explode(':', $linkline);
                            $countparts = count($parts);
 
                            $listlink = 0;
                            //  low level links
                            if ($countparts < (2 + $lowlevel)){
                                $listlink = 1;
                            }else{
                                //  children, yes - grandchildren and beyond, no
                                if ($countparts <= $countpartsID + 1){
                                    $listlink = 1;
                                    $tmppathID = '';
                                    $tmppath   = '';
                                    $i = 0;
                                    foreach ($parts as $part){
                                        if (($i > 0) && ($i < $countparts - 2)){
                                            if ('X'.$partsID[$i] != 'X') {
                                                $tmppathID .= ':'.$partsID[$i];
                                                $tmppath   .= ':'.$parts[$i];
                                                if ($tmppathID !== $tmppath){
                                                    $listlink = 0;
                                                }
                                            }
                                        }
                                        $i++;
                                    }
                                }
                            }
 
                            if ($listlink || $showall){
                                $ullevel = $countparts - 1;
                                if ($ullevel == $ullevellast){
                                    $renderer->doc .= "\n</li>";
                                }
                                if ($ullevel > $ullevellast){
                                    $renderer->doc .= "\n<ul>";
                                }
                                if ($ullevel < $ullevellast){
                                    for ($j = 0; $j < ($ullevellast - $ullevel); $j++){
                                        $renderer->doc .= "\n</li>\n</ul>";
                                    }
                                    $renderer->doc .= "\n</li>";
                                }
                                $ullevellast = $ullevel;
                                $renderer->doc .= "\n".'<li class="level'.$ullevel.'"><div class="li"> '; 
                                if (strlen($linktext_before) > 0) {
                                    $renderer->doc .= $linktext_before.' ';
                                }
                                if (strlen($linkname) > 0) {
                                    $renderer->doc .= $renderer->internallink($linkline,$linkname);
                                }else{
                                    $renderer->doc .= $renderer->internallink($linkline);
                                }
                                if (strlen($linktext_after) > 0) {
                                    $renderer->doc .= ' '.$linktext_after;
                                }
                                $renderer->doc .= "</div>";
                            }else{
                                $renderer->doc .= "";
                            }
                        }
                    } 
                    break;
 
                case DOKU_LEXER_EXIT :
                    $ullevel = 1;
                    if ($ullevel == $ullevellast){
                        $renderer->doc .= "\n</li>";
                    }
                    if ($ullevel > $ullevellast){
                        $renderer->doc .= "\n<ul>";
                    }
                    if ($ullevel < $ullevellast){
                        for ($j = 0; $j < ($ullevellast - $ullevel); $j++) {
                            $renderer->doc .= "\n</li>\n</ul>";
                        }
                        $renderer->doc .= "\n</li>";
                    }
                    $renderer->doc .= "\n</ul>";
                    break;
 
                case DOKU_LEXER_SPECIAL :
                    break;
            }
            return false;
        }
 
        // unsupported $mode
        return false;
    } 
 
    function tree_explode_node(&$str) {
        $len = strlen($str) + 1;
        $inside = false;
        $word = '';
        for ($i = 0; $i < $len; ++$i) {
            $next = $i+1;
            if ($str[$i] == "\n") {
                $out[] = $word;
                $word = '';
            } elseif ($next == $len) {
                $out[] = $word;
                $word = '';
            } else {
                $word .= $str[$i];
            }
        }
        $str = substr($str, $next);
        $out[] = $word;
        return $out;
    }
}
 
//Setup VIM: ex: et ts=4 enc=utf-8 :
?>

Tip

To have the current link highlighted when using a sidebar template:


Add the following two lines in '/dokuwiki_home/lib/tpl/template_name/main.php':

global $PPID;
$PPID = $ID;

Add in '/dokuwiki_home/inc/parser/xhtml.php' the following lines within

    function internallink($id, $name = NULL, $search=NULL,$returnonly=false) {

After

        global $ID;

add

        global $PPID;

and after

        if ($id == $ID) {
            $link['pre']    = '<span class="curid">';
            $link['suf']    = '</span>';
        }

add

        if ($id == $PPID) {
            $link['pre']    = '<span class="curid">';
            $link['suf']    = '</span>';
        }

Add the following style in '/dokuwiki_home/lib/tpl/template_name/layout.css':

.curid {
        font-weight: bold;
}

Multilingual content

With a slight modification, navilevel can become aware of a multilingual environment. The way it works in this case is demonstrated in http://www.acsys.gr/. The code for that is not included in the version of navilevel presented in this page in order to keep things simple. However, if someone is interested in it, please drop me an e-mail.

Discussion

I have no idea how to use this. Under usage you say:

You must first design your sitemap. There must be a 1st level link (located in the root namespace) as the 1st argument of the plugin. This typically will be your start page (for example index). However, there may be more 1st level links present. Thus we have: <navilevel> :index ← 1st level link . . </navilevel>

What do you mean there must be a 1st level link in the root namespace? What does this mean? I placed your <navilevel> tags in my start page, but that just creates a link to my start page. How do I make a navigation bar that dynamically lists the page hierarchy? I thought that's what this plugin did, based on your sample site. -thanks


———– 2007-01-03

This navigation only based on namespaces and the corresponding page eg. start. You must create the list in e.g. sidebar by hand. There ist no autom. generated list. (I can´t found such a function).

<navilevel>
:start||Home
:de:start||Tutorial (german)
:de:tut01:start||Tut index
:de:tut02:start||Tut installation
:de:tut03:start||Tut first step
:wiki:start||wiki docu
:wiki:de:start||German docu
:wiki:de:start||German syntax
→ and so on
</navilevel>

———– 2009-04-21 - Josef Meile jmeile [at] hotmail [dot] com

What about doing only one index file an including it on each page were you want it to appear? Just like the way the TracNav plugin does it: TracNav: The navigation bar for Trac

Actually, this is close to how this plugin works. I include the relevant code in the sidebar page (I'm obviously using a template with a sidebar) and the navigation tree adapts to the current page. The example site I mention above, demonstrates just that. However, I like the approach of the navi plugin where, unlikely navilevel, you can define a site structure independent of the actual namespace structure. Besides, it is actively developed.

Thanos Massias 2009/05/14 19:33

Unluckily this plugin won't unfold any more with angua. I've used plugin:navi instead, which Function is similar

Hella Breitkopf 2012-02-15

Actually this plugin still works. I'm now using it with “Adora Belle”. BTW, there are some cool things you can do with it by creatively using the 'some_text_before' and 'some_text_after' fields (which can include HTML) that make it irreplaceable for me. The silliest trick was including a <blink>«</blink> marker for a menu item I wanted highlighted. I also added descriptive text after the links, properly positioned with <br /> and even modified the plugin a bit to have some list items permanently expanded.

Thanos Massias 2013/03/15 10:17

plugin/navilevel.txt · Last modified: 2018-06-04 23:36 by Klap-in

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki