Add templatebody plugin and directive, and enable it by default

Also add a regression test for templatebody.
master
Simon McVittie 2014-02-21 22:45:29 +00:00
parent cbb3218db7
commit 7672014582
9 changed files with 278 additions and 8 deletions

View File

@ -152,7 +152,8 @@ sub getsetup () {
type => "internal", type => "internal",
default => [qw{mdwn link inline meta htmlscrubber passwordauth default => [qw{mdwn link inline meta htmlscrubber passwordauth
openid signinedit lockedit conditional openid signinedit lockedit conditional
recentchanges parentlinks editpage}], recentchanges parentlinks editpage
templatebody}],
description => "plugins to enable by default", description => "plugins to enable by default",
safe => 0, safe => 0,
rebuild => 1, rebuild => 1,

View File

@ -0,0 +1,81 @@
#!/usr/bin/perl
# Define self-documenting templates as wiki pages without HTML::Template
# markup leaking into IkiWiki's output.
# Copyright © 2013-2014 Simon McVittie. GPL-2+, see debian/copyright
package IkiWiki::Plugin::templatebody;
use warnings;
use strict;
use IkiWiki 3.00;
use Encode;
sub import {
hook(type => "getsetup", id => "templatebody", call => \&getsetup);
hook(type => "preprocess", id => "templatebody", call => \&preprocess,
scan => 1);
hook(type => "readtemplate", id => "templatebody",
call => \&readtemplate);
}
sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
section => "core",
},
}
# This doesn't persist between runs: we're going to read and scan the
# template file regardless, so there's no point in saving it to the index.
# Example contents:
# ("templates/note" => "<div class=\"notebox\">\n<TMPL_VAR text>\n</div>")
my %templates;
sub preprocess (@) {
my %params=@_;
# [[!templatebody "<div>hello</div>"]] results in
# preprocess("<div>hello</div>" => undef, page => ...)
my $content = $_[0];
if (length $_[1]) {
error(gettext("first parameter must be the content"));
}
$templates{$params{page}} = $content;
return "";
}
sub readtemplate {
my %params = @_;
my $tpage = $params{page};
my $content = $params{content};
my $filename = $params{filename};
# pass-through if it's a .tmpl attachment or otherwise unsuitable
return $content unless defined $tpage;
return $content if $tpage =~ /\.tmpl$/;
my $src = $pagesources{$tpage};
return $content unless defined $src;
return $content unless defined pagetype($src);
# We might be using the template for [[!template]], which has to run
# during the scan stage so that templates can include scannable
# directives which are expanded in the resulting page. Calls to
# IkiWiki::scan are in arbitrary order, so the template might
# not have been scanned yet. Make sure.
require IkiWiki::Render;
IkiWiki::scan($src);
# Having scanned it, we know whether it had a [[!templatebody]].
if (exists $templates{$tpage}) {
return $templates{$tpage};
}
# If not, return the whole thing. (Eventually, after implementing
# a transition, this can become an error.)
return $content;
}
1

View File

@ -18,17 +18,25 @@ the directive displaying a note about the template being registered, add
"silent=yes". "silent=yes".
Often the template page contains a simple skeleton for a particular type of Often the template page contains a simple skeleton for a particular type of
page. For the bug report pages in the above example, it might look page, wrapped in a [[templatebody]] directive. For the bug report pages in
something like: the above example, it might look something like:
\[[!templatebody <<ENDBODY
Package: Package:
Version: Version:
Reproducible: y/n Reproducible: y/n
Details: Details:
ENDBODY]]
The template page can also contain [[!cpan HTML::Template]] directives, The template page can also contain [[!cpan HTML::Template]] directives,
like other ikiwiki [[templates]]. Currently only one variable is like other ikiwiki [[templates]]. Currently only one variable is
set: `<TMPL_VAR name>` is replaced with the name of the page being set: `<TMPL_VAR name>` is replaced with the name of the page being
created. created.
Text outside the [[templatebody]] directive is not part of the template,
and can be used to document it.
If the template does not contain a [[templatebody]] directive, the entire
source of the page is used for the template. This is deprecated.
[[!meta robots="noindex, follow"]] [[!meta robots="noindex, follow"]]

View File

