lib/TWiki/Plugins.pm
author Colas Nahaboo <colas@nahaboo.net>
Sat, 26 Jan 2008 15:50:53 +0100
changeset 0 414e01d06fd5
permissions -rw-r--r--
RELEASE 4.2.0 freetown
colas@0
     1
# Module of TWiki Enterprise Collaboration Platform, http://TWiki.org/
colas@0
     2
#
colas@0
     3
# Copyright (C) 2000-2001 Andrea Sterbini, a.sterbini@flashnet.it
colas@0
     4
# Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org
colas@0
     5
# and TWiki Contributors. All Rights Reserved. TWiki Contributors
colas@0
     6
# are listed in the AUTHORS file in the root of this distribution.
colas@0
     7
# NOTE: Please extend that file, not this notice.
colas@0
     8
#
colas@0
     9
# This program is free software; you can redistribute it and/or
colas@0
    10
# modify it under the terms of the GNU General Public License
colas@0
    11
# as published by the Free Software Foundation; either version 2
colas@0
    12
# of the License, or (at your option) any later version. For
colas@0
    13
# more details read LICENSE in the root of this distribution.
colas@0
    14
#
colas@0
    15
# This program is distributed in the hope that it will be useful,
colas@0
    16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
colas@0
    17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
colas@0
    18
#
colas@0
    19
# As per the GPL, removal of this notice is prohibited.
colas@0
    20
colas@0
    21
=pod
colas@0
    22
colas@0
    23
---+ package TWiki::Plugins
colas@0
    24
colas@0
    25
This module defines the singleton object that handles Plugins
colas@0
    26
loading, initialization and execution.
colas@0
    27
colas@0
    28
This class uses Chain of Responsibility (GOF) pattern to dispatch
colas@0
    29
handler calls to registered plugins.
colas@0
    30
colas@0
    31
=cut
colas@0
    32
colas@0
    33
=pod
colas@0
    34
colas@0
    35
Note that as of version 1.026 of this module, TWiki internal
colas@0
    36
methods are _no longer available_ to plugins. Any calls to
colas@0
    37
TWiki internal methods must be replaced by calls via the
colas@0
    38
=$SESSION= object in this package, or via the Func package.
colas@0
    39
For example, the call:
colas@0
    40
colas@0
    41
=my $pref = TWiki::getPreferencesValue('URGH');=
colas@0
    42
colas@0
    43
should be replaced with
colas@0
    44
colas@0
    45
=my $pref = TWiki::Func::getPreferencesValue('URGH');=
colas@0
    46
colas@0
    47
and the call
colas@0
    48
colas@0
    49
=my $t = TWiki::writeWarning($message);=
colas@0
    50
colas@0
    51
should be replaced with
colas@0
    52
colas@0
    53
=my $pref = $TWiki::Plugins::SESSION->writeWarning($message);=
colas@0
    54
colas@0
    55
Methods in other modules such as Store must be accessed through
colas@0
    56
the relevant TWiki sub-object, for example
colas@0
    57
colas@0
    58
=TWiki::Store::saveTopic(...)=
colas@0
    59
colas@0
    60
should be replaced with
colas@0
    61
colas@0
    62
=$TWiki::Plugins::SESSION->{store}->saveTopic(...)=
colas@0
    63
colas@0
    64
Note that calling TWiki internal methods is very very bad practice,
colas@0
    65
and should be avoided wherever practical.
colas@0
    66
colas@0
    67
The developers of TWiki reserve the right to change internal
colas@0
    68
methods without warning, unless those methods are clearly
colas@0
    69
marked as PUBLIC. PUBLIC methods are part of the core specification
colas@0
    70
of a module and can be trusted.
colas@0
    71
colas@0
    72
=cut
colas@0
    73
colas@0
    74
package TWiki::Plugins;
colas@0
    75
colas@0
    76
use strict;
colas@0
    77
use Assert;
colas@0
    78
colas@0
    79
require TWiki::Plugin;
colas@0
    80
colas@0
    81
use vars qw ( $VERSION $SESSION $inited );
colas@0
    82
colas@0
    83
=pod
colas@0
    84
colas@0
    85
---++ PUBLIC constant $VERSION
colas@0
    86
colas@0
    87
This is the version number of the plugins package. Use it for checking
colas@0
    88
if you have a recent enough version.
colas@0
    89
colas@0
    90
---++ PUBLIC $SESSION
colas@0
    91
colas@0
    92
This is a reference to the TWiki session object. It can be used in
colas@0
    93
plugins to get at the methods of the TWiki kernel.
colas@0
    94
colas@0
    95
You are _highly_ recommended to only use the methods in the
colas@0
    96
[[TWikiFuncDotPm][Func]] interface, unless you have no other choice,
colas@0
    97
as kernel methods may change between TWiki releases.
colas@0
    98
colas@0
    99
=cut
colas@0
   100
colas@0
   101
$VERSION = '1.2';
colas@0
   102
colas@0
   103
$inited = 0;
colas@0
   104
colas@0
   105
my %onlyOnceHandlers =
colas@0
   106
  (
colas@0
   107
   registrationHandler            => 1,
colas@0
   108
   writeHeaderHandler             => 1,
colas@0
   109
   redirectCgiQueryHandler        => 1,
colas@0
   110
   renderFormFieldForEditHandler  => 1,
colas@0
   111
   renderWikiWordHandler          => 1,
colas@0
   112
  );
