[[de:plugin:nstoc|deutsch]] ====== NsToC Syntax Plugin ====== ---- plugin ---- description: Namespace Table-of-Content author : Matthias Watermann email : support@mwat.de type : syntax lastupdate : 2010-02-18 compatible : Frusterick Manners, !Hogfather depends : conflicts : similar : tocselect tags : navigation, menu, listing, namespace, !broken downloadurl: http://dev.mwat.de/dw/syntax_plugin_nstoc.zip ---- A while ago I started a project involving lots of hierar­chi­cally orde­red pa­ges -- just like a book with chapters, sub-chap­ters and para­graphs. To add (and up­date whenever a page was ad­ded/re­mo­ved/mer­ged) the TOC re­fe­ren­ces was a neces­sary but quite stu­pid job((In other words: just what com­pu­ters were made for.)). After doing this pesky task for quite a few ti­mes I de­ci­ded to auto­mate it. --- Enter "''nstoc''". This plugin offers you the ability to gene­rate a //Table Of Con­tents// for a na­me­space with an optio­nal depth. It gene­rates a (possi­bly nested) list of head­lines used in all mat­ched pages. One could say this plugin sees your whole Wiki as one huge docu­ment struc­tured by chap­ters (Wiki name­spaces), sub-chap­ters (the single pa­ges within a name­space) and ap­pro­pri­ate head­lines (H1...H5). ===== Usage ===== The basic markup is just: {{nstoc }} This will create a nested list of all pages((actually, the //head­lines// in/of the pages are shown)) in the cur­rent name­space and all sub-na­me­spa­ces. Please note the space((''space'', ASCII char #32; not to be con­fu­sed with a ''blank'', ASCII char #255)) be­hind the "''nstoc''" key­word: For­get­ting it will trigger Do­ku­Wiki's built-in ''media'' ren­de­rer which you most pro­bab­ly do //not// want here. To limit the output to -- say -- two levels use {{nstoc 2}} The result will be a list with all H1 and H2 head­lines in the cur­rent na­me­space's pages and all H1 head­lines in the pages of all sub-na­me­spaces of the cur­rent one while {{nstoc 3}} will pro­duce a list with all H1/H2/H3 head­lines in the current na­me­space's pa­ges, all H1/H2 head­lines in the pages of all sub-name­spa­ces of the cur­rent one and all H1 head­lines in the pa­ges of all sub-sub-name­spaces. Another way to limit the output is to expli­citly name the name­space: {{nstoc chapter2}} This will show the head­lines (with unlimi­ted depth) in the "''chapter2''" name­space. You may, of course, combine the optional //name­space// and //depth// ar­gu­ments: {{nstoc chapter3 1}} Here only the H1 head­lines of the pages in "''chapter3''" will be shown. ==== Compatibility ==== This plugin is also compatible with 2005-07-13 and was made for that version. It's compatible with 2017 Frusterick Manners but not Hogfather. ==== Hints ==== Here are some tips which might be help­ful for you when wor­king with this plugin. === Order === The generated output -- or, to be more precise: the //order// of the ge­ne­ra­ted list -- might not always be what you'd expect. The reason for this: You, as a hu­man be­ing((I hope ...)), have a no­tion of //meaning// while the com­puter just knows about //data//. To illu­strate this let's as­su­me you're wri­ting a book. Right now you've fini­shed (or at least crea­ted) this pa­ges: - Preface - Introduction - First Chapter - Second Chapter - Appendix When using "''nstoc''" you'll most probably expect a list like the one above. But, alas, the real result would look like * Appendix * First Chapter * Introduction * Preface * Second Chapter Not very helpful, is it? --- The reason is simple: The only thing DokuWiki and this plugin has to deal with are (file and namespace) //names//. But, as it turns out, it's quite easy for you to take this fact to your advan­tage by choo­sing the right page names. For example, name the pages((i.e. the files but //not// the H1 head­line)) like this: * 00_preface * 01_introduction * 02_first_chapter * 03_second_chapter * 99_appendix Theoretically you could even omit all the alphas and just leave the di­gits. But I guess, that would take the compu­te­ri­zing of your work a bit too far. Any­way, as long as the pa­ge and name­space //names// sort in the inten­ded order, "''nstoc''" will pro­duce useful out­put. BTW: This discussion applies to name­space names as well. This means, that you should name your name­spaces accor­ding to their inten­ded po­si­tion (i.e. ac­cor­ding to their re­spec­tive posi­tion((or relevance)) within your over­all pre­sen­ta­tion). If, for exam­ple, the first chap­ter of your book has seve­ral sub-chapters, you should name the name­space "''02_first_chap­ter''"((Note that there will be //no// collision with the //page// name ''02_first_chapter'' since the latter inter­nally uses a file­name exten­sion.)) and the pa­ges therein e.g. "''01_first_sub­ject''", "''02_se­cond_sub­ject''" and so on. Of course, the head­lines there­in should be a little more mea­ning­ful for human readers. === Accessible pages === Starting from the 2007-01-08 release of this plugin all pages not acces­sible to the re­spec­tive cur­rent user/reader are omit­ted from the ge­ne­ra­ted list. In other words: Any user will see a TOC con­tai­ning only pages he/she may ac­tually read. This avoids the in­con­ve­nience for your readers seeing a page/head­line in the TOC but when trying to read it get­ting only an "ac­cess denied" mes­sage. So, assuming you've setup your access control appro­pria­tely((what­ever that may mean for your indi­vi­dual in­stal­la­tion)) you don't have to worry about ex­po­sing (the exis­tence of) pa­ges which your users -- or, at least, some of them -- are not sup­po­sed to see. Another benefit for you is that you don't have to mo­dify the pages con­tai­ning the "''nstoc''" markup when­ever you add a name­space and/or page. To illu­strate this re­mem­ber our vir­tual book pro­ject out­lined [[#order|above]]. Suppose you've got the preface and second chapter fini­shed while still wor­king on the other parts. And you want any casual reader to see only those fini­shed pa­ges but not the work-in-pro­gress ones. So you'd write in your overview page {{nstoc 00_preface}} {{nstoc 03_second_chapter}} Some weeks later the first chapter is ready for public review. To make them ac­ces­sible you'd change the over­view page to {{nstoc 00_preface}} {{nstoc 02_first_chapter}} {{nstoc 03_second_chapter}} Then turning to chapter seven some time later again you'd change the over­view page: {{nstoc 00_preface}} {{nstoc 02_first_chapter}} {{nstoc 03_second_chapter}} {{nstoc 08_seventh_chapter}} And so on ... Using [[:acl|access control]] you would initially set the book's name­space to be un­read­able by anyone but yourself. In the overview page you just insert: {{nstoc }} Now, whenever you find one of your book's pages worth for public con­sump­tion you just add a line to your access control like book:00_preface @ALL 1 or whatever you feel appro­priate((You might find it worth­while to check out the experi­mental [[plugin:acl|ACL Plugin]].)). You won't have to change the book's over­view page ever again -- at least, not to update the "''nstoc''" markup, that is. Every­thing is ma­na­ged by this plugin and Doku­Wiki's access con­trol system. Starting from the 2007-08-15 release of this plugin pages matched by the global [[config:hidepages|'hidepages']] set­ting (i.e. a regu­lar expres­sion((see "''man 7 regex''" for details))) will be omitted in the gene­ra­ted list as well((Note that this plugin does //not// share Doku­Wiki's bug in hand­ling hidden pages if the RegEx is e.g. "''0''" but tests the ''$conf['hidepages']'' set­ting pro­perly.)). === Index pages === If the name given after the "''nstoc''" keyword resolves to a default page name (i.e. "''start''" with an un­mo­di­fied Doku­Wiki in­stal­la­tion) the re­spec­tive //name­space// is used for ge­ne­ra­ting the TOC but //not// the page. The same happens if you're poin­ting to a page with the same name as a sub-name­space. In case you're gene­ra­ting a TOC for a name­space that inclu­des sub-name­spaces all those assumed in­dex pages((i.e. pages with the ''%%$conf['start']%%'' name and pages with the same name as a sub-direc­tory)) are skip­ped as well. This fea­ture is inten­ded to avoid in­dexing pa­ges that are al­ready meant to be kind of over­view pages. === Root page === With earlier releases the //root// namespace //had// to be treated spe­cially. Star­ting with the 2007-08-12 release of this plugin the root name­space now is handled al­most as each other name­space. So you could use the basic markup {{nstoc }} to generate a TOC with all available pages in your Doku­Wiki instal­la­tion. Assuming a fairly structured installa­tion, how­ever, the pa­ges in the root name­space are most pro­bably some kind of star­ting point for one sub-name­space or another. Hence it seems sensible to use "''nstoc''" in the root name­space in a more explicit way like {{nstoc intro_page}} {{nstoc ns1 2}} {{nstoc ns2 1}} {{nstoc ns3:ns3a}} Such a markup will exclude the //pages// in the root name­space but show only those head­lines found in the speci­fied sub-name­spaces. === Numeric namespace names === Some people -- as I've been told -- do prefer to use nume­ric name­space names such as "''1''", "''23''" or "''456''". Al­though this isn't a pro­blem as such for this plugin you must be care­ful when wri­ting the ''ns­toc'' markup. I've sta­ted [[#usage|above]] that gi­ving a name­space's name is enough to get all head­lines of that name­space (incl. its sub-name­spa­ces) with unli­mi­ted depth. So {{nstoc 23}} should show the headlines of name­space "''23''", right? -- Wrong: The plugin inter­prets this as a //max. depth// va­lue of 23 for the cur­rent name­space. To make sure the "''23''" is accepted as the name­space's name you have to use the 2-ar­gu­ments vari­ant i.e. gi­ving the max. depth va­lue as well: {{nstoc 23 4}} Now the namespace called "''23''" would get in­dexed up to a nes­ting depth of 4 levels. -- Easy, isn't it? === Navigating === This plugin allows for //relative// addres­sing the desired name­space as well. Con­si­de­ring the book example above and assu­ming there's a sub-nam­espace in the first chapter called ''03_im­por­tant_points'' let's sup­pose you're in the name­space of the second chapter (i.e. in ''03_se­cond_chapter''). Now you'd like to pro­vide links to the men­ti­oned pages for your rea­ders. You could do this by either use an //abso­lute// path like {{nstoc :book:02_first_chapter:03_important_points 2}} or use a //rela­tive// path like {{nstoc ..:02_first_chapter:03_important_points 2}} With this example it's only a difference of 3 charac­ters. But the dee­per your na­me­spaces are nested the more you save ty­ping. And -- as an addi­tio­nal bene­fit -- using //rela­tive// paths lea­ves those links intact if you move the whole book to an­other place: If -- for instance -- you de­cide to move/re­name the whole ''book'' name­space into a new ''my_books'' name­space un­der the new name ''big_bang'' (or what­ever) the //abso­lute// path above would no lon­ger show any links while the //rela­tive// one will still work as ex­pected((Please note that the ''pagemove'' plugin has problems with both internal and relative links. However, this doesn't affect the ''nstoc'' markup since it isn't recognized by ''pagemove'' anyway.)). Besides DokuWiki's '':'' (colon) path sepa­rator this plugin allows the stan­dard UNIX ''/'' (slash) as well. Hence the se­cond exam­ple above could be writ­ten {{nstoc ../02_first_chapter/03_important_points 2}} as well. The respec­tive cur­rent name­space can be addres­sed as ''./'', the pa­rent name­space as ''../'' and the root name­space as ''/''. This seems to be more intui­tive at least for those who are fami­liar with a shell com­mand­line. === Caching === At least as long as the name­space/page struc­ture you're in­dexing by this plugin is like­ly to change you should place the "''%%~~NOCACHE~~%%''" direc­tive in those fi­les (pa­ges) which con­tain the "''nstoc''" markup. That should make sure that the users al­ways get an actual/cur­rent TOC. ===== Installation ===== Search and install the plugin using the [[plugin:extension|Extension Manager]]. Refer to [[:Plugins]] on how to install plugins manually. * http://dev.mwat.de/dw/syntax_plugin_nstoc.zip ===== Plugin Source ===== Here comes the [[http://www.gnu.org/licenses/gpl.html|GPLed]] PHP source((The comments within the [[#Plugin Source|source]] file are suit­able for the OSS [[http://www.stack.nl/~dimitri/doxygen/index.html|doxygen]] tool, a do­cu­men­ta­tion sy­stem for C++, C, Java, Ob­jec­tive-C, Py­thon, IDL and to some ex­tent PHP, C#, and D. --- Since I'm wor­king with dif­fe­rent pro­gram­ming lan­gua­ges it's a great ease to have one tool that handles the docs for all of them.)) for those who'd like to scan be­fore actu­ally in­stal­ling it: syntax_plugin_nstoc.php - A PHP4 class that implements * a DokuWiki plugin to generate a * namespace table of contents. * *

