From 7e8123afb595687f06c8e3d9bd7f1baabdae6177 Mon Sep 17 00:00:00 2001 From: CrawfordCurrie Date: Thu, 24 Mar 2011 14:58:25 +0000 Subject: [PATCH] Item10529: toc plugin modified to use book generated by bookmaker, and bookmaker now supports all existing formats of tocplugin. One interesting effect of this was that tocplugin now supports topics spread across different webs. git-svn-id: http://svn.foswiki.org/trunk/TocPlugin@11199 0b4bb1d4-4e5a-0410-9cc4-b2b747904278 --- data/System/TocPlugin.txt | 229 ++++++++++++++++-- data/System/WebOrder.txt | 3 +- lib/Foswiki/Plugins/TocPlugin.pm | 2 +- lib/Foswiki/Plugins/TocPlugin/DEPENDENCIES | 1 + lib/Foswiki/Plugins/TocPlugin/Section.pm | 13 +- lib/Foswiki/Plugins/TocPlugin/TOC.pm | 7 +- lib/Foswiki/Plugins/TocPlugin/TOCIF.pm | 22 +- .../Plugins/TocPlugin/TopLevelSection.pm | 45 ++-- 8 files changed, 247 insertions(+), 75 deletions(-) diff --git a/data/System/TocPlugin.txt b/data/System/TocPlugin.txt index 21756e5..06300ae 100644 --- a/data/System/TocPlugin.txt +++ b/data/System/TocPlugin.txt @@ -1,15 +1,15 @@ -%META:TOPICINFO{author="WolfgangDenk" date="1271416286" format="1.1" version="1.2"}% ---++ %TOPIC% - + %SHORTDESCRIPTION% -This plugin adds powerful support for generation and maintenance of tables -of contents and cross-references *across multiple topics*. - ----+++ Syntax Rules +%TOC% -Adds a number of new tags: +Supports the generation of tables of contents, together with symbolic +cross-references that operate _within_ and _between_ topics. +Adds a number of new macros: * %SECTIONn% - inserts an anchored section header * %CONTENTS% - generates a table of contents for a topic or an entire web, with full expansion depth control * %REF% - inserts a symbolically named cross-reference URL @@ -19,26 +19,223 @@ Example: %ATTACHURL%/screenshot.png -TocPluginHelp uses the plugin, and contains complete help information. +The rest of this topic is written using the plugin, so you will see macros such as %SECTION0% if the plugin +is not installed and enabled. ----++ %TOPIC% Settings +%SECTION1{name="WebOrder"}% Books +The plugin depends on the existance of a topic called "WebOrder". This topic is referred to as the "book", and +should contain a list of +the topics that you want to generate section numbers for. Each topic in the book is allocated a =SECTION0= +section number i.e. the first topic is section 1, the second is section 2 etc. - +The book topic is a simple list - either a bulleted list or a numbered list - and can be created interactively +using the %IF{"istopic 'BookmakerPlugin'" then="BookmakerPlugin" else="Foswiki$nop:Extensions.BookmakerPlugin"}%. + +Each entry in the book must be the name of a topic in the current web. This plugin ships with a simple WebOrder, which +you can view (but are advised not to change). + +e.g. +%ANCHOR{type="Example" name="WebOrder",isplay="no"}% An example Weborder topic + + * [[An Overview of Perl]] + 1 [[Getting Started]] + 2 [[Natural and Artificial Languages]] + 3 AGradeExample + 2 [[The Gory Details]] + * LexicalTexture + + +%SECTION1{name="SECTION"}% The =SECTION= macro +Supported attributes: =name=

+ +Subsections may be inserted in any topic using the =SECTIONn= macro, +where =n= is the required subsection level. The heading of the section +is taken as all text after the macro up to the end of line. For example, +the heading at the top of this section is marked with +