colas@0
   113
colas@0
   114
=pod
colas@0
   115
colas@0
   116
---++ ClassMethod new( $session )
colas@0
   117
colas@0
   118
Construct new singleton plugins collection object. The object is a
colas@0
   119
container for a list of plugins and the handlers registered by the plugins.
colas@0
   120
The plugins and the handlers are carefully ordered.
colas@0
   121
colas@0
   122
=cut
colas@0
   123
colas@0
   124
sub new {
colas@0
   125
    my ( $class, $session ) = @_;
colas@0
   126
    my $this = bless( { session => $session }, $class );
colas@0
   127
colas@0
   128
    unless( $inited ) {
colas@0
   129
        TWiki::registerTagHandler( 'PLUGINDESCRIPTIONS',
colas@0
   130
                                   \&_handlePLUGINDESCRIPTIONS );
colas@0
   131
        TWiki::registerTagHandler( 'ACTIVATEDPLUGINS',
colas@0
   132
                                   \&_handleACTIVATEDPLUGINS );
colas@0
   133
        TWiki::registerTagHandler( 'FAILEDPLUGINS',
colas@0
   134
                                   \&_handleFAILEDPLUGINS );
colas@0
   135
        $inited = 1;
colas@0
   136
    }
colas@0
   137
colas@0
   138
    return $this;
colas@0
   139
}
colas@0
   140
colas@0
   141
=begin twiki
colas@0
   142
colas@0
   143
---++ ObjectMethod finish()
colas@0
   144
Break circular references.
colas@0
   145
colas@0
   146
=cut
colas@0
   147
colas@0
   148
# Note to developers; please undef *all* fields in the object explicitly,
colas@0
   149
# whether they are references or not. That way this method is "golden
colas@0
   150
# documentation" of the live fields in the object.
colas@0
   151
sub finish {
colas@0
   152
    my $this = shift;
colas@0
   153
    undef $this->{registeredHandlers};
colas@0
   154
    foreach (@{$this->{plugins}}) {
colas@0
   155
        $_->finish();
colas@0
   156
    }
colas@0
   157
    undef $this->{plugins};
colas@0
   158
    undef $this->{session};
colas@0
   159
}
colas@0
   160
colas@0
   161
=pod
colas@0
   162
colas@0
   163
---++ ObjectMethod load($allDisabled) -> $loginName
colas@0
   164
colas@0
   165
Find all active plugins, and invoke the early initialisation.
colas@0
   166
Has to be done _after_ prefs are read.
colas@0
   167
colas@0
   168
Returns the user returned by the last =initializeUserHandler= to be
colas@0
   169
called.
colas@0
   170
colas@0
   171
If allDisabled is set, no plugin handlers will be called.
colas@0
   172
colas@0
   173
=cut
colas@0
   174
colas@0
   175
sub load {
colas@0
   176
    my ( $this, $allDisabled ) = @_;
colas@0
   177
colas@0
   178
    my %lookup;
colas@0
   179
colas@0
   180
    my $session = $this->{session};
colas@0
   181
    my $query = $session->{cgiQuery};
colas@0
   182
colas@0
   183
    my @pluginList = ();
colas@0
   184
    my %already;
colas@0
   185
colas@0
   186
    unless( $allDisabled ) {
colas@0
   187
        if ( $query && defined( $query->param( 'debugenableplugins' ))) {
colas@0
   188
            @pluginList = split( /[,\s]+/,
colas@0
   189
                                 $query->param( 'debugenableplugins' ));
colas@0
   190
        } else {
colas@0
   191
            if( $TWiki::cfg{PluginsOrder} ) {
colas@0
   192
                foreach my $plugin( split( /[,\s]+/,
colas@0
   193
                                           $TWiki::cfg{PluginsOrder} )) {
colas@0
   194
                    # Note this allows the same plugin to be listed
colas@0
   195
                    # multiple times! Thus their handlers can be called
colas@0
   196
                    # more than once. This is *desireable*.
colas@0
   197
                    if( $TWiki::cfg{Plugins}{$plugin}{Enabled} ) {
colas@0
   198
                        push( @pluginList, $plugin );
colas@0
   199
                        $already{$plugin} = 1;
colas@0
   200
                    }
colas@0
   201
                }
colas@0
   202
            }
colas@0
   203
            foreach my $plugin ( sort keys %{$TWiki::cfg{Plugins}} ) {
colas@0
   204
                if( $TWiki::cfg{Plugins}{$plugin}{Enabled} &&
colas@0
   205
                      !$already{$plugin} ) {
colas@0
   206
                    push( @pluginList, $plugin );
colas@0
   207
                    $already{$plugin} = 1;
colas@0
   208
                }
colas@0
   209
            }
colas@0
   210
        }
colas@0
   211
    }
colas@0
   212
colas@0
   213
    my $user; # the user login name
colas@0
   214
    my $userDefiner; # the plugin that is defining the user
colas@0
   215
    foreach my $pn ( @pluginList ) {
colas@0
   216
        my $p;
colas@0
   217
        unless( $p = $lookup{$pn} ) {
colas@0
   218
            $p = new TWiki::Plugin( $session, $pn,
colas@0
   219
                                    $TWiki::cfg{Plugins}{$pn}{Module} )
colas@0
   220
        }
colas@0
   221
        push @{$this->{plugins}}, $p;
colas@0
   222
        my $anotherUser = $p->load();
colas@0
   223
        if( $anotherUser ) {
colas@0
   224
            if( $userDefiner ) {
colas@0
   225
                die 'Two plugins - '. $userDefiner->{name} . ' and ' .
colas@0
   226
                  $p->{name} .
colas@0
   227
                    ' are both trying to define the user login name.';
colas@0
   228
            } else {
colas@0
   229
                $userDefiner = $p;
colas@0
   230
                $user = $anotherUser;
colas@0
   231
            }
colas@0
   232
        }
colas@0
   233
        # Report initialisation errors
colas@0
   234
        if( $p->{errors} ) {
colas@0
   235
            $this->{session}->writeWarning( join( "\n", @{$p->{errors}} ));
colas@0
   236
        }
colas@0
   237
        $lookup{$pn} = $p;
colas@0
   238
    }
colas@0
   239
colas@0
   240
    return $user;
colas@0
   241
}
colas@0
   242
