aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rwxr-xr-xplugins/externaldemo132
1 files changed, 132 insertions, 0 deletions
diff --git a/plugins/externaldemo b/plugins/externaldemo
new file mode 100755
index 000000000..6bbced30e
--- /dev/null
+++ b/plugins/externaldemo
@@ -0,0 +1,132 @@
+#!/usr/bin/perl
+# Demo external plugin. Kinda pointless, since it's a perl script, but
+# useful for testing or as an hint of how to write an external plugin in
+# other languages.
+use warnings;
+use strict;
+
+print STDERR "externaldemo plugin running as pid $$\n";
+
+use RPC::XML;
+use RPC::XML::Parser;
+use IO::Handle;
+
+# autoflush stdout
+$|=1;
+
+# Used to build up RPC calls as they're read from stdin.
+my $accum="";
+
+sub rpc_read {
+ # Read stdin, a line at a time, until a whole RPC call is accumulated.
+ # Parse to XML::RPC object and return.
+ while (<>) {
+ $accum.=$_;
+
+ # Kinda hackish approch to parse a single XML RPC out of the
+ # accumulated input. Relies on calls always ending with a
+ # newline, which ikiwiki's protocol requires be true.
+ if ($accum =~ /^\s*(<\?xml\s.*?<\/(?:methodCall|methodResponse)>)\n(.*)/s) {
+ $accum=$2; # the rest
+
+ # Now parse the XML RPC.
+ my $r = RPC::XML::Parser->new->parse($1);
+ if (! ref $r) {
+ die "error: XML RPC parse failure $r";
+ }
+ return $r;
+ }
+ }
+
+ return undef;
+}
+
+sub rpc_handle {
+ # Handle an incoming XML RPC command.
+ my $r=rpc_read();
+ if (! defined $r) {
+ return 0;
+ }
+ if ($r->isa("RPC::XML::request")) {
+ my $name=$r->name;
+ my @args=map { $_->value } @{$r->args};
+ # Dispatch the requested function. This could be
+ # done with a switch statement on the name, or
+ # whatever. I'll use eval to call the function with
+ # the name.
+ my $ret = eval $name.'(@args)';
+ die $@ if $@;
+
+ # Now send the repsonse from the function back,
+ # followed by a newline.
+ my $resp=RPC::XML::response->new($ret);
+ $resp->serialize(\*STDOUT);
+ print "\n";
+ # stdout needs to be flushed here. If it isn't,
+ # things will deadlock. Perl flushes it
+ # automatically when $| is set.
+ return 1;
+ }
+ elsif ($r->isa("RPC::XML::response")) {
+ die "protocol error; got a response when expecting a request";
+ }
+}
+
+sub rpc_call {
+ # Make an XML RPC call and return the result.
+ my $command=shift;
+ my @params=@_;
+
+ my $req=RPC::XML::request->new($command, @params);
+ $req->serialize(\*STDOUT);
+ print "\n";
+ # stdout needs to be flushed here to prevent deadlock. Perl does it
+ # automatically when $| is set.
+
+ my $r=rpc_read();
+ if ($r->isa("RPC::XML::response")) {
+ return $r->value->value;
+ }
+ else {
+ die "protocol error; got a request when expecting a response";
+ }
+}
+
+# Now on with the actual plugin. Let's do a simple preprocessor plugin.
+
+sub import {
+ # The import function will be called by ikiwiki when the plugin is
+ # loaded. When it's imported, it needs to hook into the preprocessor
+ # stage of ikiwiki.
+ rpc_call("hook", type => "preprocess", id => "externaldemo", call => "preprocess");
+
+ # Here's an example of how to inject an arbitrary function into
+ # ikiwiki. Ikiwiki will be able to call bob() just like any other
+ # function.
+ rpc_call("inject", name => "IkiWiki::bob", call => "bob");
+
+ # Here's an exmaple of how to access values in %IkiWiki::config.
+ print STDERR "url is set to: ".
+ rpc_call("getvar", "config", "url")."\n";
+
+ print STDERR "externaldemo plugin successfully imported\n";
+}
+
+sub preprocess {
+ # This function will be called when ikiwiki wants to preprocess
+ # something.
+ my %params=@_;
+
+ # Let's use IkiWiki's pagetitle function to turn the page name into
+ # a title.
+ my $title=rpc_call("pagetitle", $params{page});
+
+ return "externaldemo plugin preprocessing on $title!";
+}
+
+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.
+while (rpc_handle()) { print STDERR "externaldemo plugin handled RPC request\n" }