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