colas@0
   243
=pod
colas@0
   244
colas@0
   245
---++ ObjectMethod settings()
colas@0
   246
colas@0
   247
Push plugin settings onto preference stack
colas@0
   248
colas@0
   249
=cut
colas@0
   250
colas@0
   251
sub settings {
colas@0
   252
    my $this = shift;
colas@0
   253
colas@0
   254
    # Set the session for this call stack
colas@0
   255
    local $TWiki::Plugins::SESSION = $this->{session};
colas@0
   256
colas@0
   257
    foreach my $plugin ( @{$this->{plugins}} ) {
colas@0
   258
        $plugin->registerSettings( $this );
colas@0
   259
    }
colas@0
   260
}
colas@0
   261
colas@0
   262
=pod
colas@0
   263
colas@0
   264
---++ ObjectMethod enable()
colas@0
   265
colas@0
   266
Initialisation that is done after the user is known.
colas@0
   267
colas@0
   268
=cut
colas@0
   269
colas@0
   270
sub enable {
colas@0
   271
    my $this = shift;
colas@0
   272
    my $prefs = $this->{session}->{prefs};
colas@0
   273
    my $dissed = $prefs->getPreferencesValue('DISABLEDPLUGINS') || '';
colas@0
   274
    my %disabled = map { $_ => 1 } split(/,\s*/, $dissed);
colas@0
   275
colas@0
   276
    # Set the session for this call stack
colas@0
   277
    local $TWiki::Plugins::SESSION = $this->{session};
colas@0
   278
colas@0
   279
    foreach my $plugin ( @{$this->{plugins}} ) {
colas@0
   280
        if ($disabled{$plugin->{name}}) {
colas@0
   281
            $plugin->{disabled} = 1;
colas@0
   282
            push( @{$plugin->{errors}}, $plugin->{name}.' has been disabled' );
colas@0
   283
        } else {
colas@0
   284
            $plugin->registerHandlers( $this );
colas@0
   285
        }
colas@0
   286
        # Report initialisation errors
colas@0
   287
        if ( $plugin->{errors} ) {
colas@0
   288
            $this->{session}->writeWarning( join( "\n", @{$plugin->{errors}} ));
colas@0
   289
        }
colas@0
   290
    }
colas@0
   291
}
colas@0
   292
colas@0
   293
=pod
colas@0
   294
colas@0
   295
---++ ObjectMethod getPluginVersion() -> $number
colas@0
   296
colas@0
   297
Returns the $TWiki::Plugins::VERSION number if no parameter is specified,
colas@0
   298
else returns the version number of a named Plugin. If the Plugin cannot
colas@0
   299
be found or is not active, 0 is returned.
colas@0
   300
colas@0
   301
=cut
colas@0
   302
colas@0
   303
sub getPluginVersion {
colas@0
   304
    my ( $this, $thePlugin ) = @_;
colas@0
   305
colas@0
   306
    return $VERSION unless $thePlugin;
colas@0
   307
colas@0
   308
    foreach my $plugin ( @{$this->{plugins}} ) {
colas@0
   309
        if( $plugin->{name} eq $thePlugin ) {
colas@0
   310
            return $plugin->getVersion();
colas@0
   311
        }
colas@0
   312
    }
colas@0
   313
    return 0;
colas@0
   314
}
colas@0
   315
colas@0
   316
=pod
colas@0
   317
colas@0
   318
---++ ObjectMethod addListener( $command, $handler )
colas@0
   319
colas@0
   320
   * =$command= - name of the event
colas@0
   321
   * =$handler= - the handler object.
colas@0
   322
colas@0
   323
Add a listener to the end of the list of registered listeners for this event.
colas@0
   324
The listener must implement =invoke($command,...)=, which will be triggered
colas@0
   325
when the event is to be processed.
colas@0
   326
colas@0
   327
=cut
colas@0
   328
colas@0
   329
