bestlink htmllink readfile writefile pagetype srcfile pagename
displaytime will_render gettext urlto targetpage
add_underlay pagetitle titlepage linkpage newpagefile
+ inject
%config %links %pagestate %wikistate %renderedfiles
%pagesources %destsources);
our $VERSION = 2.00; # plugin interface version, next is ikiwiki version
} #}}}
sub displaytime ($;$) { #{{{
- my $time=shift;
- my $format=shift;
- if (exists $hooks{displaytime}) {
- my $ret;
- run_hooks(displaytime => sub {
- $ret=shift->($time, $format)
- });
- return $ret;
- }
- else {
- return formattime($time, $format);
- }
+ # Plugins can override this function to mark up the time to
+ # display.
+ return '<span class="date">'.formattime(@_).'</span>';
} #}}}
sub formattime ($;$) { #{{{
- # Plugins can override this function to mark up the time for
- # display.
+ # Plugins can override this function to format the time.
my $time=shift;
my $format=shift;
if (! defined $format) {
return (defined $val && lc($val) eq gettext("yes"));
} #}}}
+sub inject { #{{{
+ # Injects a new function into the symbol table to replace an
+ # exported function.
+ my %params=@_;
+
+ # This is deep ugly perl foo, beware.
+ no strict;
+ no warnings;
+ if (! defined $params{parent}) {
+ $params{parent}='::';
+ $params{old}=\&{$params{name}};
+ $params{name}=~s/.*:://;
+ }
+ my $parent=$params{parent};
+ foreach my $ns (grep /^\w+::/, keys %{$parent}) {
+ $ns = $params{parent} . $ns;
+ inject(%params, parent => $ns) unless $ns eq '::main::';
+ *{$ns . $params{name}} = $params{call}
+ if exists ${$ns}{$params{name}} &&
+ \&{${$ns}{$params{name}}} == $params{old};
+ }
+ use strict;
+ use warnings;
+} #}}}
+
sub pagespec_merge ($$) { #{{{
my $a=shift;
my $b=shift;
my $sub=pagespec_translate($spec);
return ! $@;
} #}}}
-
+
sub glob2re ($) { #{{{
my $re=quotemeta(shift);
$re=~s/\\\*/.*/g;
my $sub = sub {
IkiWiki::Plugin::external::rpc_call($plugin, $params{call}, @_)
};
+ $sub=memoize($sub) if $params{memoize};
+
+ # This will add it to the symbol table even if not present.
no warnings;
eval qq{*$params{name}=\$sub};
use warnings;
- memoize($params{name}) if $params{memoize};
+
+ # This will ensure that everywhere it was exported to sees
+ # the injected version.
+ IkiWiki::inject(name => $params{name}, call => $sub);
return 1;
} #}}}
add_underlay("javascript");
hook(type => "getsetup", id => "relativedate", call => \&getsetup);
hook(type => "format", id => "relativedate", call => \&format);
- hook(type => "displaytime", id => "relativedate", call => \&display);
+ inject(name => "IkiWiki::displaytime", call => \&mydisplaytime);
} # }}}
sub getsetup () { #{{{
'" type="text/javascript" charset="utf-8"></script>';
} #}}}
-sub display ($;$) { #{{{
+sub mydisplaytime ($;$) { #{{{
my $time=shift;
my $format=shift;
the toplevel tagpage, and not closer subpages. The html links already went
there, but internally the links were not recorded as absolute, which could
cause confusing backlinks etc.
+ * Add an inject function, that can be used by plugins that want to
+ replace one of ikiwiki's functions with their own version.
+ (This is a scary thing that grubs through the symbol table, and replaces
+ all exported occurances of a function with the injected version.)
+ * external: RPC functions can be injected to replace exported functions.
-- Joey Hess <joeyh@debian.org> Fri, 17 Oct 2008 20:11:02 -0400
>>> `targetpage`, `bestlink`, and `beautify_urlpath`. But, I noticed
>>> the other day that such wrappers around exported functions are only visible by
>>> plugins loaded after the plugin that defines them.
+>>>
+>>> Update: Take a look at the new "Function overriding" section of
+>>> [[plugins/write]]. I think you can just inject wrappers about a few ikiwiki
+>>> functions, rather than adding hooks. The `inject` function is pretty
+>>> insane^Wlow level, but seems to work great. --[[Joey]]
>>
>> The Discussion pages issue is something I am not sure about yet. But I will
>> probably decide that "slave" pages, being only translations, don't deserve
program just needs to do something like:
`use IkiWiki::Setup; IkiWiki::Setup::load($filename)`
+### Function overriding
+
+Sometimes using ikiwiki's pre-defined hooks is not enough. Your plugin
+may need to replace one of ikiwiki's own functions with a modified version,
+or wrap one of the functions.
+
+For example, your plugin might want to override `displaytime`, to change
+the html markup used when displaying a date. Or it might want to override
+`IkiWiki::formattime`, to change how a date is formatted. Or perhaps you
+want to override `bestlink` and change how ikiwiki deals with WikiLinks.
+
+By venturing into this territory, your plugin is becoming tightly tied to
+ikiwiki's internals. And it might break if those internals change. But
+don't let that stop you, if you're brave.
+
+Ikiwiki provides an `inject()` function, that is a powerful way to replace
+any function with one of your own. This even allows you to inject a
+replacement for an exported function, like `bestlink`. Everything that
+imports that function will get your version instead. Pass it the name of
+the function to replace, and a new function to call.
+
+For example, here's how to replace `displaytime` with a version using HTML 5
+markup:
+
+ inject(name => 'IkiWiki::displaytime', call => sub {
+ return "<time>".formattime(@_)."</time>";
+ });
+
+Here's how to wrap `bestlink` with a version that tries to handle
+plural words:
+
+ my $origbestlink=\&bestlink;
+ inject(name => 'IkiWiki::bestlink', call => \&mybestlink);
+
+ sub deplural ($) {
+ my $word=shift;
+ $word =~ s/e?s$//; # just an example :-)
+ return $word;
+ }
+
+ sub mybestlink ($$) {
+ my $page=shift;
+ my $link=shift;
+ my $ret=$origbestlink->($page, $link);
+ if (! length $ret) {
+ $ret=$origbestlink->($page, deplural($link));
+ }
+ return $ret;
+ }
+
### Javascript
Some plugins use javascript to make ikiwiki look a bit more web-2.0-ish.
rpc_call("getvar", "config", "url")."\n";
# Here's an example of how to inject an arbitrary function into
- # ikiwiki, replacing a core function.
- # Note use of automatic memoization.
- rpc_call("inject", name => "IkiWiki::formattime",
+ # ikiwiki. Note use of automatic memoization.
+ rpc_call("inject", name => "IkiWiki::bob",
call => "formattime", memoize => 1);
print STDERR "externaldemo plugin successfully imported\n";
return "externaldemo plugin preprocessing on $title!";
}
-sub formattime {
- print STDERR "externaldemo plugin's formattime called via RPC";
- return scalar "formatted time: ".localtime(shift);
+sub bob {
+ print STDERR "externaldemo plugin's bob called via RPC";
}
# Now all that's left to do is loop and handle each incoming RPC request.
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-10-19 20:06-0400\n"
+"POT-Creation-Date: 2008-10-21 17:51-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
msgid "You are banned."
msgstr ""
-#: ../IkiWiki/CGI.pm:385 ../IkiWiki/CGI.pm:386 ../IkiWiki.pm:1182
+#: ../IkiWiki/CGI.pm:385 ../IkiWiki/CGI.pm:386 ../IkiWiki.pm:1175
msgid "Error"
msgstr ""
msgid "refreshing wiki.."
msgstr ""
-#: ../IkiWiki.pm:458
+#: ../IkiWiki.pm:459
msgid "Must specify url to wiki with --url when using --cgi"
msgstr ""
-#: ../IkiWiki.pm:504
+#: ../IkiWiki.pm:505
msgid "cannot use multiple rcs plugins"
msgstr ""
-#: ../IkiWiki.pm:533
+#: ../IkiWiki.pm:534
#, perl-format
msgid "failed to load external plugin needed for %s plugin: %s"
msgstr ""
-#: ../IkiWiki.pm:1165
+#: ../IkiWiki.pm:1158
#, perl-format
msgid "preprocessing loop detected on %s at depth %i"
msgstr ""
-#: ../IkiWiki.pm:1674
+#: ../IkiWiki.pm:1667
msgid "yes"
msgstr ""