summaryrefslogtreecommitdiff
path: root/plugins/externaldemo
blob: 24861dcc91d4c9dfe44e4277e80ce357f1e56577 (plain)
  1. #!/usr/bin/perl
  2. # Demo external plugin. Kinda pointless, since it's a perl script, but
  3. # useful for testing or as an hint of how to write an external plugin in
  4. # other languages.
  5. use warnings;
  6. use strict;
  7. print STDERR "externaldemo plugin running as pid $$\n";
  8. use RPC::XML;
  9. use IO::Handle;
  10. # autoflush stdout
  11. $|=1;
  12. # Used to build up RPC calls as they're read from stdin.
  13. my $accum="";
  14. sub rpc_read {
  15. # Read stdin, a line at a time, until a whole RPC call is accumulated.
  16. # Parse to XML::RPC object and return.
  17. while (<>) {
  18. $accum.=$_;
  19. # Kinda hackish approach to parse a single XML RPC out of the
  20. # accumulated input. Perl's RPC::XML library doesn't
  21. # provide a better way to do it. Relies on calls always ending
  22. # with a newline, which ikiwiki's protocol requires be true.
  23. if ($accum =~ /^\s*(<\?xml\s.*?<\/(?:methodCall|methodResponse)>)\n(.*)/s) {
  24. $accum=$2; # the rest
  25. # Now parse the XML RPC.
  26. my $parser;
  27. eval q{
  28. use RPC::XML::ParserFactory;
  29. $parser = RPC::XML::ParserFactory->new;
  30. };
  31. if ($@) {
  32. # old interface
  33. eval q{
  34. use RPC::XML::Parser;
  35. $parser = RPC::XML::Parser->new;
  36. };
  37. }
  38. my $r=$parser->parse($1);
  39. if (! ref $r) {
  40. die "error: XML RPC parse failure $r";
  41. }
  42. return $r;
  43. }
  44. }
  45. return undef;
  46. }
  47. sub rpc_handle {
  48. # Handle an incoming XML RPC command.
  49. my $r=rpc_read();
  50. if (! defined $r) {
  51. return 0;
  52. }
  53. if ($r->isa("RPC::XML::request")) {
  54. my $name=$r->name;
  55. my @args=map { $_->value } @{$r->args};
  56. # Dispatch the requested function. This could be
  57. # done with a switch statement on the name, or
  58. # whatever. I'll use eval to call the function with
  59. # the name.
  60. my $ret = eval $name.'(@args)';
  61. die $@ if $@;
  62. # Now send the repsonse from the function back,
  63. # followed by a newline.
  64. my $resp=RPC::XML::response->new($ret);
  65. $resp->serialize(\*STDOUT);
  66. print "\n";
  67. # stdout needs to be flushed here. If it isn't,
  68. # things will deadlock. Perl flushes it
  69. # automatically when $| is set.
  70. return 1;
  71. }
  72. elsif ($r->isa("RPC::XML::response")) {
  73. die "protocol error; got a response when expecting a request";
  74. }
  75. }
  76. sub rpc_call {
  77. # Make an XML RPC call and return the result.
  78. my $command=shift;
  79. my @params=@_;
  80. my $req=RPC::XML::request->new($command, @params);
  81. $req->serialize(\*STDOUT);
  82. print "\n";
  83. # stdout needs to be flushed here to prevent deadlock. Perl does it
  84. # automatically when $| is set.
  85. my $r=rpc_read();
  86. if ($r->isa("RPC::XML::response")) {
  87. return $r->value->value;
  88. }
  89. else {
  90. die "protocol error; got a request when expecting a response";
  91. }
  92. }
  93. # Now on with the actual plugin. Let's do a simple preprocessor plugin.
  94. sub import {
  95. # The import function will be called by ikiwiki when the plugin is
  96. # loaded. When it's imported, it needs to hook into the preprocessor
  97. # stage of ikiwiki.
  98. rpc_call("hook", type => "preprocess", id => "externaldemo", call => "preprocess");
  99. # Here's an exmaple of how to access values in %IkiWiki::config.
  100. print STDERR "url is set to: ".
  101. rpc_call("getvar", "config", "url")."\n";
  102. # Here's an example of how to inject an arbitrary function into
  103. # ikiwiki. Note use of automatic memoization.
  104. rpc_call("inject", name => "IkiWiki::bob",
  105. call => "formattime", memoize => 1);
  106. print STDERR "externaldemo plugin successfully imported\n";
  107. }
  108. sub preprocess {
  109. # This function will be called when ikiwiki wants to preprocess
  110. # something.
  111. my %params=@_;
  112. # Let's use IkiWiki's pagetitle function to turn the page name into
  113. # a title.
  114. my $title=rpc_call("pagetitle", $params{page});
  115. return "externaldemo plugin preprocessing on $title!";
  116. }
  117. sub bob {
  118. print STDERR "externaldemo plugin's bob called via RPC";
  119. }
  120. # Now all that's left to do is loop and handle each incoming RPC request.
  121. while (rpc_handle()) { print STDERR "externaldemo plugin handled RPC request\n" }