sub addListener {
colas@0
   330
    my( $this, $c, $h ) = @_;
colas@0
   331
colas@0
   332
    push( @{$this->{registeredHandlers}{$c}}, $h );
colas@0
   333
}
colas@0
   334
colas@0
   335
sub _dispatch {
colas@0
   336
    # must be shifted to clear parameter vector
colas@0
   337
    my $this = shift;
colas@0
   338
    my $handlerName = shift;
colas@0
   339
    foreach my $plugin ( @{$this->{registeredHandlers}{$handlerName}} ) {
colas@0
   340
        # Set the value of $SESSION for this call stack
colas@0
   341
        local $SESSION = $this->{session};
colas@0
   342
        # apply handler on the remaining list of args
colas@0
   343
        no strict 'refs';
colas@0
   344
        my $status = $plugin->invoke( $handlerName, @_ );
colas@0
   345
        use strict 'refs';
colas@0
   346
        if( $status && $onlyOnceHandlers{$handlerName} ) {
colas@0
   347
            return $status;
colas@0
   348
        }
colas@0
   349
    }
colas@0
   350
    return undef;
colas@0
   351
}
colas@0
   352
colas@0
   353
=pod
colas@0
   354
colas@0
   355
---++ ObjectMethod haveHandlerFor( $handlerName ) -> $boolean
colas@0
   356
colas@0
   357
   * =$handlerName= - name of the handler e.g. preRenderingHandler
colas@0
   358
Return: true if at least one plugin has registered a handler of
colas@0
   359
this type.
colas@0
   360
colas@0
   361
=cut
colas@0
   362
colas@0
   363
sub haveHandlerFor {
colas@0
   364
    my( $this, $handlerName ) = @_;
colas@0
   365
colas@0
   366
    return 0 unless defined( $this->{registeredHandlers}{$handlerName} );
colas@0
   367
    return scalar( @{$this->{registeredHandlers}{$handlerName}} );
colas@0
   368
}
colas@0
   369
colas@0
   370
# %FAILEDPLUGINS reports reasons why plugins failed to load
colas@0
   371
# note this is invoked with the session as the first parameter
colas@0
   372
sub _handleFAILEDPLUGINS {
colas@0
   373
    my $this = shift->{plugins};
colas@0
   374
colas@0
   375
    my $text = CGI::start_table( { border => 1, class => 'twikiTable' } ).
colas@0
   376
      CGI::Tr(CGI::th('Plugin').CGI::th('Errors'));
colas@0
   377
colas@0
   378
    foreach my $plugin ( @{$this->{plugins}} ) {
colas@0
   379
        my $td;
colas@0
   380
        if ( $plugin->{errors}) {
colas@0
   381
            $td = CGI::td( {class => 'twikiAlert' },
colas@0
   382
                "\n<verbatim>\n".
colas@0
   383
                  join( "\n", @{$plugin->{errors}} ).
colas@0
   384
                    "\n</verbatim>\n" );
colas@0
   385
        } else {
colas@0
   386
            $td = CGI::td( 'none' );
colas@0
   387
        }
colas@0
   388
        $text .= CGI::Tr( { valign=>'top' },
colas@0
   389
                          CGI::td(' '.$plugin->{installWeb}.'.'.$plugin->{name}.' '). $td );
colas@0
   390
    }
colas@0
   391
colas@0
   392
    $text .= CGI::end_table().CGI::start_table({ border=>1, class => 'twikiTable' }).
colas@0
   393
      CGI::Tr(CGI::th('Handler').CGI::th('Plugins'));
colas@0
   394
colas@0
   395
    foreach my $handler (@TWiki::Plugin::registrableHandlers) {
colas@0
   396
        my $h = '';
colas@0
   397
        if ( defined( $this->{registeredHandlers}{$handler} ) ) {
colas@0
   398
            $h = join( CGI::br(),
colas@0
   399
                       map{ $_->{name} }
colas@0
   400
                       @{$this->{registeredHandlers}{$handler}} );
colas@0
   401
        }
colas@0
   402
        if ( $h ) {
colas@0
   403
            if( defined( $TWiki::Plugin::deprecated{ $handler })) {
colas@0
   404
                $h .= CGI::br() . CGI::span(
colas@0
   405
                    { class=>'twikiAlert' },
colas@0
   406
                    " __This handler is deprecated__ - please check for updated versions of the plugins that use it!" );
colas@0
   407
            }
colas@0
   408
            $text .= CGI::Tr( { valign=>'top' },
colas@0
   409
                              CGI::td( $handler ).CGI::td( $h ) );
colas@0
   410
        }
colas@0
   411
    }
colas@0
   412
colas@0
   413
    return $text.CGI::end_table()."\n*".scalar(@{$this->{plugins}}).
colas@0
   414
      " plugins*\n\n";
colas@0
   415
}
colas@0
   416
colas@0
   417
# note this is invoked with the session as the first parameter
colas@0
   418
sub _handlePLUGINDESCRIPTIONS {
colas@0
   419
    my $this = shift->{plugins};
colas@0
   420
    my $text = '';
colas@0
   421
    foreach my $plugin ( @{$this->{plugins}} ) {
colas@0
   422
        $text .= CGI::li( $plugin->getDescription() . ' ' );
colas@0
   423
    }
colas@0
   424
colas@0
   425
    return CGI::ul( $text );
colas@0
   426
}
colas@0
   427