@ -31,16 +31,25 @@ large chunks of marked up text to be embedded into a template:
## Creating a template ## Creating a template
The template is a regular wiki page, located in the `templates/` The template is in a regular wiki page, located in the `templates/`
subdirectory inside the source directory of the wiki. subdirectory inside the source directory of the wiki.
The contents of the [[templatebody]] directive are used as the
template. Anything outside that directive is not included in the template,
and is usually used as documentation describing the template.
If the template does not contain a [[templatebody]] directive, the entire
source of the page is used for the template. This is deprecated, because
it leads to the template markup being interpreted as ordinary
page source when the page is built, as well as being used as the template.
Alternatively, templates can be stored in a directory outside the wiki, Alternatively, templates can be stored in a directory outside the wiki,
as files with the extension ".tmpl". as files with the extension ".tmpl".
By default, these are searched for in `/usr/share/ikiwiki/templates`, By default, these are searched for in `/usr/share/ikiwiki/templates`,
the `templatedir` setting can be used to make another directory be searched the `templatedir` setting can be used to make another directory be searched
first. When referring to templates outside the wiki source directory, the "id" first. When referring to templates outside the wiki source directory, the "id"
parameter is not interpreted as a pagespec, and you must include the full filename parameter is not interpreted as a pagespec, you must include the full filename
of the template page, including the ".tmpl" extension. E.g.: of the template page including the ".tmpl" extension,
and the templatebody directive is not used. E.g.:
\[[!template id=blogpost.tmpl]] \[[!template id=blogpost.tmpl]]
@ -63,6 +72,7 @@ few things:
Here's a sample template: Here's a sample template:
\[[!templatebody <<ENDBODY
<span class="infobox"> <span class="infobox">
Name: \[[<TMPL_VAR raw_name>]]<br /> Name: \[[<TMPL_VAR raw_name>]]<br />
Age: <TMPL_VAR age><br /> Age: <TMPL_VAR age><br />
@ -76,6 +86,10 @@ Here's a sample template:
<TMPL_VAR notes> <TMPL_VAR notes>
</TMPL_IF> </TMPL_IF>
</span> </span>
ENDBODY]]
This template describes a person. Parameters: name, age,
color (favorite color, optional), notes (optional).
The filled out template will be formatted the same as the rest of the page The filled out template will be formatted the same as the rest of the page
that contains it, so you can include WikiLinks and all other forms of wiki that contains it, so you can include WikiLinks and all other forms of wiki

View File

@ -0,0 +1,28 @@
The `templatebody` directive is supplied by the
[[!iki plugins/templatebody desc=templatebody]] plugin.
This directive allows wiki pages to be used as templates
for the [[template]] or [[edittemplate]] directive, without having
[[!cpan HTML::Template]] markup interpreted as wiki markup when that
page is built.
This directive does not produce any output in the wiki page that
defines the template; the rest of that page can be used to to document
how to use the template.
The first, un-named parameter is the content of the template.
Because templates often contain [[directives|ikiwiki/directive]], it's
convenient to use the "here-document" syntax for it:
\[[!templatebody <<ENDBODY
[[!meta title="<TMPL_VAR name>"]]
[[!tag person]]
<dl>
<dt>Name:</dt><dd><TMPL_VAR name></dd>
<dt>Age:</dt><dd><TMPL_VAR age></dd>
</dl>
<TMPL_VAR description>
ENDBODY]]
[[!meta robots="noindex, follow"]]

View File

@ -0,0 +1,7 @@
[[!template id=plugin name=templatebody author="[[smcv]]" core=1]]
[[!tag type/special-purpose]]
This plugin provides the [[ikiwiki/directive/templatebody]]
[[ikiwiki/directive]]. With this plugin, you can set up templates
stored in the wiki for [[template]] or [[edittemplate]] without the
[[!cpan HTML::Template]] markup being interpreted as wiki markup.

View File

@ -217,6 +217,9 @@ value is ignored.
Runs on the raw source of a page or `*.tmpl` file that is being Runs on the raw source of a page or `*.tmpl` file that is being
used as a template, before it is parsed by [[!cpan HTML::Template]]. used as a template, before it is parsed by [[!cpan HTML::Template]].
For instance, the [[plugins/templatebody]] plugin uses this to return
the content of the [[ikiwiki/directive/templatebody]] directive (if there
is one) instead of the page's full content.
The function is passed named parameters: The function is passed named parameters:

View File

@ -14,8 +14,10 @@ easy to learn. All you really need to know to modify templates is this:
[[!if test="enabled(template) or enabled(edittemplate)" then=""" [[!if test="enabled(template) or enabled(edittemplate)" then="""
## template pages ## template pages
Template pages are regular wiki pages that are used as templates for other Template pages are regular wiki pages containing a
pages. [[!iki ikiwiki/directive/templatebody desc="templatebody directive"]],
used as templates for other pages. The parts of the template
page outside the directive can be used to document it.
"""]] """]]
[[!if test="enabled(template)" then=""" [[!if test="enabled(template)" then="""
@ -38,6 +40,9 @@ feeds=no archive=yes sort=title template=titlepage
rootpage=templates postformtext="Add a new template page named:"]] rootpage=templates postformtext="Add a new template page named:"]]
"""]] """]]
If the template does not contain a `templatebody` directive, the entire
source of the page is used for the template. This is deprecated.
## template files ## template files
Template files are unlike template pages in that they have the extension Template files are unlike template pages in that they have the extension

