From b8595a660455f6778266779753c6238127664a28 Mon Sep 17 00:00:00 2001
From: Andreas Gohr <andi@splitbrain.org>
Date: Sun, 5 Aug 2007 15:24:05 +0200
Subject: [PATCH] separated TOC from page

This patch introduces a tpl_toc() function which can be used to freely place
the Table of Contents in a template. When used, tpl_content should be called
with a parameter of false to supress the automatic TOC placement.

Note: if tpl_toc() us run *before* tpl_content(), TOCs will not work in the
preview. A work around is to run tpl_content() in a output buffer first.

This patch also adds a getTOC() function for admin plugins which allows plugin
authors to put create their own TOC which will be placed correctly in the
template. A convenience function html_mktocitem() is available.

The config manager was adjusted to make ue of this new feature, but some bugs
might remain.

darcs-hash:20070805132405-7ad00-77d2c3cdf66cc62b2d408cc6580f938636a109af.gz
---
 inc/html.php                 | 64 +++++++++++++++++++++++++---
 inc/init.php                 |  3 ++
 inc/parser/metadata.php      |  3 ++
 inc/parser/xhtml.php         | 43 +++----------------
 inc/parserutils.php          |  2 +-
 inc/template.php             | 82 ++++++++++++++++++++++++++++++------
 lib/plugins/admin.php        |  4 ++
 lib/plugins/config/admin.php | 49 +++++++++++----------
 8 files changed, 165 insertions(+), 85 deletions(-)

