=head1 NAME SuikaWiki::Implementation --- SuikaWiki : Wiki Core Implementation =cut package SuikaWiki::Implementation; use strict; our $VERSION = do{my @r=(q$Revision: 1.5 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; our $INTERFACE_VERSION = '2.9.1'; =head1 METHODS =over 4 =item $wiki = SuikaWiki::Implementation->new () Constructs new instance of wiki implementation =cut sub new ($;%) { my $self = bless { implementation_name => 'SuikaWiki', implementation_version => 'impl'.$VERSION, interface_version => $INTERFACE_VERSION, }, shift; $self; } =item $wiki->init_variables Initialize per-access variables. This method should be called before other init_* methods are to be called. =cut sub init_variables ($) { my $self = shift; $self->{var} = {}; $self->__raise_event (name => 'setting_initial_variables'); } =item $wiki->init_plugin Prepares to use wiki plugins =cut sub init_plugin ($) { my $self = shift; require SuikaWiki::Plugin; $self->{plugin} = SuikaWiki::Plugin->new (wiki => $self); $self->__raise_event (name => 'plugin_manager_loaded'); } =item $wiki->init_view Prepares to use wikiview =cut sub init_view ($) { my $self = shift; require SuikaWiki::View::Implementation; $self->{view} = SuikaWiki::View::Implementation->new (wiki => $self); $self->__raise_event (name => 'view_implementation_loaded'); } =item $wiki->init_db Prepares to use wiki database =cut sub init_db ($) { my $self = shift; return if ref $self->{db}; ## Already initialized $self->{config}->{lock} = {-directory => $self->{config}->{path_to}->{db__lock__dir}, -retry => 20, -error_handler => sub { my ($self, %o) = @_; if ($self->{config}->{path_to}->{db__content__error_log}) { open LOG, '>>', $self->{config}->{path_to} ->{db__content__error_log}; print LOG scalar (gmtime), "\@@{[time]} @{[$$]} {$o{level}}: LOCK: ", $o{msg}, "\n"; close LOG; } if ($o{level} eq 'fatal') { die $o{msg}; } }, }; $self->{var}->{db}->{lock_prop} = sub { my $prop = shift; my %lock = %{$self->{config}->{lock}}; $lock{-name} = $prop; $lock{-share} = defined $self->{var}->{db}->{read_only}->{$prop} ? $self->{var}->{db}->{read_only}->{$prop} : $self->{var}->{db}->{read_only}->{'#default'}; \%lock; }; require SuikaWiki::DB::Logical; $self->{db} = new SuikaWiki::DB::Logical; $self->__raise_event (name => 'database_loaded'); } =item $wiki->view_in_mode (%opt) Doing main process in accordance to the mode. Actually, this method only raises an event of 'view_in_mode'. So that "doing main process" code should be registered as an event procedure of 'view_in_mode'. =cut sub view_in_mode ($%) { my ($self, %opt) = @_; $self->__raise_event (name => 'view_in_mode', argv => [\%opt]); } sub __raise_event ($%) { my ($self, %o) = @_; for (@{$self->{event}->{$o{name}}||[]}) { &{$_} ($self, @{$o{argv}||[]}); ## TODO: canceling } 1; } sub ___raise_event ($$$) { my ($self, $name, $argv) = @_; my $event = {cancel => 0, name => $name, $name => $argv}; for (@{$self->{event}->{$name}}) { $_->($self, $event); return 0 if $event->{cancel}; } return 1; } =item $string = $wiki->version Returns version string of the WikiEngine implementation. This value is combination of the SuikaWiki Interface version and implementation's version. =cut sub version ($) { my ($self) = @_; $self->{interface_version} . '-' . $self->{implementation_version}; } sub close_db ($) { my $self = shift; $self->{db}->close if ref $self->{db}; delete $self->{db}; } sub close_view ($) { my $self = shift; $self->{view}->exit if ref $self->{view}; delete $self->{view}; } sub close_plugin ($) { my $self = shift; $self->{plugin}->exit if ref $self->{plugin}; delete $self->{plugin}; } sub close_input ($) { my $self = shift; $self->{input}->exit if ref $self->{input}; delete $self->{input}; } =item $wiki->exit Exits wiki =cut sub exit ($) { my $self = shift; return 0 unless $self->___raise_event (name => 'close'); $self->close_db; $self->close_view; $self->close_input; $self->close_plugin; $self->{exited} = 1; 1; } sub DESTROY ($) { my $self = shift; $self->exit unless $self->{exited}; } =back =head1 PUBLIC PROPERTIES =over 4 =item $wiki->{config} Persistent wiki configureation parameters (that is not changed with the situation when is who accessing in what way) =over 4 =item ->{charset}->{internal} = Character encoding scheme used in wiki implementation =item ->{charset}->{output} = Default character encoding scheme used to output content =item ->{debug}->{$category} = 1/0 (Default 0) Debug mode Categories: =over 4 =item db WikiDatabase related features =back =item ->{entity}->{expires}->{$rulename} = {delta => $seconds} How long outputed entity will be fresh. =item ->{lock} Default (prototype) properties to give SuikaWiki::DB::Util::Lock =item ->{page}->{ $name } WikiPage which has feature of $name =item ->{path_to}->{ $name } Filesystem path (or path fragment) to $name =back =item $wiki->{db} Wiki main database =item @{$wiki->{event}->{ $event_name }} Event handling procedures Standarized event names: =over 4 =item database_loaded When WikiDatabase manager is loaded. This event handler is typically used to set database property module for SuikaWiki::DB::Logical. =item plugin_manager_loaded When WikiPlugin manager is loaded. Note that plugins themselves are not loaded yet. =item setting_initial_variables On the process to set per-access variables. This event is raised before other core modules such as WikiDatabase or WikiPlugin are loaded. =back =item $wiki->{implementation_name} (default 'SuikaWiki') Product name of the WikiEngine. For interoperability, only alphanumeric characters and limited symbols (those allowed in RFC 2616 token) should be used as parts of product name. =item $wiki->{implementation_version} (default "impl$VERSION") WikiEngine implementation's version in string. For interoperability, only alphanumeric characters and limited symbols (those allowed in RFC 2616 token) should be used as parts of product name. =item $wiki->{interface_version} (Read only) SuikaWiki Interface version implemented by this wiki implementation =item $wiki->{var} Non-persistent wiki variable options (that might vary with context such as caller's argument values) =over 4 =item ->{client}->{used_for_negotiation} = [s] HTTP (request) header field names used to select variable content. This value will be used to generate HTTP Vary header field. =item ->{client}->{user_agent_name} = User agent name provided by such ways as User-Agent field (in HTTP) or HTTP_USER_AGENT meta variable (in HTTP-CGI). =item ->{db}->{lock_prop} = sub ($prop) Function returning hash reference of lock options (that will be passed to SuikaWiki::DB::Util::Lock->new). $prop, an argument to the function, is a database property name. =item ->{db}->{read_only}->{ $prop } = 1/0 Whether the database property named as $prop is opened in read only mode or not. Special property name of '#default' is used to set the default value referred when {read_only}->{$prop} is not specified explicily. Note that this value must be set before the instance of database property is loaded. =item ->{error} = [{description => Error 1}, {description => Error 2},...] Trapped errors. =item ->{input} Instance of input parameter interface (such as SuikaWiki::Input::HTTP) =item ->{mode} = mode name Wiki mode name =item ->{page} = [page] WikiPage being referred =back =item $wiki->{view} WikiView implementation (an instance of SuikaWiki::View::Implementation) =cut =head1 LICENSE Copyright 2003 Wakaba This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; # $Date: 2003/12/06 05:43:55 $