colas@0
   428
# note this is invoked with the session as the first parameter
colas@0
   429
sub _handleACTIVATEDPLUGINS {
colas@0
   430
    my $this = shift->{plugins};
colas@0
   431
    my $text = '';
colas@0
   432
    foreach my $plugin ( @{$this->{plugins}} ) {
colas@0
   433
        unless( $plugin->{disabled} ) {
colas@0
   434
            $text .= "$plugin->{installWeb}.$plugin->{name}, ";
colas@0
   435
        }
colas@0
   436
    }
colas@0
   437
    $text =~ s/\,\s*$//o;
colas@0
   438
    return $text;
colas@0
   439
}
colas@0
   440
colas@0
   441
=pod
colas@0
   442
colas@0
   443
---++ ObjectMethod registrationHandler ()
colas@0
   444
colas@0
   445
Called by the register script
colas@0
   446
colas@0
   447
=cut
colas@0
   448
colas@0
   449
sub registrationHandler {
colas@0
   450
    my $this = shift;
colas@0
   451
    #my( $web, $wikiName, $loginName ) = @_;
colas@0
   452
    _dispatch( $this, 'registrationHandler', @_ );
colas@0
   453
}
colas@0
   454
colas@0
   455
=pod
colas@0
   456
colas@0
   457
---++ ObjectMethod beforeCommonTagsHandler ()
colas@0
   458
colas@0
   459
Called at the beginning (for cache Plugins only)
colas@0
   460
colas@0
   461
=cut
colas@0
   462
colas@0
   463
sub beforeCommonTagsHandler {
colas@0
   464
    my $this = shift;
colas@0
   465
    #my( $text, $topic, $theWeb, $meta ) = @_;
colas@0
   466
    _dispatch( $this, 'beforeCommonTagsHandler', @_ );
colas@0
   467
}
colas@0
   468
colas@0
   469
=pod
colas@0
   470
colas@0
   471
---++ ObjectMethod commonTagsHandler ()
colas@0
   472
colas@0
   473
Called after %INCLUDE:"..."%
colas@0
   474
colas@0
   475
=cut
colas@0
   476
colas@0
   477
sub commonTagsHandler {
colas@0
   478
    my $this = shift;
colas@0
   479
    #my( $text, $topic, $theWeb, $meta ) = @_;
colas@0
   480
    _dispatch( $this, 'commonTagsHandler', @_ );
colas@0
   481
}
colas@0
   482
colas@0
   483
=pod
colas@0
   484
colas@0
   485
---++ ObjectMethod afterCommonTagsHandler ()
colas@0
   486
colas@0
   487
Called at the end (for cache Plugins only)
colas@0
   488
colas@0
   489
=cut
colas@0
   490
colas@0
   491
sub afterCommonTagsHandler {
colas@0
   492
    my $this = shift;
colas@0
   493
    #my( $text, $topic, $theWeb, $meta ) = @_;
colas@0
   494
    _dispatch( $this, 'afterCommonTagsHandler', @_ );
colas@0
   495
}
colas@0
   496
colas@0
   497
=pod
colas@0
   498
colas@0
   499
---++ ObjectMethod preRenderingHandler( $text, \%map )
colas@0
   500
colas@0
   501
   * =$text= - the text, with the head, verbatim and pre blocks replaced with placeholders
colas@0
   502
   * =\%removed= - reference to a hash that maps the placeholders to the removed blocks.
colas@0
   503
colas@0
   504
Placeholders are text strings constructed using the tag name and a sequence number e.g. 'pre1', "verbatim6", "head1" etc. Placeholders are inserted into the text inside \1 characters so the text will contain \1_pre1\1 for placeholder pre1.
colas@0
   505
colas@0
   506
Each removed block is represented by the block text and the parameters passed to the tag (usually empty) e.g. for
colas@0
   507
<verbatim>
colas@0
   508
<pre class='slobadob'>
colas@0
   509
XYZ
colas@0
   510
</pre>
colas@0
   511
</verbatim>
colas@0
   512
the map will contain:
colas@0
   513
<verbatim>
colas@0
   514
$removed->{'pre1'}{text}:   XYZ
colas@0
   515
$removed->{'pre1'}{params}: class="slobadob"
colas@0
   516
</verbatim>
colas@0
   517
colas@0
   518
Iterating over blocks for a single tag is easy. For example, to prepend a line number to every line of a pre block you might use this code:
colas@0
   519
colas@0
   520
<verbatim>
colas@0
   521
foreach my $placeholder ( keys %$map ) {
colas@0
   522
    if( $placeholder =~ /^pre/i ) {
colas@0
   523
       my $n = 1;
colas@0
   524
       $map->{$placeholder}{text} =~ s/^/$n++/gem;
colas@0
   525
    }
colas@0
   526
}
colas@0
   527
</verbatim>
colas@0
   528
colas@0
   529
=cut
colas@0
   530
colas@0
   531
sub preRenderingHandler {
colas@0
   532
    my $this = shift;
colas@0
   533
    _dispatch( $this, 'preRenderingHandler', @_ );
colas@0
   534
    # Apply the startRenderingHandler (*deprecated*!) if any are defined
colas@0
   535
}
colas@0
   536
