.\" Automatically generated by Pod::Man 4.07 (Pod::Simple 3.32) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .if !\nF .nr F 0 .if \nF>0 \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} .\} .\" ======================================================================== .\" .IX Title "Wiki::Toolkit::Extending 3pm" .TH Wiki::Toolkit::Extending 3pm "2016-12-31" "perl v5.24.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Extending.pod \- How to extend Wiki::Toolkit with your own plugins. .SH "LIMITATIONS" .IX Header "LIMITATIONS" The extension mechanism is currently only defined for database-backed setups, but since nobody has written any other kind of backend I think we're fine for now. .SH "THE SIMPLEST WAY" .IX Header "THE SIMPLEST WAY" You can extend Wiki::Toolkit in a fairly simplified way without the use of plugins, by supplying a hash of metadata when you write a node. For example: .PP .Vb 2 \& $wiki\->write_node( $node, $content, $checksum, \& { postcode => $postcode } ); .Ve .PP and on node retrieval you'll get it back again: .PP .Vb 2 \& my %node = $wiki\->retrieve_node( $node ); \& my $postcode = $node{metadata}{postcode}[0]; .Ve .PP You can supply more than one value for each type of metadata: .PP .Vb 3 \& $wiki\->write_node( $node, $content, $checksum, \& { postcode => "W6 9PL", \& category => [ "Thai Food", "Restaurant", "Hammersmith" ] } ); .Ve .PP And get back a list of nodes which have a given value for a given metadata type: .PP .Vb 3 \& my @nodes = $wiki\->list_nodes_by_metadata( \& metadata_type => "category", \& metadata_value => "Hammersmith" ); .Ve .PP For anything more complicated you will need to write a plugin. .SH "PLUGIN BASE CLASS" .IX Header "PLUGIN BASE CLASS" Plugins should inherit from \f(CW\*(C`Wiki::Toolkit::Plugin\*(C'\fR. This base class provides the following methods to give access to a \f(CW\*(C`Wiki::Toolkit\*(C'\fR object's backends: .IP "\(bu" 4 \&\f(CW\*(C`datastore\*(C'\fR \- returns the store object .IP "\(bu" 4 \&\f(CW\*(C`indexer\*(C'\fR \- returns the search object .IP "\(bu" 4 \&\f(CW\*(C`formatter\*(C'\fR \- returns the formatter object .PP If you want these methods to return anything useful then call .PP .Vb 1 \& $wiki\->register_plugin( plugin => $plugin); .Ve .PP before calling say .PP .Vb 1 \& my %node_data = $plugin\->datastore\->retrieve_node( "Foo" ); .Ve .SH "CALLING API" .IX Header "CALLING API" .Vb 2 \& my $plugin = Wiki::Toolkit::Plugin::Foo\->new( ...any required args... ); \& $wiki\->register_plugin( plugin => $plugin ); \& \& $wiki\->write_node( "Test Node" ,"Test", $checksum, \& { foo_data => { a => "apple", \& b => "banana" } \& } ); \& \& my $bee = $plugin\->get_word( node => "Test Node", letter => "b" ); .Ve .PP or .PP .Vb 10 \& my $plugin = OpenGuides::London::Underground\->new; \& $wiki\->register_plugin( plugin => $plugin ); \& $wiki\->write_node( "Hammersmith Station", "a station", $checksum, \& { tube_data => [ \& { line => "Piccadilly", \& direction => "Eastbound", \& next_station => "Baron\*(Aqs Court Station" \& }, \& { line => "Piccadilly", \& direction => "Westbound", \& next_station => "Acton Town Station" \& } \& ] \& } \& ); \& \& # Put more data in, then \& \& my @route = $plugin\->find_route( from => "Holborn Station", \& to => "Acton Town Station" ); .Ve .SH "STORE ACCESS" .IX Header "STORE ACCESS" A plugin named Wiki::Toolkit::Plugin::Foo may access the backend database directly like so: .IP "\(bu" 4 Read-only access to any table .IP "\(bu" 4 Read-write access to any table whose name begins with \&\f(CW"p_" . $Wiki::Toolkit::Plugin::Foo::plugin_key . "_"\fR .Sp \&\f(CW$Wiki::Toolkit::Plugin::Foo::plugin_key\fR should be different from the keys of all other plugins. No, I haven't set anything up to ensure this. .SH "REQUIREMENTS FOR PLUGIN AUTHORS" .IX Header "REQUIREMENTS FOR PLUGIN AUTHORS" Either be database-agnostic, or state clearly in your docs which databases you support, and handle errors nicely. Be aware that non-database backends may exist in the future. .PP Be aware of whether you need to check for locks explicitly in different databases (see code of Wiki::Toolkit::Store::* to find out). .SH "REQUIRED METHODS" .IX Header "REQUIRED METHODS" .IP "\fBon_register\fR" 4 .IX Item "on_register" Check that any tables you require are set up, and set them up if not. .SH "OPTIONAL METHODS" .IX Header "OPTIONAL METHODS" .IP "\fBpost_write\fR" 4 .IX Item "post_write" This will be called every time a node is written, with the arguments like so: .Sp .Vb 4 \& $plugin\->post_write( node => $node_name, \& version => $version_number, \& content => $content, \& metadata => \e%user_defined_metadata ); .Ve .Sp This will happen after the node data is all written, but before any lock is released. .Sp We could probably reimplement the searches as plugins like this if we want to, but this will require writing extra backends for Search::InvertedIndex so it can work within the same database. .Sp The user-defined metadata will already have been stored in the backend but it is available here for you to do what you will with it. .Sp Its return value should be true on success and false on error. .IP "\fBpost_read\fR" 4 .IX Item "post_read" \&\fB\s-1THIS IS NOT YET IMPLEMENTED.\s0\fR .Sp This will be called every time a node is read, with the arguments like so: .Sp .Vb 4 \& $plugin\->post_read( node => $node_name, \& version => $version_number, \& content => $content, \& metadata => \e%user_defined_metadata ); .Ve .Sp It cannot affect the data returned to the caller. It should be used for its side-effects, for example tracking the number of times a given node is accessed. .Sp Its return value should be true on success and false on error. .SH "PLUGIN CONFLICTS" .IX Header "PLUGIN CONFLICTS" What if we have more than one plugin registered? What if we change the mechanism to allow the plugins to change the data stored in the database/returned to the caller?