summaryrefslogtreecommitdiff
path: root/plugins/externaldemo
blob: 1321a4bc52e22c48f317182cc2449a97e522c31d (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 approch to parse a single XML RPC out of the
  21. # accumulated input. Relies on calls always ending with a
  22. # 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 $r = RPC::XML::Parser->new->parse($1);
  27. if (! ref $r) {
  28. die "error: XML RPC parse failure $r";
  29. }
  30. return $r;
  31. }
  32. }
  33. return undef;
  34. }
  35. sub rpc_handle {
  36. # Handle an incoming XML RPC command.
  37. my $r=rpc_read();
  38. if (! defined $r) {
  39. return 0;
  40. }
  41. if ($r->isa("RPC::XML::request")) {
  42. my $name=$r->name;
  43. my @args=map { $_->value } @{$r->args};
  44. # Dispatch the requested function. This could be
  45. # done with a switch statement on the name, or
  46. # whatever. I'll use eval to call the function with
  47. # the name.
  48. my $ret = eval $name.'(@args)';
  49. die $@ if $@;
  50. # Now send the repsonse from the function back,
  51. # followed by a newline.
  52. my $resp=RPC::XML::response->new($ret);
  53. $resp->serialize(\*STDOUT);
  54. print "\n";
  55. # stdout needs to be flushed here. If it isn't,
  56. # things will deadlock. Perl flushes it
  57. # automatically when $| is set.
  58. return 1;
  59. }
  60. elsif ($r->isa("RPC::XML::response")) {
  61. die "protocol error; got a response when expecting a request";
  62. }
  63. }
  64. sub rpc_call {
  65. # Make an XML RPC call and return the result.
  66. my $command=shift;
  67. my @params=@_;
  68. my $req=RPC::XML::request->new($command, @params);
  69. $req->serialize(\*STDOUT);
  70. print "\n";
  71. # stdout needs to be flushed here to prevent deadlock. Perl does it
  72. # automatically when $| is set.
  73. my $r=rpc_read();
  74. if ($r->isa("RPC::XML::response")) {
  75. return $r->value->value;
  76. }
  77. else {
  78. die "protocol error; got a request when expecting a response";
  79. }
  80. }
  81. # Now on with the actual plugin. Let's do a simple preprocessor plugin.
  82. sub import {
  83. # The import function will be called by ikiwiki when the plugin is
  84. # loaded. When it's imported, it needs to hook into the preprocessor
  85. # stage of ikiwiki.
  86. rpc_call("hook", type => "preprocess", id => "externaldemo", call => "preprocess");
  87. # Here's an example of how to inject an arbitrary function into
  88. # ikiwiki. Ikiwiki will be able to call bob() just like any other
  89. # function. Note use of automatic memoization.
  90. rpc_call("inject", name => "IkiWiki::bob", call => "bob",
  91. memoize => 1);
  92. # Here's an exmaple of how to access values in %IkiWiki::config.
  93. print STDERR "url is set to: ".
  94. rpc_call("getvar", "config", "url")."\n";
  95. print STDERR "externaldemo plugin successfully imported\n";
  96. }
  97. sub preprocess {
  98. # This function will be called when ikiwiki wants to preprocess
  99. # something.
  100. my %params=@_;
  101. # Let's use IkiWiki's pagetitle function to turn the page name into
  102. # a title.
  103. my $title=rpc_call("pagetitle", $params{page});
  104. return "externaldemo plugin preprocessing on $title!";
  105. }
  106. sub bob {
  107. print STDERR "externaldemo plugin's bob called via RPC";
  108. }
  109. # Now all that's left to do is loop and handle each incoming RPC request.
  110. while (rpc_handle()) { print STDERR "externaldemo plugin handled RPC request\n" }