colas@0
   537
=pod
colas@0
   538
colas@0
   539
---++ ObjectMethod postRenderingHandler( \$text )
colas@0
   540
colas@0
   541
   * =\$text= - a reference to the HTML, with the head, verbatim and pre blocks replaced with placeholders
colas@0
   542
colas@0
   543
=cut
colas@0
   544
colas@0
   545
sub postRenderingHandler {
colas@0
   546
    my $this = shift;
colas@0
   547
    _dispatch( $this, 'postRenderingHandler', @_ );
colas@0
   548
}
colas@0
   549
colas@0
   550
=pod
colas@0
   551
colas@0
   552
---++ ObjectMethod startRenderingHandler ()
colas@0
   553
colas@0
   554
Called just before the line loop
colas@0
   555
colas@0
   556
*DEPRECATED* Use preRenderingHandler instead. This handler correctly 
colas@0
   557
handles verbatim and other TWiki ML block types, and exposes them to 
colas@0
   558
the plugin.
colas@0
   559
colas@0
   560
=cut
colas@0
   561
colas@0
   562
sub startRenderingHandler {
colas@0
   563
    my $this = shift;
colas@0
   564
    #my ( $text, $web, $topic ) = @_;
colas@0
   565
    _dispatch( $this, 'startRenderingHandler', @_ );
colas@0
   566
}
colas@0
   567
colas@0
   568
=pod
colas@0
   569
colas@0
   570
---++ ObjectMethod outsidePREHandler ()
colas@0
   571
colas@0
   572
Called in line loop outside of &lt;PRE&gt; tag
colas@0
   573
colas@0
   574
*DEPRECATED* Use preRenderingHandler instead. 
colas@0
   575
This handler correctly handles pre and other 
colas@0
   576
TWiki ML block types, and is called only once 
colas@0
   577
instead of line-by-line.
colas@0
   578
colas@0
   579
=cut
colas@0
   580
colas@0
   581
sub outsidePREHandler {
colas@0
   582
    my $this = shift;
colas@0
   583
    #my( $text ) = @_;
colas@0
   584
    _dispatch( $this, 'outsidePREHandler', @_ );
colas@0
   585
}
colas@0
   586
colas@0
   587
=pod
colas@0
   588
colas@0
   589
---++ ObjectMethod insidePREHandler ()
colas@0
   590
colas@0
   591
Called in line loop inside of &lt;PRE&gt; tag
colas@0
   592
colas@0
   593
*DEPRECATED* Use preRenderingHandler instead. 
colas@0
   594
This handler correctly handles pre and other 
colas@0
   595
TWiki ML block types, and is called only once 
colas@0
   596
instead of line-by-line.
colas@0
   597
colas@0
   598
=cut
colas@0
   599
colas@0
   600
sub insidePREHandler {
colas@0
   601
    my $this = shift;
colas@0
   602
    #my( $text ) = @_;
colas@0
   603
    _dispatch( $this, 'insidePREHandler', @_ );
colas@0
   604
}
colas@0
   605
colas@0
   606
=pod
colas@0
   607
colas@0
   608
---++ ObjectMethod endRenderingHandler ()
colas@0
   609
colas@0
   610
Called just after the line loop
colas@0
   611
colas@0
   612
*DEPRECATED* Use postRenderingHandler instead.
colas@0
   613
colas@0
   614
=cut
colas@0
   615
colas@0
   616
sub endRenderingHandler {
colas@0
   617
    my $this = shift;
colas@0
   618
    #my ( $text ) = @_;
colas@0
   619
    _dispatch( $this, 'endRenderingHandler', @_ );
colas@0
   620
}
colas@0
   621
colas@0
   622
=pod
colas@0
   623
colas@0
   624
---++ ObjectMethod beforeEditHandler ()
colas@0
   625
colas@0
   626
Called by edit
colas@0
   627
colas@0
   628
=cut
colas@0
   629
colas@0
   630
sub beforeEditHandler {
colas@0
   631
    my $this = shift;
colas@0
   632
    #my( $text, $topic, $web, $meta ) = @_;
colas@0
   633
    _dispatch( $this, 'beforeEditHandler', @_ );
colas@0
   634
}
colas@0
   635
colas@0
   636
=pod
colas@0
   637
colas@0
   638
---++ ObjectMethod afterEditHandler ()
colas@0
   639
colas@0
   640
Called by edit
colas@0
   641
colas@0
   642
=cut
colas@0
   643
colas@0
   644
sub afterEditHandler {
colas@0
   645
    my $this = shift;
colas@0
   646
    #my( $text, $topic, $web ) = @_;
colas@0
   647
    _dispatch( $this, 'afterEditHandler', @_ );
colas@0
   648
}
colas@0
   649
colas@0
   650
=pod
colas@0
   651
colas@0
   652
---++ ObjectMethod beforeSaveHandler ()
colas@0
   653
colas@0
   654
Called just before the save action
colas@0
   655
colas@0
   656
=cut
colas@0
   657
colas@0
   658
