From e16746a52f40f478af1b634c532d90c25cc0ec76 Mon Sep 17 00:00:00 2001 From: joey Date: Mon, 28 Aug 2006 07:40:20 +0000 Subject: [PATCH] * Add toc (table of contents) plugin. --- IkiWiki/Plugin/skeleton.pm | 10 ++ IkiWiki/Plugin/toc.pm | 116 ++++++++++++++++++++ Makefile.PL | 2 +- debian/changelog | 3 +- doc/features.mdwn | 213 ++++++++++++++++++------------------- doc/ikiwiki.setup | 2 +- doc/plugins/toc.mdwn | 13 +++ doc/security.mdwn | 2 + doc/todo/pageindexes.mdwn | 2 + t/html.t | 2 +- 10 files changed, 253 insertions(+), 112 deletions(-) create mode 100644 IkiWiki/Plugin/toc.pm create mode 100644 doc/plugins/toc.mdwn diff --git a/IkiWiki/Plugin/skeleton.pm b/IkiWiki/Plugin/skeleton.pm index 1201d055c..4683b00ba 100644 --- a/IkiWiki/Plugin/skeleton.pm +++ b/IkiWiki/Plugin/skeleton.pm @@ -21,6 +21,8 @@ sub import { #{{{ call => \&htmlize); IkiWiki::hook(type => "sanitize", id => "skeleton", call => \&sanitize); + IkiWiki::hook(type => "format", id => "skeleton", + call => \&format); IkiWiki::hook(type => "pagetemplate", id => "skeleton", call => \&pagetemplate); IkiWiki::hook(type => "delete", id => "skeleton", @@ -71,6 +73,14 @@ sub sanitize ($) { #{{{ return $content; } # }}} +sub format ($) { #{{{ + my $content=shift; + + IkiWiki::debug("skeleton plugin running as a formatter"); + + return $content; +} # }}} + sub pagetemplate (@) { #{{{ my %params=@_; my $page=$params{page}; diff --git a/IkiWiki/Plugin/toc.pm b/IkiWiki/Plugin/toc.pm new file mode 100644 index 000000000..c36ce2893 --- /dev/null +++ b/IkiWiki/Plugin/toc.pm @@ -0,0 +1,116 @@ +#!/usr/bin/perl +# Table Of Contents generator +package IkiWiki::Plugin::toc; + +use warnings; +use strict; +use IkiWiki; +use HTML::Parser; + +sub import { #{{{ + IkiWiki::hook(type => "preprocess", id => "toc", + call => \&preprocess); + IkiWiki::hook(type => "format", id => "toc", + call => \&format); +} # }}} + +my @tocs; + +sub preprocess (@) { #{{{ + my %params=@_; + + $params{levels}=1 unless exists $params{levels}; + + # It's too early to generate the toc here, so just record the + # info. + push @tocs, \%params; + + return "\n[[toc $#tocs]]\n"; +} # }}} + +sub format ($) { #{{{ + my $content=shift; + + return $content unless @tocs && $content=~/\[\[toc (\d+)\]\]/ && $#tocs >= $1; + my $id=$1; + my %params=%{$tocs[$id]}; + + my $p=HTML::Parser->new(api_version => 3); + my $page=""; + my $index=""; + my %anchors; + my $curlevel; + my $startlevel=0; + my $liststarted=0; + my $indent=sub { "\t" x $curlevel }; + $p->handler(start => sub { + my $tagname=shift; + my $text=shift; + if ($tagname =~ /^h(\d+)$/i) { + my $level=$1; + my $anchor="index".++$anchors{$level}."h$level"; + $page.="$text"; + + # Take the first header level seen as the topmost level, + # even if there are higher levels seen later on. + if (! $startlevel) { + $startlevel=$level; + $curlevel=$startlevel-1; + } + elsif ($level < $startlevel) { + $level=$startlevel; + } + + return if $level - $startlevel >= $params{levels}; + + if ($level > $curlevel) { + while ($level > $curlevel + 1) { + $index.=&$indent."
    \n"; + $curlevel++; + $index.=&$indent."
  1. \n"; + } + $index.=&$indent."
      \n"; + $curlevel=$level; + $liststarted=1; + } + elsif ($level < $curlevel) { + while ($level < $curlevel) { + $index.=&$indent."\n" if $curlevel; + $curlevel--; + $index.=&$indent."
    \n"; + } + $liststarted=0; + } + + $p->handler(text => sub { + $page.=join("", @_); + $index.=&$indent."
  2. \n" unless $liststarted; + $liststarted=0; + $index.=&$indent."
  3. ". + "". + join("", @_). + "\n"; + $p->handler(text => undef); + }, "dtext"); + } + else { + $page.=$text; + } + }, "tagname, text"); + $p->handler(default => sub { $page.=join("", @_) }, "text"); + $p->parse($content); + $p->eof; + + while ($startlevel && $curlevel >= $startlevel) { + $index.=&$indent."
  4. \n" if $curlevel; + $curlevel--; + $index.=&$indent."
\n"; + } + + # Ignore cruft around the toc marker, probably

tags added by + # markdown which shouldn't appear in a list anyway. + $page=~s/\n.*\[\[toc $id\]\].*\n/$index/; + return $page; +} + +1 diff --git a/Makefile.PL b/Makefile.PL index 7f715eb87..d2266ad8e 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -24,7 +24,7 @@ extra_build: --plugin=orphans --plugin=haiku --plugin=meta \ --plugin=tag --plugin=polygen --plugin=pagestats \ --plugin=fortune --plugin=aggregate --plugin=map \ - --plugin=template + --plugin=template --plugin=toc ./mdwn2man ikiwiki 1 doc/usage.mdwn > ikiwiki.man ./mdwn2man ikiwiki-mass-rebuild 8 doc/ikiwiki-mass-rebuild.mdwn > ikiwiki-mass-rebuild.man diff --git a/debian/changelog b/debian/changelog index 2d1fd3777..ccff36c9a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -40,8 +40,9 @@ ikiwiki (1.22) UNRELEASED; urgency=low * Factor out the cgi header printing code into a new function. * Fix preferences page on anonok wikis; still need to sign in to get to the preferences page. + * Add toc (table of contents) plugin. - -- Joey Hess Sun, 27 Aug 2006 16:17:21 -0400 + -- Joey Hess Mon, 28 Aug 2006 02:31:56 -0400 ikiwiki (1.21) unstable; urgency=low diff --git a/doc/features.mdwn b/doc/features.mdwn index 92773f3c9..b909b864a 100644 --- a/doc/features.mdwn +++ b/doc/features.mdwn @@ -1,155 +1,152 @@ -Some of ikiwiki's features: +An overview of some of ikiwiki's features: +[[toc ]] -* Uses a real RCS +## Uses a real RCS - Rather than implement its own system for storing page histories etc, - ikiwiki uses a real RCS. This isn't because we're lazy, it's because a - real RCS is a good thing to have, and there are advantages to using one - that are not possible with a standard wiki. +Rather than implement its own system for storing page histories etc, +ikiwiki uses a real RCS. This isn't because we're lazy, it's because a +real RCS is a good thing to have, and there are advantages to using one +that are not possible with a standard wiki. - Instead of editing pages in a stupid web form, you can use vim and commit - changes via svn. Or work disconnected using svk and push your changes out - when you come online. Or use git to work in a distributed fashion all the - time. (It's also possible to [[plugins/write]] a plugin to support other - systems.) +Instead of editing pages in a stupid web form, you can use vim and commit +changes via svn. Or work disconnected using svk and push your changes out +when you come online. Or use git to work in a distributed fashion all the +time. (It's also possible to [[plugins/write]] a plugin to support other +systems.) - ikiwiki can be run from a [[post-commit]] hook to update your wiki - immediately whenever you commit. +ikiwiki can be run from a [[post-commit]] hook to update your wiki +immediately whenever you commit. - Note that ikiwiki does not require a RCS to function. If you want to - run a simple wiki without page history, it can do that too. +Note that ikiwiki does not require a RCS to function. If you want to +run a simple wiki without page history, it can do that too. -* Supports many markup languages +## Supports many markup languages - By default, pages in the wiki are written using the [[MarkDown]] format. - Any page with a filename ending in ".mdwn" is converted from markdown to html - by ikiwiki. Markdown understands text formatted as it would be in an email, - and is quite smart about converting it to html. The only additional markup - provided by ikiwiki on top of regular markdown is the [[WikiLink]] and - [[PreprocessorDirective]]. +By default, pages in the wiki are written using the [[MarkDown]] format. +Any page with a filename ending in ".mdwn" is converted from markdown to html +by ikiwiki. Markdown understands text formatted as it would be in an email, +and is quite smart about converting it to html. The only additional markup +provided by ikiwiki on top of regular markdown is the [[WikiLink]] and +[[PreprocessorDirective]]. - If you prefer to use some other markup language, ikiwiki allows others to - easily be added by [[plugins]]. For example it also supports traditional - [[plugins/WikiText]] formatted pages, pages written as pure - [[plugins/HTML]], or pages written in [[reStructuredText|plugins/rst]]. +If you prefer to use some other markup language, ikiwiki allows others to +easily be added by [[plugins]]. For example it also supports traditional +[[plugins/WikiText]] formatted pages, pages written as pure +[[plugins/HTML]], or pages written in [[reStructuredText|plugins/rst]]. -* support for other file types +## support for other file types - ikiwiki also supports files of any other type, including plain text, - images, etc. These are not converted to wiki pages, they are just copied - unchanged by ikiwiki as it builds your wiki. So you can check in an image, - program, or other special file and link to it from your wiki pages. +ikiwiki also supports files of any other type, including plain text, +images, etc. These are not converted to wiki pages, they are just copied +unchanged by ikiwiki as it builds your wiki. So you can check in an image, +program, or other special file and link to it from your wiki pages. -* Fast compiler +## Fast compiler - ikiwiki is fast and smart about updating a wiki, it only builds pages - that have changed (and tracks things like creation of new pages and links - that can indirectly cause a page to need a rebuild) +ikiwiki is fast and smart about updating a wiki, it only builds pages +that have changed (and tracks things like creation of new pages and links +that can indirectly cause a page to need a rebuild) -* [[blogging|blog]] +## [[blogging|blog]] - You can turn any page in the wiki into a [[blog]]. Pages matching a - specified [[PageSpec]] will be displayed as a weblog within the blog - page. And an RSS feed can be generated to follow the blog. +You can turn any page in the wiki into a [[blog]]. Pages matching a +specified [[PageSpec]] will be displayed as a weblog within the blog +page. And an RSS feed can be generated to follow the blog. - Ikiwiki's own [[TODO]], [[news]], and [[plugins]] pages are good examples - of some of the flexible ways that this can be used. +Ikiwiki's own [[TODO]], [[news]], and [[plugins]] pages are good examples +of some of the flexible ways that this can be used. - Ikiwiki can also [[plugins/aggregate]] external blogs, feeding them into - the wiki. This can be used to create a Planet type site that aggregates - interesting feeds. +Ikiwiki can also [[plugins/aggregate]] external blogs, feeding them into +the wiki. This can be used to create a Planet type site that aggregates +interesting feeds. -* [[tags]] +## [[tags]] - You can tag pages and use these tags in various ways. Tags will show - up in the ways you'd expect, like at the bottom of pages, in blogs, and - in rss feeds. +You can tag pages and use these tags in various ways. Tags will show +up in the ways you'd expect, like at the bottom of pages, in blogs, and +in rss feeds. -* valid html and css +## valid html and css - ikiwiki aims to produce - [valid XHTML 1.0](http://validator.w3.org/check?url=referer). - ikiwiki generates html using [[templates]], and uses css, so you can - change the look and layout of all pages in any way you would like. +ikiwiki aims to produce +[valid XHTML 1.0](http://validator.w3.org/check?url=referer). +ikiwiki generates html using [[templates]], and uses css, so you can +change the look and layout of all pages in any way you would like. -* [[SubPages|SubPage]] +## [[SubPages|SubPage]] - Arbitrarily deep hierarchies of pages with fairly simple and useful - [[SubPage/LinkingRules]] +Arbitrarily deep hierarchies of pages with fairly simple and useful +[[SubPage/LinkingRules]] -* [[BackLinks]] +## [[BackLinks]] - Automatically included on pages. Rather faster than eg MoinMoin and - always there to help with navigation. +Automatically included on pages. Rather faster than eg MoinMoin and +always there to help with navigation. -* [[PageHistory]] +## [[PageHistory]] - Well, sorta. Rather than implementing YA history browser, it can link to - [[ViewCVS]] or the like to browse the history of a wiki page. +Well, sorta. Rather than implementing YA history browser, it can link to +[[ViewCVS]] or the like to browse the history of a wiki page. -* [[RecentChanges]], editing pages in a web browser +## [[RecentChanges]], editing pages in a web browser - Nearly the definition of a wiki, although perhaps ikiwiki challenges how - much of that web gunk a wiki really needs. These features are optional - and can be enabled by enabling [[CGI]]. +Nearly the definition of a wiki, although perhaps ikiwiki challenges how +much of that web gunk a wiki really needs. These features are optional +and can be enabled by enabling [[CGI]]. -* User registration +## User registration - Can optionally be configured to allow only registered users to post - pages; online user registration form, etc. +Can optionally be configured to allow only registered users to post +pages; online user registration form, etc. -* Discussion pages +## Discussion pages - Thanks to subpages, every page can easily and automatically have a - /Discussion subpage. By default, these links are included in the - [[templates]] for each page. +Thanks to subpages, every page can easily and automatically have a +/Discussion subpage. By default, these links are included in the +[[templates]] for each page. -* Smart merging and conflict resolution in your web browser +## Smart merging and conflict resolution in your web browser - Since it uses a real RCS, ikiwiki takes advantage of its smart merging to - avoid any conflicts when two people edit different parts of the same page - at the same time. No annoying warnings about other editors, or locking, - etc, instead the other person's changes will be automatically merged with - yours when you commit. +Since it uses a real RCS, ikiwiki takes advantage of its smart merging to +avoid any conflicts when two people edit different parts of the same page +at the same time. No annoying warnings about other editors, or locking, +etc, instead the other person's changes will be automatically merged with +yours when you commit. - In the rare cases where automatic merging fails due to the same part of a - page being concurrently edited, regular commit conflict markers are - shown in the file to resolve the conflict, so if you're already familiar - with that there's no new commit marker syntax to learn. +In the rare cases where automatic merging fails due to the same part of a +page being concurrently edited, regular commit conflict markers are +shown in the file to resolve the conflict, so if you're already familiar +with that there's no new commit marker syntax to learn. -* page locking +## page locking - Wiki admins can lock pages so that only other admins can edit them. +Wiki admins can lock pages so that only other admins can edit them. -* Full text search +## Full text search - ikiwiki can use the [[HyperEstraier]] search engine to add powerful - full text search capabilities to your wiki. +ikiwiki can use the [[HyperEstraier]] search engine to add powerful +full text search capabilities to your wiki. -* Commit mails +## Commit mails - ikiwiki can be configured to send you commit mails with diffs of changes - to selected pages. +ikiwiki can be configured to send you commit mails with diffs of changes +to selected pages. -* [[Plugins]] +## [[Plugins]] - Plugins can be used to add additional features to ikiwiki. The interface - is quite flexible, allowing plugins to implement additional markup - languages, register [[PreProcessorDirective]]s, hook into [[CGI]] mode, - and more. Most of ikiwiki's features are actually provided by plugins. - Ikiwiki's backend RCS support is also pluggable, so support for new - revision control systems can be added to ikiwiki. +Plugins can be used to add additional features to ikiwiki. The interface +is quite flexible, allowing plugins to implement additional markup +languages, register [[PreProcessorDirective]]s, hook into [[CGI]] mode, +and more. Most of ikiwiki's features are actually provided by plugins. +Ikiwiki's backend RCS support is also pluggable, so support for new +revision control systems can be added to ikiwiki. -* [[todo/utf8]] +## [[todo/utf8]] - After rather a lot of fiddling, we think that ikiwiki correctly and fully - supports utf8 everywhere. +After rather a lot of fiddling, we think that ikiwiki correctly and fully +supports utf8 everywhere. -* [[w3mmode]] +## [[w3mmode]] - Can be set up so that w3m can be used to browse a wiki and edit pages - without using a web server. - ----- - -It also has some [[TODO]] items and [[Bugs]]. +Can be set up so that w3m can be used to browse a wiki and edit pages +without using a web server. diff --git a/doc/ikiwiki.setup b/doc/ikiwiki.setup index 9a4b61ec4..ee5e394be 100644 --- a/doc/ikiwiki.setup +++ b/doc/ikiwiki.setup @@ -83,7 +83,7 @@ use IkiWiki::Setup::Standard { # To add plugins, list them here. #add_plugins => [qw{meta tag pagecount brokenlinks search smiley # wikitext camelcase pagestats htmltidy fortune - # sidebar map rst}], + # sidebar map rst toc}], # If you want to disable any of the default plugins, list them here. #disable_plugins => [qw{inline htmlscrubber}], } diff --git a/doc/plugins/toc.mdwn b/doc/plugins/toc.mdwn new file mode 100644 index 000000000..57bd622ba --- /dev/null +++ b/doc/plugins/toc.mdwn @@ -0,0 +1,13 @@ +[[template id=plugin name=toc included=1 author="Joey Hess"]] +[[tag type/chrome]] + +Add a table of contents be to a page: + + \[[toc ]] + +The table of contents will be automatically generated based on the +headers of the page. By default only the largest headers present on the +page will be shown; to control how many levels of headers are shown, use +the `levels` parameter: + + \[[toc levels=2]] diff --git a/doc/security.mdwn b/doc/security.mdwn index 5cc35b338..fea0eb727 100644 --- a/doc/security.mdwn +++ b/doc/security.mdwn @@ -6,6 +6,8 @@ security issues with this program than with cat(1). If, however, you let others edit pages in your wiki, then some possible security issues do need to be kept in mind. +[[toc levels=2]] + ---- # Probable holes diff --git a/doc/todo/pageindexes.mdwn b/doc/todo/pageindexes.mdwn index 0b567332e..c2de20a34 100644 --- a/doc/todo/pageindexes.mdwn +++ b/doc/todo/pageindexes.mdwn @@ -1,3 +1,5 @@ Might be nice to support automatically generating an index based on headers in a page, for long pages. This could be done as a sanitize hook that parsed the html, with a preprocessordirective that controlled it. + +[[todo/done]] diff --git a/t/html.t b/t/html.t index 01f14d0c0..fc6888dca 100755 --- a/t/html.t +++ b/t/html.t @@ -6,7 +6,7 @@ use Test::More; my @pages; BEGIN { - @pages=qw(index todo features news plugins/map); + @pages=qw(index todo features news plugins/map security); if (! -x "/usr/bin/validate") { plan skip_all => "/usr/bin/validate html validator not present"; }