123
t/templatebody.t 100755
View File

@ -0,0 +1,123 @@
#!/usr/bin/perl
package IkiWiki;
use warnings;
use strict;
use Test::More tests => 18;
BEGIN { use_ok("IkiWiki"); }
BEGIN { use_ok("IkiWiki::Render"); }
BEGIN { use_ok("IkiWiki::Plugin::templatebody"); }
BEGIN { use_ok("IkiWiki::Plugin::mdwn"); }
BEGIN { use_ok("IkiWiki::Plugin::tag"); }
BEGIN { use_ok("IkiWiki::Plugin::template"); }
sub assert_pagespec_matches {
my $page = shift;
my $spec = shift;
my @params = @_;
@params = (location => 'index') unless @params;
my $res = pagespec_match($page, $spec, @params);
if ($res) {
pass($res);
}
else {
fail($res);
}
}
sub assert_pagespec_doesnt_match {
my $page = shift;
my $spec = shift;
my @params = @_;
@params = (location => 'index') unless @params;
my $res = pagespec_match($page, $spec, @params);
if (ref $res && $res->isa("IkiWiki::ErrorReason")) {
fail($res);
}
elsif ($res) {
fail($res);
}
else {
pass($res);
}
}
ok(! system("rm -rf t/tmp; mkdir t/tmp t/tmp/src t/tmp/dst"));
$config{verbose} = 1;
$config{srcdir} = 't/tmp/src';
$config{underlaydir} = 't/tmp/src';
$config{destdir} = 't/tmp/dst';
$config{underlaydirbase} = '.';
$config{templatedir} = 'templates';
$config{usedirs} = 1;
$config{htmlext} = 'html';
$config{wiki_file_chars} = "-[:alnum:]+/.:_";
$config{default_pageext} = "mdwn";
$config{wiki_file_prune_regexps} = [qr/^\./];
is(checkconfig(), 1);
%oldrenderedfiles=%pagectime=();
%pagesources=%pagemtime=%oldlinks=%links=%depends=%typedlinks=%oldtypedlinks=
%destsources=%renderedfiles=%pagecase=%pagestate=();
$pagesources{index} = "index.mdwn";
$pagemtime{index} = $pagectime{index} = 1000000;
writefile("index.mdwn", "t/tmp/src", <<EOF
[[!template id="deftmpl" greeting="hello" them="world"]]
[[!template id="oldtmpl" greeting="greetings" them="earthlings"]]
EOF
);
$pagesources{"templates/deftmpl"} = "templates/deftmpl.mdwn";
$pagemtime{index} = $pagectime{index} = 1000000;
writefile("templates/deftmpl.mdwn", "t/tmp/src", <<EOF
[[!templatebody <<ENDBODY
<p><b><TMPL_VAR GREETING>, <TMPL_VAR THEM></b></p>
[[!tag greeting]]
ENDBODY]]
This template says hello to someone.
[[!tag documentation]]
EOF
);
$pagesources{"templates/oldtmpl"} = "templates/oldtmpl.mdwn";
$pagemtime{index} = $pagectime{index} = 1000000;
writefile("templates/oldtmpl.mdwn", "t/tmp/src", <<EOF
<p><i><TMPL_VAR GREETING>, <TMPL_VAR THEM></i></p>
EOF
);
my %content;
foreach my $page (keys %pagesources) {
my $content = readfile("t/tmp/src/$pagesources{$page}");
$content = IkiWiki::filter($page, $page, $content);
$content = IkiWiki::preprocess($page, $page, $content);
$content{$page} = $content;
}
# Templates are expanded
like($content{index}, qr{<p><b>hello, world</b></p>});
like($content{index}, qr{<p><i>greetings, earthlings</i></p>});
assert_pagespec_matches('index', 'tagged(greeting)');
# The documentation from the templatebody-using page is not expanded
unlike($content{index}, qr{This template says hello to someone});
assert_pagespec_doesnt_match('index', 'tagged(documentation)');
# In the templatebody-using page, the documentation is expanded
like($content{'templates/deftmpl'}, qr{This template says hello to someone});
assert_pagespec_matches('templates/deftmpl', 'tagged(documentation)');
# In the templatebody-using page, the template is *not* expanded
unlike($content{'templates/deftmpl'}, qr{<p><b>hello, world</b></p>});
unlike($content{'templates/deftmpl'}, qr{<p><i>greetings, earthlings</i></p>});
assert_pagespec_doesnt_match('templates/deftmpl', 'tagged(greeting)');
1;