sub beforeSaveHandler {
colas@0
   659
    my $this = shift;
colas@0
   660
    #my ( $theText, $theTopic, $theWeb, $meta ) = @_;
colas@0
   661
    _dispatch( $this, 'beforeSaveHandler', @_ );
colas@0
   662
}
colas@0
   663
colas@0
   664
=pod
colas@0
   665
colas@0
   666
---++ ObjectMethod afterSaveHandler ()
colas@0
   667
colas@0
   668
Called just after the save action
colas@0
   669
colas@0
   670
=cut
colas@0
   671
colas@0
   672
sub afterSaveHandler {
colas@0
   673
    my $this = shift;
colas@0
   674
    #my ( $theText, $theTopic, $theWeb, $error, $meta ) = @_;
colas@0
   675
    _dispatch( $this, 'afterSaveHandler', @_ );
colas@0
   676
}
colas@0
   677
colas@0
   678
=pod
colas@0
   679
colas@0
   680
---++ ObjectMethod afterRenameHandler ( $oldWeb, $oldTopic, $oldAttachment, $newWeb, $newTopic, $newAttachment )
colas@0
   681
colas@0
   682
Called just after the rename/move/delete action of a web, topic or attachment.
colas@0
   683
colas@0
   684
   * =$oldWeb= - name of old web
colas@0
   685
   * =$oldTopic= - name of old topic (empty string if web rename)
colas@0
   686
   * =$oldAttachment= - name of old attachment (empty string if web or topic rename)
colas@0
   687
   * =$newWeb= - name of new web
colas@0
   688
   * =$newTopic= - name of new topic (empty string if web rename)
colas@0
   689
   * =$newAttachment= - name of new attachment (empty string if web or topic rename)
colas@0
   690
colas@0
   691
=cut
colas@0
   692
colas@0
   693
sub afterRenameHandler {
colas@0
   694
    my $this = shift;
colas@0
   695
    #my ( $oldWeb, $oldTopic, $oldAttachment, $newWeb, $newTopic, $newAttachment ) = @_;
colas@0
   696
    _dispatch( $this, 'afterRenameHandler', @_ );
colas@0
   697
}
colas@0
   698
colas@0
   699
=pod
colas@0
   700
colas@0
   701
---++ ObjectMethod mergeHandler ()
colas@0
   702
colas@0
   703
Called to handle text merge.
colas@0
   704
colas@0
   705
=cut
colas@0
   706
colas@0
   707
sub mergeHandler {
colas@0
   708
    my $this = shift;
colas@0
   709
    _dispatch( $this, 'mergeHandler', @_ );
colas@0
   710
}
colas@0
   711
colas@0
   712
=pod
colas@0
   713
colas@0
   714
---++ ObjectMethod beforeAttachmentSaveHandler ( $attrHashRef, $topic, $web ) 
colas@0
   715
colas@0
   716
This code provides Plugins with the opportunity to alter an uploaded attachment between the upload and save-to-store processes. It is invoked as per other Plugins.
colas@0
   717
   * =$attrHashRef= - Hash reference of attachment attributes (keys are indicated below)
colas@0
   718
   * =$topic= -     Topic name
colas@0
   719
   * =$web= -       Web name
colas@0
   720
colas@0
   721
Keys in $attrHashRef:
colas@0
   722
| *Key*       | *Value* |
colas@0
   723
| attachment  | Name of the attachment |
colas@0
   724
| tmpFilename | Name of the local file that stores the upload |
colas@0
   725
| comment     | Comment to be associated with the upload |
colas@0
   726
| user        | Login name of the person submitting the attachment, e.g. 'jsmith' |
colas@0
   727
colas@0
   728
Note: All keys should be used read-only, except for comment which can be modified.
colas@0
   729
colas@0
   730
Example usage:
colas@0
   731
colas@0
   732
<pre>
colas@0
   733
   my( $attrHashRef, $topic, $web ) = @_;
colas@0
   734
   $$attrHashRef{'comment'} .= " (NOTE: Extracted from blah.tar.gz)";
colas@0
   735
</pre>
colas@0
   736
colas@0
   737
=cut
colas@0
   738
colas@0
   739
sub beforeAttachmentSaveHandler {
colas@0
   740
    my $this = shift;
colas@0
   741
    #my ( $theAttrHash, $theTopic, $theWeb ) = @_;
colas@0
   742
    _dispatch( $this, 'beforeAttachmentSaveHandler', @_ );
colas@0
   743
}
colas@0
   744
colas@0
   745
=pod
colas@0
   746
colas@0
   747
---++ ObjectMethod afterAttachmentSaveHandler( $attachmentAttrHash, $topic, $web, $error )
colas@0
   748
colas@0
   749
deal with an uploaded attachment between the upload and save-to-store processes. It is invoked as per other plugins.
colas@0
   750
colas@0
   751
   * =$attrHashRef= - Hash reference of attachment attributes (keys are indicated below)
colas@0
   752
   * =$topic= -     Topic name
colas@0
   753
   * =$web= -       Web name
colas@0
   754
   * =$error= -     Error string of save action, empty if OK
colas@0
   755
colas@0
   756
Keys in $attrHashRef:
colas@0
   757
| *Key*       | *Value* |
colas@0
   758
| attachment  | Name of the attachment |
colas@0
   759
