SimpleTableOfContents

Note: The recipes here are for PmWiki versions 0.6 and 1.0 only. For PmWiki 2.0 recipes, see Cookbook.


Goal

Automatically generate a Table of Contents for pages with many headings.

PmWiki (1.0.13) doesn't come with a TOC feature, as PITS/00027 said, there feature request suspended for there already a Cookbook PageTableOfContents. But the cookbook TOC module seems a little complicated for me. All I want is a autogenerated TOC from all levels of headings in a page, just like the WikiPedia style.

Solution

Add some script to pmwiki.php to generate a list of all levels of headings. Feature:

  • auto generate TOC list for all level headings
  • use heading text as the target name
  • do not need to modify exist wiki text

First, add a function named ProcessTOC in pmwiki.php to insert Wiki text for TOC before print text begin.

function ProcessTOC($text) {

  $tocText = '';
  $bodyText = '';
  $lines = explode("\n",$text);
  $lineNo = 0;

  $minLevel = 9;
  foreach($lines as $x) {
    $lineNo += 1;
    if (preg_match("/^(!{1,6})/",$x,$match)) {
      $level = strlen($match[1]);
      if ($level < $minLevel ) $minLevel = $level;     # record the min heading level

      # get the heading text, rid leading ! and white space
      $strAnchor = preg_replace("/^(!{1,6}\s*)/","",$x); 

      # 'T_' for target, encode the heading text
      $strAnchor = 'T_'.preg_replace("/[+|%]/","_",urlencode($strAnchor));

      # add a line of toc text
      $toc = strlen($match[1])."","";
      $tocText .= $toc."\n";

      # add a target before heading text
      $x = "\n".$x;
    }
    $bodyText .= $x."\n";   # add a line of body text
  }

  $toc = "";
  if ($tocText != "") {
    # use substr to rid the tailing '\n'
    $lines = explode("\n",substr($tocText,0, strlen($tocText)-1) );    

    foreach($lines as $x) {
      $level = $x[0];

      # set a correct number of '*'
      $toc .= str_repeat('*', $level - $minLevel +1 ).substr($x,1)."\n";   
    }
    # add table border for TOC
    $tocText = "(:table border=1 cellpadding=10:)

\n(:cell:) \n".$toc."(:tableend:) \n\n";

  }
  return $tocText.$bodyText;

}

Then, at the beginning lines of PrintText function, change

  if ($text=="") $text=$Text;

to

  if ($text=="") $text = ProcessTOC($Text);

That's OK

For Example, these headings

  !! North America
  !!! New York
  !!! Washington
  !! Asia
  !!! China
  !!!! Shanghai
  !!! Japan

will turn to a TOC section of :

Discussion

I've only used PmWiki for a week, and am a PHP newhand (ProcessTOC is the first fuction code I write in PHP), so the solution here a very simple one but seems convenient for myself.

Consider a better solution:

  • Is it possible to write a module script for this and do not modify pmwiki.php ?
  • If targets defined in the heading text line, the result is error. the line of heading should not contain other targets, if need a special target name, define it in the line before heading line. (This happens to PmWiki.TextFormattingRules)
  • Compare to WikiPedia, the TOC need these features :
    • define __notoc__ for pages
    • toc section show/hide
    • do not show TOC when headings less than two lines
    • add outline style number before toc heading text. (e.g. 1.2.2)

See Also

Cookbook PageTableOfContents

History

2004.11.22 - Initial idea by LiuPing -> mailto:lp_ [snail] 163 [period] net

Comments & Bugs

Contributors

  • LiuPing -> mailto:lp_ [snail] 163 [period] net

pmwiki-2.5.9 -- Last modified by {{}}

from IP: 85.171.160.186 ip should be disabled by default for security reasons

Array
(
    [post_max_size] => 64M
    [$_POST keys] => 
    [$_REQUEST keys] => n
    [$_SERVER] => Array
        (
            [CONTEXT_DOCUMENT_ROOT] => /home/pmwiki/public_html
            [CONTEXT_PREFIX] => 
            [DOCUMENT_ROOT] => /home/pmwiki/public_html
            [GATEWAY_INTERFACE] => CGI/1.1
            [HTTPS] => on
            [HTTP_ACCEPT] => */*
            [HTTP_ACCEPT_ENCODING] => gzip, br, zstd, deflate
            [HTTP_HOST] => www.pmwiki.org
            [HTTP_USER_AGENT] => Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
            [HTTP_X_HTTPS] => 1
            [PATH] => /bin:/usr/bin
            [PHP_INI_SCAN_DIR] => /opt/cpanel/ea-php70/root/etc:/opt/cpanel/ea-php70/root/etc/php.d:.
            [QUERY_STRING] => n=Cookbook%2dV1%2fSimpleTableOfContents
            [REDIRECT_HTTPS] => on
            [REDIRECT_QUERY_STRING] => n=Cookbook%2dV1%2fSimpleTableOfContents
            [REDIRECT_SCRIPT_URI] => https://www.pmwiki.org/wiki/Cookbook-V1/SimpleTableOfContents
            [REDIRECT_SCRIPT_URL] => /wiki/Cookbook-V1/SimpleTableOfContents
            [REDIRECT_SSL_TLS_SNI] => www.pmwiki.org
            [REDIRECT_STATUS] => 200
            [REDIRECT_UNIQUE_ID] => afIk0W2D0nLbFBbh918OoAAAAAE
            [REDIRECT_URL] => /wiki/Cookbook-V1/SimpleTableOfContents
            [REMOTE_ADDR] => 216.73.216.31
            [REMOTE_PORT] => 57190
            [REQUEST_METHOD] => GET
            [REQUEST_SCHEME] => https
            [REQUEST_URI] => /wiki/Cookbook-V1/SimpleTableOfContents
            [SCRIPT_FILENAME] => /home/pmwiki/public_html/index.php
            [SCRIPT_NAME] => /index.php
            [SCRIPT_URI] => https://www.pmwiki.org/wiki/Cookbook-V1/SimpleTableOfContents
            [SCRIPT_URL] => /wiki/Cookbook-V1/SimpleTableOfContents
            [SERVER_ADDR] => 23.254.203.248
            [SERVER_ADMIN] => webmaster@pmwiki.org
            [SERVER_NAME] => www.pmwiki.org
            [SERVER_PORT] => 443
            [SERVER_PROTOCOL] => HTTP/1.1
            [SERVER_SIGNATURE] => 
            [SERVER_SOFTWARE] => Apache
            [SSL_TLS_SNI] => www.pmwiki.org
            [TZ] => America/Los_Angeles
            [UNIQUE_ID] => afIk0W2D0nLbFBbh918OoAAAAAE
            [PHP_SELF] => /index.php
            [REQUEST_TIME_FLOAT] => 1777476817.6461
            [REQUEST_TIME] => 1777476817
            [argv] => Array
                (
                    [0] => n=Cookbook%2dV1%2fSimpleTableOfContents
                )

            [argc] => 1
        )

)