+%SECTION1{name="SECTION"}% The =%SECTION= macro
+
+ +*NOTES* + * See also %REF{type="Section" name="IndentedWebOrder"}% for information about modifying section numbering from the book topic. + * Sections do not _have_ to be named, but if they are not then they can only be referred to by knowing the exact section number. Section names must be unique within the topic. + * The only way to _close_ a section is to start a new section with a different level, or to end the topic. + * You can still use standard HTML heading tags such as <H1>, but sections marked this way will *not* be included in the table of contents. + +%SECTION3% The %SECTION0% macro +If a %SECTION0% macro occurs in a topic, the heading of that section will replace the topic name in the table of contents. + +*NOTES* + * The =name= attribute cannot be used to refer to a %SECTION0% macro. + +%SECTION2{name="TOC"}% Building the table of contents +Supported attributes: =depth topic= + +You can build a table of contents by inserting +
+%CONTENTS%
+
+in a topic. The first level of the table of contents is normally the +topics in the order of the list in WebOrder, though see +%REF{type="Section" name="IndentedWebOrder"}% for information about +modifying section numbering from the WebOrder topic. Subsections +listed in the table are automatically linked to the target =SECTION=. + + * The =topic= attribute may be used to generate a table of contents for just one topic. + * The =depth= attribute may be used to set the maximum number of levels to generate. + +%SECTION3% Output from %CONTENTS{depth=2}% macro for this web +%CONTENTS{depth="2"}% +%ANCHOR{type="Example" name="TOC"}% Table of contents for this web + +%SECTION3% Output from %CONTENTS% macro for this topic +%CONTENTS{topic="%TOPIC%"}% +%ANCHOR{type="Example" name="TopicTOC"}% Table of contents for this topic + +%X% Note that the CONTENTS macro is quite slow. This is because it has to open and analyse all topics it refers to. + +%I% You can still use the standard =%TOC%= macro in topics that use !TocPlugin. However only sections defined using the standard heading syntax will be visible. + +%SECTION2{name="TOCCHECK"}% The =TOCCHECK= macro + +Any topic (but most usually the WebOrder topic) may include the +
%TOCCHECK%
+macro. This causes the entries in the WebOrder topic to be +cross-referenced against the files actually stored in the web (see +WebIndex). Any topics which exist as files in the web but are missing +from the WebOrder will be listed. + +%I% Any topics that begin with the characters "Web" are special topics and are excluded from the list, though they can still be listed in the WebOrder and will appear in the table of contents. + +%SECTION1% Anchors and References - the =ANCHOR=, =REF= and =REFTABLE= macros +Bookmarks and references can be inserted into text using the ANCHOR +and REF macros. These can be used for references, for example, to tables +or figures. + +%SECTION2{name="ANCHOR"}% The =ANCHOR= macro +Supported attributes: =type name display= + +The ANCHOR macro creates a jump target suitable for jumping to from +somewhere else. The =type= adds the anchor to a "group"; this group is +required when generating a reference to the anchor, and may be used to +generate tables of same-type anchors (see +%REF{type="Section" name="REFTABLE"}% below). The =type= can be any name, +though convention suggests the use of types such as =Figure= and +=Table=. The special group =Section= is used internally to refer to +sections and subsections. Avoid using it for an =ANCHOR= or you may +see strange results. + +The =ANCHOR= macro is normally visible in the output, though it may be +made invisible by setting the =display= attribute to =no= . For +example: %ANCHOR{type="Figure" name="A" display="no"}% Here be sea monsters +
%ANCHOR{type="Figure" name="A" display="no"}% Here be sea
+monsters
will generate an invisible anchor on the text (there's +one one the line above, honest!) and +
<A name="#Figure_A"> </A>
+
%ANCHOR{type="Table" name="A"}% A wooden table
+will generate: +%ANCHOR{type="Table" name="A" display="yes"}% A wooden table + +All the text between the anchor and the next end-of-line will be +used to create the anchor. If the anchor is invisible, this text will +be invisible too. + +%SECTION2{name="REF"}% The =REF= macro +Supported attributes: =type topic name= + +The =REF= macro may be used to refer to an anchor. Anchors are +automatically inserted by =SECTION= macros or may be added using the +=ANCHOR= macro. For a =REF= macro to work, the type of the target must be +known. For example: +
+See %REF{type="Example" name="WebOrder"}% for more information about WebOrder
+
+will generate: + +See %REF{type="Example" name="WebOrder"}% for more information about WebOrder + +To refer to anchors in a different topic, use the =topic= attribute. +You can refer to sections by name by using the special type =Section= +e.g. %REF{type="Section" name="TOCCHECK"}%. + +If you refer to a non-existant anchor you are warned: for example, +
%REF{type="Reference" name="NonExistantAnchor"}%
generates

+ +%REF{type="Reference" name="NonExistantAnchor"}% + +%SECTION2{name="REFTABLE"}% The =REFTABLE= macro +Supported attributes: =type= + +The =REFTABLE= macro can be used to build tables of references based on +the type assigned to anchors. For example, if you have a lot of +anchors of type =Example= you can build a table of all these anchors +thus: +

%REFTABLE{type="Example"}%
+%ANCHOR{type="Example" name="example1" display="no"}% REFTABLE{type="Table"} example +This will insert a table like this: +%REFTABLE{type="Example"}% +and
%REFTABLE{type=Figure}%
+will insert a table like this: +%ANCHOR{type="Example" name="example2" display="no"}% REFTABLE{type="Figure"} example +%REFTABLE{type="Figure"}% +All topics listed in the WebOrder are scanned, but only anchors of the +requested type will be listed. + +%I% If you use =REFTABLE= with the type =Section= the table will contain a list of all _named_ sections. %ANCHOR{type="Example" name="example2" display="no"}% %REFTABLE{type="Section"}% + +%SECTION1{name="AddTocButtons"}% Adding navigation buttons +When using the =WebOrder= special topic to collect a list of topics +into a somewhat "linearized" form (a "book"), it is often very +convenient to be able to add navigation buttons to the previous and +the next pages as well as to the home page (table of contents). This +can be done by adding the %TOCBUTTONS% macro to your pages. For +example, you can use it in a template which is included either at the +top or the bottom of your pages. The included +="view.tocbuttons.tmpl"= template (intended to be used with !NatSkin) +adds the =%TOCBUTTONS%= macro to the content footer of all pages in +the web. To activate it, use ="* Set SKIN = tocbuttons, nat"= in your +WebPreferences. + +Note that the "Prev", "Home" and "Next" links will be added only for +such topics that are listed in the =WebOrder= special topic, and they +will only be inserted when viewing a page, i. e. they will for +example not show up when printing such a topic or the whole "book". + +%SECTION1{name="IndentedWebOrder"}% Getting clever +It is possible to change the way the table of contents for the web is +ordered by using extra levels of indent in the WebOrder. If you indent +a topic below another topic, then that topic will be treated as a +section of the parent topic. Section numbers within the subtopic are +adjusted accordingly. For example, say the WebOrder contains +
+	* [[Top level topic]]
+	* AnotherTopLevelTopic
+
+TopLevelTopic will be numbered 1., and the first =SECTION1= within TopLevelTopic will be 1.1. AnotherTopLevelTopic will be numbered 2. If, instead, WebOrder contains +
+	* [[Top level topic]]
+		* [[Second level topic]]
+	* AnotherTopLevelTopic
+
+TopLevelTopic will still be numbered 1., but now SecondLevelTopic will be numbered 1.1., +and the first =SECTION1= within SecondLevelTopic will be 1.1.1. The first =SECTION1= within +TopLevelTopic will now be numbered 1.2. AnotherTopLevelTopic will still be numbered 2. + +%SECTION1{name="tips"}% Hints and Tips + * Include a %TOCCHECK% macro at the end of the table of contents topic. + * Name all sections. This makes it easier to refer to them by symbolic names rather than trying to REF numbered sections. ----+++ Plugin Installation Instructions +---+++ Installation Instructions %$INSTALL_INSTRUCTIONS% - * If you see a link here: %REF{topic=TocPluginHelp,type=Section,name=installation}% then installation was successful - * Check the TocPluginHelp topic. You should see expanded section numbers. ----+++ Plugin Info +---+++ Info | One Line Description: | Table of contents and cross-reference management | | Author: | Foswiki:Main.CrawfordCurrie http://c-dot.co.uk | | Release: | %$RELEASE% | | Version: | %$VERSION% | | Change History: | +| 23 Mar 2011 | Made compatible with !BookmakerPlugin, which it now depends on. Now supports topics in different webs. | | 22 Dec 2009 | Updated for Foswiki | | 20 Apr 2006 | Minor doc update, headings no longer marked up as anchor text (Foswiki:Main.SteffenPoulsen) | | 1 Oct 2001 | Corrected directory naming (no thanks to WindowsME!) | diff --git a/data/System/WebOrder.txt b/data/System/WebOrder.txt index 6808c08..cce3ff2 100644 --- a/data/System/WebOrder.txt +++ b/data/System/WebOrder.txt @@ -1,4 +1,3 @@ - * TocPluginHelp - * TocPlugin + 1 TocPlugin %TOCCHECK% diff --git a/lib/Foswiki/Plugins/TocPlugin.pm b/lib/Foswiki/Plugins/TocPlugin.pm index 78b1b60..52606dd 100644 --- a/lib/Foswiki/Plugins/TocPlugin.pm +++ b/lib/Foswiki/Plugins/TocPlugin.pm @@ -17,7 +17,7 @@ package Foswiki::Plugins::TocPlugin; our $VERSION = '$Rev$'; -our $RELEASE = '2.0'; +our $RELEASE = '2.1'; our $wif; our $SHORTDESCRIPTION = 'Sophisticated table of contents generation'; our $NO_PREFS_IN_TOPIC = 1; diff --git a/lib/Foswiki/Plugins/TocPlugin/DEPENDENCIES b/lib/Foswiki/Plugins/TocPlugin/DEPENDENCIES index e69de29..8db76ac 100644 --- a/lib/Foswiki/Plugins/TocPlugin/DEPENDENCIES +++ b/lib/Foswiki/Plugins/TocPlugin/DEPENDENCIES @@ -0,0 +1 @@ +Foswiki::Plugins::BookmakerPlugin,>=0,perl,Required, available from Foswiki.org diff --git a/lib/Foswiki/Plugins/TocPlugin/Section.pm b/lib/Foswiki/Plugins/TocPlugin/Section.pm index 40439ee..5fc2df3 100644 --- a/lib/Foswiki/Plugins/TocPlugin/Section.pm +++ b/lib/Foswiki/Plugins/TocPlugin/Section.pm @@ -18,6 +18,7 @@ package Foswiki::Plugins::TocPlugin::Section; use strict; use integer; +use Assert; use Foswiki::Plugins::TocPlugin::Attrs (); use Foswiki::Plugins::TocPlugin::Anchor (); @@ -44,7 +45,7 @@ sub new { $this->{PARENT} = undef; # position in the parent section's subsection list $this->{POSITION} = -1; - # files only - wiki name of this section + # files only - wiki name of this section (web.topic) $this->{WIKINAME} = undef; # file only - has been loaded $this->{IS_LOADED} = undef; @@ -182,7 +183,7 @@ sub _getLastSubsection { sub _addSection { my ($this, $newEntry) = @_; - die "Section depth not relative to root" if + die "Section depth not relative to root ".$newEntry->level()." <= ".$this->level() if $newEntry->level() <= $this->level(); if ($newEntry->level() == $this->level() + 1) { @@ -500,13 +501,14 @@ sub _processREFTag { # Load the topic into this section sub parseTopicText { my ( $this, $text) = @_; - + my $i = 0; + while ($text =~ s/%(SECTION[0-9]+|ANCHOR)({[^%]*})?%(.*)//o) { - my $key = $1; + my ($key, $title) = ($1, $3); my $attrs = Foswiki::Plugins::TocPlugin::Attrs->new($2); - my $title = $3; $title =~ s/(^\s+|\s+$)//go; $attrs->set("text", $title); + $attrs->{name} ||= $i++; if ($key =~ s/([0-9]+)//o) { $key = $1; @@ -580,7 +582,6 @@ sub toPrint { my $ct = $toc->_findTopic($this->wikiName); my $text = $wif->readTopic($this->wikiName); - # $res .= "Expanding wikiName= " . $wif->webName . "." . $this->wikiName() . " "; $res .= Foswiki::Plugins::TocPlugin::TOC::_printWithTOCTags($toc, $wif, $ct, $text); } diff --git a/lib/Foswiki/Plugins/TocPlugin/TOC.pm b/lib/Foswiki/Plugins/TocPlugin/TOC.pm index cf04a29..c1c064e 100644 --- a/lib/Foswiki/Plugins/TocPlugin/TOC.pm +++ b/lib/Foswiki/Plugins/TocPlugin/TOC.pm @@ -76,9 +76,9 @@ sub _processTOCTags { if ($tag =~ s/SECTION([0-9]+)//o) { my $level = $1; $attrs->set("level", $ct->level() + $level); - $text =~ s/\/&_processTag($toc, $wif, $toc->currentTopic, "SECTION", $attrs)/eo; + $text =~ s/\/&_processTag($toc, $wif, $toc->currentTopic, "SECTION", $attrs)/e; } else { - $text =~ s/\/&_processTag($toc, $wif, $toc->currentTopic, "ANCHOR", $attrs)/eo; + $text =~ s/\/&_processTag($toc, $wif, $toc->currentTopic, "ANCHOR", $attrs)/e; } } } @@ -95,8 +95,7 @@ sub _processTOCTags { my $toc = undef; my $mess = undef; -sub processTOCBUTTONSTag -{ +sub processTOCBUTTONSTag { my ($toc, $wif, $topic) = @_; return "" unless (defined $toc); diff --git a/lib/Foswiki/Plugins/TocPlugin/TOCIF.pm b/lib/Foswiki/Plugins/TocPlugin/TOCIF.pm index 528e86e..ce7bcd8 100644 --- a/lib/Foswiki/Plugins/TocPlugin/TOCIF.pm +++ b/lib/Foswiki/Plugins/TocPlugin/TOCIF.pm @@ -39,32 +39,24 @@ sub getInterface { $singleton = $this; } $this->{WEBNAME} = $web; - $this->{TOPIC} = $topic; + $this->{TOPICNAME} = $topic; return bless($this, $class); } -sub webName { - my $this = shift; - return $this->{WEBNAME}; -} - -sub topicName { - my $this = shift; - return $this->{TOPIC}; -} - sub topicExists { my ($this, $topic) = @_; - return Foswiki::Func::topicExists($this->webName(), $topic); + my ($w, $t) = Foswiki::Func::normalizeWebTopicName($this->{WEBNAME}, $topic); + return Foswiki::Func::topicExists($w, $t); } sub readTopic { my ($this, $topic) = @_; my $text = ""; # read the topic - $text = Foswiki::Func::readTopic($this->webName(), $topic); + my ($w, $t) = Foswiki::Func::normalizeWebTopicName($this->{WEBNAME}, $topic); + $text = Foswiki::Func::readTopic($w, $t); # expand the variables -- in the context of the appropriate topic - $text = Foswiki::Func::expandCommonVariables($text, $topic, $this->webName()); + $text = Foswiki::Func::expandCommonVariables($text, $t, $w); # this can't be right -- turn verbatim tags into pre tags $text =~ s//
/go;
     $text =~ s/<\/verbatim>/<\/pre>/go;
@@ -73,7 +65,7 @@ sub readTopic {
 
 sub webDirList {
     my $this = shift;
-    return Foswiki::Func::getTopicList($this->webName());
+    return Foswiki::Func::getTopicList($this->{WEBNAME});
 }
 
 1;
diff --git a/lib/Foswiki/Plugins/TocPlugin/TopLevelSection.pm b/lib/Foswiki/Plugins/TocPlugin/TopLevelSection.pm
index 56d8270..ca04b77 100644
--- a/lib/Foswiki/Plugins/TocPlugin/TopLevelSection.pm
+++ b/lib/Foswiki/Plugins/TocPlugin/TopLevelSection.pm
@@ -21,6 +21,7 @@ use integer;
 
 use Foswiki::Plugins::TocPlugin::Attrs ();
 use Foswiki::Plugins::TocPlugin::Section ();
+use Foswiki::Plugins::BookmakerPlugin::Book ();
 
 # A specialisation of Section for the root of the table of contents
 
@@ -55,7 +56,7 @@ sub web {
 # about to perform an operation that requires it.
 sub currentTopic {
     my $this = shift;
-    my $currTopic = $this->_findTopic($this->wif()->topicName());
+    my $currTopic = $this->_findTopic($this->wif()->{TOPICNAME});
     $currTopic->loaded(1) if $currTopic;
     return $currTopic;
 }
@@ -87,7 +88,7 @@ sub loadTopic {
 }
 
 # PUBLIC STATIC
-# Factor method to generate the TOC for a web.
+# Factory method to generate the TOC for a web.
 # Initialise the table of contents from the WebOrder topic. The
 # table will only be partially populated; no topics will actually
 # be parsed yet.
@@ -98,41 +99,24 @@ sub createTOC {
     my $retmess = "";
 
     # load the table of contents topic
+    # Note that this does not use the BookmakerPlugin API for the simple reason that it has to
+    # support an old format for the WebOrder topic. However it is fully compatible.
     if ($wif->topicExists("WebOrder")) {
-        my $tocText = $wif->readTopic("WebOrder");
-
-        # allow [[Odd Wiki Word]] links
-        my @tocNames = split( /[\n\r]/, $tocText );
-        # extract the bulleted list
-        @tocNames = grep( /^\s+\*\s/, @tocNames );
-    
-        my $tocEntry;
+        my $book = Foswiki::Plugins::BookmakerPlugin::Book->new("$web.WebOrder");
         my $attrs = Foswiki::Plugins::TocPlugin::Attrs->new("");
 
         # Check that each topic in the WebOrder only appears once
-        my %seen;
 
-        foreach $tocEntry ( @tocNames ) {
-            my $name = $tocEntry;
-            $name =~ s/^[\s\*]*//o;
+	my $bit = $book->each();
+
+        while ($bit->hasNext()) {
+	    my $tocEntry = $bit->next();
+            my $name = $tocEntry->{topic}; # Ignore web; tocs only work in a single web
         
-            my $level = 0;
-            while ($tocEntry =~ s/^(\t|   )//o) {
-                $level++;
-            }
-
-            if ($seen{$name}) {
-                $retmess .=
-                  Foswiki::Plugins::TocPlugin::Section::_error(
-                      "Topic $name used more than once in WebOrder
"); - } - $seen{$name} = 1; - - $attrs->set("level", $level); + $attrs->set("level", $tocEntry->{level} + 1); $attrs->set("text", $name); my $ne = $this->processSECTIONTag($attrs); - my $wn = Foswiki::Plugins::TocPlugin::Section::_toWikiName($name); - $ne->wikiName($wn) if ($wif->topicExists($wn)); + $ne->wikiName($name) if ($wif->topicExists($name)); } } else { } @@ -219,8 +203,7 @@ sub toPrint { sub toString { my ($this, $nohtml) = @_; my $res = $this->{ISA}."(web=" . - $this->{WEB} . " topic=" . - $this->wif()->topicName() . ") "; + $this->{WEB} . ") "; $res .= "" unless $nohtml; $res .= "ISA"; $res .= "" unless $nohtml;