| tmpFilename | Name of the local file that stores the upload |
colas@0
   760
| comment     | Comment to be associated with the upload |
colas@0
   761
| user        | Login name of the person submitting the attachment, e.g. 'jsmith' |
colas@0
   762
colas@0
   763
Note: The hash is *read-only*
colas@0
   764
colas@0
   765
=cut
colas@0
   766
colas@0
   767
sub afterAttachmentSaveHandler {
colas@0
   768
    my $this = shift;
colas@0
   769
    #my ( $theText, $theTopic, $theWeb ) = @_;
colas@0
   770
    _dispatch( $this, 'afterAttachmentSaveHandler', @_ );
colas@0
   771
}
colas@0
   772
colas@0
   773
colas@0
   774
=pod
colas@0
   775
colas@0
   776
---++ ObjectMethod writeHeaderHandler () -> $headers
colas@0
   777
colas@0
   778
*DEPRECATED* Use modifyHeaderHandler instead. it is a lot 
colas@0
   779
more flexible, and allows you to modify existing headers 
colas@0
   780
as well as add new ones. It also works correctly when 
colas@0
   781
multiple plugins want to modify headers.
colas@0
   782
colas@0
   783
=cut
colas@0
   784
colas@0
   785
sub writeHeaderHandler {
colas@0
   786
    my $this = shift;
colas@0
   787
    return _dispatch( $this, 'writeHeaderHandler', @_ );
colas@0
   788
}
colas@0
   789
colas@0
   790
=pod
colas@0
   791
colas@0
   792
---++ ObjectMethod modifyHeaderHandler ( \@headers, $query )
colas@0
   793
colas@0
   794
=cut
colas@0
   795
colas@0
   796
sub modifyHeaderHandler {
colas@0
   797
    my $this = shift;
colas@0
   798
    return _dispatch( $this, 'modifyHeaderHandler', @_ );
colas@0
   799
}
colas@0
   800
colas@0
   801
=pod
colas@0
   802
colas@0
   803
---++ ObjectMethod completePageHandler ( $text, $pageType, $contentType )
colas@0
   804
colas@0
   805
=cut
colas@0
   806
colas@0
   807
sub completePageHandler {
colas@0
   808
    my $this = shift;
colas@0
   809
    return _dispatch( $this, 'completePageHandler', @_ );
colas@0
   810
}
colas@0
   811
colas@0
   812
=pod
colas@0
   813
colas@0
   814
---++ ObjectMethod redirectCgiQueryHandler () -> $result
colas@0
   815
colas@0
   816
Called by TWiki::redirect
colas@0
   817
colas@0
   818
=cut
colas@0
   819
colas@0
   820
sub redirectCgiQueryHandler {
colas@0
   821
    my $this = shift;
colas@0
   822
    return _dispatch( $this, 'redirectCgiQueryHandler', @_ );
colas@0
   823
}
colas@0
   824
colas@0
   825
=pod
colas@0
   826
colas@0
   827
---++ ObjectMethod renderFormFieldForEditHandler ( $name, $type, $size, $value, $attributes, $possibleValues ) -> $html
colas@0
   828
colas@0
   829
This handler is called before built-in types are considered. It generates the HTML text rendering this form field, or false, if the rendering should be done by the built-in type handlers.
colas@0
   830
   * =$name= - name of form field
colas@0
   831
   * =$type= - type of form field
colas@0
   832
   * =$size= - size of form field
colas@0
   833
   * =$value= - value held in the form field
colas@0
   834
   * =$attributes= - attributes of form field 
colas@0
   835
   * =$possibleValues= - the values defined as options for form field, if any. May be a scalar (one legal value) or an array (several legal values)
colas@0
   836
Return HTML text that renders this field. If false, form rendering continues by considering the built-in types.
colas@0
   837
colas@0
   838
Note that a common application would be to generate formatting of the
colas@0
   839
field involving generation of javascript. Such usually also requires
colas@0
   840
the insertion of some common javascript into the page header. Unfortunately,
colas@0
   841
there is currently no mechanism to pass that script to where the header of
colas@0
   842
the page is visible. Consequentially, the common javascript may have to
colas@0
   843
be emitted as part of the field formatting and might be duplicated many
colas@0
   844
times throughout the page.
colas@0
   845
colas@0
   846
=cut
colas@0
   847
colas@0
   848
sub renderFormFieldForEditHandler {
colas@0
   849
    my $this = shift;
colas@0
   850
    return _dispatch( $this, 'renderFormFieldForEditHandler', @_ );
colas@0
   851
}
colas@0
   852
colas@0
   853
=pod
colas@0
   854
colas@0
   855
---++ ObjectMethod renderWikiWordHandler () -> $result
colas@0
   856
colas@0
   857
Change how a WikiWord is rendered
colas@0
   858
colas@0
   859
Originated from the TWiki:Plugins.SpacedWikiWordPlugin hack
colas@0
   860
colas@0
   861
=cut
colas@0
   862
colas@0
   863
sub renderWikiWordHandler {
colas@0
   864
    my $this = shift;
colas@0
   865
    return _dispatch( $this, 'renderWikiWordHandler', @_ );
colas@0
   866
}
colas@0
   867
colas@0
   868
1;