/[pub]/suikawiki/script/lib/SuikaWiki/Implementation.pm
Suika

Contents of /suikawiki/script/lib/SuikaWiki/Implementation.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.17 - (show annotations) (download)
Sun Jul 25 06:54:29 2004 UTC (21 years, 11 months ago) by wakaba
Branch: MAIN
CVS Tags: suikawiki3-redirect, HEAD
Branch point for: helowiki, helowiki-2005
Changes since 1.16: +3 -3 lines
Property Editor implemented

1
2 =head1 NAME
3
4 SuikaWiki::Implementation - SuikaWiki: WikiEngine Core
5
6 =head1 DESCRIPTION
7
8 This module implements core part of the SuikaWiki WikiEngine.
9 All implemented features of WikiEngine can be called directly
10 or indirectly from instance of this module (with some exception
11 such as functions provided by WikiPlugin modules).
12
13 This module is part of SuikaWiki.
14
15 =head1 SYNOPSIS
16
17 require SuikaWiki::Implementation;
18 my $WIKI = new SuikaWiki::Implementation;
19 ...
20 $WIKI->exit;
21
22 C<lib/suikawiki.pl> might be a good example for instanciating WikiEngine.
23
24 =cut
25
26 package SuikaWiki::Implementation;
27 use strict;
28 our $VERSION = do{my @r=(q$Revision: 1.16 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
29
30 =head1 METHODS
31
32 =over 4
33
34 =item $wiki = SuikaWiki::Implementation->new ()
35
36 Constructs new instance of wiki implementation
37
38 =cut
39
40 sub new ($;%) {
41 my $self = bless {
42 driver_name => 'WikiImplementation',
43 driver_version => '0.0',
44 driver_uri_reference => q<about:>,
45 engine_name => 'SuikaWiki',
46 engine_version => '3.0.1',
47 engine_uri_reference => q<http://suika.fam.cx/~wakaba/-temp/wiki/wiki?SuikaWiki>,
48 }, shift;
49
50 $self;
51 }
52
53 =item $wiki->init_variables
54
55 Initialize per-access variables. This method should be called
56 before other init_* methods are to be called.
57
58 =cut
59
60 sub init_variables ($) {
61 my $self = shift;
62 $self->close_input;
63 $self->{var} = {};
64 $self->___raise_event (name => 'setting_initial_variables');
65 }
66
67 =item $wiki->init_plugin
68
69 Prepares to use wiki plugins
70
71 =cut
72
73 sub init_plugin ($) {
74 my $self = shift;
75 require SuikaWiki::Plugin;
76 $self->{plugin} = SuikaWiki::Plugin->new (wiki => $self);
77
78 $self->___raise_event (name => 'plugin_manager_loaded');
79 }
80
81 =item $wiki->init_view
82
83 Prepares to use wikiview
84
85 =cut
86
87 sub init_view ($) {
88 my $self = shift;
89 require SuikaWiki::View::Implementation;
90 $self->{view} = SuikaWiki::View::Implementation->new (wiki => $self);
91
92 $self->___raise_event (name => 'view_implementation_loaded');
93 }
94
95 =item $wiki->init_db
96
97 Prepares to use wiki database
98
99 =cut
100
101 sub init_db ($) {
102 my $wiki = shift;
103 return if ref $wiki->{db}; ## Already initialized
104 $wiki->{config}->{lock}
105 ||= {-directory => $wiki->{config}->{path_to}->{db__lock__dir},
106 -retry => 40,
107 -error_handler => sub {
108 my ($self, %o) = @_;
109 if ($o{level} eq 'fatal' or $wiki->{config}->{debug}->{db}) {
110 if ($wiki->{config}->{path_to}->{db__lock__log_file}) {
111 open LOG, '>>', $wiki->{config}->{path_to}
112 ->{db__lock__log_file};
113 print LOG scalar (gmtime),
114 "\@@{[time]} @{[$$]} {$o{level}}: LOCK: ",
115 $o{msg}, Carp::longmess,"\n";
116 close LOG;
117 }
118 }
119 if ($o{level} eq 'fatal') {
120 die $o{msg};
121 }
122 },
123 };
124 $wiki->{var}->{db}->{lock_prop} = sub {
125 my $prop = shift;
126 my %lock = %{$wiki->{config}->{lock}};
127 $lock{-name} = $prop;
128 $lock{-share} = defined $wiki->{var}->{db}->{read_only}->{$prop}
129 ? $wiki->{var}->{db}->{read_only}->{$prop}
130 : $wiki->{var}->{db}->{read_only}->{'#default'};
131 if ($lock{-share}) {
132 $lock{-module} = (defined $wiki->{var}->{db}->{read_lock_module}->{$prop}
133 ? $wiki->{var}->{db}->{read_lock_module}->{$prop}
134 : $wiki->{var}->{db}->{read_lock_module}->{'#default'});
135 } else {
136 $lock{-module} = (defined $wiki->{var}->{db}->{lock_module}->{$prop}
137 ? $wiki->{var}->{db}->{lock_module}->{$prop}
138 : $wiki->{var}->{db}->{lock_module}->{'#default'});
139 }
140 \%lock;
141 };
142
143 require SuikaWiki::DB::Logical;
144 $wiki->{db} = new SuikaWiki::DB::Logical;
145
146 $wiki->___raise_event (name => 'database_loaded');
147 }
148
149 =item $wiki->view_in_mode (%opt)
150
151 Doing main process in accordance to the mode.
152
153 Actually, this method only raises an event of 'view_in_mode'.
154 So that "doing main process" code should be registered as an event procedure
155 of 'view_in_mode'.
156
157 =cut
158
159 sub view_in_mode ($%) {
160 my ($self, %opt) = @_;
161 $self->___raise_event (name => 'view_in_mode', argv => \%opt);
162 }
163
164
165 =item $uri = $wiki->uri_reference (%option)
166
167 Returning URI reference that refers the wiki or a WikiPage.
168
169 Load {input} before calling this method or specify appropriate C<wiki_uri>
170 option to get proper result.
171
172 One or two URI reference(s) is returned as C<URI> object.
173 See C<base> option.
174
175 Available options:
176
177 =over 4
178
179 =item anchor_no => positive-integer (default: none)
180
181 Numeral anchor index. With this option, C<fragment> option
182 is ignored.
183
184 =item base => URI reference (default: none)
185
186 Base URI reference. C<wantarray ? (relative, absolute) : relative> is
187 returned when C<base> is specified. Otherwise, C<(absolute, absolute)>
188 is returned.
189
190 =item fragment => URI reference fragment (default: none)
191
192 URI refernece fragment. This option value MUST be encoded
193 by URI escape encoding.
194
195 =item mode => mode-name (default: "default")
196
197 WikiView mode in which referred.
198
199 =item page => [WikiName] (default: none)
200
201 WikiName to that WikiPage URI reference is referring.
202
203 =item param => {name1 => value1, name2 => value2,...} (default: none)
204
205 Additional query parameters. Names and values are automatically
206 encoded by URI escape encoding if necessary.
207
208 =item up_to_date => 1/0 (default: 0)
209
210 "Up-to-date" URI query parameter for cheating cache.
211
212 =item wiki_uri => URI reference (default: auto)
213
214 A base URI reference referring the wiki itself.
215
216 =item with_lm => 1/0 (default: 0)
217
218 "Last modified" URI query parameter for chating WWW browser history.
219
220 =back
221
222 =cut
223
224 sub uri_reference ($;%) {
225 my ($self, %opt) = @_; ## Note: $opt{wiki_uri} must be a URI(.pm) if any.
226 my $uri = $opt{wiki_uri} || $self->___get_wiki_uri;
227
228 ## SuikaWiki 3.0 format
229 my $query_param = qr/[^0-9A-Za-z_.-]/;
230 my @param = map {my $n = $_; $n =~ tr/_/-/;
231 $self->___uri_escape_encode ($n, $query_param).'='.
232 $self->___uri_escape_encode ($opt{param}->{$_}, $query_param)}
233 keys %{$opt{param}};
234 push @param, 'mode='.$self->___uri_escape_encode ($opt{mode}, $query_param)
235 if $opt{mode};
236 push @param, 'x-d='.time if $opt{up_to_date};
237 if ($opt{page}) {
238 if ($opt{with_lm} and ref $self->{db}) {
239 push @param, 'x-lm='
240 . $self->___uri_escape_encode
241 ($self->{db}->get (lastmodified => $opt{page}),
242 $query_param);
243 }
244 my $page = $opt{page}->stringify (wiki => $self);
245 if (@param) {
246 ## TODO: Encode by $wiki->{config}->{charset}->{uri_param_encode}
247 unshift @param, 'mypage='.$self->___uri_escape_encode
248 ($page, $query_param);
249 push @param, '_charset_='.$self->{config}->{charset}->{uri_param_encode};
250 ## TODO: downgrade to &
251 $uri->query (join ';', sort @param);
252 } else {
253 ## TODO: Encode by $wiki->{config}->{charset}->{uri_query_encode}
254 $uri->query ($self->___uri_escape_encode ($page, $query_param));
255 }
256 } elsif (@param) {
257 push @param, '_charset_='.$self->{config}->{charset}->{uri_param_encode};
258 $uri->query (join ';', sort @param);
259 }
260
261 if ($opt{anchor_no}) {
262 $uri->fragment ('anchor-'.$opt{anchor_no});
263 } elsif ($opt{fragment}) {
264 $uri->fragment ($opt{fragment});
265 }
266
267 if (defined $opt{base}) {
268 $opt{base} = $self->{input}->request_uri
269 if ref $self->{input} and not ref $opt{base} and $opt{base} eq '1';
270 return wantarray ? ($uri->rel ($opt{base}), $uri) : $uri->rel ($opt{base});
271 } else {
272 return ($uri, $uri);
273 }
274 }
275
276 =item 1/0 = $wiki->uri_is_part_of_wiki ($uri-reference)
277
278 Check whether given URI reference is "part of" the wiki.
279
280 =cut
281
282 sub uri_is_part_of_wiki ($$) {
283 my ($self, $uri) = @_;
284 my $wiki_uri = ''.$self->___get_wiki_uri;
285 $uri = URI->new (substr ($uri, 0, length ($wiki_uri)));
286 $uri eq $wiki_uri ? 1 : 0;
287 }
288
289 sub ___get_wiki_uri ($) {
290 my ($self) = shift;
291 my $uri;
292 if (ref $self->{___uri}) {
293 $uri = $self->{___uri}->clone;
294 } elsif (ref $self->{input}) {
295 $uri = $self->{input}->request_uri (no_path_info => 1, no_query => 1);
296 $self->{___uri} = $uri->clone;
297 } else {
298 $uri = URI->new;
299 }
300 $uri;
301 }
302
303 sub ___uri_escape_encode ($$;$) {
304 my ($self, $s, $char) = @_;
305 $char ||= qr([^0-9A-Za-z_.!~*'();/?:\@&=+\$,-]);
306 ## TODO: Some fix required when utf8'ized
307 # require Encode;
308 # $s = Encode::decode ('utf8', $s);
309 $s =~ s/($char)/sprintf '%%%02X', ord $1/ge;
310 $s;
311 }
312
313 sub name ($$%) {
314 require SuikaWiki::Name;
315 my ($wiki, $name, %opt) = @_;
316 SuikaWiki::Name->new ($name, wiki => $wiki, %opt);
317 }
318
319 =item $wiki->close_input
320
321 Closing input manager (C<< $wiki->{input} >>).
322
323 =cut
324
325 sub close_input ($) {
326 my $self = shift;
327 if (ref $self->{input}) {
328 $self->___raise_event (name => 'input_close');
329 $self->{input}->exit;
330 }
331 delete $self->{input};
332 }
333
334 =item $wiki->close_view
335
336 Closing WikiView manager (C<< $wiki->close_view >>).
337
338 =cut
339
340 sub close_view ($) {
341 my $self = shift;
342 $self->{view}->exit if ref $self->{view};
343 delete $self->{view};
344 }
345
346 =item $wiki->close_db
347
348 Closing WikiDB (C<$wiki->{db}>).
349
350 Although this method is automatically called by C<< $wiki->exit >>,
351 it is good practice to explicitly close something opened explicitly.
352
353 This method make C<database_close> event called before database is
354 actually closed. Future version of this module might make event handler
355 being able to cancel closing.
356
357 Calling this method when database is not opened makes no sense,
358 nor being C<database_close> event raisen.
359
360 =cut
361
362 sub close_db ($) {
363 my $self = shift;
364 if (ref $self->{db}) {
365 $self->___raise_event (name => 'database_close');
366 $self->{db}->close;
367 }
368 delete $self->{db};
369 }
370
371 =item $wiki->close_plugin
372
373 Closing WikiPlugin manager (C<< $wiki->{plugin} >>).
374 Note that this method does not unload WikiPlugin modules.
375 (They are "merged" to script namespace so that unloading them
376 is almost impossible.)
377
378 =cut
379
380 sub close_plugin ($) {
381 my $self = shift;
382 $self->{plugin}->exit if ref $self->{plugin};
383 delete $self->{plugin};
384 }
385
386 =item 1/0 = $wiki->exit
387
388 Exitign the wiki. This method closes input manager, WikiDB manager,
389 WikiView manager and WikiPlugin manager after C<close> event is raised.
390 Note that C<close> event handler can "cancel" exiting,
391 it makes this method return C<0>.
392
393 This method is automatically called before C<$wiki> is destoroyed.
394
395 =cut
396
397 sub exit ($) {
398 my $self = shift;
399 return 0 unless $self->___raise_event (name => 'close');
400 $self->close_input;
401 $self->close_db;
402 $self->close_view;
403 $self->close_plugin;
404 $self->{exited} = 1;
405 1;
406 }
407
408 ## TODO: Provides "cancelable" to close event.
409
410 sub DESTROY ($) {
411 my $self = shift;
412 $self->exit unless $self->{exited};
413 }
414
415 =back
416
417 =head1 PUBLIC PROPERTIES
418
419 =over 4
420
421 =item $wiki->{config}
422
423 Persistent wiki configureation parameters
424 (that is not changed with the situation when is who accessing in what way)
425
426 =over 4
427
428 =item ->{charset}->{internal} = <IANA charset name (in lower case)>
429
430 Character encoding scheme used in wiki implementation
431
432 =item ->{charset}->{output} = <IANA charset name (in lower case)>
433
434 Default character encoding scheme used to output content
435
436 =item ->{debug}->{$category} = 1/0 (Default 0)
437
438 Debug mode
439
440 Categories:
441
442 =over 4
443
444 =item db
445
446 WikiDatabase related features
447
448 =item general
449
450 Generic.
451
452 =item view
453
454 WikiView related.
455
456 =back
457
458 =item ->{entity}->{expires}->{$rulename} = {delta => $seconds}
459
460 How long outputed entity will be fresh.
461
462 =item ->{lock}
463
464 Default (prototype) properties to give SuikaWiki::DB::Util::Lock
465
466 =item ->{nmz__uri_to_uri} = I<CODE>
467
468 A function that converts URI reference in Namazu index into global
469 URI reference. Example:
470
471 $wiki->{config}->{nmz__uri_to_uri} = sub {
472 my ($uri, %opt) = @_;
473 $uri =~ m!\w+$!;
474 return $opt{o}->{wiki}->uri_reference (page => $1);
475 };
476
477 =item ->{page}->{ $name }
478
479 WikiPage which has feature of $name
480
481 =item ->{path_to}->{ $name }
482
483 Filesystem path (or path fragment) to $name
484
485 =back
486
487 =item $wiki->{db}
488
489 Wiki main database
490
491 =item $wiki->{driver_name}
492
493 Product name of the WikiDriver.
494
495 For interoperability, only alphanumeric characters and limited symbols
496 (those allowed in RFC 2616 token) should be used as parts of product name.
497
498 =item $wiki->{driver_version}
499
500 WikiDriver version in string.
501
502 For interoperability, only alphanumeric characters and limited symbols
503 (those allowed in RFC 2616 token) should be used as parts of product name.
504
505 =item $wiki->{engine_name} (Read only)
506
507 SuikaWiki WikiEngine name
508
509 =item $wiki->{engine_version} (Read only)
510
511 SuikaWiki WikiEngine version
512
513 =item @{$wiki->{event}->{ $event_name }}
514
515 Event handling procedures. See also EVENT MODEL section.
516
517 =item $wiki->{var}
518
519 Non-persistent wiki variable options
520 (that might vary with context such as caller's argument values)
521
522 =over 4
523
524 =item ->{client}->{downgrade}->{ $feature } = $parameter
525
526 Whether downgrade is required. See C<Downgrade> plugin module.
527
528 =item ->{client}->{used_for_negotiation} = [<HTTP field name>s]
529
530 HTTP (request) header field names used to select variable content.
531 This value will be used to generate HTTP Vary header field.
532
533 =item ->{client}->{user_agent_name} = <HTTP User-Agent field body value>
534
535 User agent name provided by such ways as User-Agent field (in HTTP)
536 or HTTP_USER_AGENT meta variable (in HTTP-CGI).
537
538 =item ->{db}->{lock_module}->{ $prop } = I<module-name>
539
540 Name for Perl module associated with C<$prop> (read and write mode),
541 implementing WikiDB Locking interface. Special C<$prop> C<#default>
542 provides fallback module name.
543
544 =item ->{db}->{lock_prop} = sub ($prop)
545
546 Function returning hash reference of lock options
547 (that will be passed to SuikaWiki::DB::Util::Lock->new).
548
549 $prop, an argument to the function, is a database property name.
550
551 =item ->{db}->{read_lock_module}->{ $prop } = I<module-name>
552
553 Name for Perl module associated with C<$prop> (read-only mode),
554 implementing WikiDB Locking interface. Special C<$prop> C<#default>
555 provides fallback module name.
556
557 =item ->{db}->{read_only}->{ $prop } = 1/0
558
559 Whether the database property named as $prop is opened in read only
560 mode or not. Special property name of '#default' is used to set
561 the default value referred when {read_only}->{$prop} is not specified
562 explicily.
563
564 Note that this value must be set before the instance of database property
565 is loaded.
566
567 =item ->{error} = [{description => Error 1}, {description => Error 2},...]
568
569 Trapped errors.
570
571 =item ->{input}
572
573 Instance of input parameter interface (such as SuikaWiki::Input::HTTP)
574
575 =item ->{mode} = mode name
576
577 Wiki mode name
578
579 =item ->{page} = [page]
580
581 WikiPage being referred
582
583 =back
584
585 =item $wiki->{view}
586
587 WikiView implementation (an instance of SuikaWiki::View::Implementation)
588
589 =back
590
591 =head1 INTERNAL METHODS
592
593 This section describes some internal methods which only intend to be
594 used by this module. These methods MUST NOT be used out of this
595 module.
596
597 =over 4
598
599 =item 1/0 = $wiki->___raise_event (%option)
600
601 Raise an event. C<0> is returned if "cancel"ed, otherwise C<1>
602 returned.
603
604 Options:
605
606 =over 4
607
608 =item name => event-name (required)
609
610 Event name.
611
612 =item argv => some-value (default: none)
613
614 Some argument value, which is to be passed to event handlers
615 as C<< $event->{ I<argv_name> } >>.
616
617 =item argv_name => string (default: same as C<name>)
618
619 Name of argument. C<argv> (or C<name> if missing) is passed to event
620 handlers with this name.
621
622 =back
623
624 =cut
625
626 sub ___raise_event ($%) {
627 my ($self, %opt) = @_;
628 my $event = {cancel => 0, name => $opt{name},
629 ($opt{argv_name}||$opt{name}) => $opt{argv}};
630 for (@{$self->{event}->{$opt{name}}}) {
631 $_->($self, $event);
632 return 0 if $event->{cancel};
633 }
634 return 1;
635 }
636
637 =back
638
639 =head1 EVENT MODEL
640
641 Wiki implementation object (C<$wiki>) "raise"s some event.
642
643 WARNING: Event model of SuikaWiki 3 is not completed yet.
644 Future revision of SuikaWiki might introduce incompatible modification
645 to the interface.
646
647 @@TBD
648
649 =head2 Events
650
651 =over 4
652
653 =item close
654
655 When the wiki implementation is to be closed.
656
657 This event is uncancelabel in current implementation.
658
659 =item database_close
660
661 When the WikiDatabase is to be closed.
662
663 This event is uncancelable in current implementation.
664
665 =item database_loaded
666
667 When WikiDatabase manager is loaded. This event handler is typically
668 used to set database property module for SuikaWiki::DB::Logical.
669
670 This event is uncancelable.
671
672 =item plugin_manager_loaded
673
674 When WikiPlugin manager is loaded. Note that plugins themselves are not
675 loaded yet.
676
677 This event is uncancelable.
678
679 =item setting_initial_variables
680
681 On the process to set per-access variables.
682 This event is raised before other core modules such as WikiDatabase
683 or WikiPlugin are loaded.
684
685 This event is uncancelable.
686
687 =item view_error
688
689 Something wrong with or something useful message is available from WikiView
690 manager.
691
692 This event is uncancelable.
693
694 =item view_in_mode
695
696 C<view_in_mode> method is called.
697
698 This event is uncancelable in current implementation.
699
700 =back
701
702 =head2 Example
703
704 ## Installing a new event handler
705 push @{$wiki->{event}->{ $some_event_name }}, sub {
706 my ($wiki, $event) = @_;
707
708 # something
709
710 $event->{cancel} = 1 if $some_condition;
711 };
712
713 =head1 LICENSE
714
715 Copyright 2003-2004 Wakaba <w@suika.fam.cx>. All rights reserved.
716
717 This program is free software; you can redistribute it and/or
718 modify it under the same terms as Perl itself.
719
720 =cut
721
722 1; # $Date: 2004/04/25 07:06:50 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24