1Wiki::Toolkit(3) User Contributed Perl Documentation Wiki::Toolkit(3)
2
3
4
6 Wiki::Toolkit - A toolkit for building Wikis.
7
9 Helps you develop Wikis quickly by taking care of the boring bits for
10 you. You will still need to write some code - this isn't an instant
11 Wiki.
12
14 # Set up a wiki object with an SQLite storage backend, and an
15 # inverted index/DB_File search backend. This store/search
16 # combination can be used on systems with no access to an actual
17 # database server.
18
19 my $store = Wiki::Toolkit::Store::SQLite->new(
20 dbname => "/home/wiki/store.db" );
21 my $indexdb = Search::InvertedIndex::DB::DB_File_SplitHash->new(
22 -map_name => "/home/wiki/indexes.db",
23 -lock_mode => "EX" );
24 my $search = Wiki::Toolkit::Search::SII->new(
25 indexdb => $indexdb );
26
27 my $wiki = Wiki::Toolkit->new( store => $store,
28 search => $search );
29
30 # Do all the CGI stuff.
31 my $q = CGI->new;
32 my $action = $q->param("action");
33 my $node = $q->param("node");
34
35 if ($action eq 'display') {
36 my $raw = $wiki->retrieve_node($node);
37 my $cooked = $wiki->format($raw);
38 print_page(node => $node,
39 content => $cooked);
40 } elsif ($action eq 'preview') {
41 my $submitted_content = $q->param("content");
42 my $preview_html = $wiki->format($submitted_content);
43 print_editform(node => $node,
44 content => $submitted_content,
45 preview => $preview_html);
46 } elsif ($action eq 'commit') {
47 my $submitted_content = $q->param("content");
48 my $cksum = $q->param("checksum");
49 my $written = $wiki->write_node($node, $submitted_content, $cksum);
50 if ($written) {
51 print_success($node);
52 } else {
53 handle_conflict($node, $submitted_content);
54 }
55 }
56
58 new
59 # Set up store, search and formatter objects.
60 my $store = Wiki::Toolkit::Store::SQLite->new(
61 dbname => "/home/wiki/store.db" );
62 my $indexdb = Search::InvertedIndex::DB::DB_File_SplitHash->new(
63 -map_name => "/home/wiki/indexes.db",
64 -lock_mode => "EX" );
65 my $search = Wiki::Toolkit::Search::SII->new(
66 indexdb => $indexdb );
67 my $formatter = My::HomeMade::Formatter->new;
68
69 my $wiki = Wiki::Toolkit->new(
70 store => $store, # mandatory
71 search => $search, # defaults to undef
72 formatter => $formatter # defaults to something suitable
73 );
74
75 "store" must be an object of type "Wiki::Toolkit::Store::*" and
76 "search" if supplied must be of type "Wiki::Toolkit::Search::*"
77 (though this isn't checked yet - FIXME). If "formatter" isn't
78 supplied, it defaults to an object of class
79 Wiki::Toolkit::Formatter::Default.
80
81 You can get a searchable Wiki up and running on a system without an
82 actual database server by using the SQLite storage backend with the
83 SII/DB_File search backend - cut and paste the lines above for a
84 quick start, and see Wiki::Toolkit::Store::SQLite,
85 Wiki::Toolkit::Search::SII, and
86 Search::InvertedIndex::DB::DB_File_SplitHash when you want to learn
87 the details.
88
89 "formatter" can be any object that behaves in the right way; this
90 essentially means that it needs to provide a "format" method which
91 takes in raw text and returns the formatted version. See
92 Wiki::Toolkit::Formatter::Default for a simple example. Note that
93 you can create a suitable object from a sub very quickly by using
94 Test::MockObject like so:
95
96 my $formatter = Test::MockObject->new();
97 $formatter->mock( 'format', sub { my ($self, $raw) = @_;
98 return uc( $raw );
99 } );
100
101 I'm not sure whether to put this in the module or not - it'd let
102 you just supply a sub instead of an object as the formatter, but it
103 feels wrong to be using a Test::* module in actual code.
104
105 retrieve_node
106 my $content = $wiki->retrieve_node($node);
107
108 # Or get additional data about the node as well.
109 my %node = $wiki->retrieve_node("HomePage");
110 print "Current Version: " . $node{version};
111
112 # Maybe we stored some of our own custom metadata too.
113 my $categories = $node{metadata}{category};
114 print "Categories: " . join(", ", @$categories);
115 print "Postcode: $node{metadata}{postcode}[0]";
116
117 # Or get an earlier version:
118 my %node = $wiki->retrieve_node( name => "HomePage",
119 version => 2,
120 );
121 print $node{content};
122
123 In scalar context, returns the current (raw Wiki language) contents
124 of the specified node. In list context, returns a hash containing
125 the contents of the node plus additional data:
126
127 last_modified
128 version
129 checksum
130 metadata - a reference to a hash containing any caller-supplied
131 metadata sent along the last time the node was written
132
133 The "node" parameter is mandatory. The "version" parameter is
134 optional and defaults to the newest version. If the node hasn't
135 been created yet, it is considered to exist but be empty (this
136 behaviour might change).
137
138 Note on metadata - each hash value is returned as an array ref,
139 even if that type of metadata only has one value.
140
141 moderate_node
142 my $ok = $wiki->moderate_node(name => $node, version => $version);
143
144 Marks the given version of the node as moderated. If this is the
145 highest moderated version, then update the node's contents to hold
146 this version.
147
148 set_node_moderation
149 my $ok = $wiki->set_node_moderation(name => $node, required => $required);
150
151 Sets if a node requires moderation or not. (Moderation is required
152 when $required is true).
153
154 When moderation is required, new versions of a node will sit about
155 until they're tagged as moderated, when they will become the new
156 node.
157
158 rename_node
159 my $ok = $wiki->rename_node(old_name => $old_name, new_name => $new_name, create_new_versions => $create_new_versions );
160
161 Renames a node, updating any references to it as required.
162
163 Uses the internal_links table to identify the nodes that link to
164 this one, and re-writes any wiki links in these to point to the new
165 name. If required, it can mark these updates to other pages as a
166 new version.
167
168 verify_checksum
169 my $ok = $wiki->verify_checksum($node, $checksum);
170
171 Sees whether your checksum is current for the given node. Returns
172 true if so, false if not.
173
174 NOTE: Be aware that when called directly and without locking, this
175 might not be accurate, since there is a small window between the
176 checking and the returning where the node might be changed, so
177 don't rely on it for safe commits; use "write_node" for that. It
178 can however be useful when previewing edits, for example.
179
180 list_backlinks
181 # List all nodes that link to the Home Page.
182 my @links = $wiki->list_backlinks( node => "Home Page" );
183
184 list_dangling_links
185 # List all nodes that have been linked to from other nodes but don't
186 # yet exist.
187 my @links = $wiki->list_dangling_links;
188
189 Each node is returned once only, regardless of how many other nodes
190 link to it.
191
192 list_all_nodes
193 my @nodes = $wiki->list_all_nodes;
194
195 Returns a list containing the name of every existing node. The
196 list won't be in any kind of order; do any sorting in your calling
197 script.
198
199 list_nodes_by_metadata
200 # All documentation nodes.
201 my @nodes = $wiki->list_nodes_by_metadata(
202 metadata_type => "category",
203 metadata_value => "documentation",
204 ignore_case => 1, # optional but recommended (see below)
205 );
206
207 # All pubs in Hammersmith.
208 my @pubs = $wiki->list_nodes_by_metadata(
209 metadata_type => "category",
210 metadata_value => "Pub",
211 );
212 my @hsm = $wiki->list_nodes_by_metadata(
213 metadata_type => "category",
214 metadata_value => "Hammersmith",
215 );
216 my @results = my_l33t_method_for_ANDing_arrays( \@pubs, \@hsm );
217
218 Returns a list containing the name of every node whose caller-
219 supplied metadata matches the criteria given in the parameters.
220
221 By default, the case-sensitivity of both "metadata_type" and
222 "metadata_value" depends on your database - if it will return rows
223 with an attribute value of "Pubs" when you asked for "pubs", or
224 not. If you supply a true value to the "ignore_case" parameter,
225 then you can be sure of its being case-insensitive. This is
226 recommended.
227
228 If you don't supply any criteria then you'll get an empty list.
229
230 This is a really really really simple way of finding things; if you
231 want to be more complicated then you'll need to call the method
232 multiple times and combine the results yourself, or write a plugin.
233
234 list_nodes_by_missing_metadata Returns nodes where either the metadata
235 doesn't exist, or is blank
236 Unlike list_nodes_by_metadata(), the metadata value is optional
237 (the metadata type is required).
238
239 # All nodes missing documentation
240 my @nodes = $store->list_nodes_by_missing_metadata(
241 metadata_type => "category",
242 metadata_value => "documentation",
243 ignore_case => 1, # optional but recommended (see below)
244 );
245
246 # All nodes which don't have a latitude defined
247 my @nodes = $store->list_nodes_by_missing_metadata(
248 metadata_type => "latitude"
249 );
250
251 list_recent_changes
252 This is documented in Wiki::Toolkit::Store::Database; see there for
253 parameters and return values. All parameters are passed through
254 directly to the store object, so, for example,
255
256 my @nodes = $wiki->list_recent_changes( days => 7 );
257
258 does exactly the same thing as
259
260 my @nodes = $wiki->store->list_recent_changes( days => 7 );
261
262 list_unmoderated_nodes
263 my @nodes = $wiki->list_unmoderated_nodes();
264 my @nodes = $wiki->list_unmoderated_nodes(
265 only_where_latest => 1
266 );
267
268 $nodes[0]->{'name'} # The name of the node
269 $nodes[0]->{'node_id'} # The id of the node
270 $nodes[0]->{'version'} # The version in need of moderation
271 $nodes[0]->{'moderated_version'} # The newest moderated version
272
273 Fetches details of all the node versions that require moderation (id,
274 name, version, and latest moderated version).
275
276 If only_where_latest is set, then only the latest version of nodes where
277 the latest version needs moderating are returned.
278 Otherwise, all node versions (including old ones, and possibly multiple
279 per node) are returned.
280
281 list_node_all_versions
282 my @versions = $wiki->list_node_all_versions("HomePage");
283
284 my @versions = $wiki->list_node_all_versions(
285 name => 'HomePage',
286 with_content => 1,
287 with_metadata => 0
288 );
289
290 Returns all the versions of a node, optionally including the
291 content and metadata, as an array of hashes (newest versions
292 first).
293
294 list_last_version_before List the last version of every node before a
295 given date. If no version existed before that date, will return undef
296 for version. Returns a hash of id, name, version and date
297 my @nv = $wiki->list_last_version_before('2007-01-02 10:34:11')
298 foreach my $data (@nv) {
299
300 }
301
302 node_exists
303 my $ok = $wiki->node_exists( "Wombat Defenestration" );
304
305 # or ignore case - optional but recommended
306 my $ok = $wiki->node_exists(
307 name => "monkey brains",
308 ignore_case => 1,
309 );
310
311 Returns true if the node has ever been created (even if it is
312 currently empty), and false otherwise.
313
314 By default, the case-sensitivity of "node_exists" depends on your
315 store backend. If you supply a true value to the "ignore_case"
316 parameter, then you can be sure of its being case-insensitive.
317 This is recommended.
318
319 node_required_moderation
320 my $needs = $wiki->node_required_moderation( "Wombat Defenestration" );
321
322 Returns true if the node exists and requires moderation, and false
323 otherwise.
324
325 delete_node
326 $wiki->delete_node( name => "Home Page", version => 15 );
327
328 "version" is optional. If it is supplied then only that version of
329 the node will be deleted. Otherwise the node and all its history
330 will be completely deleted.
331
332 Doesn't do any locking though - to fix? You probably don't want to
333 let anyone except Wiki admins call this. You may not want to use it
334 at all.
335
336 Croaks on error, silently does nothing if the node or version
337 doesn't exist, returns true if no error.
338
339 search_nodes
340 # Find all the nodes which contain the word 'expert'.
341 my %results = $wiki->search_nodes('expert');
342
343 Returns a (possibly empty) hash whose keys are the node names and
344 whose values are the scores in some kind of relevance-scoring
345 system I haven't entirely come up with yet. For OR searches, this
346 could initially be the number of terms that appear in the node,
347 perhaps.
348
349 Defaults to AND searches (if $and_or is not supplied, or is
350 anything other than "OR" or "or").
351
352 Searches are case-insensitive.
353
354 Croaks if you haven't defined a search backend.
355
356 supports_phrase_searches
357 if ( $wiki->supports_phrase_searches ) {
358 return $wiki->search_nodes( '"fox in socks"' );
359 }
360
361 Returns true if your chosen search backend supports phrase
362 searching, and false otherwise.
363
364 supports_fuzzy_searches
365 if ( $wiki->supports_fuzzy_searches ) {
366 return $wiki->fuzzy_title_match( 'Kings Cross, St Pancreas' );
367 }
368
369 Returns true if your chosen search backend supports fuzzy title
370 searching, and false otherwise.
371
372 fuzzy_title_match
373 NOTE: This section of the documentation assumes you are using a
374 search engine which supports fuzzy matching. (See above.) The
375 Wiki::Toolkit::Search::DBIxFTS backend in particular does not.
376
377 $wiki->write_node( "King's Cross St Pancras", "A station." );
378 my %matches = $wiki->fuzzy_title_match( "Kings Cross St. Pancras" );
379
380 Returns a (possibly empty) hash whose keys are the node names and
381 whose values are the scores in some kind of relevance-scoring
382 system I haven't entirely come up with yet.
383
384 Note that even if an exact match is found, any other similar enough
385 matches will also be returned. However, any exact match is
386 guaranteed to have the highest relevance score.
387
388 The matching is done against "canonicalised" forms of the search
389 string and the node titles in the database: stripping vowels,
390 repeated letters and non-word characters, and lowercasing.
391
392 Croaks if you haven't defined a search backend.
393
394 register_plugin
395 my $plugin = Wiki::Toolkit::Plugin::Foo->new;
396 $wiki->register_plugin( plugin => $plugin );
397
398 Registers the plugin with the wiki as one that needs to be informed
399 when we write a node.
400
401 If the plugin "isa" Wiki::Toolkit::Plugin, calls the methods set up
402 by that parent class to let it know about the backend store, search
403 and formatter objects.
404
405 Finally, calls the plugin class's "on_register" method, which
406 should be used to check tables are set up etc. Note that because of
407 the order these things are done in, "on_register" for
408 Wiki::Toolkit::Plugin subclasses can use the "datastore", "indexer"
409 and "formatter" methods as it needs to.
410
411 get_registered_plugins
412 my @plugins = $wiki->get_registered_plugins;
413
414 Returns an array of plugin objects.
415
416 write_node
417 my $written = $wiki->write_node($node, $content, $checksum, \%metadata, $requires_moderation);
418 if ($written) {
419 display_node($node);
420 } else {
421 handle_conflict();
422 }
423
424 Writes the specified content into the specified node in the backend
425 storage; and indexes/reindexes the node in the search indexes (if a
426 search is set up); calls "post_write" on any registered plugins.
427
428 Note that you can blank out a node without deleting it by passing
429 the empty string as $content, if you want to.
430
431 If you expect the node to already exist, you must supply a
432 checksum, and the node is write-locked until either your checksum
433 has been proved old, or your checksum has been accepted and your
434 change committed. If no checksum is supplied, and the node is
435 found to already exist and be nonempty, a conflict will be raised.
436
437 The first two parameters are mandatory, the others optional. If you
438 want to supply metadata but have no checksum (for a newly-created
439 node), supply a checksum of "undef".
440
441 The final parameter, $requires_moderation (which defaults to
442 false), is ignored except on new nodes. For existing nodes, use
443 $wiki->toggle_node_moderation to change the node moderation flag.
444
445 Returns the version of the updated node on success, 0 on conflict,
446 croaks on error.
447
448 Note on the metadata hashref: Any data in here that you wish to
449 access directly later must be a key-value pair in which the value
450 is either a scalar or a reference to an array of scalars. For
451 example:
452
453 $wiki->write_node( "Calthorpe Arms", "nice pub", $checksum,
454 { category => [ "Pubs", "Bloomsbury" ],
455 postcode => "WC1X 8JR" } );
456
457 # and later
458
459 my @nodes = $wiki->list_nodes_by_metadata(
460 metadata_type => "category",
461 metadata_value => "Pubs" );
462
463 For more advanced usage (passing data through to registered
464 plugins) you may if you wish pass key-value pairs in which the
465 value is a hashref or an array of hashrefs. The data in the
466 hashrefs will not be stored as metadata; it will be checksummed and
467 the checksum will be stored instead. Such data can only be accessed
468 via plugins.
469
470 format
471 my $cooked = $wiki->format($raw, $metadata);
472
473 Passed straight through to your chosen formatter object. You do not
474 have to supply the $metadata hashref, but if your formatter allows
475 node metadata to affect the rendering of the node then you will
476 want to.
477
478 store
479 my $store = $wiki->store;
480 my $dbname = eval { $wiki->store->dbname; }
481 or warn "Not a DB backend";
482
483 Returns the storage backend object.
484
485 search_obj
486 my $search_obj = $wiki->search_obj;
487
488 Returns the search backend object.
489
490 formatter
491 my $formatter = $wiki->formatter;
492
493 Returns the formatter backend object.
494
496 For a very quick Wiki startup without any of that icky programming
497 stuff, see Tom Insam's Wiki::Toolkit::Kwiki, an instant wiki based on
498 Wiki::Toolkit.
499
500 Or for the specialised application of a wiki about a city, see the
501 OpenGuides distribution.
502
503 Wiki::Toolkit allows you to use different formatting modules.
504 Text::WikiFormat might be useful for anyone wanting to write a custom
505 formatter. Existing formatters include:
506
507 · Wiki::Toolkit::Formatter::Default (in this distro)
508
509 · Wiki::Toolkit::Formatter::Pod
510
511 · Wiki::Toolkit::Formatter::UseMod
512
513 There's currently a choice of three storage backends - all database-
514 backed.
515
516 · Wiki::Toolkit::Store::MySQL (in this distro)
517
518 · Wiki::Toolkit::Store::Pg (in this distro)
519
520 · Wiki::Toolkit::Store::SQLite (in this distro)
521
522 · Wiki::Toolkit::Store::Database (parent class for the above - in
523 this distro)
524
525 A search backend is optional:
526
527 · Wiki::Toolkit::Search::DBIxFTS (in this distro, uses
528 DBIx::FullTextSearch)
529
530 · Wiki::Toolkit::Search::SII (in this distro, uses
531 Search::InvertedIndex)
532
533 Standalone plugins can also be written - currently they should only
534 read from the backend storage, but write access guidelines are coming
535 soon. Plugins written so far and available from CPAN:
536
537 · Wiki::Toolkit::Plugin::GeoCache
538
539 · Wiki::Toolkit::Plugin::Categoriser
540
541 · Wiki::Toolkit::Plugin::Locator::UK
542
543 · Wiki::Toolkit::Plugin::RSS::ModWiki
544
545 If writing a plugin you might want an easy way to run tests for it on
546 all possible backends:
547
548 · Wiki::Toolkit::TestConfig::Utilities (in this distro)
549
550 Other ways to implement Wikis in Perl include:
551
552 · CGI::Kwiki (an instant wiki)
553
554 · CGI::pWiki
555
556 · AxKit::XSP::Wiki
557
558 · Apache::MiniWiki
559
560 · UseModWiki <http://usemod.com>
561
562 · Chiq Chaq <http://chiqchaq.sourceforge.net/>
563
565 Kake Pugh (kake@earth.li) and the Wiki::Toolkit team (including Nick
566 Burch and Dominic Hargreaves)
567
569 Questions should go to cgi-wiki-dev@earth.li.
570
572 Copyright (C) 2002-2004 Kake Pugh. All Rights Reserved.
573 Copyright (C) 2006-2013 the Wiki::Toolkit team. All Rights Reserved.
574
575 This module is free software; you can redistribute it and/or modify it
576 under the same terms as Perl itself.
577
579 The developer web site and bug tracker is at
580 http://www.wiki-toolkit.org/ - please file bugs there as appropriate.
581
582 You could also subscribe to the dev list at
583 http://www.earth.li/cgi-bin/mailman/listinfo/cgi-wiki-dev
584
586 Versions between 0.75 and 0.79 inclusive contain a bug which prevents
587 Recent Changes routines from working correctly if minor changes are
588 excluded <http://www.wiki-toolkit.org/ticket/41>. You may wish to avoid
589 upgrading to this version until it is fixed if this is important to
590 you; the fix is however not trivial so noone has been able to step up
591 yet.
592
593 Other minor bugs are documented at <http://www.wiki-toolkit.org/report>
594
596 Various London.pm types helped out with code review, encouragement,
597 JFDI, style advice, code snippets, module recommendations, and so on;
598 far too many to name individually, but particularly Richard Clamp, Tony
599 Fisher, Mark Fowler, and Chris Ball.
600
601 blair christensen sent patches and gave me some good ideas. chromatic
602 continues to patiently apply my patches to Text::WikiFormat and help me
603 get it working in just the way I need. Paul Makepeace helped me add
604 support for connecting to non-local databases. Shevek has been prodding
605 me a lot lately. The OpenGuides team keep me well-supplied with
606 encouragement and bug reports.
607
608 Nick Burch has been leading the way with development leading up to the
609 release under the Wiki::Toolkit name.
610
612 I'm only obsessed with Wikis because of the Open Guide to London --
613 <http://openguides.org/london/>
614
615
616
617perl v5.32.0 2020-07-28 Wiki::Toolkit(3)