* Usage:
* {{nstoc [namespace [maxdepth]]}} *

 *  Copyright (C) 2006, 2010  M.Watermann, D-10247 Berlin, FRG
 *      All rights reserved
 *    EMail : <support@mwat.de>
 * 
* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either * version 3 of the * License, or (at your option) any later version.
* This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *
* @author Matthias Watermann * @version $Id: syntax_plugin_nstoc.php,v 1.16 2010/02/18 10:30:46 matthias Exp $ * @since created 23-Dec-2006 */ class syntax_plugin_nstoc extends DokuWiki_Syntax_Plugin { /** * @privatesection */ //@{ /** * Callback function for use by the global search() function. * * @private * @see render() */ var $_callback = NULL; /** * HTML special characters to replace in render(). * *

* This property is used to avoid repeated memory allocations * inside the _doMarkup() loops. *

* @private * @since created 09-Aug-2007 * @see _doMarkup() */ var $_Chars = array('&', '<', '>', '"'); /** * Entity replacements for HTML special characters. * *

* This property is used to avoid repeated memory allocations * inside the _doMarkup() loops. *

* @private * @since created 09-Aug-2007 * @see _doMarkup() */ var $_Ents = array('&', '<', '>', '"'); /** * Lookup table for headlines ./. levels. * * @private * @since 12-Aug-2007 * @see _getHeadings() */ var $_Hlevels = array('======' => 1, '=====' => 2, '====' => 3, '===' => 4, '==' => 5, '=' => 6); /** * Additional markup used with older DokuWiki installations. * * @private * @since created 20-Feb-2007 * @see _fixJS() */ var $_JSmarkup = FALSE; /** * Prepare the (X)HTML markup. * *

* Each entry of the given $aList (indexed by page ID) * is expected to be a list of arrays with the respective entry's level * at index 0 (zero) and the headline's text at index * 1 (one) the latter of which is used to construct the * respective hypertext link fragment identifier. *

* @param $aList Array The list of headlines in $aID. * @param $aRenderer Object Reference to the Doku_Renderer_xhtml * object to use. * @return String * @private * @see render() */ function _doMarkup(&$aList, &$aRenderer) { $divOpen = array_fill(0, 0xff, 0); //XXX 255 levels as in "handle()" $curLvl = 0; while (list($id, $ul) = each($aList)) { unset($aList[$id]); // free mem $link = '
  • '; ++$divOpen[$curLvl]; } while ($curLvl < $l[0]); } else if ($curLvl > $l[0]) { // need to close the current level do { if (0 < $divOpen[$curLvl]) { $aRenderer->doc .= '
    '; --$divOpen[$curLvl]; } // if --$curLvl; $aRenderer->doc .= '
  • '; if (0 < $divOpen[$curLvl]) { $aRenderer->doc .= ''; --$divOpen[$curLvl]; } // if } while ($curLvl > $l[0]); $aRenderer->doc .= '
  • '; ++$divOpen[$curLvl]; } else { // still the current nesting level if (0 < $divOpen[$curLvl]) { $aRenderer->doc .= '
    '; } // if $aRenderer->doc .= '
  • '; } // if $aRenderer->doc .= '
  • '; --$curLvl; } // while } // _doMarkup() /** * Add markup to load JavaScript/CSS with older DokuWiki versions. * * @param $aRenderer Object The renderer used. * @private * @since created 20-Feb-2007 * @see render() */ function _fixJS(&$aRenderer) { if ($this->_JSmarkup) { return; // Markup already added (or not needed) } // if //XXX This test will break if that DokuWiki file gets renamed: if (@file_exists(DOKU_INC . 'lib/exe/js.php')) { // Assuming a fairly recent DokuWiki installation // handling the plugin files on its own. $this->_JSmarkup = TRUE; return; } // if $localdir = realpath(dirname(__FILE__)) . '/'; $webdir = DOKU_BASE . 'lib/plugins/nstoc/'; $css = ''; if (file_exists($localdir . 'style.css')) { ob_start(); @include($localdir . 'style.css'); // Remove whitespace from CSS and expand IMG paths: if ($css = preg_replace( array('|\s*/\x2A.*?\x2A/\s*|s', '|\s*([:;\{\},+!])\s*|', '|(?:url\x28\s*)([^/])|', '|^\s*|', '|\s*$|'), array(' ', '\1', 'url(' . $webdir . '\1'), ob_get_contents())) { $css = ''; } // if ob_end_clean(); } // if $js = (file_exists($localdir . 'script.js')) ? '' : ''; if ($this->_JSmarkup = $css . $js) { // Place the additional markup at top'o'page: $aRenderer->doc = $this->_JSmarkup . preg_replace('|\s*

    \s*

    \s*|', '', $aRenderer->doc); } else { // Neither CSS nor JS files found. // Set member field to skip tests with next call: $this->_JSmarkup = TRUE; } // if } // _fixJS() /** * Get a list of the headlines in the given $aID page. * *

    * Each entry of the returned zero-based list is an array with the * respective headline's level at index 0 (zero) * and the headline's text at index 1 (one). *

    * @param $aID String The wiki ID to process. * @param $aStartLevel Integer The initial namespace depth. * @param $aMaxLevel Integer The max. nesting level allowed. * @param $aDecLevel Integer Number of levels to reduce the computed * level of the returned entries; either 0 (zero) or 1. * @return Mixed An array (list) of headlines or FALSE * if no headline markup was found. * @private * @see render() */ function _getHeadings(&$aID, &$aStartLevel, &$aMaxLevel, &$aDecLevel) { $absLvl = $aStartLevel + $aMaxLevel; // The prepended colon is essential to make sure we're always // starting with level "1" even if processing a page/file in // the root namespace: $cl = substr_count(':' . $aID, ':'); $hits = $result = array(); if ($c = preg_match_all('|\n\s*(={2,6}?)[\t ]*?([^=][^\n]*[^=])\s*?\1|U', "\n" . io_readfile(wikiFN($aID), FALSE), $hits, PREG_SET_ORDER)) { for ($i = 0; $c > $i; ++$i) { if (($l = $cl + $this->_Hlevels[$hits[$i][1]]) && ($l < $absLvl)) { $result[] = array( ($l - $aStartLevel) - $aDecLevel, $hits[$i][2]); } // if unset($hits[$i]); // free mem } // for } // if // Return the list only if there was something found: return (0 < count($result)) ? $result : FALSE; } // _getHeadings() /** * Resolve the given $aPath in relation to the specified * $aNamespace. * *

    * This method tries to resolve relative and absolute * pathnames depending on the given $aNamespace value. *

    * Note that this implementation is not bulletproof but just uses * string operations for its intended purpose. * It's called by the public handle() method where further * checks are applied. *

    * @param $aNamespace String The base namespace of $aPath: * @param $aPath String The (possibly relative) path to resolve. * @return String The absolute namespace/page name. * @private * @since created 11-Aug-2007 * @see handle() * @static */ function _path($aNamespace, $aPath) { // Make sure the NS ends with a colon: if ($len = strlen($aNamespace)) { if (':' != $aNamespace{--$len}) { $aNamespace .= ':'; } // if } else { $aNamespace = ':'; } // if if ($len = strlen($aPath)) { if ('.' == $aPath) { return $aNamespace; } // if // Check for absolute path: if (':' == $aPath{0}) { return $aPath; } // if } else { // Empty path => return current namespace: return $aNamespace; } // if // Check for relative paths: if ((1 < $len) && ('.' == $aPath{0})) { if (':' == $aPath{1}) { return syntax_plugin_nstoc::_path($aNamespace, substr($aPath, 2)); } // if if ('.' == $aPath{1}) { // We use "preg_split()" instead of "explode()" to // omit empty entries: $path = preg_split('|:|', $aNamespace, -1, PREG_SPLIT_NO_EMPTY); if (count($path)) { // Remove the last NS element: array_pop($path); // Rebuild the whole NS path: $aNamespace = implode(':', $path); return ((2 < $len) && (':' == $aPath{2})) ? syntax_plugin_nstoc::_path($aNamespace, substr($aPath, 3)) : syntax_plugin_nstoc::_path($aNamespace, substr($aPath, 2)); } // if // Trying to go beyond the NS start ... return ':'; } // if } // if return $aNamespace . $aPath; } // _path() //@} /** * @publicsection */ //@{ /** * Tell the parser whether the plugin accepts syntax mode * $aMode within its own markup. * * @param $aMode String The requested syntaxmode. * @return Boolean FALSE always since no nested markup * is possible with this plugin. * @public */ function accepts($aMode) { return FALSE; } // accepts() /** * Connect lookup pattern to lexer. * * @param $aMode String The desired rendermode. * @public * @see render() */ function connectTo($aMode) { $this->Lexer->addSpecialPattern('\x7B\x7Bnstoc\s+[^\}\n\r]*\x7D\x7D', $aMode, 'plugin_nstoc'); } // connectTo() /** * Get an associative array with plugin info. * *

    * The returned array holds the following fields: *

    *
    author
    Author of the plugin
    *
    email
    Email address to contact the author
    *
    date
    Last modified date of the plugin in * YYYY-MM-DD format
    *
    name
    Name of the plugin
    *
    desc
    Short description of the plugin (Text only)
    *
    url
    Website with more information on the plugin * (eg. syntax description)
    *
    * @return Array Information about this plugin class. * @public * @static */ function getInfo() { return array( 'author' => 'Matthias Watermann', 'email' => 'support@mwat.de', 'date' => '2010-02-18', 'name' => 'NsToC Syntax Plugin', 'desc' => 'Add a namespace\'s table of contents {' . '{nstoc [namespace [maxdepth]]}}', 'url' => 'http://www.dokuwiki.org/plugin:nstoc'); } // getInfo() /** * Define how this plugin is handled regarding paragraphs. * * @return String "block" (open paragraphs need to be closed * before plugin output). * @public * @static */ function getPType() { return 'block'; } // getPType() /** * Where to sort in? * * @return Integer 298 * (smaller Doku_Parser_Mode_internallink). * @public * @static */ function getSort() { return 298; } // getSort() /** * Get the type of syntax this plugin defines. * * @return String "substition" (i.e. substitution). * @public * @static */ function getType() { return 'substition'; // sic! should be __substitution__ } // getType() /** * Handler to prepare matched data for the rendering process. * *

    * The $aState parameter gives the type of pattern * which triggered the call to this method: *

    *
    DOKU_LEXER_SPECIAL
    *
    a pattern set by addSpecialPattern()
    *

    * Any other $aState value results in a no-op. *

    * @param $aMatch String The text matched by the patterns. * @param $aState Integer The lexer state for the match; all states but * DOKU_LEXER_SPECIAL are ignored by this implementation. * @param $aPos Integer The character position of the matched text. * @param $aHandler Object Reference to the Doku_Handler object. * @return Array List of parsed data: Index * [0] holds the current $aState, * [1] the base namespace to process (possibly empty), * [2] the allowed nesting depth, * [3] the initial nesting depth of the given base namespace * and [4] a flag indicating whether to start with a file * (TRUE) or directory (FALSE). * @public * @see render() * @static */ function handle($aMatch, $aState, $aPos, Doku_Handler $aHandler) { if (DOKU_LEXER_SPECIAL != $aState) { // This causes "render()" to do nothing ... return array(DOKU_LEXER_EXIT); } // if // Compute current page and namespace: $current = str_replace('/', ':', getID('id', FALSE)); $f = strrchr($current, ':'); $dir = (FALSE === $f) ? '' : substr($current, 0, $f); if (! $dir) { // For unknown reasons the "strrchr()" call above // sometimes doesn't work ... for ($f = strlen($current); 0 < $f; --$f) { if (':' == $current{$f}) { $dir = substr($current, 0, $f); break; } // if } // for } // if // Extract the 0|1|2 arguments: $args = ($aMatch = substr($aMatch, 7, -2)) ? preg_split('|\s+|', $aMatch, -1, PREG_SPLIT_NO_EMPTY) : NULL; switch (count($args)) { case 0: $args = array('', 0); break; case 1: if (is_numeric($args[0])) { // There's a depth value only, make it numeric: $args[1] = $args[0] * 1; $args[0] = ''; } else { $args[0] = str_replace('/', ':', $args[0]); // There's a namespace only, add depth value: $args[1] = 0; } // if break; default: $args[0] = str_replace('/', ':', $args[0]); // Make the (assumed) depth value numeric: $args[1] *= 1; break; } // switch // Resolve paths relative to current namespace $args[0] = syntax_plugin_nstoc::_path($dir, $args[0]); // Check whether we've got the index page of a namespace: global $conf; $idx = (isset($conf['start']) && strlen($conf['start'])) ? $conf['start'] : 'start'; if ($args[0] == $idx) { $args[0] = ''; } else { $idx = ':' . $idx; $f = strlen($idx) * -1; if (substr($args[0], $f) == $idx) { $args[0] = substr($args[0], 0, $f); } // if } // if $f = 0; // file flag // Now check whether we've got in fact a valid namespace/page: if ($ns = cleanID($args[0])) { // To compute the actual nesting level we have to test // whether the given ID refers to a file or directory. if ($f = file_exists($fn = wikiFN($ns))) { // If there is a file set the flag to FALSE if there's // a directory (i.e. namespace) with the same name: $f = (! is_dir(substr($fn, 0, -4))); } // if // Make the file flag numeric so it's usable for // computing the actual starting level: $f *= 1; // Compute the initial nesting level: $args[0] = ($f) ? 2 + substr_count($ns, ':') : 1 + substr_count($ns, ':'); } else { // we're in the root namespace either explicitely or // by an argument that resolved to root. $args[0] = 1; } // if // Check the allowed nesting level value: if (0 < $args[1]) { if (! $f) { // For directories we need extra levels if ('' == $ns) { ++$args[1]; } else { $args[1] += 2; } // if } // if } else { //XXX In case no depth argument was given we use a value of 255 // which should be reasonably great enough (see "_doMarkup()"). $args[1] = 0xff; } // if // Finally prepare the data used by "render()": return array(DOKU_LEXER_SPECIAL, $ns, $args[1], $args[0], (bool)$f); } // handle() /** * Handle the actual output creation. * *

    * The method checks for the given $aFormat and returns * FALSE when a format isn't supported. * $aRenderer contains a reference to the renderer object * which is currently handling the rendering. * The contents of $aData is the return value of the * handle() method. *

    * This implementation uses the precomputed values of $aData * to generate a list of headlines marked up as a (X)HTML list. *

    * @param $aFormat String The output format to generate. * @param $aRenderer Object Reference to the Doku_Renderer_xhtml * object to use. * @param $aData Array The data created/returned by the * handle() method. * @return Boolean TRUE if rendered successfully, or * FALSE otherwise. * @public * @see handle() */ function render($aFormat, &$aRenderer, &$aData) { if ('xhtml' != $aFormat) { return FALSE; // nothing to do for other formats } // if if (DOKU_LEXER_SPECIAL != $aData[0]) { return TRUE; // nothing to do for other states } // if global $conf; $ids = array(); if ($aData[4]) { // It's just a single file to process $ids[0] = $aData[1]; // The var is recycled to hold the level decrement value used by // "_getHeadings()" to compute the actual LI level attribute: $aData[1] = -1; } else { // Unfortunately the global "search()" function isn't able to use // methods (even static class methods) but insists on an ordinary // function to be passed as a calltime argument (at least up to // DokuWiki 2006-03-05). To avoid polluting the global namespace // even more than it already is we use a private member function // which we can pass to DokuWiki's global "search()" function. if (! $this->_callback) { $idx = (isset($conf['start']) && strlen($conf['start'])) ? $conf['start'] : 'start'; $iLen = (strlen($idx) + 1) * -1; // "+1" for the NS colon // Here we filter out the "index" pages i.e. pages either // named as configured in the global "$conf['start']" or // with the same name as a sub-directory. $this->_callback = create_function( '&$aData, $aBase, $aFile, $aType, $aLvl, $opts', 'if (("f" == $aType) && (".txt" == substr($aFile, -4))' . '&& (! is_dir($aBase . "/" . substr($aFile, 0, -4)))' . '&& ($aFile = pathID($aFile)) && ($aFile != "' . $idx . '")' . '&& (substr($aFile, ' . $iLen . ') != ":' . $idx . '")) {' . '$aData[] = $aFile;' . '}' . 'return TRUE;'); } // if // Call DokuWiki's global search function: if (('' == $aData[1])) { search($ids, $conf['datadir'], $this->_callback, FALSE, $aData[1], 0); $aData[1] = 0; // setup level decrement for "_getHeadings()" } else { search($ids, $conf['datadir'], $this->_callback, FALSE, str_replace(':', '/', $aData[1]), 0); $aData[1] = 1; // setup level decrement for "_getHeadings()" } // if sort($ids); } // if global $USERINFO; $g =& $USERINFO['grps']; // Preparing references saves array .. $u =& $_SERVER['REMOTE_USER']; // .. lookups within the loops below. $pages = array(); // To avoid repeated boolean and regEx tests if unneeded // we unroll the loop saving lots of CPU cycles. if (isset($conf['hidepages']) && strlen($conf['hidepages'])) { $re = '/' . $conf['hidepages'] . '/ui'; while (list($i, $entry) = each($ids)) { unset($ids[$i]); // free mem // Use only pages which are actually readable for the // current user and not supposed to be "hidden": if ((0 < auth_aclcheck($entry, $u, $g)) && (! preg_match($re, ':' . $entry)) && ($i = $this->_getHeadings($entry, $aData[3], $aData[2], $aData[1]))) { $pages[$entry] = $i; } // if } // while unset($entry, $i, $ids, $re); // free mem } else { while (list($i, $entry) = each($ids)) { unset($ids[$i]); // free mem // Use only pages which are actually // readable for the current user: if ((0 < auth_aclcheck($entry, $u, $g)) && ($i = $this->_getHeadings($entry, $aData[3], $aData[2], $aData[1]))) { $pages[$entry] = $i; } // if } // while unset($entry, $i, $ids); // free mem } // if if (0 < count($pages)) { $this->_fixJS($aRenderer); // check for old DokuWiki versions $this->_doMarkup($pages, $aRenderer); } // if return TRUE; } // render() //@} } // class syntax_plugin_nstoc } // if //Setup VIM: ex: et ts=2 enc=utf-8 : ?>
    ==== Presentation ==== The accompanying CSS presentation rules: div.level1 ul.nstoc,div.level2 ul.nstoc,div.level3 ul.nstoc,div.level4 ul.nstoc,div.level5 ul.nstoc,div.level6 ul.nstoc{margin:0;padding:0;} ul.nstoc{list-style-position:inside;list-style-type:none;font-size:100%;margin:0;padding:0;text-align:left;line-height:1.4;} ul.nstoc li{margin:0;padding:0 0 0 0.5ex;list-style-type:none;} ul.nstoc li.level1{margin-top:0.3ex;padding:0;font-size:111.1%;font-weight:500;font-variant:small-caps;letter-spacing:1pt;background:inherit;color:#000;} ul.nstoc li.level2{font-variant:normal;} ul.nstoc li.level3{letter-spacing:normal;} ul.nstoc li.level2,ul.nstoc li.level3,ul.nstoc li.level4,ul.nstoc li.level5,ul.nstoc li.level6,ul.nstoc li.level7,ul.nstoc li.level8{font-size:96.6%;padding-left:1.3ex;} ul.nstoc li a,ul.nstoc li a.wikilink1{background:inherit;color:#003;border:none;font-size:inherit;font-variant:inherit;line-height:inherit;text-decoration:none;} ul.nstoc li a.wikilink1:before,ul.nstoc li a.wikilink1:after{display:none;} ul.nstoc li a:hover,ul.nstoc li a.wikilink1:hover{text-decoration:underline;} Of course, you're free to modify this styles((The [[http://dev.mwat.de/dw/syntax_plugin_nstoc.zip|source archive]] contains a commented and indented stylesheet for your in­for­ma­tion.)) to suit your personal needs or aesthe­tics((Just be careful when modifying a CSS file: both the order and the selector grou­pings are im­por­tant for CSS to work as intended/expected.)). ==== Changes ==== __2010-02-18__:\\ * minor change in 'handle()' to avoid type conversion; __2008-03-30__:\\ * modified private '_doMarkup()' method to use the current renderer instance directly;\\ - removed obsoleted property '_sepChar' and private '_makeID()' method and updated 'render()' accordingly; __2008-03-28__:\\ * modified CSS to explicitly overwrite some broken default settings; __2007-08-29__:\\ * little doc corrections; __2007-08-26__:\\ * modified 'handle()' and 'render()' to ignore nested index pages; __2007-08-15__:\\ + implemented use of global '$conf["hidepages"]' setting;\\ * added GPL link and fixed some doc problems; __2007-08-12__:\\ + implemented new private '_path()' method and rewrote public 'handle()' to use it (thus allowing better relative navigation);\\ * various internal optimizations in several places; + added 'aDecLevel' argument in '_getHeadings()' interface to allow for handling namespaces, sub-pages and root pages differently;\\ * modified 'render()' to handle different page/list types; __2007-08-09__:\\ + added private members '_Chars' and '_Ents' and modified '_doMarkup()' to use them (so the lists are created only once when the object is instantiated instead of with each method call); __2007-08-08__:\\ * modified 'handle()' to explicitly check for index pages;\\ * changed internal handling of directories vs. pages;\\ * slightly decreased font-size of 'level3'/'level4' LIs;\\ * modified anchor selector to inherit bg-colour; __2007-08-06__:\\ + added ':before/:after' selectors for links in case the default setting (by the main template) specifies something else; __2007-06-12__:\\ # modified 'handle()' to fix handling of omitted namespace names;\\ * changed/corrected some doc comments; __2007-02-23__:\\ * removed function test in '_fixJS()' (the PHP file might not be loaded); __2007-02-20__:\\ + implemented support for older DokuWikis; __2007-01-31__:\\ * minor change in 'render()' setting up private '_sepChar' member; __2007-01-28__:\\ * modified 'handle()' to slightly improve handling of sub-namespaces; __2007-01-14__:\\ * modified RegEx in '_getHeadings()' to ignore empty headline markup and skip consecutive '=' characters used in patch files;\\ + implemented private member '_callback' for use in 'render()' instead of a global callback for the global 'search()' function; __2007-01-08__:\\ + implemented utilization of access rights in 'render()'; __2006-12-31__:\\ + initial release; //[[support@mwat.de|Matthias Watermann]] 2007-08-26// ===== See also ===== Consult DokuWiki's [[:acl|access control]] docs. ==== Plugins by the same author ==== * [[bomfix|BOMfix Plugin]] -- ignore Byte-Order-Mark characters in your pages * [[code2|Code Syntax Plugin]] -- use syntax highlighting of code fragments in your pages * [[deflist|Definition List Syntax Plugin]] -- use the only complete definition lists in your pages * [[diff|Diff Syntax Plugin]] -- use highlighting of diff files (aka "patches") in your pages((obsoleted by incorporating its ability into the [[code2|Code]] plugin)) * [[hr|HR Syntax Plugin]] -- use horizontal rules in nested block elements of your pages * [[lang|LANGuage Syntax Plugin]] -- markup different languages in your pages * [[lists|Lists Syntax Plugin]] -- use the only complete un-/ordered lists in your pages * [[nbsp|NBSP Syntax Plugin]] -- use Non-Breakable-Spaces in your pages * [[nstoc|NsToC Syntax Plugin]] -- use automatically generated namespace indices * [[shy|Shy Syntax Plugin]] -- use soft hyphens in your pages * [[tip|Tip Syntax Plugin]] -- add hint areas to your pages ===== Discussion ===== Hints, comments, suggestions ... //(Please add new suggestions at the end to maintain some sort of chronological order.)// > The idea of this plugin is interesting. It could be very useful, but it doesn't work in my DokuWiki installation... Do I have to change something before using it?? >> Could you provide some more information? What did you try, what did you expect and what happened actually?\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-01-30 12:57// I'm pretty happy with this plugin, but I will need to modify it some for my purposes. I have a [[http://www.patentblurb.com|news blurb site]] which is still very new. (I set up an example of what the home page will look like using this plug in in the [[http://www.patentblurb.com/doku.php/playground:playground| sandbox]]. Currently, I am adding blurbs to the home page, then moving them to various categories when they get old. I would like to modify this plug in so that it only shows the first 5 second-level headings from each page. That way, I can just add the blurb to a specific category and it will automatically show up on the home page. I can strip out the top-level headings with the CSS, but will need to modify the plug-in itself to stop after 5 subheadings. I may also want to include the entire section for the top two of each page, but I need to play around with it more to see how it will work. Thanks for your good work on this one, I was going to start making one that does this sort of thing from scratch (and I have never programmed in PHP before, so it would be a real learning experience!) :p --Len 2007-01-30 12:30 p.m. -8:00. > Thanks for your input! I've had a look at your site to get the idea. (BTW: Your CSS isn't yet valid; both Firefox and Opera show a lot of warnings. Try http://jigsaw.w3.org/css-validator/ to help you get it fixed.) And I'd recommend to either add a left margin/padding to the "''ul.nstoc''" CSS selector or use the "''list-style-type: none''" setting as shown in the [[#CSS|CSS]] above. The 'hopping' LI markers don't look that good to me.\\ About your intended changes: I fail to see the reason to skip the first-level headings (apart from the fact that in some pages the first heading is a ''H2'' instead of ''H1'') -- but that's not my concern. What really worries me is the //markup// the user has to type in. There are already four variants: (1) neither namespace nor max-depth, (2) only namespace, (3) only max-depth, (4) both namespace and max-depth. Now, we'd need to express additionally to (a) skip the ''H1'', (b) use only up to "''x''" ''H2''s while %%(c)%% skipping all ''H3/H4/H5'' lines.\\ The last point %%(c)%% //seems// to be the easiest one because the max-depth argument implicitly takes care of this: ''%%{{nstoc :legal 2}}%%'' would make sure that only H1 and H2 in the ''legal'' namespace will get used. However, once there will be sub-namespaces (like '':legal:whatever'') only the ''H1''s of the pages therein will show up. So obviously this approach wouldn't work for you in the long term if used this way.\\ Point (b) (delimiting the ''H2''s to use) kind of breaks the plugin's intended purpose insofar as the resulting list would no longer represent a complete TOC. On the other side I wonder whether a single page with several ''H2''s shouldn't get split up into several pages of their own (probably in their own "sub-chapter" namespace).\\ That leaves (a) i.e. skipping ''H1''. This as well doesn't fit with the plugin's purpose, I fear. And assuming that ''H1'' should provide fairly relevant information I must admit that I fail to see the point in omitting it.\\ Well, although this reasoning doesn't seem to be very encouraging I think you could get what you want by slightly adjusting your point of view upon your wiki. Try to think of it as a big book like a reference of a programming language or such alike. You have the main topics to be covered which are the book's chapters (and wiki 1st level namespaces). So your 1st level index/start page could be nothing more than: "''%%{{nstoc :book}}%%''" This would produce an //outline// of all topics covered but no real content/information yet. As appropriate for each of the chapters, there would be either a page (providing content/information actually or just ''%%{{nstoc :book:chapter1}}%%'') or sub-chapters (i.e. wiki 2nd level namespaces). And so on...\\ I'm aware that wiki //users// don't tend to think in terms like "document" or "structure" and alike but more in the lines of contents (leaving anything else to the "magic under the hood" i.e. the respective wiki software). But as a //maintainer// of a DokuWiki installation you've got the power to enforce some -- say -- "politics" about how to structure your web-presentation. And judging from your start page and your words above you've already given some thought to this matter. Hence I think you'll only need some "fine tuning" in your namespace usage and the distribution of contents between files (pages) therein. --- Thinking such an approach to its end could mean that DokuWiki's built-in "index" (your ''%%http://www.patentblurb.com/doku.php?idx=wiki%%'' URL) could provide all navigational aid required to use your site.\\ Having said that please feel free to followup as I very well may have missed your point.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-01-31 12:29// >>Thanks, I know the site has only been up for a week or so, and it still needs tweaking. Before discovering DokuWiki, I have been running WikiServer internally on my company's intranet, so I just copied the content and reformatted it with DokuWiki's syntax. I agree about the hopping LI markers :o thanks for the hint. Also I agree that skipping H1 is not necessary, as I think I can make what I want work with CSS changes. Part of the problem is that some of the name spaces have pages that I don't want included in the TOC, but I guess that is easy to fix by moving them to a different namespace... so that's ok, really, come to think of it. >> >> The one concern I have is that as these categories fill up with blurbs, the TOC will grow huge, which is why I wanted to limit to X number of headings (not X depth). So that, lets say the H1 and only the first 5 H2s under each H1 would be listed, with maybe a "...more" underneath which would point to the H1 location. --Len (2007-01-31 3:57 -8:00) >>> About name­spaces to not include: If it's not only a matter of quan­ti­ty pos­sib­ly DokuWiki's [[:acl|access control]] can help?\\ The other point is one (huge lists) I stumbled over myself recently. While it would not be too difficult to implement another number option as such the question is: What would it be supposed to mean? (a) The overall max. number of list entries, (b) the max. number of entries per namespace (in­clu­ding (b.a) all sub-namespaces or (b.b) just the respective current one), %%(c)%% the max. num­ber of en­tries per page, or (d) ... --- What­ever we'd decide the resulting list would match our intentions only with certain com­bi­na­tions of name­spa­ces, sub-name­spa­ces, pages and head­lines therein while with other combina­tions the list would get either too short or still too large.\\ Above I mentioned the book analogy several times already. In its shortest form "''%%{{nstoc :book}}%%''" would produce a complete TOC of the whole thing. //But// that doesn't mean that you //must// use the plugin that way and only that way. It's just one way intended to save you some typing. Let me introduce another analogy: a tree. The wiki's namespaces are the branches and the pages the leafs. Additionally we say that neither the tree trunk nor in­ter­me­dia­te bran­ches may have leafs (i.e. real con­tent pages). All namespaces (trunk and bran­ches) except the outermost ones contain just one index page like ====== Name of this branch ====== {{nstoc sub-ns-one 1}} {{nstoc sub-ns-two 1}} {{nstoc sub-ns-three 1}} Since there's only one headline in such an index page it doesn't contribute too much to the overall list (the one that starts at the very top). But if the list is still too long, you could just //hide all intermediate branches// by using a top index like ====== My presentation ====== {{nstoc :ns1:ns1a:ns1a1:outermost 2}} {{nstoc :ns1:ns1b:ns1b1:outermost 2}} {{nstoc :ns2:ns2a:ns2a1:outermost 2}} By explicitly naming the namespaces to use combined with the depth option you'd make sure that only the ''H1'' and ''H2'' headings of the pages in the re­spec­ti­ve ''outer­most'' name­space (branch, sub-chapter) show up in the final list. In case even that would give too huge a list you could name the pages explicitly (omit­ting those you do not want to show up in the list) e.g. ====== My presentation ====== {{nstoc :ns1:ns1a:ns1a1:outermost:page1 1}} {{nstoc :ns1:ns1a:ns1a1:outermost:page4 3}} {{nstoc :ns1:ns1b:ns1b1:outermost 4}} {{nstoc :ns2:ns2a:ns2a1:outermost:page77 2}} Well, that might not look too good at first glance, but I hope you'll get the idea. --- Ad­mit­ted­ly this is a little more typing initially than just "''%%{{nstoc }}%%''". But, after all, you've to do that only once when your tree has grown its in­ten­ded struc­ture. --- Like every gardener, however, be careful with your plants ''%%;-)%%''\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-02-01 13:42// >Hi, this plugin is exactly what I need and I am able to create the TOC of nested namespaces if I explicitly list them but the simple syntax %%{{nstoc }}%% simply doesn't work on my DokuWiki. Note: I'm not in the root namespace so it's not the special case mentioned in the article. If I include %%{{nstoc }}%% on page namespace:some_page, it ignores the other pages in ''namespace'' and includes only heading from some_page. And I certainly have enough permissions to view all pages as I'm in an admin group. >> Well, it took some time to track that down. But (hopefully) it's fixed by now. Let me know if there are still problems.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-06-12 13:26// >>> It still doesn't work on my DokuWiki (version 2007-06-26). I was able to simulate the desired result with ''%%{{nstoc * 4 }}%%''\\ //Borek 2007-07-16 14:29// >>>> Thanks for your note. Please check again with the plugin's latest release. I've run several tests but was unable to reproduce what you're describing.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-08-26 22:19// \\ >It would be nice if the plug in can search for specific tokens in the page, so I can create a resume page of interesting points.\\ //[[rmontes@surlab.com|Rodrigo Montes]] 2007-05-11 16:11// >> Hmmm, I can't see how that could be implemented conveniently. After all, how would you define //"specific tokens"// in a way that fits all DokuWiki installations? Most probably you'd end up with a (theoretically endless) list of tokens which has to be parsed & processed with each "candidate" (i.e. file) resulting in a lot of runtime overhead.\\ If, however, you have root access to your *NIX/Linux based web server you could get what you want by creating "pseudo namespaces" (in need for a better term) which solely consist (as far as the filesystem is concerned) of sym-links organized & named in a way that fits your needs.\\ While I haven't tested this idea in real life I imagine you could generate your "page of interesting points" that way. Just remember that all DokuWiki (and this plugin) "knows" is the name of your namespaces and pages. So, for example, if one of your "specific tokens" would be //tennis// you'd just create a namespace (i.e. directory) called "tennis" and sym-link all your tennis related pages (i.e. files, wherever they may reside originally in your namespace hierarchy) into that directory (possibly using other names for the sym-links as the original files if that helps to "tokenize" them). Of course, you have to review any relative hyperlinks in such a page but that's something you must do anyway whenever setting up and/or modifying a namespace.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-06-12 13:32// > I added a couple of lines at the start of function handle:\\ function handle($aMatch, $aState, $aPos, &$aHandler) { $aMatch = str_replace('@YEAR@',date('Y'),$aMatch); $aMatch = str_replace('@MONTH@',date('n'),$aMatch); ... } \\ This allows me to use @YEAR@ and @MONTH@ to insert the current year and month.\\ Erik Itland >> While I can see your point I really don't think that something like that is in the "domain of responsibility" of this plugin. See my comments above.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-06-12 13:49// \\ Several quirks when I try to use this: * specifying namespaces only works if I use the full namespace path, starting with root * the TOC outputted shows from H1 down, without the page title; how do I make the page title appear? > Hi anonymous! If you want a list starting //relative// to the current namespace just specify it e.g.: %%{{nstoc .:sub_namespace}}%% or %%{{nstoc .:sub_namespace:pagename}}%%. -- About "page title": I'm not sure what you mean by that term. The filename? The namespace name? Anyway, the TOC gets generated based on the headings (and a page's title is usually the H1 heading).\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-07-26 12:05// >>I meant filename - is there anyway to get nstoc to list pages by the name of the page instead of the first H1 in the page? The reason is that many of the pages I am trying to get in the TOC have headings that are different from the page name. ---Thanks >>> Sorry, no. I consider the filename just a technical item with no relevance to the user/reader. This plugin is supposed to generate a Table of //Contents// not a directory listing. Apart from that: The filename is subject to various modifications (e.g. lowercasing, replacement of certain characters etc.) so it would be quite hard to produce a readable string (at least if one's using not only plain ASCII characters). -- BTW: Do you know about the "''$conf['useheading']''" setting? -- Isn't it, well, confusing if in the URL shows up a filename of "tea" while the page's headline says "coffee"? -- Anyway, there's always DokuWiki's "''?do=index''" CGI argument you could use to get a filename based overview.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-08-12 15:26// \\ > I wish I could use only the first H1 in a document I am using your excellent plugin here - http://dokuwiki.healthwealthandmusic.co.uk/. I think the pages look better if I use an HTML H1 as the page title with nice size and colour and then DokuWiki H1s as sections like this - http://dokuwiki.healthwealthandmusic.co.uk/doku.php/computer:dokuwiki. But because the plugin reads H1 as if it were a new document it will list all the H1s in a single document when all I really want is the first H1. If you click on Sitemap you will see what I mean. (I'll leave it for a day or two - from 19 Oct). I can understand I might have to revert back to the standard way but it would be nice if the algorithm allowed me to specify //only the first H1 and NOT the rest//? > Well, unless you're willing to properly structure your documents there isn't an easy solution, I'm afraid. Each single document can only have //one// main headline (''H1''). If there are more this indicates that (a) either the document should be split into two (or more) documents each of which having its own distinguished headline or (b) the document's markup doesn't represent its structure properly.\\ Another possible reason might be that your page authors are abusing the ''Hx'' for //design// instead of //structure//. If that's the case you should tell them the difference between a bold typeface and a document headline.\\ So whatever the reason might have been in the first place, as long as your document's markup reflects its structure you should be fine.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/03/28 10:26// I like this plugin very much as it saves me a lot of redundant work, but I have a small issue with how the TOC is displayed. I get a new line after every bullet button, so the actual text is in a separate new line, which doesn't give me a particular nice layout.\\ This seems to be problem in Firefox (2) only. It doesn't appear in IE7 or IE6. Is this something which Firefox does wrong?\\ I haven't modified any CSS (Should I be copying the //style.css// somewhere?). Is there an easy way to resolve this and to remove the new line without changing the actual code of the plugin?\\ --- //Max 2008-03-17// > I've been a bit confused at first, since list bullets shouldn't show up at all in a ''nstoc'' list and I haven't been able to reproduce the problem (BTW: bullets, colours, indentations etc. all are a matter of CSS, meaning it's up to the browser how to handle them). However, after a while I realized that there might be conflicting CSS rules with DokuWiki's default styles((which I do not use because it's broken in various ways)). To make a long story short: I've added some rules to the plugin's CSS file to enforce the ''nstoc'' layout even when used with DokuWiki's default CSS/template. I've verified it with both Opera and Firefox((but I didn't bother to check with M$IE since that crap is broken anyway)) and it works fine.\\ So please try to re-install the plugin and see whether it works out for you.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/03/28 10:14// > I have exactly the opposite wish as one colleague above: I'd like the plugin to be able to skip the H1 header. I'm using the plugin in two ways: > a) to replace the standard DokuWiki TOC. In this case it doesn't really make sense to have the H1 heading in the TOC, just beneath the H1 heading :-) > b) to refer to a page from an other ("FAQ mode"). In this case I structure the main FAQ page myself and would like to list all headings **but** H1 > An ideal solution could be to have some kind of '-/+' syntax: -1 skips H1, -2 skips H2 etc. So that {{nstoc -1,2+3}} > would show H3 but not H1 and not H2..? > Just a proposition > --- //Frank, 2008/12/04 16:21// **Make link hack**\\ This hack fixing such errors as: * Incorrect romanization links handling * Makes inc number to duplicated headings To do it you gotta replace this function on ''/nstoc/syntax.php'' function _makeID(&$aID) { $title = str_replace(':','',cleanID($aID)); $title = ltrim($title,'0123456789._-'); if(empty($title)) $title='section'; // make sure tiles are unique $num = ''; while(in_array($title.$num,$this->headers)){ ($num) ? $num++ : $num = 1; } $title = $title.$num; $this->headers[] = $title; return $title; } // _makeID() --- //[[kirbez@mail.ru|Kirill Bezrukov]] 2008/03/23 11:49// > Hmmm, I'm not sure what you're trying to accomplish here. The headlines of a page/document must be unique in the first place (otherwise you'd not only confuse the readers but also break DokuWiki's automatic link generation). So if your pages have duplicated headlines you should correct those pages instead of a plugin which simply //uses// those pages.\\ Adding a number (or anything else) to a //hypertext link// fragment identifier will break that link. Please be aware that the private method you're "fixing" must return a value exactly as DokuWiki's internal link generation, which it does //not// in your variant. Apart from that: There's no object property named ''headers'', so at least the code snippet above is incomplete.\\ As for "incorrect romanization" I really don't know what you're talking about. As the comments in the (original) source code state there are not only the official W3C rules to take into account when creating anchor names but an additional constraint imposed by DokuWiki to use only lowercase letters. Your "fix" fails on both.\\ I'd strongly **discourage** everybody from using the "fix" above: It will //break// your DokuWiki and possibly invalidate the page.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/03/28 11:09// >> I got this code from \parser\xhtml.php line 997 function _headerToLink. May be the best way just use this function from DokuWiki core, but I don't know how.\\ About Romanization: if your wiki not in English, for example in Russian as I have, and you use $conf['deaccent'] = '2'; option (when Russian names are converted in translation (English letters) the headers in generated TOC have empty links (just #). And after I made this, it became fine.\\ And I just forgot about $headers array, it must be defined as plugin class global var and clearing before link generation --- //[[kirbez@mail.ru|Kirill Bezrukov]] 2008/03/29 22:04// >>> Following your suggestion I've modified the plugin to use the method you mentioned. As already discussed by private email this does not guard against poorly structured documents. I hope it helps, anyway.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/03/30 20:09// \\ Hi Matthias, Did you consider using metadata rather than searching the wiki text in your _getHeadings() method? The TOC (admittedly a restricted set of headings) is available in array form through ''p_get_metadata($id,'description tableofcontents');''. If the TOC is acceptable, it maybe a faster method for accessing the raw heading information. --- //[[chris@jalakai.co.uk|Christopher Smith]] 2008/12/04 14:33// Great plugin! did a bit of a hack to allow me to exclude pages from getting indexed site-wide. I did this because I'm building a nav menu where each namespace has a file named sidebar and a file named nav, and I didn't want those to show up in the nstoc listing. In syntax.php, line 693: $this->_callback = create_function( '&$aData, $aBase, $aFile, $aType, $aLvl, $opts', 'if (("f" == $aType) && (".txt" == substr($aFile, -4))' . '&& (! is_dir($aBase . "/" . substr($aFile, 0, -4)))' . '&& ($aFile = pathID($aFile)) && ($aFile != "' . $idx . '")' . '&& (substr($aFile, -3) != "nav")' // Added by DREW to exclude pages named "nav" . '&& (substr($aFile, ' . $iLen . ') != ":' . $idx . '")) {' . '$aData[] = $aFile;' . '}' . 'return TRUE;'); You can continue to copy and paste to add any other exclusions you'd like. Also, remember to change the number on the substr to be the number of letters in the filename you're trying to exclude (nav has 3 letters, so -3; sidebar has 7 letters, so -7). While I'm sure this isn't the best way to do it, it works for me. I could see this as being useful to be able to define per instance of nstoc, though, so perhaps a suggestion to the plugin is to add that? (ie: {{nstoc :namespace 1 excludeMe1 excludeMe2 ...) Thanks for the great plugin! //[[sen.of.peter@gmail.com|Andrew Petersen]] 2009/03/01 00:03// \\ Thanks for this plugin. It looks like exactly what I was looking for. The only thing I would like to have would be to replace the contents of the built-in Table of Contents by the HTML output of this table. How could be this done? --- //[[jmeile@hotmail.com|Josef Meile]] 2009/04/21 20:00// > Hmm, as far as I can see that's not easily possible. And I fail to see the benefit: While a page's TOC is meant to provide overview of the //current// page (i.e. the one actually displayed by the browser) the ''nstoc'' output gives the page titles of one or more name­spaces (possibly but not necessarily including the current page). In other words: Both lists serve a completely different purpose.\\ --- //[[support@mwat.de|Matthias Watermann]] 2009/08/07 11:05// \\ This plugin is great, but the 'title' attribute of the links is different to the title attribute created by DokuWiki for an internal link. E.g.: nstoc creates: ''%%Architectural Overview%%'' vs. DokuWiki: ''%%architectural overview%%''. This means that offline processors can't use the title attribute for determining which page it points to e.g. offline plugin. Any chance you can say how to quickly change that? > I'm not sure about your point. The "title" attribute offers "advisory information about the element for which it is set" (see e.g. [[http://www.w3.org/TR/html401/struct/global.html#adef-title|W3C]]), it is //not// intended for "determining which page it points to". Usually it's displayed as a so-called "tooltip" window by visual brow­sers. The "href" attribute, however, "specifies the location of a Web resource" (see e.g. [[http://www.w3.org/TR/html401/struct/links.html#edef-A|W3C]]) thus providing exactly the information you're obviously looking for.\\ --- //[[support@mwat.de|Matthias Watermann]] 2009/08/07 10:57// >> Thanks for your reply. When using the offline plugin, the links created by nstoc are not converted correctly to local URLs. I assumed that the offline plugin was reading the HTML output of rendering a page and the only different between links that were converted correctly (manual links) and those that were not (nstoc) was the title attribute. I should probably hit up the offline plugin guys for the cause of this, they may be incorrectly assuming that the title attribute can help them find the referred page rather than parsing the href. If there is no convention for using the title attribute for this then its their problem I guess. - //Lindsay Smith// 09-08-2009 \\ Nice plugin but it seems to me that there is a small bug if a page has same name than a namespace. In such a case, the page is ignored. For instance, if you have a namespace ///organization// and a page ///organisation.txt// the plugin will display ///organisation/district_A.txt// and ///organisation/district_B.txt// but not ///organisation.txt// which may introduce the other pages. \\ > Maybe a patch should suppress ligne 643 in syntax.php . '&& (! is_dir($aBase . "/" . substr($aFile, 0, -4)))' --- //Florent Chabaud 2009/12/23 20:51// > What you're describing, Florent, is indeed an intended feature and documented [[#index_pages|above]]. I'd recommend to rename the pages you do //not// want to be skipped to something like ''oldname_overview''.\\ --- //[[support@mwat.de|Matthias Watermann]] 2010/02/18 11:17// >> @Florent: Thanks. This little "hack" solved exactly this problem I had >> @Matthias: I understand the motivation of the current implementation, but e.g. in our environment, we completely got rid of the ''start'' pages and use pages with the same name as the namespaces as overview or index pages (usually using the nstoc plugin) (motivation: clean URLs). Renaming them is not an option because it would be against the intended results. Maybe this could become a configurable option for the plugin: To respect those "index" pages or not? \\ --- //Frank Thommen 2010/05/27// >> @Florent: Cheers for the patch, works a treat. @Frank: +1. The current behavior may be intentional, but it "feels" broken when a significant number of your namespace headings disappear from the TOC, so their pages appear as children of the previous namespace. \\ --- //Steve, 2010/05/29// \\ I use your plugin to have a table of content at the beginning of a page.\\ The headings are automatically numbered by the numberedheadings plugin. Unfortunately nstoc does not display the numbers of the heading.\\ ''" - heading"'' is shown instead of ''"1 heading"''.\\ (the syntax of numberedheadings plugin is to add a '-' between the '=' and the headingtext e.g. ''===== - heading ====='')\\ Does anybody has an idea how to fix this? //joachim 2010-02-08// > This plugin reads the headings as they're stored in the filesystem. So, if there's a hyphen in the heading a hyphen is shown. You might want to have a look at [[tips:numbered_headings|another approach]] for numbered headings. \\ --- //[[support@mwat.de|Matthias Watermann]] 2010/02/18 11:28// \\ >Don't work when ns name is not in English? I tried to set $conf['deaccent']=2, but still no result. >> Yep I have the same problem in my wiki too,... namespaces in Greek don't show up, but this is inconsistent... some cases work some others don't, the mb_ extension is not available atm on that PHP environment could it be related ? --- [[user>petsagouris|George Petsagourakis]] //2011/02/13 12:54// \\ > I want to change the default green color but I don't know how to solve this. I try with the style.css in nstoc dir but it doesn't cnange anything .. [[isma.ballo@gmail.com|Isma]] //2011/02/16 14:00// \\ \\ > Hello, in handle function at line 488 why are you passing a string at param 3 for the substr function ? When cache is regenerated i got warning about this param which should be a long instead of a string... > I suggest that : >$dir = substr($current, 0, -mb_strlen(strrchr($current, ':'))); > --- [[user>JohanGuilbaud|JohanGuilbaud]] //2011/10/11 09:18// ===== Feature Requests ===== ==== Numbered Headings ==== This plugin is almost perfect, but something is missing: I use the numbered heading plugin to automatically number my sections, but these numbers are not shown in the TOC build by this plugin. Maybe this could be solved by just using the built-in TOC generator of DokuWiki, so you can provide the same design and same features. ~~~~ ==== Play well with publish plugin ==== Thanks for putting this together, I use it all over the place. My one request would be to see if you could make the plugin play well with [[plugin:publish|publish plugin]]. I realize you can play well with every plugin, but draft is pretty popular. In particular, it would be great to have an option so that a page that is saved in draft form but does not currently have any published versions would not show up in the list. ==== List length ==== It would be great, if it is possible to restrict the headlinelist. like: [[nstoc [namespace [maxdepth][maxlength]]