diff --git a/inc/html.php b/inc/html.php
index ff2194f2b..58886dce5 100644
--- a/inc/html.php
+++ b/inc/html.php
@@ -200,6 +200,7 @@ function html_show($txt=''){
   global $ID;
   global $REV;
   global $HIGH;
+  global $INFO;
   //disable section editing for old revisions or in preview
   if($txt || $REV){
     $secedit = false;
@@ -209,18 +210,22 @@ function html_show($txt=''){
 
   if ($txt){
     //PreviewHeader
-    print '<br id="scroll__here" />';
-    print p_locale_xhtml('preview');
-    print '<div class="preview">';
-    print html_secedit(p_render('xhtml',p_get_instructions($txt),$info),$secedit);
-    print '<div class="clearer"></div>';
-    print '</div>';
+    echo '<br id="scroll__here" />';
+    echo p_locale_xhtml('preview');
+    echo '<div class="preview">';
+    $html = html_secedit(p_render('xhtml',p_get_instructions($txt),$info),$secedit);
+    if($INFO['prependTOC']) $html = tpl_toc(true).$html;
+    echo $html;
+    echo '<div class="clearer"></div>';
+    echo '</div>';
 
   }else{
     if ($REV) print p_locale_xhtml('showrev');
     $html = p_wiki_xhtml($ID,$REV,true);
     $html = html_secedit($html,$secedit);
-    print html_hilight($html,$HIGH);
+    if($INFO['prependTOC']) $html = tpl_toc(true).$html;
+    $html = html_hilight($html,$HIGH);
+    echo $html;
   }
 }
 
@@ -1245,6 +1250,51 @@ function html_resendpwd() {
   print '</div>'.NL;
 }
 
+/**
+ * Return the TOC rendered to XHTML
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function html_TOC($toc){
+    if(!count($toc)) return '';
+    global $lang;
+    $out  = '<!-- TOC START -->'.DOKU_LF;
+    $out .= '<div class="toc">'.DOKU_LF;
+    $out .= '<div class="tocheader toctoggle" id="toc__header">';
+    $out .= $lang['toc'];
+    $out .= '</div>'.DOKU_LF;
+    $out .= '<div id="toc__inside">'.DOKU_LF;
+    $out .= html_buildlist($toc,'toc','html_list_toc');
+    $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
+    $out .= '<!-- TOC END -->'.DOKU_LF;
+    return $out;                                                                                                }
+
+/**
+ * Callback for html_buildlist
+ */
+function html_list_toc($item){
+    return '<span class="li"><a href="'.$item['link'].'" class="toc">'.
+           hsc($item['title']).'</a></span>';
+}
+
+/**
+ * Helper function to build TOC items
+ *
+ * Returns an array ready to be added to a TOC array
+ *
+ * @param string $link  - where to link (if $hash set to '#' it's a local anchor)
+ * @param string $text  - what to display in the TOC
+ * @param int    $level - nesting level
+ * @param string $hash  - is prepended to the given $link, set blank if you want full links
+ */
+function html_mktocitem($link, $text, $level, $hash='#'){
+    global $conf;
+    return  array( 'link'  => $hash.$link,
+                   'title' => $text,
+                   'type'  => 'ul',
+                   'level' => $level-$conf['toptoclevel']+1);
+}
+
 /**
  * Output a Doku_Form object.
  * Triggers an event with the form name: HTML_{$name}FORM_OUTPUT
diff --git a/inc/init.php b/inc/init.php
index f8697a845..286850e94 100644
--- a/inc/init.php
+++ b/inc/init.php
@@ -63,6 +63,9 @@
     }
   }
 
+  // define whitespace
+  if(!defined('DOKU_LF')) define ('DOKU_LF',"\n");
+  if(!defined('DOKU_TAB')) define ('DOKU_TAB',"\t");
 
   // define cookie and session id
   if (!defined('DOKU_COOKIE')) define('DOKU_COOKIE', 'DW'.md5(DOKU_URL));
diff --git a/inc/parser/metadata.php b/inc/parser/metadata.php
index 5f06865cf..6e93fd61f 100644
--- a/inc/parser/metadata.php
+++ b/inc/parser/metadata.php
@@ -42,6 +42,9 @@ class Doku_Renderer_metadata extends Doku_Renderer {
   }
 
   function document_end(){
+    // store internal info in metadata (notoc,nocache)
+    $this->meta['internal'] = $this->info;
+
     if (!$this->meta['description']['abstract']){
       // cut off too long abstracts
       $this->doc = trim($this->doc);
diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php
index 30e154af6..e22298fa3 100644
--- a/inc/parser/xhtml.php
+++ b/inc/parser/xhtml.php
@@ -82,55 +82,22 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
             $this->doc .= '</div>'.DOKU_LF;
         }
 
-        // prepend the TOC
-        if($this->info['toc']){
-            $this->doc = $this->render_TOC($this->toc).$this->doc;
+        // Prepare the TOC
+        if($this->info['toc'] && is_array($this->toc) && count($this->toc) > 2){
+            global $TOC;
+            $TOC = $this->toc;
         }
 
         // make sure there are no empty paragraphs
         $this->doc = preg_replace('#<p>\s*</p>#','',$this->doc);
     }
 
-    /**
-     * Return the TOC rendered to XHTML
-     *
-     * @author Andreas Gohr <andi@splitbrain.org>
-     */
-    function render_TOC($toc=null){
-        if(is_null($toc) && is_array($this->toc)) $toc = $this->toc;
-
-        if(count($toc) < 3) return '';
-        global $lang;
-        $out  = '<!-- TOC START -->'.DOKU_LF;
-        $out .= '<div class="toc">'.DOKU_LF;
-        $out .= '<div class="tocheader toctoggle" id="toc__header">';
-        $out .= $lang['toc'];
-        $out .= '</div>'.DOKU_LF;
-        $out .= '<div id="toc__inside">'.DOKU_LF;
-        $out .= html_buildlist($toc,'toc',array(__CLASS__,'_tocitem'));
-        $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
-        $out .= '<!-- TOC END -->'.DOKU_LF;
-        return $out;
-    }
-
-    /**
-     * Callback for html_buildlist
-     */
-    function _tocitem($item){
-        return '<span class="li"><a href="#'.$item['hid'].'" class="toc">'.
-               Doku_Renderer_xhtml::_xmlEntities($item['title']).'</a></span>';
-    }
-
     function toc_additem($id, $text, $level) {
         global $conf;
 
         //handle TOC
         if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){
-            // the TOC is one of our standard ul list arrays ;-)
-            $this->toc[] = array( 'hid'   => $id,
-                                  'title' => $text,
-                                  'type'  => 'ul',
-                                  'level' => $level-$conf['toptoclevel']+1);
+            $this->toc[] = html_mktocitem("#$id", $text, $level);
         }
     }
 
diff --git a/inc/parserutils.php b/inc/parserutils.php
index 20971ac03..e4e51c5a5 100644
--- a/inc/parserutils.php
+++ b/inc/parserutils.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * Utilities for collecting data from config files
+ * Utilities for accessing the parser
  *
  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
  * @author     Harry Fuecks <hfuecks@gmail.com>
diff --git a/inc/template.php b/inc/template.php
index 10ecd9f70..196aedb4b 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -12,7 +12,6 @@
 /**
  * Returns the path to the given template, uses
  * default one if the custom version doesn't exist.
- * Also enables gzip compression if configured.
  *
  * @author Andreas Gohr <andi@splitbrain.org>
  */
@@ -37,18 +36,17 @@ function template($tpl){
  *
  * @author Andreas Gohr <andi@splitbrain.org>
  */
-function tpl_content() {
-  global $ACT;
-
-  ob_start();
-
-  trigger_event('TPL_ACT_RENDER',$ACT,'tpl_content_core');
-
-  $html_output = ob_get_clean();
+function tpl_content($prependTOC=true) {
+    global $ACT;
+    global $INFO;
+    $INFO['prependTOC'] = $prependTOC;
 
-  trigger_event('TPL_CONTENT_DISPLAY',$html_output,'ptln');
+    ob_start();
+    trigger_event('TPL_ACT_RENDER',$ACT,'tpl_content_core');
+    $html_output = ob_get_clean();
+    trigger_event('TPL_CONTENT_DISPLAY',$html_output,'ptln');
 
-  return !empty($html_output);
+    return !empty($html_output);
 }
 
 function tpl_content_core(){
@@ -135,6 +133,59 @@ function tpl_content_core(){
   return true;
 }
 
+/**
+ * Places the TOC where the function is called
+ *
+ * If you use this you most probably want to call tpl_content with
+ * a false argument
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function tpl_toc($return=false){
+    global $TOC;
+    global $ACT;
+    global $ID;
+    global $REV;
+    global $INFO;
+    $toc = array();
+
+    if(is_array($TOC)){
+        // if a TOC was prepared in global scope, always use it
+        $toc = $TOC;
+    }elseif($ACT == 'show' && !$REV){
+        // get TOC from metadata, render if neccessary
+        $meta = p_get_metadata($ID, false, true);
+        if(isset($meta['internal']['toc'])){
+            $tocok = $meta['internal']['toc'];
+        }else{
+            $tokok = true;
+        }
+        $toc   = $meta['description']['tableofcontents'];
+        if(!$tocok || !is_array($toc) || count($toc) < 3){
+            $toc = array();
+        }
+    }elseif($ACT == 'admin'){
+        // try to load admin plugin TOC FIXME: duplicates code from tpl_admin
+        $plugin = null;
+        if (!empty($_REQUEST['page'])) {
+            $pluginlist = plugin_list('admin');
+            if (in_array($_REQUEST['page'], $pluginlist)) {
+                // attempt to load the plugin
+                $plugin =& plugin_load('admin',$_REQUEST['page']);
+            }
+        }
+        if ( ($plugin !== null) &&
+             (!$plugin->forAdminOnly() || $INFO['isadmin']) ){
+            $toc = $plugin->getTOC();
+            $TOC = $toc; // avoid later rebuild
+        }
+    }
+
+    $html = html_TOC($toc);
+    if($return) return $html;
+    echo $html;
+}
+
 /**
  * Handle the admin page contents
  *
@@ -142,8 +193,9 @@ function tpl_content_core(){
  */
 function tpl_admin(){
     global $INFO;
+    global $TOC;
 
-    $plugin = NULL;
+    $plugin = null;
     if (!empty($_REQUEST['page'])) {
         $pluginlist = plugin_list('admin');
 
@@ -154,11 +206,13 @@ function tpl_admin(){
         }
     }
 
-    if ($plugin !== NULL){
+    if ($plugin !== null){
         if($plugin->forAdminOnly() && !$INFO['isadmin']){
             msg('For admins only',-1);
             html_admin();
         }else{
+            if(!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet
+            if($INFO['prependTOC']) tpl_toc();
             $plugin->html();
         }
     }else{
@@ -1061,4 +1115,4 @@ function tpl_mediaTree(){
   ptln('</div>');
 }
 
-//Setup VIM: ex: et ts=2 enc=utf-8 :
+//Setup VIM: ex: et ts=4 enc=utf-8 :
diff --git a/lib/plugins/admin.php b/lib/plugins/admin.php
index 00a07a054..2eeda3f7b 100644
--- a/lib/plugins/admin.php
+++ b/lib/plugins/admin.php
@@ -41,5 +41,9 @@ class DokuWiki_Admin_Plugin extends DokuWiki_Plugin {
     function forAdminOnly() {
         return true;
     }
+
+    function getTOC(){
+        return array();
+    }
 }
 //Setup VIM: ex: et ts=4 enc=utf-8 :
diff --git a/lib/plugins/config/admin.php b/lib/plugins/config/admin.php
index 1e12df53a..26880b390 100644
--- a/lib/plugins/config/admin.php
+++ b/lib/plugins/config/admin.php
@@ -32,7 +32,7 @@ class admin_plugin_config extends DokuWiki_Admin_Plugin {
     var $_changed = false;          // set to true if configuration has altered
     var $_error = false;
     var $_session_started = false;
-        var $_localised_prompts = false;
+    var $_localised_prompts = false;
 
     /**
      * return some info
@@ -42,7 +42,7 @@ class admin_plugin_config extends DokuWiki_Admin_Plugin {
       return array(
         'author' => 'Christopher Smith',
         'email'  => 'chris@jalakai.co.uk',
-        'date'   => '2006-01-24',
+        'date'   => '2007-08-05',
         'name'   => 'Configuration Manager',
         'desc'   => "Manage Dokuwiki's Configuration Settings",
         'url'    => 'http://wiki.splitbrain.org/plugin:config',
@@ -99,7 +99,6 @@ class admin_plugin_config extends DokuWiki_Admin_Plugin {
       if (is_null($this->_config)) { $this->_config = new configuration($this->_file); }
       $this->setupLocale(true);
 
-      $this->_print_config_toc();
       print $this->locale_xhtml('intro');
 
       ptln('<div id="config__manager">');
@@ -277,10 +276,10 @@ class admin_plugin_config extends DokuWiki_Admin_Plugin {
         }
         closedir($dh);
       }
-      
+
       // the same for the active template
       $tpl = $conf['template'];
-      
+
       if (@file_exists(DOKU_TPLINC.$enlangfile)){
         $lang = array();
         @include(DOKU_TPLINC.$enlangfile);
@@ -295,17 +294,19 @@ class admin_plugin_config extends DokuWiki_Admin_Plugin {
         $this->lang['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.'template_settings_name'] =
           ucwords(str_replace('_', ' ', $tpl)).' '.$this->getLang('_template_sufix');
       }
-      
+
       return true;
     }
 
     /**
-    * Generates a two-level table of contents for the config plugin.
-    * Uses inc/parser/xhtml.php#render_TOC to format the output.
-    *
-    * @author Ben Coburn <btcoburn@silicodon.net>
-    */
-    function _print_config_toc() {
+     * Generates a two-level table of contents for the config plugin.
+     *
+     * @author Ben Coburn <btcoburn@silicodon.net>
+     */
+    function getTOC() {
+      if (is_null($this->_config)) { $this->_config = new configuration($this->_file); }
+      $this->setupLocale(true);
+
       $allow_debug = $GLOBALS['conf']['allowdebug']; // avoid global $conf; here.
 
       // gather toc data
@@ -325,35 +326,33 @@ class admin_plugin_config extends DokuWiki_Admin_Plugin {
         }
       }
 
-      // use the xhtml renderer to make the toc
-      require_once(DOKU_INC.'inc/parser/xhtml.php');
-      $r = new Doku_Renderer_xhtml;
-
       // build toc
-      $r->toc_additem('configuration_manager', $this->getLang('_configuration_manager'), 1);
-      $r->toc_additem('dokuwiki_settings', $this->getLang('_header_dokuwiki'), 1);
+      $t = array();
+
+      $t[] = html_mktocitem('configuration_manager', $this->getLang('_configuration_manager'), 1);
+      $t[] = html_mktocitem('dokuwiki_settings', $this->getLang('_header_dokuwiki'), 1);
       foreach($toc['conf'] as $setting) {
         $name = $setting->prompt($this);
-        $r->toc_additem($setting->_key, $name, 2);
+        $t[] = html_mktocitem($setting->_key, $name, 2);
       }
       if (!empty($toc['plugin'])) {
-        $r->toc_additem('plugin_settings', $this->getLang('_header_plugin'), 1);
+        $t[] = html_mktocitem('plugin_settings', $this->getLang('_header_plugin'), 1);
       }
       foreach($toc['plugin'] as $setting) {
         $name = $setting->prompt($this);
-        $r->toc_additem($setting->_key, $name, 2);
+        $t[] = html_mktocitem($setting->_key, $name, 2);
       }
       if (isset($toc['template'])) {
-        $r->toc_additem('template_settings', $this->getLang('_header_template'), 1);
+        $t[] = html_mktocitem('template_settings', $this->getLang('_header_template'), 1);
         $setting = $toc['template'];
         $name = $setting->prompt($this);
-        $r->toc_additem($setting->_key, $name, 2);
+        $t[] = html_mktocitem($setting->_key, $name, 2);
       }
       if ($has_undefined && $allow_debug) {
-        $r->toc_additem('undefined_settings', $this->getLang('_header_undefined'), 1);
+        $t[] = html_mktocitem('undefined_settings', $this->getLang('_header_undefined'), 1);
       }
 
-      print $r->render_TOC();
+      return $t;
     }
 
     function _print_h1($id, $text) {
-- 
GitLab