]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/blobdiff - IkiWiki/Plugin/external.pm
Remember to run all the tests before committing!
[git.ikiwiki.info.git] / IkiWiki / Plugin / external.pm
index 204442c1e0b731db6d985cef61181e0c7b0fc424..a4cc1dd3ce32bade13d9477efcba68fdc4a50e1a 100644 (file)
@@ -1,20 +1,19 @@
 #!/usr/bin/perl
 # Support for external plugins written in other languages.
-# Communication via XML RPC a pipe.
+# Communication via XML RPC to a pipe.
 # See externaldemo for an example of a plugin that uses this.
 package IkiWiki::Plugin::external;
 
 use warnings;
 use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
 use RPC::XML;
-use RPC::XML::Parser;
 use IPC::Open2;
 use IO::Handle;
 
 my %plugins;
 
-sub import { #{{{
+sub import {
        my $self=shift;
        my $plugin=shift;
        return unless defined $plugin;
@@ -29,20 +28,22 @@ sub import { #{{{
 
        $plugins{$plugin}={in => $plugin_read, out => $plugin_write, pid => $pid,
                accum => ""};
+
        $RPC::XML::ENCODING="utf-8";
+       $RPC::XML::FORCE_STRING_ENCODING="true";
 
        rpc_call($plugins{$plugin}, "import");
-} #}}}
+}
 
-sub rpc_write ($$) { #{{{
+sub rpc_write ($$) {
        my $fh=shift;
        my $string=shift;
 
        $fh->print($string."\n");
        $fh->flush;
-} #}}}
+}
 
-sub rpc_call ($$;@) { #{{{
+sub rpc_call ($$;@) {
        my $plugin=shift;
        my $command=shift;
 
@@ -55,7 +56,19 @@ sub rpc_call ($$;@) { #{{{
                $plugin->{accum}.=$_;
                while ($plugin->{accum} =~ /^\s*(<\?xml\s.*?<\/(?:methodCall|methodResponse)>)\n(.*)/s) {
                        $plugin->{accum}=$2;
-                       my $r = RPC::XML::Parser->new->parse($1);
+                       my $parser;
+                       eval q{
+                               use RPC::XML::ParserFactory;
+                               $parser = RPC::XML::ParserFactory->new;
+                       };
+                       if ($@) {
+                               # old interface
+                               eval q{
+                                       use RPC::XML::Parser;
+                                       $parser = RPC::XML::Parser->new;
+                               };
+                       }
+                       my $r=$parser->parse($1);
                        error("XML RPC parser failure: $r") unless ref $r;
                        if ($r->isa('RPC::XML::response')) {
                                my $value=$r->value;
@@ -72,9 +85,9 @@ sub rpc_call ($$;@) { #{{{
 
                                        # XML-RPC v1 does not allow for
                                        # nil/null/None/undef values to be
-                                       # transmitted, so until
-                                       # XML::RPC::Parser honours v2
-                                       # (<nil/>), external plugins send
+                                       # transmitted. The <nil/> extension
+                                       # is the right fix, but for
+                                       # back-compat, let external plugins send
                                        # a hash with one key "null" pointing
                                        # to an empty string.
                                        if (exists $hash{null} &&
@@ -131,12 +144,12 @@ sub rpc_call ($$;@) { #{{{
        }
 
        return undef;
-} #}}}
+}
 
 package IkiWiki::RPC::XML;
 use Memoize;
 
-sub getvar ($$$) { #{{{
+sub getvar ($$$) {
        my $plugin=shift;
        my $varname="IkiWiki::".shift;
        my $key=shift;
@@ -145,9 +158,9 @@ sub getvar ($$$) { #{{{
        my $ret=$varname->{$key};
        use strict 'refs';
        return $ret;
-} #}}}
+}
 
-sub setvar ($$$;@) { #{{{
+sub setvar ($$$;@) {
        my $plugin=shift;
        my $varname="IkiWiki::".shift;
        my $key=shift;
@@ -157,18 +170,18 @@ sub setvar ($$$;@) { #{{{
        my $ret=$varname->{$key}=$value;
        use strict 'refs';
        return $ret;
-} #}}}
+}
 
-sub getstate ($$$$) { #{{{
+sub getstate ($$$$) {
        my $plugin=shift;
        my $page=shift;
        my $id=shift;
        my $key=shift;
 
        return $IkiWiki::pagestate{$page}{$id}{$key};
-} #}}}
+}
 
-sub setstate ($$$$;@) { #{{{
+sub setstate ($$$$;@) {
        my $plugin=shift;
        my $page=shift;
        my $id=shift;
@@ -176,22 +189,22 @@ sub setstate ($$$$;@) { #{{{
        my $value=shift;
 
        return $IkiWiki::pagestate{$page}{$id}{$key}=$value;
-} #}}}
+}
 
-sub getargv ($) { #{{{
+sub getargv ($) {
        my $plugin=shift;
 
        return \@ARGV;
-} #}}}
+}
 
-sub setargv ($@) { #{{{
+sub setargv ($@) {
        my $plugin=shift;
        my $array=shift;
 
        @ARGV=@$array;
-} #}}}
+}
 
-sub inject ($@) { #{{{
+sub inject ($@) {
        # Bind a given perl function name to a particular RPC request.
        my $plugin=shift;
        my %params=@_;
@@ -202,12 +215,20 @@ sub inject ($@) { #{{{
        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};
-       memoize($params{name}) if $params{memoize};
+       use warnings;
+
+       # This will ensure that everywhere it was exported to sees
+       # the injected version.
+       IkiWiki::inject(name => $params{name}, call => $sub);
        return 1;
-} #}}}
+}
 
-sub hook ($@) { #{{{
+sub hook ($@) {
        # the call parameter is a function name to call, since XML RPC
        # cannot pass a function reference
        my $plugin=shift;
@@ -217,16 +238,18 @@ sub hook ($@) { #{{{
        delete $params{call};
 
        IkiWiki::hook(%params, call => sub {
-               my $ret=IkiWiki::Plugin::external::rpc_call($plugin, $callback, @_);
-               return $ret;
+               IkiWiki::Plugin::external::rpc_call($plugin, $callback, @_);
        });
-} #}}}
+}
 
-sub pagespec_match ($@) { #{{{
-       # convert pagespec_match's return object into a XML RPC boolean
+sub pagespec_match ($@) {
+       # convert return object into a XML RPC boolean
        my $plugin=shift;
+       my $page=shift;
+       my $spec=shift;
 
-       return RPC::XML::boolean->new(0 + IkiWiki::pagespec_march(@_));
-} #}}}
+       return RPC::XML::boolean->new(0 + IkiWiki::pagespec_match(
+                       $page, $spec, @_));
+}
 
 1