2007-08-13 05:07:31 +02:00
|
|
|
#!/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.=$_;
|
|
|
|
|
2007-10-14 00:32:55 +02:00
|
|
|
# Kinda hackish approach to parse a single XML RPC out of the
|
|
|
|
# accumulated input. Perl's RPC::XML library doesn't
|
|
|
|
# provide a better way to do it. Relies on calls always ending
|
|
|
|
# with a newline, which ikiwiki's protocol requires be true.
|
2007-08-13 05:07:31 +02:00
|
|
|
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
|
2007-08-13 09:00:53 +02:00
|
|
|
# function. Note use of automatic memoization.
|
|
|
|
rpc_call("inject", name => "IkiWiki::bob", call => "bob",
|
|
|
|
memoize => 1);
|
2007-08-13 05:07:31 +02:00
|
|
|
|
|
|
|
# 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" }
|