1# Module of TWiki Enterprise Collaboration Platform, http://TWiki.org/
2#
3# Copyright (C) 2000-2018 Peter Thoeny, peter[at]thoeny.org
4# and TWiki Contributors. All Rights Reserved. TWiki Contributors
5# are listed in the AUTHORS file in the root of this distribution.
6# NOTE: Please extend that file, not this notice.
7#
8# This program is free software; you can redistribute it and/or
9# modify it under the terms of the GNU General Public License
10# as published by the Free Software Foundation; either version 3
11# of the License, or (at your option) any later version. For
12# more details read LICENSE in the root of this distribution.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17#
18# As per the GPL, removal of this notice is prohibited.
19
20=pod
21
22---+ Package TWiki::Func
23
24<!-- STARTINCLUDE required for huge TWikiDocumentation topic -->
25%STARTINCLUDE%
26
27_Official list of stable TWiki functions for Plugin developers_
28
29This module defines official functions that [[%SYSTEMWEB%.TWikiPlugins][TWiki plugins]]
30can use to interact with the TWiki engine and content.
31
32Refer to TWiki.EmptyPlugin and lib/TWiki/Plugins/EmptyPlugin.pm for a template
33plugin and documentation on how to write a plugin.
34
35Plugins should *only* use functions published in this module. If you use
36functions in other TWiki libraries you might create a security hole and
37you will probably need to change your plugin when you upgrade TWiki.
38
39Deprecated functions will still work in older code, though they should
40_not_ be called in new plugins and should be replaced in older plugins
41as soon as possible.
42
43The version of the TWiki::Func module is defined by the VERSION number of the
44TWiki::Plugins module, currently %PLUGINVERSION%. This can be shown
45by the =%<nop>PLUGINVERSION%= TWiki variable, and accessed in code using
46=$TWiki::Plugins::VERSION=. The 'Since' field in the function
47documentation refers to =$TWiki::Plugins::VERSION=.
48
49Notes on use of =$TWiki::Plugins::VERSION= 6.00 and later:
50
51   * The version number is now aligned with the TWiki release version.
52   * A TWiki-6.7.8 release will have a =$TWiki::Plugins::VERSION = 6.78=.
53   * In an unlikely case where the patch number is 10 or larger, the patch
54     number is added to the previous patch number. For example, TWiki-6.7.9
55     will have version =6.79=, TWiki-6.7.10 will have =6.7910=, and
56     TWiki-6.7.11 will have =6.7911=. This ensures that the version number
57     can sort properly.
58   * =TWiki::Plugins::VERSION= also applies to the plugin handlers. The
59     handlers are documented in the !EmptyPlugin, and that module indicates
60     what version of =TWiki::Plugins::VERSION= it relates to.
61
62A full history of the changes to this API can be found at the end of this
63topic.
64
65=cut
66
67package TWiki::Func;
68
69use strict;
70use Error qw( :try );
71use Assert;
72
73require TWiki;
74require TWiki::Plugins;
75require TWiki::UI;
76
77=pod
78
79#EnvironmentFunctions
80---++ Environment
81
82=cut
83
84=pod
85
86#GetSkin
87---+++ getSkin( ) -> $skin
88
89Get the skin path, set by the =SKIN= and =COVER= preferences variables or the =skin= and =cover= CGI parameters
90
91Return: =$skin= Comma-separated list of skins, e.g. ='gnu,tartan'=. Empty string if none.
92
93*Since:* TWiki::Plugins::VERSION 1.000 (29 Jul 2001)
94
95=cut
96
97sub getSkin {
98    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
99
100    return $TWiki::Plugins::SESSION->getSkin();
101}
102
103=pod
104
105#GetUrlHost
106---+++ getUrlHost( ) -> $host
107
108Get protocol, domain and optional port of script URL
109
110Return: =$host= URL host, e.g. ="http://example.com:80"=
111
112*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
113
114=cut
115
116sub getUrlHost {
117    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
118
119    return $TWiki::Plugins::SESSION->{urlHost};
120}
121
122=pod
123
124#GetScriptUrl
125---+++ getScriptUrl( $web, $topic, $script, ... ) -> $url
126
127Compose fully qualified URL
128   * =$web=    - Web name, e.g. ='Main'=
129   * =$topic=  - Topic name, e.g. ='WebNotify'=
130   * =$script= - Script name, e.g. ='view'=
131   * =...= - an arbitrary number of name=>value parameter pairs that will be url-encoded and added to the url. The special parameter name '#' is reserved for specifying an anchor. e.g. <tt>getScriptUrl('x','y','view','#'=>'XXX',a=>1,b=>2)</tt> will give <tt>.../view/x/y?a=1&b=2#XXX</tt>
132
133Return: =$url=       URL, e.g. ="http://example.com:80/cgi-bin/view.pl/Main/WebNotify"=
134
135*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
136
137=cut
138
139sub getScriptUrl {
140    my $web = shift;
141    my $topic = shift;
142    my $script = shift;
143    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
144
145    return $TWiki::Plugins::SESSION->getScriptUrl( 1, $script, $web, $topic, @_ );
146}
147
148=pod
149
150#GetViewUrl
151---+++ getViewUrl( $web, $topic ) -> $url
152
153Compose fully qualified view URL
154   * =$web=   - Web name, e.g. ='Main'=. The current web is taken if empty
155   * =$topic= - Topic name, e.g. ='WebNotify'=
156Return: =$url=      URL, e.g. ="http://example.com:80/cgi-bin/view.pl/Main/WebNotify"=
157
158*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
159
160=cut
161
162sub getViewUrl {
163    my( $web, $topic ) = @_;
164    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
165
166    $web ||= $TWiki::Plugins::SESSION->{webName} || $TWiki::cfg{UsersWebName};
167    return getScriptUrl( $web, $topic, 'view' );
168}
169
170=pod
171
172#GetPubUrlPath
173---+++ getPubUrlPath( ) -> $path
174
175Get pub URL path
176
177Return: =$path= URL path of pub directory, e.g. ="/pub"=
178
179*Since:* TWiki::Plugins::VERSION 1.000 (14 Jul 2001)
180
181=cut
182
183sub getPubUrlPath {
184    return $TWiki::cfg{PubUrlPath};
185}
186
187=pod
188
189#GetExternalResource
190---+++ getExternalResource( $url, \@headers, \%params ) -> $response
191
192Get whatever is at the other end of a URL (using an HTTP GET request). Will
193only work for encrypted protocols such as =https= if the =LWP= CPAN module is
194installed.
195
196Note that the =$url= may have an optional user and password, as specified by
197the relevant RFC. Any proxy set in =configure= is honored.
198
199Optional parameters may be supplied:
200   * =\@headers= (an array ref): Additional HTTP headers of form 'name1',
201     'value1', 'name2', 'value2'.
202     User-Agent header is set to "TWiki::Net/### libwww-perl/#.##" by default,
203     where ### is the revision number of TWiki::Net and #.## is the version
204     of LWP.
205   * =\%params= (a hash ref): Additional options.
206
207Below is the list of available =%params=. See CPAN:LWP::UserAgent for more
208details.
209| *Name* | *Usage* |
210| =agent= | =&#61;&gt; $useragent= ("User-Agent:" header) |
211| =cookie_jar= | =&#61;&gt; $cookies= |
212| =credentials= | =&#61;&gt; [$netloc, $realm, $uname, $pass]= |
213| =handlers= | =&#61;&gt; {$phase &#61;&gt; \&cb, ...}= \
214  Note: =%matchspec= is not available. |
215| =local_address= | =&#61;&gt; $address= |
216| =max_redirect= | =&#61;&gt; $n= |
217| =max_size= | =&#61;&gt; $bytes= |
218| =method= * | =&#61;&gt; $method= E.g. 'HEAD' |
219| =parse_head= | =&#61;&gt; $boolean= |
220| =requests_redirectable= | =&#61;&gt; \@requests= |
221| =ssl_opts= | =&#61;&gt; {$key &#61;&gt; $value, ...}= |
222| =timeout= * | =&#61;&gt; $secs= |
223The parameters with * do not require =LWP=.
224
225Example:
226<verbatim>
227my $response = getExternalResource($url,
228    ['Cache-Control' => 'max-age=0'], {timeout => 10});
229</verbatim>
230
231The =$response= is an object that is known to implement the following subset of
232the methods of =HTTP::Response=. It may in fact be an =HTTP::Response= object,
233but it may also not be if =LWP= is not available, so callers may only assume
234the following subset of methods is available:
235| =code()= |
236| =message()= |
237| =header($field)= |
238| =content()= |
239| =is_error()= |
240| =is_redirect()= |
241
242Note that if LWP is *not* available, this function:
243   1 can only really be trusted for HTTP/1.0 urls. If HTTP/1.1 or another
244     protocol is required, you are *strongly* recommended to =require LWP=.
245   1 Will not parse multipart content
246
247In the event of the server returning an error, then =is_error()= will return
248true, =code()= will return a valid HTTP status code
249as specified in RFC 2616 and RFC 2518, and =message()= will return the
250message that was received from
251the server. In the event of a client-side error (e.g. an unparseable URL)
252then =is_error()= will return true and =message()= will return an explanatory
253message. =code()= will return 400 (BAD REQUEST).
254
255Note: Callers can easily check the availability of other HTTP::Response methods
256as follows:
257
258<verbatim>
259my $response = TWiki::Func::getExternalResource($url);
260if (!$response->is_error() && $response->isa('HTTP::Response')) {
261    $text = $response->content();
262    # ... other methods of HTTP::Response may be called
263} else {
264    # ... only the methods listed above may be called
265}
266</verbatim>
267
268*Since:* TWiki::Plugins::VERSION 1.2
269
270__Note:__ The optional parameters \@headers and \%params were added in
271TWiki::Plugins::VERSION 6.00
272
273=cut
274
275sub getExternalResource {
276    my( $url, @options ) = @_;
277    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
278    ASSERT(defined $url) if DEBUG;
279
280    return $TWiki::Plugins::SESSION->net->getExternalResource( $url, @options );
281}
282
283=pod
284
285#PostExternalResource
286---+++ postExternalResource( $url, $text, \@headers, \%params ) -> $response
287
288This method is essentially the same as =getExternalResource()= except that it uses
289an HTTP POST method and that the additional =$text= parameter is required.
290
291The =$text= is sent to the server as the body content of the HTTP request.
292
293See =getExternalResource()= for more details.
294
295*Since:* TWiki::Plugins::VERSION 6.00
296
297=cut
298
299sub postExternalResource {
300    my( $url, $text, @options ) = @_;
301    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
302    ASSERT(defined $url) if DEBUG;
303
304    return $TWiki::Plugins::SESSION->net->postExternalResource( $url, $text, @options );
305}
306
307=pod
308
309#GetCgiQuery
310---+++ getCgiQuery( ) -> $query
311
312Get CGI query object. Important: Plugins cannot assume that scripts run under CGI, Plugins must always test if the CGI query object is set
313
314Return: =$query= CGI query object; or 0 if script is called as a shell script
315
316*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
317
318=cut
319
320sub getCgiQuery {
321    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
322    return $TWiki::Plugins::SESSION->{request};
323}
324
325=pod
326
327#GetSessionKeys
328---+++ getSessionKeys() -> @keys
329Get a list of all the names of session variables. The list is unsorted.
330
331Session keys are stored and retrieved using =setSessionValue= and
332=getSessionValue=.
333
334*Since:* TWiki::Plugins::VERSION 1.2
335
336=cut
337
338sub getSessionKeys {
339    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
340    my $hash = $TWiki::Plugins::SESSION->{users}->{loginManager}->getSessionValues();
341    return keys %{$hash};
342}
343
344=pod
345
346#GetSessionValue
347---+++ getSessionValue( $key ) -> $value
348
349Get a session value from the client session module
350   * =$key= - Session key
351Return: =$value=  Value associated with key; empty string if not set
352
353*Since:* TWiki::Plugins::VERSION 1.000 (27 Feb 200)
354
355=cut
356
357sub getSessionValue {
358#   my( $key ) = @_;
359    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
360
361    return $TWiki::Plugins::SESSION->{users}->{loginManager}->getSessionValue( @_ );
362}
363
364
365=pod
366
367#SetSessionValue
368---+++ setSessionValue( $key, $value ) -> $boolean
369
370Set a session value.
371   * =$key=   - Session key
372   * =$value= - Value associated with key
373Return: true if function succeeded
374
375*Since:* TWiki::Plugins::VERSION 1.000 (17 Aug 2001)
376
377=cut
378
379sub setSessionValue {
380#   my( $key, $value ) = @_;
381    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
382
383    $TWiki::Plugins::SESSION->{users}->{loginManager}->setSessionValue( @_ );
384}
385
386=pod
387
388#ClearSessionValue
389---+++ clearSessionValue( $key ) -> $boolean
390
391Clear a session value that was set using =setSessionValue=.
392   * =$key= - name of value stored in session to be cleared. Note that
393   you *cannot* clear =AUTHUSER=.
394Return: true if the session value was cleared
395
396*Since:* TWiki::Plugins::VERSION 1.1
397
398=cut
399
400sub clearSessionValue {
401    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
402
403    return $TWiki::Plugins::SESSION->{users}->{loginManager}->clearSessionValue( @_ );
404}
405
406=pod
407
408#GetContext
409---+++ getContext() -> \%hash
410
411Get a hash of context identifiers representing the currently active
412context.
413
414The context is a set of identifiers that are set
415during specific phases of TWiki processing. For example, each of
416the standard scripts in the 'bin' directory each has a context
417identifier - the view script has 'view', the edit script has 'edit'
418etc. So you can easily tell what 'type' of script your Plugin is
419being called within. The core context identifiers are listed
420in the %SYSTEMWEB%.IfStatements topic. Please be careful not to
421overwrite any of these identifiers!
422
423Context identifiers can be used to communicate between Plugins, and between
424Plugins and templates. For example, in FirstPlugin.pm, you might write:
425<verbatim>
426sub initPlugin {
427   TWiki::Func::getContext()->{'MyID'} = 1;
428   ...
429</verbatim>
430This can be used in !SecondPlugin.pm like this:
431<verbatim>
432sub initPlugin {
433   if( TWiki::Func::getContext()->{'MyID'} ) {
434      ...
435   }
436   ...
437</verbatim>
438or in a template, like this:
439<verbatim>
440%TMPL:DEF{"ON"}% Not off %TMPL:END%
441%TMPL:DEF{"OFF"}% Not on %TMPL:END%
442%TMPL:P{context="MyID" then="ON" else="OFF"}%
443</verbatim>
444or in a topic:
445<verbatim>
446%IF{"context MyID" then="MyID is ON" else="MyID is OFF"}%
447</verbatim>
448__Note__: *all* plugins have an *automatically generated* context identifier
449if they are installed and initialised. For example, if the FirstPlugin is
450working, the context ID 'FirstPluginEnabled' will be set.
451
452*Since:* TWiki::Plugins::VERSION 1.1
453
454=cut
455
456sub getContext {
457    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
458    return $TWiki::Plugins::SESSION->{context};
459}
460
461=pod
462
463#PushTopicContext
464---+++ pushTopicContext($web, $topic)
465   * =$web= - new web
466   * =$topic= - new topic
467Change the TWiki context so it behaves as if it was processing =$web.$topic=
468from now on. All the preferences will be reset to those of the new topic.
469Note that if the new topic is not readable by the logged in user due to
470access control considerations, there will *not* be an exception. It is the
471duty of the caller to check access permissions before changing the topic.
472
473It is the duty of the caller to restore the original context by calling
474=popTopicContext=.
475
476Note that this call does *not* re-initialise plugins, so if you have used
477global variables to remember the web and topic in =initPlugin=, then those
478values will be unchanged.
479
480*Since:* TWiki::Plugins::VERSION 1.2
481
482=cut
483
484sub pushTopicContext {
485    my $twiki = $TWiki::Plugins::SESSION;
486    ASSERT($twiki) if DEBUG;
487    my( $web, $topic ) = $twiki->normalizeWebTopicName( @_ );
488    my $old = {
489        web => $twiki->{webName},
490        topic => $twiki->{topicName},
491        mark => $twiki->{prefs}->mark() };
492
493    push( @{$twiki->{_FUNC_PREFS_STACK}}, $old );
494    $twiki->{webName} = $web;
495    $twiki->{topicName} = $topic;
496    $twiki->{prefs}->pushWebPreferences( $web );
497    $twiki->{prefs}->pushPreferences( $web, $topic, 'TOPIC' );
498    $twiki->{prefs}->pushPreferenceValues(
499        'SESSION', $twiki->{users}->{loginManager}->getSessionValues() );
500}
501
502=pod
503
504#PopTopicContext
505---+++ popTopicContext()
506
507Returns the TWiki context to the state it was in before the
508=pushTopicContext= was called.
509
510*Since:* TWiki::Plugins::VERSION 1.2
511
512=cut
513
514sub popTopicContext {
515    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
516    my $twiki = $TWiki::Plugins::SESSION;
517    ASSERT(scalar(@{$twiki->{_FUNC_PREFS_STACK}})) if DEBUG;
518    my $old = pop( @{$twiki->{_FUNC_PREFS_STACK}} );
519    $twiki->{prefs}->restore( $old->{mark});
520    $twiki->{webName} = $old->{web};
521    $twiki->{topicName} = $old->{topic};
522}
523
524=pod
525
526#PreferencesFunctions
527---++ Preferences
528
529=cut
530
531=pod
532
533#GetPreferencesValue
534---+++ getPreferencesValue( $key, $web ) -> $value
535
536Get a preferences value from TWiki or from a Plugin
537   * =$key= - Preferences key
538   * =$web= - Name of web, optional. Current web if not specified; does not apply to settings of Plugin topics
539Return: =$value=  Preferences value; empty string if not set
540
541*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
542
543   * Example for Plugin setting:
544      * !MyPlugin topic has: =* Set COLOR = red=
545      * Use ="MYPLUGIN_COLOR"= for =$key=
546      * =my $color = TWiki::Func::getPreferencesValue( "MYPLUGIN_COLOR" );=
547
548   * Example for preferences setting:
549      * WebPreferences topic has: =* Set WEBBGCOLOR = #FFFFC0=
550      * =my $webColor = TWiki::Func::getPreferencesValue( 'WEBBGCOLOR', 'Sandbox' );=
551
552*NOTE:* As of TWiki-4.1, if =$NO_PREFS_IN_TOPIC= is enabled in the plugin, then
553preferences set in the plugin topic will be ignored.
554
555=cut
556
557sub getPreferencesValue {
558    my( $key, $web ) = @_;
559    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
560    if( $web ) {
561        return $TWiki::Plugins::SESSION->{prefs}->getWebPreferencesValue(
562            $key, $web );
563    } else {
564        return $TWiki::Plugins::SESSION->{prefs}->getPreferencesValue( $key );
565    }
566}
567
568=pod
569
570#GetPluginPreferencesValue
571---+++ getPluginPreferencesValue( $key ) -> $value
572
573Get a preferences value from your Plugin
574   * =$key= - Plugin Preferences key w/o PLUGINNAME_ prefix.
575Return: =$value=  Preferences value; empty string if not set
576
577__Note__: This function will will *only* work when called from the Plugin.pm file itself. it *will not work* if called from a sub-package (e.g. TWiki::Plugins::MyPlugin::MyModule)
578
579*Since:* TWiki::Plugins::VERSION 1.021 (27 Mar 2004)
580
581*NOTE:* As of TWiki-4.1, if =$NO_PREFS_IN_TOPIC= is enabled in the plugin, then
582preferences set in the plugin topic will be ignored.
583
584=cut
585
586sub getPluginPreferencesValue {
587    my( $key ) = @_;
588    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
589    my $package = caller;
590    $package =~ s/.*:://; # strip off TWiki::Plugins:: prefix
591    return $TWiki::Plugins::SESSION->{prefs}->getPreferencesValue( "\U$package\E_$key" );
592}
593
594=pod
595
596#GetPreferencesFlag
597---+++ getPreferencesFlag( $key, $web ) -> $value
598
599Get a preferences flag from TWiki or from a Plugin
600   * =$key= - Preferences key
601   * =$web= - Name of web, optional. Current web if not specified; does not apply to settings of Plugin topics
602Return: =$value=  Preferences flag ='1'= (if set), or ="0"= (for preferences values ="off"=, ="no"= and ="0"=)
603
604*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
605
606   * Example for Plugin setting:
607      * !MyPlugin topic has: =* Set SHOWHELP = off=
608      * Use ="MYPLUGIN_SHOWHELP"= for =$key=
609      * =my $showHelp = TWiki::Func::getPreferencesFlag( "MYPLUGIN_SHOWHELP" );=
610
611*NOTE:* As of TWiki-4.1, if =$NO_PREFS_IN_TOPIC= is enabled in the plugin, then
612preferences set in the plugin topic will be ignored.
613
614=cut
615
616sub getPreferencesFlag {
617#   my( $key, $web ) = @_;
618    my $t = getPreferencesValue( @_ );
619    return TWiki::isTrue( $t );
620}
621
622=pod
623
624#GetPluginPreferencesFlag
625---+++ getPluginPreferencesFlag( $key ) -> $boolean
626
627Get a preferences flag from your Plugin
628   * =$key= - Plugin Preferences key w/o PLUGINNAME_ prefix.
629Return: false for preferences values ="off"=, ="no"= and ="0"=, or values not set at all. True otherwise.
630
631__Note__: This function will will *only* work when called from the Plugin.pm file itself. it *will not work* if called from a sub-package (e.g. TWiki::Plugins::MyPlugin::MyModule)
632
633*Since:* TWiki::Plugins::VERSION 1.021 (27 Mar 2004)
634
635*NOTE:* As of TWiki-4.1, if =$NO_PREFS_IN_TOPIC= is enabled in the plugin, then
636preferences set in the plugin topic will be ignored.
637
638=cut
639
640sub getPluginPreferencesFlag {
641    my( $key ) = @_;
642    my $package = caller;
643    $package =~ s/.*:://; # strip off TWiki::Plugins:: prefix
644    return getPreferencesFlag( "\U$package\E_$key" );
645}
646
647=pod
648
649#SetPreferencesValue
650---+++ setPreferencesValue($name, $val)
651
652Set the preferences value so that future calls to getPreferencesValue will
653return this value, and =%$name%= will expand to the preference when used in
654future variable expansions.
655
656The preference only persists for the rest of this request. Finalised
657preferences cannot be redefined using this function.
658
659Returns 1 if the preference was defined, and 0 otherwise.
660
661=cut
662
663sub setPreferencesValue {
664    return $TWiki::Plugins::SESSION->{prefs}->setPreferencesValue(@_);
665}
666
667=pod
668
669#GetWikiToolName
670---+++ getWikiToolName( ) -> $name
671
672Get toolname as defined in TWiki.cfg
673
674Return: =$name= Name of tool, e.g. ='TWiki'=
675
676*Since:* TWiki::Plugins::VERSION 1.000 (27 Feb 2001)
677
678=cut
679
680sub getWikiToolName {
681    return $TWiki::cfg{WikiToolName};
682}
683
684=pod
685
686#GetMainWebname
687---+++ getMainWebname( ) -> $name
688
689Get name of Main web as defined in TWiki.cfg
690
691Return: =$name= Name, e.g. ='Main'=
692
693*Since:* TWiki::Plugins::VERSION 1.000 (27 Feb 2001)
694
695=cut
696
697sub getMainWebname {
698    return $TWiki::cfg{UsersWebName};
699}
700
701=pod
702
703#GetTwikiWebname
704---+++ getTwikiWebname( ) -> $name
705
706Get name of TWiki documentation web as defined in TWiki.cfg
707
708Return: =$name= Name, e.g. ='TWiki'=
709
710*Since:* TWiki::Plugins::VERSION 1.000 (27 Feb 2001)
711
712=cut
713
714sub getTwikiWebname {
715    return $TWiki::cfg{SystemWebName};
716}
717
718=pod
719
720#UserHandlingAndAccessControlFunctions
721#UserHandlingFunctions
722---++ User Handling and Access Control
723
724#GetDefaultUserName
725---+++ getDefaultUserName( ) -> $loginName
726Get default user name as defined in the configuration as =DefaultUserLogin=
727
728Return: =$loginName= Default user name, e.g. ='guest'=
729
730*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
731
732=cut
733
734sub getDefaultUserName {
735    return $TWiki::cfg{DefaultUserLogin};
736}
737
738=pod
739
740#GetCanonicalUserID
741---+++ getCanonicalUserID( $user ) -> $cUID
742   * =$user= can be a login, wikiname or web.wikiname
743Return the cUID of the specified user. A cUID is a unique identifier which
744is assigned by TWiki for each user.
745BEWARE: While the default TWikiUserMapping uses a cUID that looks like a user's
746LoginName, some characters are modified to make them compatible with rcs.
747Other usermappings may use other conventions - the !JoomlaUserMapping
748for example, has cUIDs like 'JoomlaeUserMapping_1234'.
749
750If $user is undefined, it assumes the currently logged-in user.
751
752Return: =$cUID=, an internal unique and portable escaped identifier for
753registered users. This may be autogenerated for an authenticated but
754unregistered user.
755
756*Since:* TWiki::Plugins::VERSION 1.2
757
758=cut
759
760sub getCanonicalUserID {
761    my $user = shift;
762    return $TWiki::Plugins::SESSION->{user} unless ($user);
763    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
764    my $cUID;
765    if ($user) {
766        $cUID =
767          $TWiki::Plugins::SESSION->{users}->getCanonicalUserID( $user );
768        if (!$cUID) {
769            # Not a login name or a wiki name. Is it a valid cUID?
770            my $ln = $TWiki::Plugins::SESSION->{users}->getLoginName($user);
771            $cUID = $user if defined $ln && $ln ne 'unknown';
772        }
773    } else {
774        $cUID = $TWiki::Plugins::SESSION->{user};
775    }
776    return $cUID;
777}
778
779=pod
780
781#GetWikiName
782---+++ getWikiName( $user ) -> $wikiName
783
784Return the WikiName of the specified user.
785If $user is undefined get Wiki name of logged-in user.
786
787   * $user can be a cUID, login, wikiname or web.wikiname
788
789Return: =$wikiName= Wiki Name, e.g. ='JohnDoe'=
790
791*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
792
793=cut
794
795sub getWikiName {
796    my $user = shift;
797    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
798    # | *Mapping*         | *cUID*     | *login*   | *wikiname* |
799    # | TWikiUserMapping  | JoeSchmoe  | JoeSchmoe | JoeSchmoe  |
800    # | CustomUserMapping | CM_jschmoe | jschmoe   | JoeSchmoe  |
801    # TWikiUserMapping:
802    #   $users->getLoginName('JoeSchmoe') -> 'JoeSchmoe'
803    #   $users->getLoginName('NotExist')  -> undef
804    # CustomUsrMapping:
805    #   $users->getLoginName('JoeSchmoe') -> undef
806    #   $users->getLoginName('NotExist')  -> undef
807    #   $users->getLoginName('CM_jschmoe')-> 'jschmoe'
808    #   $users->getLoginName('jschmoe')   -> undef
809    # Given this, getLoginName() is used to check if $user is cUID
810    my $users = $TWiki::Plugins::SESSION->{users};
811    my $ln = $users->getLoginName($user);
812    my $cUID;
813    if ( defined($ln) && $ln ne 'unknown' ) {
814        # $user is a cUID
815        $cUID = $user;
816    }
817    else {
818        $cUID = getCanonicalUserID( $user );
819        unless( defined $cUID ) {
820            my ($w, $u) =
821                normalizeWebTopicName($TWiki::cfg{UsersWebName}, $user);
822            return $u;
823        }
824    }
825    return $TWiki::Plugins::SESSION->{users}->getWikiName( $cUID );
826}
827
828=pod
829
830#GetWikiUserName
831---+++ getWikiUserName( $user ) -> $wikiName
832
833Return the userWeb.WikiName of the specified user.
834If $user is undefined get Wiki name of logged-in user.
835
836   * $user can be a cUID, login, wikiname or web.wikiname
837
838Return: =$wikiName= Wiki Name, e.g. ="Main.JohnDoe"=
839
840*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
841
842=cut
843
844sub getWikiUserName {
845    my $user = shift;
846    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
847    my $cUID = getCanonicalUserID( $user );
848    unless( defined $cUID ) {
849        my ($w, $u) = normalizeWebTopicName($TWiki::cfg{UsersWebName}, $user);
850        return "$w.$u";
851    }
852    return $TWiki::Plugins::SESSION->{users}->webDotWikiName($cUID);
853}
854
855=pod
856
857#WikiToUserName
858---+++ wikiToUserName( $id ) -> $loginName
859Translate a Wiki name to a login name.
860   * =$id= - Wiki name, required, e.g. ='Main.JohnDoe'= or ='JohnDoe'=.
861     Since TWiki 4.2.1, $id may also be a login name. This will normally
862     be transparent, but should be borne in mind if you have login names
863     that are also legal wiki names.
864
865Return: =$loginName=   Login name of user, e.g. ='jdoe'=, or undef if not
866matched.
867
868Note that it is possible for several login names to map to the same wikiname.
869This function will only return the *first* login name that maps to the
870wikiname.
871
872Returns undef if the WikiName is not found.
873
874To get the login name of the currently logged in user use:
875<verbatim>
876    my $user = TWiki::Func::wikiToUserName( TWiki::Func::getWikiName() );
877</verbatim>
878
879*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
880
881=cut
882
883sub wikiToUserName {
884    my( $wiki ) = @_;
885    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
886    return '' unless $wiki;
887
888    my $cUID = getCanonicalUserID($wiki);
889    if ($cUID) {
890        my $login = $TWiki::Plugins::SESSION->{users}->getLoginName($cUID);
891        return undef if !$login || $login eq 'unknown';
892        return $login;
893    }
894    return undef;
895}
896
897=pod
898
899#UserToWikiName
900---+++ userToWikiName( $loginName, $dontAddWeb ) -> $wikiName
901Translate a login name to a Wiki name
902   * =$loginName=  - Login name, e.g. ='jdoe'=. Since TWiki 4.2.1 this may
903     also be a wiki name. This will normally be transparent, but may be
904     relevant if you have login names that are also valid wiki names.
905   * =$dontAddWeb= - Do not add web prefix if ="1"=
906
907Return: =$wikiName=      Wiki name of user, e.g. ='Main.JohnDoe'= or ='JohnDoe'=
908
909userToWikiName will always return a name. If the user does not
910exist in the mapping, the $loginName parameter is returned. (backward compatibility)
911
912*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
913
914=cut
915
916sub userToWikiName {
917    my( $login, $dontAddWeb ) = @_;
918    return '' unless $login;
919    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
920    my $users = $TWiki::Plugins::SESSION->{users};
921    my $user = getCanonicalUserID( $login );
922    return ( $dontAddWeb ? $login :
923               ( $TWiki::cfg{UsersWebName} . '.' . $login ) )
924      unless $users->userExists( $user );
925    return $users->getWikiName( $user ) if $dontAddWeb;
926    return $users->webDotWikiName($user);
927}
928
929=pod
930
931#EmailToWikiNames
932---+++ emailToWikiNames( $email, $dontAddWeb ) -> @wikiNames
933   * =$email= - email address to look up
934   * =$dontAddWeb= - Do not add web prefix if ="1"=
935
936Find the wikinames of all users who have the given email address as their
937registered address. Since several users could register with the same email
938address, this returns a list of wikinames rather than a single wikiname.
939
940*Since:* TWiki::Plugins::VERSION 1.2
941
942=cut
943
944sub emailToWikiNames {
945    my( $email, $dontAddWeb ) = @_;
946    ASSERT($email) if DEBUG;
947
948    my %matches;
949    my $users = $TWiki::Plugins::SESSION->{users};
950    my $ua = $users->findUserByEmail( $email );
951    if ($ua) {
952        foreach my $user (@$ua) {
953            if( $dontAddWeb ) {
954                $matches{$users->getWikiName($user)} = 1;
955            } else {
956                $matches{$users->webDotWikiName($user)} = 1;
957            }
958        }
959    }
960
961    return sort keys %matches;
962}
963
964=pod
965
966#WikinameToEmails
967---+++ wikinameToEmails( $user ) -> @emails
968   * =$user= - wikiname of user to look up
969
970Returns the registered email addresses of the named user. If $user is
971undef, returns the registered email addresses for the logged-in user.
972
973Since TWiki 4.2.1, $user may also be a login name, or the name of a group.
974
975*Since:* TWiki::Plugins::VERSION 1.2
976
977=cut
978
979sub wikinameToEmails {
980    my( $wikiname ) = @_;
981    if( $wikiname ) {
982        if (isGroup($wikiname)) {
983            return $TWiki::Plugins::SESSION->{users}->getEmails( $wikiname );
984        } else {
985            my $uids = $TWiki::Plugins::SESSION->{users}->findUserByWikiName(
986                $wikiname );
987            my @em = ();
988            foreach my $user (@$uids) {
989                push(@em, $TWiki::Plugins::SESSION->{users}->getEmails( $user ));
990            }
991            return @em;
992        }
993    } else {
994        my $user = $TWiki::Plugins::SESSION->{user};
995        return $TWiki::Plugins::SESSION->{users}->getEmails( $user );
996    }
997}
998
999=pod
1000
1001#IsGuest
1002---+++ isGuest( ) -> $boolean
1003
1004Test if logged in user is a guest (!TWikiGuest)
1005
1006*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
1007
1008=cut
1009
1010sub isGuest {
1011    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1012    return $TWiki::Plugins::SESSION->{user} eq
1013      $TWiki::Plugins::SESSION->{users}->getCanonicalUserID(
1014          $TWiki::cfg{DefaultUserLogin} );
1015}
1016
1017=pod
1018
1019#IsAnAdmin
1020---+++ isAnAdmin( $user, $topic, $web ) -> $boolean
1021
1022Find out if the user is an admin or not. If the user is not given,
1023the currently logged-in user is assumed.
1024   * $user can be either a login name or a WikiName
1025   * a user mapping handler's isAdmin() may take $topic and $web arguments.
1026     That's why this function takes them too. For a user mapping handler
1027     whose isAdmin() doesn't care $topic and $web (e.g. !TWikiUserMapping),
1028     $topic and $web are irrelevant, needless to say.
1029
1030*Since:* TWiki::Plugins::VERSION 1.2
1031
1032__Note:__ The parameters $topic and $web were added in
1033TWiki::Plugins::VERSION 6.00
1034
1035=cut
1036
1037sub isAnAdmin {
1038    #my ($user, $topic, $web) = @_
1039    return $TWiki::Plugins::SESSION->{users}->isAdmin(
1040        getCanonicalUserID( $_[0] ), $_[1], $_[2]);
1041}
1042
1043=pod
1044
1045#IsGroupMember
1046---+++ isGroupMember( $group, $id ) -> $boolean
1047
1048Find out if $id is in the named group. e.g.
1049<verbatim>
1050if( TWiki::Func::isGroupMember( "HesperionXXGroup", "jordi" )) {
1051    ...
1052}
1053</verbatim>
1054If =$user= is =undef=, it defaults to the currently logged-in user.
1055
1056   * $id can be a login name or a WikiName
1057
1058*Since:* TWiki::Plugins::VERSION 1.2
1059
1060=cut
1061
1062sub isGroupMember {
1063    my ($group, $user) = @_;
1064    my $users = $TWiki::Plugins::SESSION->{users};
1065
1066    return () unless $users->isGroup($group);
1067    if( $user ) {
1068        #my $login = wikiToUserName( $user );
1069        #return 0 unless $login;
1070        $user = getCanonicalUserID( $user );
1071    } else {
1072        $user = $TWiki::Plugins::SESSION->{user};
1073    }
1074    return $users->isInGroup( $user, $group );
1075}
1076
1077=pod
1078
1079#EachUser
1080---+++ eachUser() -> $iterator
1081Get an iterator over the list of all the registered users *not* including
1082groups. The iterator will return each wiki name in turn (e.g. 'FredBloggs').
1083
1084Use it as follows:
1085<verbatim>
1086    my $iterator = TWiki::Func::eachUser();
1087    while ($it->hasNext()) {
1088        my $user = $it->next();
1089        # $user is a wikiname
1090    }
1091</verbatim>
1092
1093*WARNING* on large sites, this could be a long list!
1094
1095*Since:* TWiki::Plugins::VERSION 1.2
1096
1097=cut
1098
1099sub eachUser {
1100    my $it = $TWiki::Plugins::SESSION->{users}->eachUser();
1101    $it->{process} = sub {
1102        return $TWiki::Plugins::SESSION->{users}->getWikiName( $_[0] );
1103    };
1104    return $it;
1105}
1106
1107=pod
1108
1109#EachMembership
1110---+++ eachMembership($id) -> $iterator
1111   * =$id= - WikiName or login name of the user.
1112     If =$id= is =undef=, defaults to the currently logged-in user.
1113Get an iterator over the names of all groups that the user is a member of.
1114
1115*Since:* TWiki::Plugins::VERSION 1.2
1116
1117=cut
1118
1119sub eachMembership {
1120    my ($user) = @_;
1121    my $users = $TWiki::Plugins::SESSION->{users};
1122
1123    if( $user ) {
1124        my $login = wikiToUserName( $user );
1125        return 0 unless $login;
1126        $user = getCanonicalUserID( $login );
1127    } else {
1128        $user = $TWiki::Plugins::SESSION->{user};
1129    }
1130
1131    return $users->eachMembership($user);
1132}
1133
1134=pod
1135
1136#EachGroup
1137---+++ eachGroup() -> $iterator
1138Get an iterator over all groups.
1139
1140Use it as follows:
1141<verbatim>
1142    my $iterator = TWiki::Func::eachGroup();
1143    while ($it->hasNext()) {
1144        my $group = $it->next();
1145        # $group is a group name e.g. TWikiAdminGroup
1146    }
1147</verbatim>
1148
1149*WARNING* on large sites, this could be a long list!
1150
1151*Since:* TWiki::Plugins::VERSION 1.2
1152
1153=cut
1154
1155sub eachGroup {
1156    my $session = $TWiki::Plugins::SESSION;
1157    my $it = $session->{users}->eachGroup();
1158    return $it;
1159}
1160
1161=pod
1162
1163#IsGroup
1164---+++ isGroup( $group ) -> $boolean
1165
1166Checks if =$group= is the name of a group known to TWiki.
1167
1168=cut
1169
1170sub isGroup {
1171    my( $group ) = @_;
1172
1173    return $TWiki::Plugins::SESSION->{users}->isGroup( $group );
1174}
1175
1176=pod
1177
1178#EachGroupMember
1179---+++ eachGroupMember($group) -> $iterator
1180Get an iterator over all the members of the named group. Returns undef if
1181$group is not a valid group.
1182
1183Use it as follows:
1184<verbatim>
1185    my $iterator = TWiki::Func::eachGroupMember('RadioheadGroup');
1186    while ($it->hasNext()) {
1187        my $user = $it->next();
1188        # $user is a wiki name e.g. 'TomYorke', 'PhilSelway'
1189    }
1190</verbatim>
1191
1192*WARNING* on large sites, this could be a long list!
1193
1194*Since:* TWiki::Plugins::VERSION 1.2
1195
1196=cut
1197
1198sub eachGroupMember {
1199    my $user = shift;
1200    my $session = $TWiki::Plugins::SESSION;
1201    return undef unless
1202      $TWiki::Plugins::SESSION->{users}->isGroup($user);
1203    my $it = $TWiki::Plugins::SESSION->{users}->eachGroupMember($user);
1204    $it->{process} = sub {
1205        return $TWiki::Plugins::SESSION->{users}->getWikiName( $_[0] );
1206    };
1207    return $it;
1208}
1209
1210=pod
1211
1212#CheckAccessPermission
1213---+++ checkAccessPermission( $type, $id, $text, $topic, $web, $meta ) -> $boolean
1214
1215Check access permission for a topic based on the
1216[[%SYSTEMWEB%.TWikiAccessControl]] rules
1217   * =$type=     - Access type, required, e.g. ='VIEW'=, ='CHANGE'=.
1218   * =$id=  - WikiName of remote user, required, e.g. ="PeterThoeny"=. From
1219     TWiki 4.2.1, $id may also be a login name.
1220     If =$id= is '', 0 or =undef= then access is *always permitted*.
1221   * =$text=     - Topic text, optional. If 'perl false' (undef, 0 or ''),
1222     topic =$web.$topic= is consulted. =$text= may optionally contain embedded
1223     =%META:PREFERENCE= tags. Provide this parameter if:
1224      1 You are setting different access controls in the text to those defined
1225      in the stored topic,
1226      1 You already have the topic text in hand, and want to help TWiki avoid
1227        having to read it again,
1228      1 You are providing a =$meta= parameter.
1229   * =$topic=    - Topic name, required, e.g. ='PrivateStuff'=
1230   * =$web=      - Web name, required, e.g. ='Sandbox'=
1231   * =$meta=     - Meta-data object, as returned by =readTopic=. Optional.
1232     If =undef=, but =$text= is defined, then access controls will be parsed
1233     from =$text=. If defined, then metadata embedded in =$text= will be
1234     ignored. This parameter is always ignored if =$text= is undefined.
1235     Settings in =$meta= override =Set= settings in $text.
1236A perl true result indicates that access is permitted.
1237
1238*Note* the weird parameter order is due to compatibility constraints with
1239earlier TWiki releases.
1240
1241*Tip* if you want, you can use this method to check your own access control types. For example, if you:
1242   * Set ALLOWTOPICSPIN = !IncyWincy
1243in =ThatWeb.ThisTopic=, then a call to =checkAccessPermission('SPIN', 'IncyWincy', undef, 'ThisTopic', 'ThatWeb', undef)= will return =true=.
1244
1245*Since:* TWiki::Plugins::VERSION 1.000 (27 Feb 2001)
1246
1247=cut
1248
1249sub checkAccessPermission {
1250    my( $type, $user, $text, $topic, $web, $meta ) = @_;
1251    return 1 unless ( $user );
1252    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1253    $text = undef unless $text;
1254    my $cUID = getCanonicalUserID($user) ||
1255        getCanonicalUserID($TWiki::cfg{DefaultUserLogin});
1256    return $TWiki::Plugins::SESSION->security->checkAccessPermission(
1257        $type, $cUID, $text, $meta, $topic, $web );
1258}
1259
1260=pod
1261
1262#WebsTopicsAndAttachmentsFunctions
1263---++ Webs, Topics and Attachments
1264
1265=cut
1266
1267=pod
1268
1269#GetListOfWebs
1270---+++ getListOfWebs( $filter ) -> @webs
1271
1272   * =$filter= - spec of web types to recover
1273Gets a list of webs, filtered according to the spec in the $filter,
1274which may include one of:
1275   1 'user' (for only user webs)
1276   2 'template' (for only template webs i.e. those starting with "_")
1277=$filter= may also contain the word 'public' which will further filter
1278out webs that have NOSEARCHALL set on them.
1279'allowed' filters out webs the current user can't read.
1280
1281For example, the deprecated getPublicWebList function can be duplicated
1282as follows:
1283<verbatim>
1284   my @webs = TWiki::Func::getListOfWebs( "user,public" );
1285</verbatim>
1286
1287*Since:* TWiki::Plugins::VERSION 1.1
1288
1289=cut
1290
1291sub getListOfWebs {
1292    my $filter = shift;
1293    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1294    return $TWiki::Plugins::SESSION->{store}->getListOfWebs($filter);
1295}
1296
1297=pod
1298
1299#WebExists
1300---+++ webExists( $web ) -> $boolean
1301
1302Test if web exists
1303   * =$web= - Web name, required, e.g. ='Sandbox'=
1304
1305*Since:* TWiki::Plugins::VERSION 1.000 (14 Jul 2001)
1306
1307=cut
1308
1309sub webExists {
1310#   my( $web ) = @_;
1311    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1312    return $TWiki::Plugins::SESSION->{store}->webExists( @_ );
1313}
1314
1315=pod
1316
1317---+++ isValidWebName( $name, $templateWeb ) -> $boolean
1318
1319Check for a valid web name.
1320   * =$name= - web name
1321   * =$templateWeb= - flag, optional. If true, then template web names (starting with _)
1322     are considered valid, otherwise only user web names are valid.
1323Return: true if web name is valid
1324
1325If $TWiki::cfg{EnableHierarchicalWebs} is off, it will also return false when a nested
1326web name is passed to it.
1327
1328*Since:* TWiki::Plugins::VERSION 1.4
1329
1330=cut
1331
1332sub isValidWebName {
1333    return TWiki::isValidWebName( @_ );
1334}
1335
1336=pod
1337
1338#CreateWeb
1339---+++ createWeb( $newWeb, $baseWeb, $opts )
1340
1341   * =$newWeb= is the name of the new web.
1342   * =$baseWeb= is the name of an existing web (a template web). If the base web is a system web, all topics in it will be copied into the new web. If it is a normal web, only topics starting with 'Web' will be copied. If no base web is specified, an empty web (with no topics) will be created. If it is specified but does not exist, an error will be thrown.
1343   * =$opts= is a ref to a hash that contains settings to be modified in
1344the web preferences topic in the new web.
1345
1346<verbatim>
1347use Error qw( :try );
1348use TWiki::AccessControlException;
1349
1350try {
1351    TWiki::Func::createWeb( "Newweb" );
1352} catch Error::Simple with {
1353    my $e = shift;
1354    # see documentation on Error::Simple
1355} catch TWiki::AccessControlException with {
1356    my $e = shift;
1357    # see documentation on TWiki::AccessControlException
1358} otherwise {
1359    ...
1360};
1361</verbatim>
1362
1363*Since:* TWiki::Plugins::VERSION 1.1
1364
1365=cut
1366
1367sub createWeb {
1368    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1369    $TWiki::Plugins::SESSION->{store}->createWeb(
1370        $TWiki::Plugins::SESSION->{user}, @_ );
1371}
1372
1373=pod
1374
1375#MoveWeb
1376---+++ moveWeb( $oldName, $newName )
1377
1378Move (rename) a web.
1379
1380<verbatim>
1381use Error qw( :try );
1382use TWiki::AccessControlException;
1383
1384try {
1385    TWiki::Func::moveWeb( "Oldweb", "Newweb" );
1386} catch Error::Simple with {
1387    my $e = shift;
1388    # see documentation on Error::Simple
1389} catch TWiki::AccessControlException with {
1390    my $e = shift;
1391    # see documentation on TWiki::AccessControlException
1392} otherwise {
1393    ...
1394};
1395</verbatim>
1396
1397To delete a web, move it to a subweb of =Trash=
1398<verbatim>
1399TWiki::Func::moveWeb( "Deadweb", "Trash.Deadweb" );
1400</verbatim>
1401
1402*Since:* TWiki::Plugins::VERSION 1.1
1403
1404=cut
1405
1406sub moveWeb {
1407    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1408    return $TWiki::Plugins::SESSION->{store}->moveWeb(
1409        @_, $TWiki::Plugins::SESSION->{user});
1410
1411}
1412
1413=pod
1414
1415#EachChangeSince
1416---+++ eachChangeSince($web, $time) -> $iterator
1417
1418Get an iterator over the list of all the changes in the given web between
1419=$time= and now. $time is a time in seconds since 1st Jan 1970, and is not
1420guaranteed to return any changes that occurred before (now -
1421{Store}{RememberChangesFor}). {Store}{RememberChangesFor}) is a
1422setting in =configure=. Changes are returned in *most-recent-first*
1423order.
1424
1425Use it as follows:
1426<verbatim>
1427    my $iterator = TWiki::Func::eachChangeSince(
1428        $web, time() - 7 * 24 * 60 * 60); # the last 7 days
1429    while ($iterator->hasNext()) {
1430        my $change = $iterator->next();
1431        # $change is a perl hash that contains the following fields:
1432        # topic => topic name
1433        # user => wikiname - wikiname of user who made the change
1434        # time => time of the change
1435        # revision => revision number *after* the change
1436        # more => more info about the change (e.g. 'minor')
1437    }
1438</verbatim>
1439
1440*Since:* TWiki::Plugins::VERSION 1.2
1441
1442=cut
1443
1444sub eachChangeSince {
1445    my( $web, $time ) = @_;
1446    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1447    ASSERT($TWiki::Plugins::SESSION->{store}->webExists($web)) if DEBUG;
1448
1449    my $iterator =
1450      $TWiki::Plugins::SESSION->{store}->eachChange( $web, $time );
1451    return $iterator;
1452}
1453
1454=pod
1455
1456#GetTopicList
1457---+++ getTopicList( $web ) -> @topics
1458
1459Get list of all topics in a web
1460   * =$web= - Web name, required, e.g. ='Sandbox'=
1461Return: =@topics= Topic list, e.g. =( 'WebChanges',  'WebHome', 'WebIndex', 'WebNotify' )=
1462
1463*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
1464
1465=cut
1466
1467sub getTopicList {
1468#   my( $web ) = @_;
1469    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1470    return $TWiki::Plugins::SESSION->{store}->getTopicNames ( @_ );
1471}
1472
1473=pod
1474
1475#TopicExists
1476---+++ topicExists( $web, $topic ) -> $boolean
1477
1478Test if topic exists.
1479   * =$web=   - Web name, optional, e.g. ='Main'=.
1480   * =$topic= - Topic name, required, e.g. ='TokyoOffice'=, or ="Main.TokyoOffice"=
1481
1482$web and $topic are parsed as described in the documentation for =normalizeWebTopicName=.
1483Specifically, the %USERSWEB% is used if $web is not specified and $topic has no web specifier.
1484To get an expected behaviour it is recommened to specify the current web for $web; don't leave it empty.
1485
1486*Since:* TWiki::Plugins::VERSION 1.000 (14 Jul 2001)
1487
1488=cut
1489
1490sub topicExists {
1491    my( $web, $topic ) = $TWiki::Plugins::SESSION->normalizeWebTopicName( @_ );
1492    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1493    return $TWiki::Plugins::SESSION->{store}->topicExists( $web, $topic );
1494}
1495
1496=pod
1497
1498---+++ isValidTopicName( $name ) -> $boolean
1499
1500Check for a valid topic name. Names considerd valid for autolinking are
1501%SYSTEMWEB%.WikiWords (such as 'SanFrancisco') and acronym (such as 'SWMBO').
1502   * =$name= - topic name
1503Return: true if topic name is valid
1504
1505*Since:* TWiki::Plugins::VERSION 1.4
1506
1507=cut
1508
1509sub isValidTopicName {
1510    return TWiki::isValidTopicName( @_ );
1511}
1512
1513=pod
1514
1515#CheckTopicEditLock
1516---+++ checkTopicEditLock( $web, $topic, $script ) -> ( $oopsUrl, $loginName, $unlockTime )
1517
1518Check if a lease has been taken by some other user.
1519   * =$web= Web name, e.g. ="Main"=, or empty
1520   * =$topic= Topic name, e.g. ="MyTopic"=, or ="Main.MyTopic"=
1521Return: =( $oopsUrl, $loginName, $unlockTime )= - The =$oopsUrl= for calling redirectCgiQuery(), user's =$loginName=, and estimated =$unlockTime= in minutes, or ( '', '', 0 ) if no lease exists.
1522   * =$script= The script to invoke when continuing with the edit
1523
1524*Since:* TWiki::Plugins::VERSION 1.010 (31 Dec 2002)
1525
1526=cut
1527
1528sub checkTopicEditLock {
1529    my( $web, $topic, $script ) = @_;
1530    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1531
1532    ( $web, $topic ) = normalizeWebTopicName( $web, $topic );
1533    $script ||= 'edit';
1534
1535    my $lease = $TWiki::Plugins::SESSION->{store}->getLease( $web, $topic );
1536    if( $lease ) {
1537        my $remain = $lease->{expires} - time();
1538        my $session = $TWiki::Plugins::SESSION;
1539
1540        if( $remain > 0 ) {
1541            my $who = $lease->{user};
1542            require TWiki::Time;
1543            my $past = TWiki::Time::formatDelta(
1544                time()-$lease->{taken},
1545                $TWiki::Plugins::SESSION->i18n
1546               );
1547            my $future = TWiki::Time::formatDelta(
1548                $lease->{expires}-time(),
1549                $TWiki::Plugins::SESSION->i18n
1550               );
1551            my $url = getScriptUrl(
1552                $web, $topic, 'oops',
1553                template => 'oopsleaseconflict',
1554                def => 'lease_active',
1555                param1 => $who,
1556                param2 => $past,
1557                param3 => $future,
1558                param4 => $script );
1559            my $login = $session->{users}->getLoginName($who);
1560            return( $url, $login, $remain / 60 );
1561        }
1562    }
1563    return ('', '', 0);
1564}
1565
1566=pod
1567
1568#SetTopicEditLock
1569---+++ setTopicEditLock( $web, $topic, $lock )
1570
1571   * =$web= Web name, e.g. ="Main"=, or empty
1572   * =$topic= Topic name, e.g. ="MyTopic"=, or ="Main.MyTopic"=
1573   * =$lock= 1 to lease the topic, 0 to clear an existing lease
1574
1575Takes out a "lease" on the topic. The lease doesn't prevent
1576anyone from editing and changing the topic, but it does redirect them
1577to a warning screen, so this provides some protection. The =edit= script
1578always takes out a lease.
1579
1580It is *impossible* to fully lock a topic. Concurrent changes will be
1581merged.
1582
1583*Since:* TWiki::Plugins::VERSION 1.010 (31 Dec 2002)
1584
1585=cut
1586
1587sub setTopicEditLock {
1588    my( $web, $topic, $lock ) = @_;
1589    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1590    my $session = $TWiki::Plugins::SESSION;
1591    my $store = $session->{store};
1592    if( $lock ) {
1593        $store->setLease( $web, $topic, $session->{user},
1594                          $TWiki::cfg{LeaseLength} );
1595    } else {
1596        $store->clearLease( $web, $topic );
1597    }
1598    return '';
1599}
1600
1601=pod
1602
1603#SaveTopic
1604---+++ saveTopic( $web, $topic, $meta, $text, $options ) -> $error
1605
1606   * =$web= - web for the topic
1607   * =$topic= - topic name
1608   * =$meta= - reference to TWiki::Meta object
1609   * =$text= - text of the topic (without embedded meta-data!!!
1610   * =\%options= - ref to hash of save options
1611     =\%options= may include:
1612     | =dontlog= | don't log this change in twiki log |
1613     | =forcenewrevision= | force the save to increment the revision counter |
1614     | =minor= | True if this is a minor change, and is not to be notified |
1615Return: error message or undef.
1616
1617*Since:* TWiki::Plugins::VERSION 1.000 (29 Jul 2001)
1618
1619Example:
1620<verbatim>
1621    # read topic:
1622    my( $topicMeta, $topicText ) = TWiki::Func::readTopic( $web, $topic );
1623    # example to change topic text:
1624    $topicText =~ s/APPLE/ORANGE/g;
1625    # example to change TWiki form field:
1626    my $field = $topicMeta->get( 'FIELD', 'Title' );
1627    if( $field ) {
1628        $field->{value} = $newTitle;
1629        $topicMeta->putKeyed( 'FIELD', $field );
1630    }
1631    # save updated topic:
1632    TWiki::Func::saveTopic( $web, $topic, $topicMeta, $topicText, { forcenewrevision => 1 } );
1633</verbatim>
1634
1635__Note:__ Plugins handlers ( e.g. =beforeSaveHandler= ) will be called as
1636appropriate.
1637
1638=cut
1639
1640sub saveTopic {
1641    my( $web, $topic, $meta, $text, $options ) = @_;
1642    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1643
1644    return $TWiki::Plugins::SESSION->{store}->saveTopic
1645      ( $TWiki::Plugins::SESSION->{user}, $web, $topic, $text, $meta,
1646        $options );
1647
1648}
1649
1650=pod
1651
1652#SaveTopicText
1653---+++ saveTopicText( $web, $topic, $text, $ignorePermissions, $dontNotify ) -> $oopsUrl
1654
1655Save topic text, typically obtained by readTopicText(). Topic data usually includes meta data; the file attachment meta data is replaced by the meta data from the topic file if it exists.
1656   * =$web=                - Web name, e.g. ='Main'=, or empty
1657   * =$topic=              - Topic name, e.g. ='MyTopic'=, or ="Main.MyTopic"=
1658   * =$text=               - Topic text to save, assumed to include meta data
1659   * =$ignorePermissions=  - Set to ="1"= if checkAccessPermission() is already performed and OK
1660   * =$dontNotify=         - Set to ="1"= if not to notify users of the change
1661Return: =$oopsUrl=               Empty string if OK; the =$oopsUrl= for calling redirectCgiQuery() in case of error
1662
1663This method is a lot less efficient and much more dangerous than =saveTopic=.
1664
1665*Since:* TWiki::Plugins::VERSION 1.010 (31 Dec 2002)
1666
1667<verbatim>
1668my $text = TWiki::Func::readTopicText( $web, $topic );
1669
1670# check for oops URL in case of error:
1671if( $text =~ /^http.*?\/oops/ ) {
1672    TWiki::Func::redirectCgiQuery( $query, $text );
1673    return;
1674}
1675# do topic text manipulation like:
1676$text =~ s/old/new/g;
1677# do meta data manipulation like:
1678$text =~ s/(META\:FIELD.*?name\=\"TopicClassification\".*?value\=\")[^\"]*/$1BugResolved/;
1679$oopsUrl = TWiki::Func::saveTopicText( $web, $topic, $text ); # save topic text
1680</verbatim>
1681
1682=cut
1683
1684sub saveTopicText {
1685    my( $web, $topic, $text, $ignorePermissions, $dontNotify ) = @_;
1686    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1687
1688    my $session = $TWiki::Plugins::SESSION;
1689    TWiki::UI::checkWebWritable($session, $web);
1690
1691    # check access permission
1692    unless( $ignorePermissions ||
1693            $session->security->checkAccessPermission(
1694                'CHANGE', $session->{user}, undef, undef,
1695                $topic, $web )
1696          ) {
1697        my @plugin = caller();
1698        return getScriptUrl(
1699            $web, $topic, 'oops',
1700            template => 'oopsaccessdenied',
1701            def => 'topic_access',
1702            param1 => 'in',
1703            param2 => $plugin[0] );
1704    }
1705
1706    return getScriptUrl(
1707        $web, $topic, 'oops', template => 'oopsattention', def => 'save_error',
1708        param1 => 'No text' )
1709      unless( defined $text );
1710
1711    # extract meta data and merge old attachment meta data
1712    require TWiki::Meta;
1713    my $meta = new TWiki::Meta( $session, $web, $topic, $text );
1714
1715    $meta->remove( 'FILEATTACHMENT' );
1716
1717    my( $oldMeta, $oldText ) =
1718      $session->{store}->readTopic( undef, $web, $topic, undef );
1719    $meta->copyFrom( $oldMeta, 'FILEATTACHMENT' );
1720    # save topic
1721    my $error =
1722      $session->{store}->saveTopic
1723        ( $session->{user}, $web, $topic, $meta->text(), $meta,
1724          { notify => $dontNotify, ignorePermissions => $ignorePermissions } );
1725    return getScriptUrl(
1726        $web, $topic, 'oops', template => 'oopsattention', def => 'save_error',
1727          param1 => $error ) if( $error );
1728    return '';
1729}
1730
1731=pod
1732
1733#MoveTopic
1734---+++ moveTopic( $web, $topic, $newWeb, $newTopic )
1735
1736   * =$web= source web - required
1737   * =$topic= source topic - required
1738   * =$newWeb= dest web
1739   * =$newTopic= dest topic
1740Renames the topic. Throws an exception if something went wrong.
1741If $newWeb is undef, it defaults to $web. If $newTopic is undef, it defaults
1742to $topic.
1743
1744The destination topic must not already exist.
1745
1746Rename a topic to the $TWiki::cfg{TrashWebName} to delete it.
1747
1748*Since:* TWiki::Plugins::VERSION 1.1
1749
1750<verbatim>
1751use Error qw( :try );
1752
1753try {
1754    moveTopic( "Work", "TokyoOffice", "Trash", "ClosedOffice" );
1755} catch Error::Simple with {
1756    my $e = shift;
1757    # see documentation on Error::Simple
1758} catch TWiki::AccessControlException with {
1759    my $e = shift;
1760    # see documentation on TWiki::AccessControlException
1761} otherwise {
1762    ...
1763};
1764</verbatim>
1765
1766=cut
1767
1768sub moveTopic {
1769    my( $web, $topic, $newWeb, $newTopic ) = @_;
1770    $newWeb ||= $web;
1771    $newTopic ||= $topic;
1772
1773    return if( $newWeb eq $web && $newTopic eq $topic );
1774
1775    $TWiki::Plugins::SESSION->{store}->moveTopic(
1776        $web, $topic,
1777        $newWeb, $newTopic,
1778        $TWiki::Plugins::SESSION->{user} );
1779}
1780
1781=pod
1782
1783#GetRevisionInfo
1784---+++ getRevisionInfo($web, $topic, $rev, $attachment ) -> ( $date, $user, $rev, $comment )
1785
1786Get revision info of a topic or attachment
1787   * =$web= - Web name, optional, e.g. ='Main'=
1788   * =$topic=   - Topic name, required, e.g. ='TokyoOffice'=
1789   * =$rev=     - revsion number, or tag name (can be in the format 1.2, or just the minor number)
1790   * =$attachment=                 -attachment filename
1791Return: =( $date, $user, $rev, $comment )= List with: ( last update date, login name of last user, minor part of top revision number ), e.g. =( 1234561, 'phoeny', "5" )=
1792| $date | in epochSec |
1793| $user | Wiki name of the author (*not* login name) |
1794| $rev | actual rev number |
1795| $comment | WHAT COMMENT? |
1796
1797NOTE: if you are trying to get revision info for a topic, use
1798=$meta->getRevisionInfo= instead if you can - it is significantly
1799more efficient.
1800
1801*Since:* TWiki::Plugins::VERSION 1.000 (29 Jul 2001)
1802
1803=cut
1804
1805sub getRevisionInfo {
1806    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1807    my( $date, $user, $rev, $comment );
1808    if ( $_[3] ) {
1809        # attachment specified
1810        ( $date, $user, $rev, $comment ) =
1811            $TWiki::Plugins::SESSION->{store}->getRevisionInfo( @_ );
1812        $user = getWikiName( $user );
1813            # Item7238: you must not use TWiki::Users::getWikiName() here
1814            # because it assumes a cUID and causes problems if a wikiname is
1815            # handed.
1816    }
1817    else {
1818        my $revInfo =
1819            $TWiki::Plugins::SESSION->renderer->renderRevisionInfo(
1820                $_[0], $_[1], undef, $_[2], '$epoch|$wikiname|$rev|$comment' );
1821        ( $date, $user, $rev, $comment ) = split(/\|/, $revInfo, 4);
1822    }
1823    return ( $date, $user, $rev, $comment );
1824}
1825
1826=pod
1827
1828#GetRevisionAtTime
1829---+++ getRevisionAtTime( $web, $topic, $time ) -> $rev
1830
1831Get the revision number of a topic at a specific time.
1832   * =$web= - web for topic
1833   * =$topic= - topic
1834   * =$time= - time (in epoch secs) for the rev
1835Return: Single-digit revision number, or undef if it couldn't be determined
1836(either because the topic isn't that old, or there was a problem)
1837
1838*Since:* TWiki::Plugins::VERSION 1.1
1839
1840=cut
1841
1842sub getRevisionAtTime {
1843    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1844    return $TWiki::Plugins::SESSION->{store}->getRevisionAtTime( @_ );
1845}
1846
1847=pod
1848
1849#ReadTopic
1850---+++ readTopic( $web, $topic, $rev ) -> ( $meta, $text )
1851
1852Read topic text and meta data, regardless of access permissions.
1853   * =$web= - Web name, required, e.g. ='Main'=
1854   * =$topic= - Topic name, required, e.g. ='TokyoOffice'=
1855   * =$rev= - revision to read (default latest)
1856Return: =( $meta, $text )= Meta data object and topic text
1857
1858=$meta= is a perl 'object' of class =TWiki::Meta=. This class is
1859fully documented in the source code documentation shipped with the
1860release, or can be inspected in the =lib/TWiki/Meta.pm= file.
1861
1862This method *ignores* topic access permissions. You should be careful to use
1863=checkAccessPermission= to ensure the current user has read access to the
1864topic.
1865
1866See usage example at [[#SaveTopic][TWiki::Func::saveTopic]].
1867
1868*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
1869
1870=cut
1871
1872sub readTopic {
1873    #my( $web, $topic, $rev ) = @_;
1874    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1875
1876    return $TWiki::Plugins::SESSION->{store}->readTopic( undef, @_ );
1877}
1878
1879=pod
1880
1881#ReadTopicText
1882---+++ readTopicText( $web, $topic, $rev, $ignorePermissions ) -> $text
1883
1884Read topic text, including meta data
1885   * =$web=                - Web name, e.g. ='Main'=, or empty
1886   * =$topic=              - Topic name, e.g. ='MyTopic'=, or ="Main.MyTopic"=
1887   * =$rev=                - Topic revision to read, optional. Specify the minor part of the revision, e.g. ="5"=, not ="1.5"=; the top revision is returned if omitted or empty.
1888   * =$ignorePermissions=  - Set to ="1"= if checkAccessPermission() is already performed and OK; an oops URL is returned if user has no permission
1889Return: =$text=                  Topic text with embedded meta data; an oops URL for calling redirectCgiQuery() is returned in case of an error
1890
1891This method is more efficient than =readTopic=, but returns meta-data embedded in the text. Plugins authors must be very careful to avoid damaging meta-data. You are recommended to use readTopic instead, which is a lot safer.
1892
1893*Since:* TWiki::Plugins::VERSION 1.010 (31 Dec 2002)
1894
1895=cut
1896
1897sub readTopicText {
1898    my( $web, $topic, $rev, $ignorePermissions ) = @_;
1899    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1900
1901    my $user;
1902    $user = $TWiki::Plugins::SESSION->{user}
1903      unless defined( $ignorePermissions );
1904
1905    my $text;
1906    try {
1907        $text =
1908          $TWiki::Plugins::SESSION->{store}->readTopicRaw
1909            ( $user, $web, $topic, $rev );
1910    } catch TWiki::AccessControlException with {
1911        my $e = shift;
1912        $text = getScriptUrl(
1913            $web, $topic, 'oops',
1914            template => 'oopsaccessdenied', def=>'topic_access',
1915            param1 => $e->{mode},
1916            param2 => $e->{reason} );
1917    };
1918
1919    return $text;
1920}
1921
1922=pod
1923
1924#AttachmentExists
1925---+++ attachmentExists( $web, $topic, $attachment ) -> $boolean
1926
1927Test if attachment exists
1928   * =$web=   - Web name, optional, e.g. =Main=.
1929   * =$topic= - Topic name, required, e.g. =TokyoOffice=, or =Main.TokyoOffice=
1930   * =$attachment= - attachment name, e.g. =logo.gif=
1931$web and $topic are parsed as described in the documentation for =normalizeWebTopicName=.
1932
1933*Since:* TWiki::Plugins::VERSION 1.1
1934
1935=cut
1936
1937sub attachmentExists {
1938    my( $web, $topic, $attachment ) = @_;
1939    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1940
1941    ( $web, $topic ) =
1942      $TWiki::Plugins::SESSION->normalizeWebTopicName( $web, $topic );
1943    return $TWiki::Plugins::SESSION->{store}->attachmentExists(
1944        $web, $topic, $attachment );
1945}
1946
1947=pod
1948
1949#ReadAttachment
1950---+++ readAttachment( $web, $topic, $name, $rev ) -> $data
1951
1952   * =$web= - web for topic
1953   * =$topic= - topic
1954   * =$name= - attachment name
1955   * =$rev= - revision to read (default latest)
1956Read an attachment from the store for a topic, and return it as a string. The
1957names of attachments on a topic can be recovered from the meta-data returned
1958by =readTopic=. If the attachment does not exist, or cannot be read, undef
1959will be returned. If the revision is not specified, the latest version will
1960be returned.
1961
1962View permission on the topic is required for the
1963read to be successful.  Access control violations are flagged by a
1964TWiki::AccessControlException. Permissions are checked for the current user.
1965
1966<verbatim>
1967my( $meta, $text ) = TWiki::Func::readTopic( $web, $topic );
1968my @attachments = $meta->find( 'FILEATTACHMENT' );
1969foreach my $a ( @attachments ) {
1970   try {
1971       my $data = TWiki::Func::readAttachment( $web, $topic, $a->{name} );
1972       ...
1973   } catch TWiki::AccessControlException with {
1974   };
1975}
1976</verbatim>
1977
1978*Since:* TWiki::Plugins::VERSION 1.1
1979
1980=cut
1981
1982sub readAttachment {
1983    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
1984    my $result;
1985
1986#    try {
1987        $result = $TWiki::Plugins::SESSION->{store}->readAttachment(
1988            $TWiki::Plugins::SESSION->{user}, @_ );
1989#    } catch Error::Simple with {
1990#    };
1991    return $result;
1992}
1993
1994=pod
1995
1996#SaveAttachment
1997---+++ saveAttachment( $web, $topic, $attachment, $opts )
1998
1999   * =$web= - web for topic
2000   * =$topic= - topic to atach to
2001   * =$attachment= - name of the attachment
2002   * =$opts= - Ref to hash of options
2003   * =$ignorePermissions=  - Set to ="1"= if checkAccessPermission() is already performed and OK.
2004=$opts= may include:
2005| =dontlog= | don't log this change in twiki log |
2006| =comment= | comment for save |
2007| =hide= | if the attachment is to be hidden in normal topic view |
2008| =stream= | Stream of file to upload |
2009| =file= | Name of a file to use for the attachment data. ignored if stream is set. Local file on the server. |
2010| =filepath= | Client path to file |
2011| =filesize= | Size of uploaded data |
2012| =filedate= | Date |
2013
2014Save an attachment to the store for a topic. On success, returns undef. If there is an error, an exception will be thrown.
2015
2016<verbatim>
2017    try {
2018        TWiki::Func::saveAttachment( $web, $topic, 'image.gif',
2019                                     { file => 'image.gif',
2020                                       comment => 'Picture of Health',
2021                                       hide => 1 } );
2022   } catch Error::Simple with {
2023      # see documentation on Error
2024   } otherwise {
2025      ...
2026   };
2027</verbatim>
2028
2029*Since:* TWiki::Plugins::VERSION 1.1
2030
2031=cut
2032
2033sub saveAttachment {
2034    my( $web, $topic, $name, $data, $ignorePermissions ) = @_;
2035    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2036    my $result = undef;
2037    $data->{ignorePermissions} = $ignorePermissions || '';
2038
2039    try {
2040        $TWiki::Plugins::SESSION->{store}->saveAttachment(
2041            $web, $topic, $name,
2042            $TWiki::Plugins::SESSION->{user},
2043            $data );
2044    } catch Error::Simple with {
2045        $result = shift->{-text};
2046    };
2047
2048    return $result;
2049}
2050
2051=pod
2052
2053#MoveAttachment
2054---+++ moveAttachment( $web, $topic, $attachment, $newWeb, $newTopic, $newAttachment )
2055
2056   * =$web= source web - required
2057   * =$topic= source topic - required
2058   * =$attachment= source attachment - required
2059   * =$newWeb= dest web
2060   * =$newTopic= dest topic
2061   * =$newAttachment= dest attachment
2062Renames the topic. Throws an exception on error or access violation.
2063If $newWeb is undef, it defaults to $web. If $newTopic is undef, it defaults
2064to $topic. If $newAttachment is undef, it defaults to $attachment. If all of $newWeb, $newTopic and $newAttachment are undef, it is an error.
2065
2066The destination topic must already exist, but the destination attachment must
2067*not* exist.
2068
2069Rename an attachment to $TWiki::cfg{TrashWebName}.TrashAttament to delete it.
2070
2071<verbatim>
2072use Error qw( :try );
2073
2074try {
2075   # move attachment between topics
2076   moveAttachment( "Countries", "Germany", "AlsaceLorraine.dat",
2077                     "Countries", "France" );
2078   # Note destination attachment name is defaulted to the same as source
2079} catch TWiki::AccessControlException with {
2080   my $e = shift;
2081   # see documentation on TWiki::AccessControlException
2082} catch Error::Simple with {
2083   my $e = shift;
2084   # see documentation on Error::Simple
2085};
2086</verbatim>
2087
2088*Since:* TWiki::Plugins::VERSION 1.1
2089
2090=cut
2091
2092sub moveAttachment {
2093    my( $web, $topic, $attachment, $newWeb, $newTopic, $newAttachment ) = @_;
2094
2095    $newWeb ||= $web;
2096    $newTopic ||= $topic;
2097    $newAttachment ||= $attachment;
2098
2099    return if( $newWeb eq $web &&
2100                 $newTopic eq $topic &&
2101                   $newAttachment eq $attachment );
2102
2103    $TWiki::Plugins::SESSION->{store}->moveAttachment(
2104        $web, $topic, $attachment,
2105        $newWeb, $newTopic, $newAttachment,
2106        $TWiki::Plugins::SESSION->{user} );
2107}
2108
2109=pod
2110
2111#AssemblingPagesFunctions
2112---++ Assembling Pages
2113
2114=cut
2115
2116=pod
2117
2118#ReadTemplate
2119---+++ readTemplate( $name, $skin ) -> $text
2120
2121Read a template or skin. Embedded [[%SYSTEMWEB%.TWikiTemplates][template directives]] get expanded
2122   * =$name= - Template name, e.g. ='view'=
2123   * =$skin= - Comma-separated list of skin names, optional, e.g. ='print'=
2124Return: =$text=    Template text
2125
2126*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
2127
2128=cut
2129
2130sub readTemplate {
2131#   my( $name, $skin ) = @_;
2132    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2133    return $TWiki::Plugins::SESSION->templates->readTemplate( @_ );
2134}
2135
2136=pod
2137
2138#LoadTemplate
2139---+++ loadTemplate ( $name, $skin, $web ) -> $text
2140
2141   * =$name= - template file name
2142   * =$skin= - comma-separated list of skins to use (default: current skin)
2143   * =$web= - the web to look in for topics that contain templates (default: current web)
2144Return: expanded template text (what's left after removal of all %<nop>TMPL:DEF% statements)
2145
2146*Since:* TWiki::Plugins::VERSION 1.1
2147
2148Reads a template and extracts template definitions, adding them to the
2149list of loaded templates, overwriting any previous definition.
2150
2151How TWiki searches for templates is described in TWikiTemplates.
2152
2153If template text is found, extracts include statements and fully expands them.
2154
2155=cut
2156
2157sub loadTemplate {
2158    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2159    return $TWiki::Plugins::SESSION->templates->readTemplate( @_ );
2160}
2161
2162=pod
2163
2164#ExpandTemplate
2165---+++ expandTemplate( $def  ) -> $string
2166
2167Do a %<nop>TMPL:P{$def}%, only expanding the template (not expanding any variables other than %TMPL)
2168   * =$def= - template name
2169Return: the text of the expanded template
2170
2171*Since:* TWiki::Plugins::VERSION 1.1
2172
2173A template is defined using a %<nop>TMPL:DEF% statement in a template
2174file. See the documentation on TWiki templates for more information.
2175
2176=cut
2177
2178sub expandTemplate {
2179    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2180    return $TWiki::Plugins::SESSION->templates->expandTemplate( @_ );
2181}
2182
2183=pod
2184
2185#WriteHeader
2186---+++ writeHeader( )
2187
2188Print a basic content-type HTML header for text/html to standard out. No return value.
2189
2190Note: In TWiki versions earlier than TWiki::Plugins::VERSION 1.3, this function used to have =$query= and =$contentLength= parameters. Both were marked "you should _not_ pass this parameter".
2191
2192*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
2193
2194=cut
2195
2196sub writeHeader {
2197    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2198    $TWiki::Plugins::SESSION->generateHTTPHeaders();
2199}
2200
2201=pod
2202
2203#RedirectCgiQuery
2204---+++ redirectCgiQuery( $query, $url, $passthru, $viaCache )
2205
2206Redirect to URL
2207   * =$query= - CGI query object. Ignored, only there for compatibility. The session CGI query object is used instead.
2208   * =$url=   - URL to redirect to
2209   * =$passthru= - enable passthrough.
2210   * =$viaCache= - forcibly cache a redirect CGI query. It cuts off all the params in a GET url and replace with a "?$cache=..." param. "$viaCache" is meaningful only if "$passthru" is true.
2211
2212Return:             none
2213
2214Print output to STDOUT that will cause a 302 redirect to a new URL.
2215Nothing more should be printed to STDOUT after this method has been called.
2216
2217The =$passthru= parameter allows you to pass the parameters that were passed
2218to the current query on to the target URL, as long as it is another URL on the
2219same TWiki installation. If =$passthru= is set to a true value, then TWiki
2220will save the current URL parameters, and then try to restore them on the
2221other side of the redirect. Parameters are stored on the server in a cache
2222file.
2223
2224Note that if =$passthru= is set, then any parameters in =$url= will be lost
2225when the old parameters are restored. if you want to change any parameter
2226values, you will need to do that in the current CGI query before redirecting
2227e.g.
2228<verbatim>
2229my $query = TWiki::Func::getCgiQuery();
2230$query->param(-name => 'text', -value => 'Different text');
2231TWiki::Func::redirectCgiQuery(
2232  undef, TWiki::Func::getScriptUrl($web, $topic, 'edit'), 1);
2233</verbatim>
2234=$passthru= does nothing if =$url= does not point to a script in the current
2235TWiki installation.
2236
2237*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
2238
2239=cut
2240
2241sub redirectCgiQuery {
2242    my( $query, $url, $passthru, $viaCache ) = @_;
2243    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2244    return $TWiki::Plugins::SESSION->redirect( $url, $passthru, 0, $viaCache );
2245}
2246
2247=pod
2248
2249#AddToHEAD
2250---+++ addToHEAD( $id, $header, $requires )
2251
2252Adds =$header= to the HTML header (the <head> tag).
2253This is useful for Plugins that want to include some javascript and custom css.
2254   * =$id= - Unique ID to prevent the same HTML from being duplicated. Plugins should use a prefix to prevent name clashes (e.g EDITTABLEPLUGIN_JSCALENDAR)
2255   * =$header= - the HTML to be added to the <head> section. The HTML must be valid in a HEAD tag - no checks are performed.
2256   * =requires= optional, comma-separated list of id's of other head blocks this one depends on. Those blocks will be loaded first.
2257
2258All TWiki variables present in =$header= will be expanded before being inserted into the =<head>= section.
2259
2260Note that this is _not_ the same as the HTTP header, which is modified through the Plugins =modifyHeaderHandler=.
2261
2262*Since:* TWiki::Plugins::VERSION 1.1
2263
2264example:
2265<verbatim>
2266TWiki::Func::addToHEAD('PATTERN_STYLE','<link id="twikiLayoutCss" rel="stylesheet" type="text/css" href="%PUBURL%/TWiki/PatternSkin/layout.css" media="all" />');
2267</verbatim>
2268
2269=cut=
2270
2271sub addToHEAD {
2272    my( $tag, $header, $requires ) = @_;
2273    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2274    $TWiki::Plugins::SESSION->addToHEAD( @_ );
2275}
2276
2277=pod
2278
2279#ExpandCommonVariables
2280---+++ expandCommonVariables( $text, $topic, $web, $meta ) -> $text
2281
2282Expand all common =%<nop>VARIABLES%=
2283   * =$text=  - Text with variables to expand, e.g. ='Current user is %<nop>WIKIUSER%'=
2284   * =$topic= - Current topic name, e.g. ='WebNotify'=
2285   * =$web=   - Web name, optional, e.g. ='Main'=. The current web is taken if missing
2286   * =$meta=  - topic meta-data to use while expanding (Since TWiki::Plugins::VERSION 1.2)
2287Return: =$text=     Expanded text, e.g. ='Current user is <nop>TWikiGuest'=
2288
2289*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
2290
2291See also: expandVariablesOnTopicCreation
2292
2293=cut
2294
2295sub expandCommonVariables {
2296    my( $text, $topic, $web, $meta ) = @_;
2297    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2298    $topic ||= $TWiki::Plugins::SESSION->{topicName};
2299    $web ||= $TWiki::Plugins::SESSION->{webName};
2300    return $TWiki::Plugins::SESSION->handleCommonTags(
2301        $text, $web, $topic, $meta );
2302}
2303
2304=pod
2305
2306#RenderText
2307---+++ renderText( $text, $web ) -> $text
2308
2309Render text from TWiki markup into XHTML as defined in [[%SYSTEMWEB%.TextFormattingRules]]
2310   * =$text= - Text to render, e.g. ='*bold* text and =fixed font='=
2311   * =$web=  - Web name, optional, e.g. ='Main'=. The current web is taken if missing
2312Return: =$text=    XHTML text, e.g. ='&lt;b>bold&lt;/b> and &lt;code>fixed font&lt;/code>'=
2313
2314*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
2315
2316=cut
2317
2318sub renderText {
2319#   my( $text, $web ) = @_;
2320    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2321    return $TWiki::Plugins::SESSION->renderer->getRenderedVersion( @_ );
2322}
2323
2324=pod
2325
2326#InternalLink
2327---+++ internalLink( $pre, $web, $topic, $label, $anchor, $createLink ) -> $text
2328
2329Render topic name and link label into an XHTML link. Normally you do not need to call this funtion, it is called internally by =renderText()=
2330   * =$pre=        - Text occuring before the TWiki link syntax, optional
2331   * =$web=        - Web name, required, e.g. ='Main'=
2332   * =$topic=      - Topic name to link to, required, e.g. ='WebNotify'=
2333   * =$label=      - Link label, required. Usually the same as =$topic=, e.g. ='notify'=
2334   * =$anchor=     - Anchor, optional, e.g. ='#Jump'=
2335   * =$createLink= - Set to ='1'= to add question linked mark after topic name if topic does not exist;<br /> set to ='0'= to suppress link for non-existing topics
2336Return: =$text=          XHTML anchor, e.g. ='&lt;a href='/cgi-bin/view/Main/WebNotify#Jump'>notify&lt;/a>'=
2337
2338*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
2339
2340=cut
2341
2342sub internalLink {
2343    my $pre = shift;
2344    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2345#   my( $web, $topic, $label, $anchor, $anchor, $createLink ) = @_;
2346    return $pre . $TWiki::Plugins::SESSION->renderer->internalLink( @_ );
2347}
2348
2349=pod
2350
2351#EmailFunctions
2352---++ E-mail
2353
2354#SendEmail
2355---+++ sendEmail ( $text, $retries ) -> $error
2356
2357   * =$text= - text of the mail, including MIME headers
2358   * =$retries= - number of times to retry the send (default 1)
2359Send an e-mail specified as MIME format content. To specify MIME
2360format mails, you create a string that contains a set of header
2361lines that contain field definitions and a message body such as:
2362<verbatim>
2363To: liz@windsor.gov.uk
2364From: serf@hovel.net
2365CC: george@whitehouse.gov
2366Subject: Revolution
2367
2368Dear Liz,
2369
2370Please abolish the monarchy (with King George's permission, of course)
2371
2372Thanks,
2373
2374A. Peasant
2375</verbatim>
2376Leave a blank line between the last header field and the message body.
2377
2378*Since:* TWiki::Plugins::VERSION 1.1
2379
2380=cut
2381
2382sub sendEmail {
2383    #my( $text, $retries ) = @_;
2384    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2385    return $TWiki::Plugins::SESSION->net->sendEmail( @_ );
2386}
2387
2388=pod
2389
2390#WikiToEmail
2391---+++ wikiToEmail( $wikiName ) -> $email
2392
2393   * =$wikiname= - wiki name of the user
2394Get the e-mail address(es) of the named user. If the user has multiple
2395e-mail addresses (for example, the user is a group), then the list will
2396be comma-separated.
2397
2398*Since:* TWiki::Plugins::VERSION 1.1
2399
2400*Deprecated* in favour of wikinameToEmails, because this function only
2401returns a single email address, where a user may in fact have several.
2402
2403Since TWiki 4.2.1, $wikiName may also be a login name.
2404
2405=cut
2406
2407sub wikiToEmail {
2408    my( $user ) = @_;
2409    my @emails = wikinameToEmails( $user );
2410    if (scalar(@emails)) {
2411        return $emails[0];
2412    }
2413    return '';
2414}
2415
2416=pod
2417
2418#CreatingNewTopicsFunctions
2419---++ Creating New Topics
2420
2421=cut
2422
2423=pod
2424
2425#ExpandVariablesOnTopicCreation
2426---+++ expandVariablesOnTopicCreation ( $text, $web, $topic ) -> $text
2427
2428Expand the limited set of variables that are always expanded during topic creation
2429   * =$text= - the text to process
2430   * =$web= - name of web, optional
2431   * =$topic= - name of topic, optional
2432Return: text with variables expanded
2433
2434*Since:* TWiki::Plugins::VERSION 1.1
2435
2436Expands only the variables expected in templates that must be statically
2437expanded in new content.
2438
2439The expanded variables are:
2440   * =%<nop>DATE%= Signature-format date
2441   * =%<nop>SERVERTIME%= See TWikiVariables
2442   * =%<nop>GMTIME%= See TWikiVariables
2443   * =%<nop>USERNAME%= Base login name
2444   * =%<nop>WIKINAME%= Wiki name
2445   * =%<nop>WIKIUSERNAME%= Wiki name with prepended web
2446   * =%<nop>URLPARAM{...}%= - Parameters to the current CGI query
2447   * =%<nop>NOP%= No-op
2448
2449See also: expandCommonVariables
2450
2451=cut
2452
2453sub expandVariablesOnTopicCreation {
2454    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2455    return $TWiki::Plugins::SESSION->expandVariablesOnTopicCreation( shift, $TWiki::Plugins::SESSION->{user}, @_ );
2456}
2457
2458=pod
2459
2460#SpecialHandlersFunctions
2461---++ Special Handlers
2462
2463Special handlers can be defined to make functions in plugins behave as if they were built-in to TWiki.
2464
2465=cut
2466
2467=pod=
2468
2469#RegisterTagHandler
2470---+++ registerTagHandler( $var, \&fn, $syntax )
2471
2472Should only be called from initPlugin.
2473
2474Register a function to handle a simple variable. Handles both %<nop>VAR% and %<nop>VAR{...}%. Registered variables are treated the same as TWiki internal variables, and are expanded at the same time. This is a _lot_ more efficient than using the =commonTagsHandler=.
2475   * =$var= - The name of the variable, i.e. the 'MYVAR' part of %<nop>MYVAR%. The variable name *must* match /^[A-Z][A-Z0-9_]*$/ or it won't work.
2476   * =\&fn= - Reference to the handler function.
2477   * =$syntax= can be 'classic' (the default) or 'context-free'. 'classic' syntax is appropriate where you want the variable to support classic TWiki syntax i.e. to accept the standard =%<nop>MYVAR{ "unnamed" param1="value1" param2="value2" }%= syntax, as well as an unquoted default parameter, such as =%<nop>MYVAR{unquoted parameter}%=. If your variable will only use named parameters, you can use 'context-free' syntax, which supports a more relaxed syntax. For example, %MYVAR{param1=value1, value 2, param3="value 3", param4='value 5"}%
2478
2479*Since:* TWiki::Plugins::VERSION 1.1
2480
2481The variable handler function must be of the form:
2482<verbatim>
2483sub handler(\%session, \%params, $topic, $web)
2484</verbatim>
2485where:
2486   * =\%session= - a reference to the TWiki session object (may be ignored)
2487   * =\%params= - a reference to a TWiki::Attrs object containing parameters. This can be used as a simple hash that maps parameter names to values, with _DEFAULT being the name for the default parameter.
2488   * =$topic= - name of the topic in the query
2489   * =$web= - name of the web in the query
2490   * =$meta= - topic meta-data to use while expanding, can be undef (Since TWiki::Plugins::VERSION 1.4)
2491   * =$textRef= - reference to unexpanded topic text, can be undef (Since TWiki::Plugins::VERSION 1.4)
2492for example, to execute an arbitrary command on the server, you might do this:
2493<verbatim>
2494sub initPlugin{
2495   TWiki::Func::registerTagHandler('EXEC', \&boo);
2496}
2497
2498sub boo {
2499    my( $session, $params, $topic, $web ) = @_;
2500    my $cmd = $params->{_DEFAULT};
2501
2502    return "NO COMMAND SPECIFIED" unless $cmd;
2503
2504    my $result = `$cmd 2>&1`;
2505    return $params->{silent} ? '' : $result;
2506}
2507</verbatim>
2508would let you do this:
2509=%<nop>EXEC{"ps -Af" silent="on"}%=
2510
2511Registered tags differ from tags implemented using the old TWiki approach (text substitution in =commonTagsHandler=) in the following ways:
2512   * registered tags are evaluated at the same time as system tags, such as %SERVERTIME. =commonTagsHandler= is only called later, when all system tags have already been expanded (though they are expanded _again_ after =commonTagsHandler= returns).
2513   * registered tag names can only contain alphanumerics and _ (underscore)
2514   * registering a tag =FRED= defines both =%<nop>FRED{...}%= *and also* =%FRED%=.
2515   * registered tag handlers *cannot* return another tag as their only result (e.g. =return '%<nop>SERVERTIME%';=). It won't work.
2516
2517=cut
2518
2519sub registerTagHandler {
2520    my( $tag, $function, $syntax ) = @_;
2521    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2522    # Use an anonymous function so it gets inlined at compile time.
2523    # Make sure we don't mangle the session reference.
2524    TWiki::registerTagHandler( $tag,
2525                               sub {
2526                                   my $record = $TWiki::Plugins::SESSION;
2527                                   $TWiki::Plugins::SESSION = $_[0];
2528                                   my $result = &$function( @_ );
2529                                   $TWiki::Plugins::SESSION = $record;
2530                                   return $result;
2531                               },
2532                               $syntax
2533                             );
2534}
2535
2536=pod=
2537
2538#RegisterRESTHandler
2539---+++ registerRESTHandler( $alias, \&fn, )
2540
2541Should only be called from initPlugin.
2542
2543Adds a function to the dispatch table of the REST interface
2544   * =$alias= - The name .
2545   * =\&fn= - Reference to the function.
2546
2547*Since:* TWiki::Plugins::VERSION 1.1
2548
2549The handler function must be of the form:
2550<verbatim>
2551sub handler(\%session)
2552</verbatim>
2553where:
2554   * =\%session= - a reference to the TWiki session object (may be ignored)
2555
2556From the REST interface, the name of the plugin must be used
2557as the subject of the invokation.
2558
2559Example
2560-------
2561
2562The EmptyPlugin has the following call in the initPlugin handler:
2563<verbatim>
2564   TWiki::Func::registerRESTHandler('example', \&restExample);
2565</verbatim>
2566
2567This adds the =restExample= function to the REST dispatch table
2568for the EmptyPlugin under the 'example' alias, and allows it
2569to be invoked using the URL
2570
2571=http://server:port/bin/rest/EmptyPlugin/example=
2572
2573note that the URL
2574
2575=http://server:port/bin/rest/EmptyPlugin/restExample=
2576
2577(ie, with the name of the function instead of the alias) will not work.
2578
2579=cut
2580
2581sub registerRESTHandler {
2582    my( $alias, $function) = @_;
2583    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2584    my $plugin = caller;
2585    $plugin =~ s/.*:://; # strip off TWiki::Plugins:: prefix
2586
2587    # Use an anonymous function so it gets inlined at compile time.
2588    # Make sure we don't mangle the session reference.
2589    TWiki::registerRESTHandler( $plugin,
2590                                $alias,
2591                               sub {
2592                                   my $record = $TWiki::Plugins::SESSION;
2593                                   $TWiki::Plugins::SESSION = $_[0];
2594                                   my $result = &$function( @_ );
2595                                   $TWiki::Plugins::SESSION = $record;
2596                                   return $result;
2597                               }
2598                             );
2599}
2600
2601=pod
2602
2603#RegisterExternalHTTPHandler
2604---+++ registerExternalHTTPHandler( \&fn )
2605
2606Should only be called from initPlugin.
2607
2608Adds a function to modify all the HTTP requests to any external resources.
2609   * =\&fn= - Reference to the function.
2610
2611The handler function must be of the form:
2612<verbatim>
2613sub handler(\%session, $url) -> (\@headers, \%params)
2614</verbatim>
2615where:
2616   * =\%session= - a reference to the TWiki session object (may be ignored)
2617   * =$url= - a URL being requested
2618
2619The returned =\@headers= and =\%params= are added to the request in the same
2620manner as =getExternalResource=, except that =\%params= will not override any
2621entries that have been set earlier.
2622All the params explicitly given by the caller of =getExternalResource= or
2623=postExternalResource= will have the highest precedence.
2624
2625Example:
2626<verbatim>
2627sub initPlugin {
2628    TWiki::Func::registerExternalHTTPHandler( \&handleExternalHTTPRequest );
2629}
2630
2631sub handleExternalHTTPRequest {
2632    my ($session, $url) = @_;
2633    my @headers;
2634    my %params;
2635
2636    # Add any necessary @headers and %params
2637    push @headers, 'X-Example-Header' => 'Value';
2638    $params{timeout} = 5;
2639
2640    # Return refs to both
2641    return (\@headers, \%params);
2642}
2643</verbatim>
2644
2645*Since:* TWiki::Plugins::VERSION 6.00
2646
2647=cut
2648
2649sub registerExternalHTTPHandler {
2650    my($function) = @_;
2651    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2652
2653    $TWiki::Plugins::SESSION->net->registerExternalHTTPHandler(
2654        sub {
2655            my $record = $TWiki::Plugins::SESSION;
2656            $TWiki::Plugins::SESSION = $_[0];
2657            my @result = &$function( @_ );
2658            $TWiki::Plugins::SESSION = $record;
2659            return @result;
2660        },
2661    );
2662}
2663
2664=pod
2665
2666#DecodeFormatTokens
2667---+++ decodeFormatTokens($str) -> $unencodedString
2668
2669TWiki has an informal standard set of tokens used in =format=
2670parameters that are used to block evaluation of paramater strings.
2671For example, if you were to write
2672
2673=%<nop>MYTAG{format="%<nop>WURBLE%"}%=
2674
2675then %<nop>WURBLE would be expanded *before* %<NOP>MYTAG is evaluated. To avoid
2676this TWiki uses escapes in the format string. For example:
2677
2678=%<nop>MYTAG{format="$percntWURBLE$percnt"}%=
2679
2680This lets you enter arbitrary strings into parameters without worrying that
2681TWiki will expand them before your plugin gets a chance to deal with them
2682properly. Once you have processed your tag, you will want to expand these
2683tokens to their proper value. That's what this function does.
2684
2685| *Escape:* | *Expands To:* |
2686| =$n= or =$n()= | New line. Use =$n()= if followed by alphanumeric character, e.g. write =Foo$n()Bar= instead of =Foo$nBar= |
2687| =$nop= or =$nop()= | Is a "no operation". |
2688| =$quot= | Double quote (="=) |
2689| =$aquot= | Apostrophe quote (='=) |
2690| =$percnt= | Percent sign (=%=) |
2691| =$dollar= | Dollar sign (=$=) |
2692| =$lt= | Less than sign (=<=) |
2693| =$gt= | Greater than sign (=>=) |
2694
2695Note that $quot, $aquot, $percnt and $dollar all work *even if they are followed by
2696alphanumeric characters*. You have been warned!
2697
2698*Since:* TWiki::Plugins::VERSION 1.2
2699
2700=cut
2701
2702sub decodeFormatTokens {
2703    return TWiki::expandStandardEscapes( @_ );
2704}
2705
2706=pod
2707
2708#SearchingFunctions
2709---++ Searching
2710
2711=cut
2712
2713=pod
2714
2715#SearchInWebContent
2716---+++ searchInWebContent($searchString, $web, \@topics, \%options ) -> \%map
2717
2718Search for a string in the content of a web. The search is over all content, including meta-data. Meta-data matches will be returned as formatted lines within the topic content (meta-data matches are returned as lines of the format %META:\w+{.*}%)
2719   * =$searchString= - the search string, in egrep format
2720   * =$web= - The web to search in
2721   * =\@topics= - reference to a list of topics to search
2722   * =\%option= - reference to an options hash
2723The =\%options= hash may contain the following options:
2724   * =type= - if =regex= will perform a egrep-syntax RE search (default '')
2725   * =casesensitive= - false to ignore case (default true)
2726   * =files_without_match= - true to return files only (default false). If =files_without_match= is specified, it will return on the first match in each topic (i.e. it will return only one match per topic, and will not return matching lines).
2727
2728The return value is a reference to a hash which maps each matching topic
2729name to a list of the lines in that topic that matched the search,
2730as would be returned by 'grep'.
2731
2732To iterate over the returned topics use:
2733<verbatim>
2734my $result = TWiki::Func::searchInWebContent( "Slimy Toad", $web, \@topics,
2735   { casesensitive => 0, files_without_match => 0 } );
2736foreach my $topic (keys %$result ) {
2737   foreach my $matching_line ( @{$result->{$topic}} ) {
2738      ...etc
2739</verbatim>
2740
2741*Since:* TWiki::Plugins::VERSION 1.1
2742
2743=cut
2744
2745sub searchInWebContent {
2746    #my( $searchString, $web, $topics, $options ) = @_;
2747
2748    return $TWiki::Plugins::SESSION->{store}->searchInWebContent( @_ );
2749}
2750
2751=pod
2752
2753#PluginSpecificFileHandlingFunctions
2754---++ Plugin-specific File Handling
2755
2756=cut
2757
2758=pod
2759
2760#GetWorkArea
2761---+++ getWorkArea( $pluginName ) -> $directorypath
2762
2763Gets a private directory for Plugin use. The Plugin is entirely responsible
2764for managing this directory; TWiki will not read from it, or write to it.
2765
2766The directory is guaranteed to exist, and to be writable by the webserver
2767user. By default it will *not* be web accessible.
2768
2769The directory and it's contents are permanent, so Plugins must be careful
2770to keep their areas tidy.
2771
2772*Since:* TWiki::Plugins::VERSION 1.1 (Dec 2005)
2773
2774=cut
2775
2776sub getWorkArea {
2777    my( $plugin ) = @_;
2778    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2779    return $TWiki::Plugins::SESSION->{store}->getWorkArea( $plugin );
2780}
2781
2782=pod
2783
2784#ReadFile
2785---+++ readFile( $filename ) -> $text
2786
2787Read file, low level. Used for Plugin workarea.
2788   * =$filename= - Full path name of file
2789Return: =$text= Content of file, empty if not found
2790
2791__NOTE:__ Use this function only for the Plugin workarea, *not* for topics and attachments. Use the appropriate functions to manipulate topics and attachments.
2792
2793*Since:* TWiki::Plugins::VERSION 1.000 (07 Dec 2002)
2794
2795=cut
2796
2797sub readFile {
2798    my $name = shift;
2799    my $data = '';
2800    open( IN_FILE, "<$name" ) || return '';
2801    local $/ = undef; # set to read to EOF
2802    $data = <IN_FILE>;
2803    close( IN_FILE );
2804    $data = '' unless $data; # no undefined
2805    return $data;
2806}
2807
2808=pod
2809
2810#SaveFile
2811---+++ saveFile( $filename, $text )
2812
2813Save file, low level. Used for Plugin workarea.
2814   * =$filename= - Full path name of file
2815   * =$text=     - Text to save
2816Return:                none
2817
2818__NOTE:__ Use this function only for the Plugin workarea, *not* for topics and attachments. Use the appropriate functions to manipulate topics and attachments.
2819
2820*Since:* TWiki::Plugins::VERSION 1.000 (07 Dec 2002)
2821
2822=cut
2823
2824sub saveFile {
2825    my( $name, $text ) = @_;
2826
2827    unless ( open( FILE, ">$name" ) )  {
2828        die "Can't create file $name - $!\n";
2829    }
2830    print FILE $text;
2831    close( FILE);
2832}
2833
2834=pod
2835
2836#ReadOnlyAndMirrorWebs
2837---++ Read-only and Mirror Webs
2838
2839The following functions are for ReadOnlyAndMirrorWebs.
2840
2841=cut
2842
2843=pod
2844
2845#GetSiteName
2846---+++ getSiteName() -> $siteName
2847
2848Returns the current site name if it's defined. Otherwise returns the null
2849string.
2850=cut
2851
2852sub getSiteName {
2853    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2854
2855    return $TWiki::cfg{ReadOnlyAndMirrorWebs}{SiteName} || '';
2856}
2857
2858
2859
2860=pod
2861
2862#GetContentMode
2863---+++ getContentMode( $web ) -> $contentMode
2864
2865Returns the content mode of the specified $web.
2866Please read ReadOnlyAndMirrorWebs about content mode.
2867
2868*Since:* TWiki::Plugins::VERSION 6.00
2869=cut
2870
2871sub getContentMode {
2872#    my $web = shift;
2873    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2874
2875    $TWiki::Plugins::SESSION->getContentMode($_[0]);
2876}
2877
2878=pod
2879
2880#WebWritable
2881---+++ webWritable( $web ) -> $boolean
2882
2883Checks if the web is wriable on this site - if it's master or local.
2884Returns true if it's writable. Returns false otherwise.
2885
2886*Since:* TWiki::Plugins::VERSION 6.00
2887=cut
2888
2889sub webWritable {
2890#    my $web = shift;
2891    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2892
2893    return $TWiki::Plugins::SESSION->webWritable($_[0]);
2894}
2895
2896=pod
2897
2898#UsingMultipleDisks
2899---++ Using multiple disks
2900
2901The following functions are for UsingMultipleDisks.
2902
2903=cut
2904
2905=pod
2906
2907#GetDiskList
2908---+++ getDiskList() -> @diskIDs
2909
2910Returns IDs of disks used by TWiki. An disk ID is "" (a null string) or a decimal number without leading 0.
2911
2912*Since:* TWiki::Plugins::VERSION 6.00
2913=cut
2914
2915sub getDiskList {
2916    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2917
2918    return $TWiki::Plugins::SESSION->getDiskList();
2919}
2920
2921=pod
2922
2923#GetDiskInfo
2924---+++ getDiskInfo($web, [$diskID]) -> ($dataDir, $pubDir, $diskID)
2925
2926Returns the relevant paths and the disk ID of the specified web on the specified site.
2927
2928*Since:* TWiki::Plugins::VERSION 6.00
2929=cut
2930
2931sub getDiskInfo {
2932    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2933
2934    return $TWiki::Plugins::SESSION->getDiskInfo(@_);
2935}
2936
2937=pod
2938
2939#TrashWebName
2940---+++ trashWebName(web => $web | disk => $diskID) -> $trashWebName
2941
2942Returns the name of the trash web to which topics of the =$web= web are moved.
2943Or returns the name of the trash web of the specified disk.
2944
2945Each disk (file system) TWiki uses needs to have a trash web since a topic deletion may entail an attachment directory move, which is possible only within the same disk/file system.
2946
2947*Since:* TWiki::Plugins::VERSION 6.00
2948=cut
2949
2950sub trashWebName {
2951    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
2952
2953    return $TWiki::Plugins::SESSION->trashWebName(@_);
2954}
2955
2956=pod
2957
2958#GeneralUtilitiesFunctions
2959---++ General Utilities
2960
2961=cut
2962
2963=pod
2964
2965#GetRegularExpression
2966---+++ getRegularExpression( $name ) -> $expr
2967
2968Retrieves a TWiki predefined regular expression or character class.
2969   * =$name= - Name of the expression to retrieve.  See notes below
2970Return: String or precompiled regular expression matching as described below.
2971
2972*Since:* TWiki::Plugins::VERSION 1.020 (9 Feb 2004)
2973
2974__Note:__ TWiki internally precompiles several regular expressions to
2975represent various string entities in an <nop>I18N-compatible manner. Plugins
2976authors are encouraged to use these in matching where appropriate. The
2977following are guaranteed to be present. Others may exist, but their use
2978is unsupported and they may be removed in future TWiki versions.
2979
2980In the table below, the expression marked type 'String' are intended for
2981use within character classes (i.e. for use within square brackets inside
2982a regular expression), for example:
2983<verbatim>
2984   my $upper = TWiki::Func::getRegularExpression('upperAlpha');
2985   my $alpha = TWiki::Func::getRegularExpression('mixedAlpha');
2986   my $capitalized = qr/[$upper][$alpha]+/;
2987</verbatim>
2988Those expressions marked type 'RE' are precompiled regular expressions that can be used outside square brackets. For example:
2989<verbatim>
2990   my $webRE = TWiki::Func::getRegularExpression('webNameRegex');
2991   my $isWebName = ( $s =~ m/$webRE/ );
2992</verbatim>
2993
2994| *Name*         | *Matches*                        | *Type* |
2995| upperAlpha     | Upper case characters            | String |
2996| upperAlphaNum  | Upper case characters and digits | String |
2997| lowerAlpha     | Lower case characters            | String |
2998| lowerAlphaNum  | Lower case characters and digits | String |
2999| numeric        | Digits                           | String |
3000| mixedAlpha     | Alphabetic characters            | String |
3001| mixedAlphaNum  | Alphanumeric characters          | String |
3002| wikiWordRegex  | WikiWords                        | RE |
3003| webNameRegex   | User web names                   | RE |
3004| anchorRegex    | #AnchorNames                     | RE |
3005| abbrevRegex    | Abbreviations e.g. GOV, IRS      | RE |
3006| emailAddrRegex | email@address.com                | RE |
3007| tagNameRegex   | Standard variable names e.g. %<nop>THIS_BIT% (THIS_BIT only) | RE |
3008
3009=cut
3010
3011sub getRegularExpression {
3012    my ( $regexName ) = @_;
3013    return $TWiki::regex{$regexName};
3014}
3015
3016=pod
3017
3018#NormalizeWebTopicName
3019---+++ normalizeWebTopicName($web, $topic) -> ($web, $topic)
3020
3021Parse a web and topic name, supplying defaults as appropriate.
3022   * =$web= - Web name, identifying variable, or empty string
3023   * =$topic= - Topic name, may be a web.topic string, required.
3024Return: the parsed Web/Topic pair
3025
3026*Since:* TWiki::Plugins::VERSION 1.1
3027
3028| *Input*                               | *Return*  |
3029| <tt>( 'Web', 'Topic' ) </tt>          | <tt>( 'Web', 'Topic' ) </tt>  |
3030| <tt>( '', 'Topic' ) </tt>             | <tt>( 'Main', 'Topic' ) </tt>  |
3031| <tt>( '', '' ) </tt>                  | <tt>( 'Main', 'WebHome' ) </tt>  |
3032| <tt>( '', 'Web/Topic' ) </tt>         | <tt>( 'Web', 'Topic' ) </tt>  |
3033| <tt>( '', 'Web/Subweb/Topic' ) </tt>  | <tt>( 'Web/Subweb', 'Topic' ) </tt>  |
3034| <tt>( '', 'Web.Topic' ) </tt>         | <tt>( 'Web', 'Topic' ) </tt>  |
3035| <tt>( '', 'Web.Subweb.Topic' ) </tt>  | <tt>( 'Web/Subweb', 'Topic' ) </tt>  |
3036| <tt>( 'Web1', 'Web2.Topic' )</tt>     | <tt>( 'Web2', 'Topic' ) </tt>  |
3037
3038Note that hierarchical web names (!Web.SubWeb) are only available if hierarchical webs are enabled in =configure=.
3039
3040The symbols %<nop>USERSWEB%, %<nop>SYSTEMWEB% and %<nop>DOCWEB% can be used in the input to represent the web names set in $cfg{UsersWebName} and $cfg{SystemWebName}. For example:
3041| *Input*                               | *Return* |
3042| <tt>( '%<nop>USERSWEB%', 'Topic' )</tt>     | <tt>( 'Main', 'Topic' ) </tt>  |
3043| <tt>( '%<nop>SYSTEMWEB%', 'Topic' )</tt>    | <tt>( 'TWiki', 'Topic' ) </tt>  |
3044| <tt>( '', '%<nop>DOCWEB%.Topic' )</tt>    | <tt>( 'TWiki', 'Topic' ) </tt>  |
3045
3046=cut
3047
3048sub normalizeWebTopicName {
3049    #my( $web, $topic ) = @_;
3050    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
3051    return $TWiki::Plugins::SESSION->normalizeWebTopicName( @_ );
3052}
3053
3054=pod
3055
3056#SanitizeAttachmentName
3057---+++ sanitizeAttachmentName($fname) -> ($fileName, $origName)
3058
3059Given a file namer, sanitise it according to the rules for transforming
3060attachment names. Returns
3061the sanitised name together with the basename before sanitisation.
3062
3063Sanitation includes filtering illegal characters and mapping client
3064file names to legal server names.
3065
3066*Since:* TWiki::Plugins::VERSION 1.2
3067
3068=cut
3069
3070sub sanitizeAttachmentName {
3071    require TWiki::Sandbox;
3072    return TWiki::Sandbox::sanitizeAttachmentName(@_);
3073}
3074
3075=pod
3076
3077#BuildWikiWord
3078---+++ buildWikiWord( $text ) -> $text
3079
3080Converts arbitrary text to a WikiWord.
3081   * =$pre=        - Arbitrary Text
3082Return: =$text=
3083
3084*Since:* TWiki::Plugins::VERSION 1.3 (18 Jan 2010)
3085
3086=cut
3087
3088sub buildWikiWord {
3089    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
3090    return $TWiki::Plugins::SESSION->{renderer}->buildWikiWord( @_ );
3091}
3092
3093=pod
3094
3095#SpaceOutWikiWord
3096---+++ spaceOutWikiWord( $word, $sep ) -> $text
3097
3098Spaces out a wiki word by inserting a string between each word component.
3099Word component boundaries are transitions from lowercase to uppercase or numeric,
3100from numeric to uppercase or lowercase, and from uppercase to numeric characters.
3101
3102Parameter $sep defines the separator between the word components, the default is a space.
3103
3104Example: "ABC2015ProjectCharter" results in "ABC 2015 Project Charter"
3105
3106*Since:* TWiki::Plugins::VERSION 1.2
3107
3108=cut
3109
3110sub spaceOutWikiWord {
3111    #my ( $word, $sep ) = @_;
3112    return TWiki::spaceOutWikiWord(@_);
3113}
3114
3115=pod
3116
3117#WriteWarning
3118---+++ writeWarning( $text )
3119
3120Log Warning that may require admin intervention to data/warning.txt
3121   * =$text= - Text to write; timestamp gets added
3122Return:            none
3123
3124*Since:* TWiki::Plugins::VERSION 1.020 (16 Feb 2004)
3125
3126=cut
3127
3128sub writeWarning {
3129#   my( $text ) = @_;
3130    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
3131    my ($message)=@_;
3132    return $TWiki::Plugins::SESSION->writeWarning( "(".caller().") ".$message );
3133}
3134
3135=pod
3136
3137#WriteDebug
3138---+++ writeDebug( $text )
3139
3140Log debug message to data/debug.txt
3141   * =$text= - Text to write; timestamp gets added
3142Return:            none
3143
3144*Since:* TWiki::Plugins::VERSION 1.020 (16 Feb 2004)
3145
3146=cut
3147
3148sub writeDebug {
3149#   my( $text ) = @_;
3150    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
3151    return $TWiki::Plugins::SESSION->writeDebug( @_ );
3152}
3153
3154=pod
3155
3156---+++ writeLog( $action, $extra, $web, $topic, $user )
3157
3158Write the log for an event to the logfile.
3159   * =$action= - name of the event, such as ='blacklist'=.
3160   * =$extra= - arbitrary extra information to add to the event.
3161   * =$web= - web name, optional. Base web is taken if empty. Ignored if web is specified in =$topic=.
3162   * =$topic= - topic name, optional. Can be =Topic= or =Web.Topic=. Base topic is taken if empty.
3163   * =$user= - !WikiName, login name or cUID of user, optional. Name of logged-in user is taken if not specified.
3164Return: none
3165
3166*Since:* TWiki::Plugins::VERSION 1.4
3167
3168__Example:__ Calling =TWiki::Func::writeLog( 'blacklist', 'Magic number is missing' )=
3169will add a log entry like this:
3170<verbatim>
3171| 2011-01-19 - 01:13 | guest | blacklist | TWiki.TWikiRegistration | Magic number is missing | 1.2.3.4 |
3172</verbatim>
3173
3174__Note:__ Older plugins that use =$TWiki::cfg{LogFileName}= or call the internal TWiki
3175function directly should be fixed to use =writeLog=.
3176
3177To maintain compatibility with older TWiki releases, you can write conditional code as follows:
3178<verbatim>
3179  if( defined &TWiki::Func::writeLog ) {
3180    # use writeLog
3181    TWiki::Func::writeLog( 'myevent', $extra, $web, $topic );
3182  } else {
3183    # deprecated code if plugin is used in older TWiki releases
3184    $TWiki::Plugins::VERSION > 1.1
3185      ? $TWiki::Plugins::SESSION->writeLog( 'myevent', "$web.$topic", $extra )
3186      : TWiki::Store::writeLog( 'myevent', "$web.$topic", $extra );
3187  }
3188</verbatim>
3189
3190=cut
3191
3192sub writeLog {
3193    my( $action, $extra, $web, $topic, $user ) = @_;
3194    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
3195
3196    $web   ||= $TWiki::Plugins::SESSION->{SESSION_TAGS}{BASEWEB};
3197    $topic ||= $TWiki::Plugins::SESSION->{SESSION_TAGS}{BASETOPIC};
3198    ( $web, $topic ) = normalizeWebTopicName( $web, $topic );
3199
3200    return $TWiki::Plugins::SESSION->writeLog( $action, "$web.$topic", $extra, $user );
3201}
3202
3203=pod
3204
3205#FormatTime
3206---+++ formatTime( $time, $format, $timezone ) -> $text
3207
3208Format the time in seconds into the desired time string
3209   * =$time=     - Time in epoc seconds
3210   * =$format=   - Format type, optional. Default e.g. ='2014-12-31 - 19:30'=. Can be ='$iso'= (e.g. ='2014-12-31T19:30Z'=), ='$rcs'= (e.g. ='2014/12/31 23:59:59'=, ='$http'= for HTTP header format (e.g. ='The, 2014-07-23 07:21:56 GMT'=), or any string with tokens ='$seconds, $minutes, $hours, $day, $wday, $month, $mo, $year, $ye, $tz'= for seconds, minutes, hours, day of month, day of week, 3 letter month, 2 digit month, 4 digit year, 2 digit year, timezone string, respectively
3211   * =$timezone= - either not defined (uses the displaytime setting), 'gmtime', or 'servertime'
3212Return: =$text=        Formatted time string
3213
3214__Note:__ If you used the removed formatGmTime, add a third parameter 'gmtime'
3215
3216*Since:* TWiki::Plugins::VERSION 1.020 (26 Feb 2004)
3217
3218=cut
3219
3220sub formatTime {
3221#   my ( $epSecs, $format, $timezone ) = @_;
3222    require TWiki::Time;
3223    return TWiki::Time::formatTime( @_ );
3224}
3225
3226=pod
3227
3228#IsTrue
3229---+++ isTrue( $value, $default ) -> $boolean
3230
3231Returns 1 if =$value= is true, and 0 otherwise. "true" means set to
3232something with a Perl true value, with the special cases that "off",
3233"false" and "no" (case insensitive) are forced to false. Leading and
3234trailing spaces in =$value= are ignored.
3235
3236If the value is undef, then =$default= is returned. If =$default= is
3237not specified it is taken as 0.
3238
3239*Since:* TWiki::Plugins::VERSION 1.2
3240
3241=cut
3242
3243sub isTrue {
3244#   my ( $value, $default ) = @_;
3245
3246    return TWiki::isTrue( @_ );
3247}
3248
3249=pod
3250
3251#IsValidWikiWord
3252---+++ isValidWikiWord ( $text ) -> $boolean
3253
3254Check for a valid WikiWord or WikiName
3255   * =$text= - Word to test
3256
3257*Since:* TWiki::Plugins::VERSION 1.100 (Dec 2005)
3258
3259=cut
3260
3261sub isValidWikiWord {
3262   return TWiki::isValidWikiWord(@_);
3263}
3264
3265=pod
3266
3267#ExtractParameters
3268---+++ extractParameters($attr ) -> %params
3269
3270Extract all parameters from a variable string and returns a hash of parameters
3271   * =$attr= - Attribute string
3272Return: =%params=  Hash containing all parameters. The nameless parameter is stored in key =_DEFAULT=
3273
3274*Since:* TWiki::Plugins::VERSION 1.025 (26 Aug 2004)
3275
3276   * Example:
3277      * Variable: =%<nop>TEST{ 'nameless' name1="val1" name2="val2" }%=
3278      * First extract text between ={...}= to get: ='nameless' name1="val1" name2="val2"=
3279      * Then call this on the text: <br />
3280   * params = TWiki::Func::extractParameters( $text );=
3281      * The =%params= hash contains now: <br />
3282        =_DEFAULT => 'nameless'= <br />
3283        =name1 => "val1"= <br />
3284        =name2 => "val2"=
3285
3286=cut
3287
3288sub extractParameters {
3289    my( $attr ) = @_;
3290    require TWiki::Attrs;
3291    my $params = new TWiki::Attrs( $attr );
3292    # take out _RAW and _ERROR (compatibility)
3293    delete $params->{_RAW};
3294    delete $params->{_ERROR};
3295    return %$params;
3296}
3297
3298=pod
3299
3300#ExtractNameValuePair
3301---+++ extractNameValuePair( $attr, $name ) -> $value
3302
3303Extract a named or unnamed value from a variable parameter string.
3304   * =$attr= - Attribute string
3305   * =$name= - Name, optional
3306Return: =$value=   Extracted value
3307
3308*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
3309
3310   * Example:
3311      * Variable: =%<nop>TEST{ 'nameless' name1="val1" name2="val2" }%=
3312      * First extract text between ={...}= to get: ='nameless' name1="val1" name2="val2"=
3313      * Then call this on the text: <br />
3314        =my $noname = TWiki::Func::extractNameValuePair( $text );= <br />
3315        =my $val1  = TWiki::Func::extractNameValuePair( $text, "name1" );= <br />
3316        =my $val2  = TWiki::Func::extractNameValuePair( $text, "name2" );=
3317
3318__Note:__ Function TWiki::Func::extractParameters is more efficient for extracting several parameters
3319
3320=cut
3321
3322sub extractNameValuePair {
3323    require TWiki::Attrs;
3324    return TWiki::Attrs::extractValue( @_ );
3325}
3326
3327=pod
3328
3329#EntityEncode
3330---+++ entityEncode( $text, $extra ) -> $text
3331
3332Entity encode text.
3333   * =$text= - Text to encode, may be empty
3334   * =$extra= - Additional characters to include in the set of encoded
3335                characters, optional
3336Return: =$text=   Entity encoded text
3337
3338*Since:* TWiki::Plugins::VERSION 6.00
3339
3340Escape special characters to HTML numeric entities. This is *not* a generic
3341encoding, it is tuned specifically for use in TWiki.
3342
3343HTML4.0 spec:
3344"Certain characters in HTML are reserved for use as markup and must be
3345escaped to appear literally. The "&lt;" character may be represented with
3346an <em>entity</em>, <strong class=html>&amp;lt;</strong>. Similarly, "&gt;"
3347is escaped as <strong class=html>&amp;gt;</strong>, and "&amp;" is escaped
3348as <strong class=html>&amp;amp;</strong>. If an attribute value contains a
3349double quotation mark and is delimited by double quotation marks, then the
3350quote should be escaped as <strong class=html>&amp;quot;</strong>.
3351
3352Other entities exist for special characters that cannot easily be entered
3353with some keyboards..."
3354
3355This method encodes HTML special and any non-printable ASCII
3356characters (except for \n and \r) using numeric entities.
3357
3358FURTHER this method also encodes characters that are special in TWiki
3359meta-language.
3360
3361$extras is an optional param that may be used to include *additional*
3362characters in the set of encoded characters. It should be a string
3363containing the additional chars.
3364
3365=cut
3366
3367sub entityEncode {
3368#   my ( $text, $type ) = @_;
3369    return TWiki::entityEncode( @_ );
3370}
3371
3372=pod
3373
3374#EntityDecode
3375---+++ entityDecode( $text ) -> $text
3376
3377Decode all numeric entities (e.g. &amp;#123;). _Does not_ decode
3378named entities such as &amp;amp; (use HTML::Entities for that)
3379   * =$text= - Text to decode, may be empty
3380Return: =$text=   Entity decoded text
3381
3382*Since:* TWiki::Plugins::VERSION 6.00
3383
3384=cut
3385
3386sub entityDecode {
3387    return TWiki::entityDecode( @_ );
3388}
3389
3390=pod
3391
3392#UrlEncode
3393---+++ urlEncode( $text ) -> $text
3394
3395URL encode text, mainly used to encode URL parameters.
3396   * =$text= - Text to encode, may be empty
3397Return: =$text=   URL encoded text
3398
3399*Since:* TWiki::Plugins::VERSION 6.00
3400
3401Encoding is done by converting characters that are illegal in
3402URLs to their %NN equivalents. This method is used for encoding
3403strings that must be embedded _verbatim_ in URLs; it cannot
3404be applied to URLs themselves, as it escapes reserved
3405characters such as = and ?.
3406
3407RFC 1738, Dec. '94:
3408<verbatim>
3409    ...Only alphanumerics [0-9a-zA-Z], the special
3410    characters $-_.+!*'(), and reserved characters used for their
3411    reserved purposes may be used unencoded within a URL.
3412</verbatim>
3413
3414Reserved characters are $&+,/:;=?@ - these are _also_ encoded by
3415this method.
3416
3417This URL-encoding handles all character encodings including ISO-8859-*,
3418KOI8-R, EUC-* and UTF-8.
3419
3420This may not handle EBCDIC properly, as it generates an EBCDIC URL-encoded
3421URL, but mainframe web servers seem to translate this outbound before it hits browser
3422- see CGI::Util::escape for another approach.
3423
3424=cut
3425
3426sub urlEncode {
3427    return TWiki::urlEncode( @_ );
3428}
3429
3430=pod
3431
3432#UrlDecode
3433---+++ urlDecode( $text ) -> $text
3434
3435URL decode text, mainly used to decode URL parameters.
3436   * =$text= - Text to decode, may be empty
3437Return: =$text=   URL decoded text
3438
3439*Since:* TWiki::Plugins::VERSION 6.00
3440
3441=cut
3442
3443sub urlDecode {
3444    return TWiki::urlDecode( @_ );
3445}
3446
3447=pod
3448
3449#GetDebugFilePath
3450---+++ getDebugFilePath() -> $path
3451
3452You may want to have an external program write to the debug file.
3453This function returns the path to it.  An external program may corrupt
3454the debug file, but that's no big deal -- it's a debug file.
3455
3456The return value is =$TWiki::cfg{DebugFileName}= with =%<nop>DATE%=
3457and other "variables" resolved.  =$TWiki::cfg{DebugFileName}= is an
3458absolute path in most cases. If it's not, then the current directory
3459is prepended in the return value.
3460
3461*Since:* TWiki::Plugins::VERSION 6.10
3462
3463=cut
3464
3465use Cwd;
3466
3467sub getDebugFilePath {
3468    my $path = TWiki::_fileNameToPath( $TWiki::cfg{DebugFileName} );
3469    unless ( $path =~ m:^/: ) {
3470        $path = abs_path($path);
3471    }
3472    return $path;
3473}
3474
3475=pod
3476
3477#DeprecatedFunctions
3478---++ Deprecated functions
3479
3480From time-to-time, the TWiki developers will add new functions to the interface (either to TWikiFuncDotPm, or new handlers). Sometimes these improvements mean that old functions have to be deprecated to keep the code manageable. When this happens, the deprecated functions will be supported in the interface for at least one more TWiki release, and probably longer, though this cannot be guaranteed.
3481
3482Updated plugins may still need to define deprecated handlers for compatibility with old TWiki versions. In this case, the plugin package that defines old handlers can suppress the warnings in %<nop>FAILEDPLUGINS%.
3483
3484This is done by defining a map from the handler name to the =TWiki::Plugins= version _in which the handler was first deprecated_. For example, if we need to define the =endRenderingHandler= for compatibility with =TWiki::Plugins= versions before 1.1, we would add this to the plugin:
3485<verbatim>
3486package TWiki::Plugins::SinkPlugin;
3487use vars qw( %TWikiCompatibility );
3488$TWikiCompatibility{endRenderingHandler} = 1.1;
3489</verbatim>
3490If the currently-running TWiki version is 1.1 _or later_, then the _handler will not be called_ and _the warning will not be issued_. TWiki with versions of =TWiki::Plugins= before 1.1 will still call the handler as required.
3491
3492The following functions are retained for compatibility only. You should
3493stop using them as soon as possible.
3494
3495#GetScriptUrlPath
3496---+++ getScriptUrlPath( ) -> $path
3497
3498Get script URL path
3499
3500*DEPRECATED* since 1.1 - use =getScriptUrl= instead.
3501
3502Return: =$path= URL path of TWiki scripts, e.g. ="/cgi-bin"=
3503
3504*WARNING:* you are strongly recommended *not* to use this function, as the
3505{ScriptUrlPaths} URL rewriting rules will not apply to urls generated
3506using it.
3507
3508*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
3509
3510=cut
3511
3512sub getScriptUrlPath {
3513    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
3514    return $TWiki::Plugins::SESSION->getScriptUrl( 0, '' );
3515}
3516
3517=pod
3518
3519#GetOopsUrl
3520---+++ getOopsUrl( $web, $topic, $template, $param1, $param2, $param3, $param4 ) -> $url
3521
3522Compose fully qualified 'oops' dialog URL
3523   * =$web=                  - Web name, e.g. ='Main'=. The current web is taken if empty
3524   * =$topic=                - Topic name, e.g. ='WebNotify'=
3525   * =$template=             - Oops template name, e.g. ='oopsmistake'=. The 'oops' is optional; 'mistake' will translate to 'oopsmistake'.
3526   * =$param1= ... =$param4= - Parameter values for %<nop>PARAM1% ... %<nop>PARAMn% variables in template, optional
3527Return: =$url=                     URL, e.g. ="http://example.com:80/cgi-bin/oops.pl/ Main/WebNotify?template=oopslocked&amp;param1=joe"=
3528
3529*DEPRECATED* since 1.1, the recommended approach is to throw an [[TWikiOopsExceptionDotPm][oops exception]].
3530<verbatim>
3531   use Error qw( :try );
3532
3533   throw TWiki::OopsException(
3534      'toestuckerror',
3535      web => $web,
3536      topic => $topic,
3537      params => [ 'I got my toe stuck' ]);
3538</verbatim>
3539(this example will use the =oopstoestuckerror= template.)
3540
3541If this is not possible (e.g. in a REST handler that does not trap the exception)
3542then you can use =getScriptUrl= instead:
3543<verbatim>
3544   my $url = TWiki::Func::getScriptUrl($web, $topic, 'oops',
3545            template => 'oopstoestuckerror',
3546            param1 => 'I got my toe stuck');
3547   TWiki::Func::redirectCgiQuery( undef, $url );
3548   return 0;
3549</verbatim>
3550
3551*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
3552
3553=cut
3554
3555sub getOopsUrl {
3556    my( $web, $topic, $template, @params ) = @_;
3557
3558    my $n = 1;
3559    @params = map { 'param'.($n++) => $_ } @params;
3560    return getScriptUrl( $web, $topic, 'oops',
3561                         template => $template,
3562                         @params );
3563}
3564
3565=pod
3566
3567#PermissionsSet
3568---+++ permissionsSet( $web ) -> $boolean
3569
3570Test if any access restrictions are set for this web, ignoring settings on
3571individual pages
3572   * =$web= - Web name, required, e.g. ='Sandbox'=
3573
3574*Since:* TWiki::Plugins::VERSION 1.000 (27 Feb 2001)
3575
3576*DEPRECATED* since 1.2 - use =getPreferencesValue= instead to determine
3577what permissions are set on the web, for example:
3578<verbatim>
3579foreach my $type ( 'ALLOW', 'DENY' ) {
3580    foreach my $action ( 'CHANGE', 'VIEW' ) {
3581        my $pref = $type . 'WEB' . $action;
3582        my $val = getPreferencesValue( $pref, $web ) || '';
3583        if( $val =~ /\S/ ) {
3584            print "$pref is set to $val on $web\n";
3585        }
3586    }
3587}
3588</verbatim>
3589
3590=cut
3591
3592sub permissionsSet {
3593    my( $web ) = @_;
3594
3595    foreach my $type ( 'ALLOW', 'DENY' ) {
3596        foreach my $action ( 'CHANGE', 'VIEW', 'RENAME' ) {
3597            my $pref = $type . 'WEB' . $action;
3598            my $val = getPreferencesValue( $pref, $web ) || '';
3599            return 1 if( $val =~ /\S/ );
3600        }
3601    }
3602
3603    return 0;
3604}
3605
3606=pod
3607
3608#GetPublicWebList
3609---+++ getPublicWebList( ) -> @webs
3610
3611*DEPRECATED* since 1.1 - use =getListOfWebs= instead.
3612
3613Get list of all public webs, e.g. all webs that do not have the =NOSEARCHALL= flag set in the WebPreferences
3614
3615Return: =@webs= List of all public webs, e.g. =( 'Main',  'Know', 'TWiki' )=
3616
3617*Since:* TWiki::Plugins::VERSION 1.000 (07 Dec 2002)
3618
3619=cut
3620
3621sub getPublicWebList {
3622    ASSERT($TWiki::Plugins::SESSION) if DEBUG;
3623    return $TWiki::Plugins::SESSION->{store}->getListOfWebs("user,public");
3624}
3625
3626=pod
3627
3628#FormatGmTime
3629---+++ formatGmTime( $time, $format ) -> $text
3630
3631*DEPRECATED* since 1.1 - use =formatTime= instead.
3632
3633Format the time to GM time
3634   * =$time=   - Time in epoc seconds
3635   * =$format= - Format type, optional. Default e.g. ='31 Dec 2002 - 19:30'=, can be ='iso'= (e.g. ='2002-12-31T19:30Z'=), ='rcs'= (e.g. ='2001/12/31 23:59:59'=, ='http'= for HTTP header format (e.g. ='Thu, 23 Jul 1998 07:21:56 GMT'=)
3636Return: =$text=      Formatted time string
3637
3638*Since:* TWiki::Plugins::VERSION 1.000 (7 Dec 2002)
3639
3640=cut
3641
3642sub formatGmTime {
3643#   my ( $epSecs, $format ) = @_;
3644
3645    # FIXME: Write warning based on flag (disabled for now); indicate who is calling this function
3646    ## writeWarning( 'deprecated use of Func::formatGmTime' );
3647
3648    require TWiki::Time;
3649    return TWiki::Time::formatTime( @_, 'gmtime' );
3650}
3651
3652=pod
3653
3654#GetDataDir
3655---+++ getDataDir( ) -> $dir
3656
3657*DEPRECATED* since 1.1 - use the "Webs, Topics and Attachments" functions to manipulate topics instead
3658
3659Get data directory (topic file root)
3660
3661Return: =$dir= Data directory, e.g. ='/twiki/data'=
3662
3663This function violates store encapsulation and is therefore *deprecated*.
3664
3665*Since:* TWiki::Plugins::VERSION 1.000 (07 Dec 2002)
3666
3667=cut
3668
3669sub getDataDir {
3670    return $TWiki::Plugins::SESSION->getDataDir($_[0]);
3671}
3672
3673=pod
3674
3675#GetPubDir
3676---+++ getPubDir( ) -> $dir
3677
3678*DEPRECATED* since 1.1 - use the "Webs, Topics and Attachments" functions to manipulateattachments instead
3679
3680Get pub directory (file attachment root). Attachments are in =$dir/Web/TopicName=
3681
3682Return: =$dir= Pub directory, e.g. ='/htdocs/twiki/pub'=
3683
3684This function violates store encapsulation and is therefore *deprecated*.
3685
3686Use =readAttachment= and =saveAttachment= instead.
3687
3688*Since:* TWiki::Plugins::VERSION 1.000 (07 Dec 2002)
3689
3690=cut
3691
3692sub getPubDir {
3693    return $TWiki::Plugins::SESSION->getPubDir($_[0]);
3694}
3695
3696=pod
3697
3698#CheckDependencies
3699---+++ checkDependencies( $moduleName, $dependenciesRef ) -> $error
3700
3701*DEPRECATED* since 1.1 - use TWiki:Plugins.BuildContrib and define DEPENDENCIES that can be statically
3702evaluated at install time instead. It is a lot more efficient.
3703
3704*Since:* TWiki::Plugins::VERSION 1.025 (01 Aug 2004)
3705
3706=cut
3707
3708sub checkDependencies {
3709    my ( $context, $deps ) = @_;
3710    my $report = '';
3711    my $depsOK = 1;
3712    foreach my $dep ( @$deps ) {
3713        my ( $ok, $ver ) = ( 1, 0 );
3714        my $msg = '';
3715        my $const = '';
3716
3717        eval "require $dep->{package}";
3718        if ( $@ ) {
3719            $msg .= "it could not be found: $@";
3720            $ok = 0;
3721        } else {
3722            if ( defined( $dep->{constraint} ) ) {
3723                $const = $dep->{constraint};
3724                eval "\$ver = \$$dep->{package}::VERSION;";
3725                if ( $@ ) {
3726                    $msg .= "the VERSION of the package could not be found: $@";
3727                    $ok = 0;
3728                } else {
3729                    eval "\$ok = ( \$ver $const )";
3730                    if ( $@ || ! $ok ) {
3731                        $msg .= " $ver is currently installed: $@";
3732                        $ok = 0;
3733                    }
3734                }
3735            }
3736        }
3737        unless ( $ok ) {
3738            $report .= "WARNING: $dep->{package}$const is required for $context, but $msg\n";
3739            $depsOK = 0;
3740        }
3741    }
3742    return undef if( $depsOK );
3743
3744    return $report;
3745}
3746
3747=pod
3748
3749#TWikiAPIHistory
3750---++ TWiki API History
3751
3752---+++ TWiki-2001-09-01 (Athens Release)
3753$TWiki::Plugins::VERSION 1.000
3754---++++ EmptyPlugin.pm
3755   * =commonTagsHandler($text, $topic, $web)=
3756   * =endRenderingHandler($text)=
3757   * =outsidePREHandler($text)=
3758   * =insidePREHandler($text)=
3759   * =startRenderingHandler($text, $web)=
3760---++++ Func.pm
3761   * =checkAccessPermission($type, $login, $topicText, $topicName, $webName) -> $boolean=
3762   * =expandCommonVariables($text, $topic, $web) -> $text=
3763   * =extractNameValuePair($attrs, $name) -> $value=
3764   * =formatGmTime($time) -> $text=
3765   * =getCgiQuery() -> $query=
3766   * =getDataDir() -> $dir=
3767   * =getDefaultUserName() -> $loginName=
3768   * =getMainWebname() -> $name=
3769   * =getOopsUrl($web, $topic, $template, @theParams) -> $url=
3770   * =getPreferencesFlag($key) -> $boolean=
3771   * =getPreferencesValue($key, $web) -> $value=
3772   * =getPublicWebList() -> @webs=
3773   * =getPubDir() -> $dir=
3774   * =getPubUrlPath() -> $path=
3775   * =getRevisionInfo($webName, $topic, $rev, $attachment) -> ($date, $user, $rev, $comment)=
3776   * =getScriptUrl($web, $topic, $script) -> $url=
3777   * =getScriptUrlPath() -> $path=
3778   * =getSessionValue($key) -> $value=
3779   * =getSkin() -> $skin=
3780   * =getTopicList($web) -> @topics=
3781   * =getTwikiWebname() -> $name=
3782   * =getUrlHost() -> $host=
3783   * =getViewUrl($web, $topic) -> $url=
3784   * =getWikiName() -> $wikiName=
3785   * =getWikiUserName($text) -> $wikiName=
3786   * =getWikiToolName() -> $name=
3787   * =internalLink($preamble, $web, $topic, $linkText, $anchor, $createLink) -> $text=
3788   * =isGuest() -> $boolean=
3789   * =permissionsSet($web) -> $boolean=
3790   * =readFile($filename) -> $text=
3791   * =readTemplate($name, $skin) -> $text=
3792   * =readTopic($webName, $topic) -> ($meta, $text)=
3793   * =redirectCgiQuery($query, $url)=
3794   * =renderText($text, $web) -> $text=
3795   * =saveFile($filename, $text)=
3796   * =setSessionValue($key, $value)=
3797   * =topicExists($web, $topic) -> $boolean=
3798   * =userToWikiName($user, $dontAddWeb) -> $wikiName=
3799   * =webExists($web) -> $boolean=
3800   * =wikiToUserName($wiki) -> $loginName=
3801   * =writeDebug($text)=
3802   * =writeHeader()=
3803   * =writeWarning($text)=
3804
3805---+++ TWiki-2003-02-01 (Beijing Release)
3806$TWiki::Plugins::VERSION 1.010
3807---++++ EmptyPlugin.pm
3808   * =afterEditHandler($text, $topic, $web)=
3809   * =beforeEditHandler($text, $topic, $web)=
3810   * =beforeSaveHandler($text, $topic, $web)=
3811   * =initializeUserHandler($loginName, $url, $pathInfo)=
3812   * =redirectCgiQueryHandler($query, $url)=
3813   * =registrationHandler($web, $wikiName, $loginName)=
3814   * =writeHeaderHandler($query)=
3815---++++ Func.pm
3816   * =checkTopicEditLock($web, $topic) ->($oopsUrl, $loginName, $unlockTime)=
3817   * =readTopicText($web, $topic, $rev, $ignorePermissions) -> $text=
3818   * =saveTopicText($web, $topic, $text, $ignorePermissions, $dontNotify) -> $oopsUrl=
3819   * =setTopicEditLock($web, $topic, $lock) -> $oopsUrl=
3820
3821---+++ TWiki-2004-09-02 (Cairo Release)
3822$TWiki::Plugins::VERSION 1.025
3823---++++ EmptyPlugin.pm
3824   * =afterCommonTagsHandler($text, $topic, $web)=
3825   * =afterSaveHandler($text, $topic, $web, $error)=
3826   * =beforeCommonTagsHandler($text, $topic, $web)=
3827   * =earlyInitPlugin()=
3828---++++ Func.pm
3829   * =afterAttachmentSaveHandler(\%attrHash, $topic, $web, $error, $meta)=
3830   * =beforeAttachmentSaveHandler(\%attrHash, $topic, $web, $meta)=
3831   * =checkDependencies($moduleName, $dependenciesRef) -> $error=
3832   * =extractParameters($attr) -> %params=
3833   * =formatTime($time, $format, $timezone) -> $text=
3834   * =getPluginPreferencesFlag($key) -> $boolean=
3835   * =getPluginPreferencesValue($key) -> $value=
3836   * =getRegularExpression($regexName) -> $pattern=
3837
3838---+++ TWiki-4.0.0 (Dakar Release)
3839$TWiki::Plugins::VERSION 1.1
3840---++++ EmptyPlugin.pm
3841   * =mergeHandler($diff, $old, $new, \%info) -> $text=
3842   * =modifyHeaderHandler(\%headers, $query)=
3843   * =postRenderingHandler($text)=
3844   * =preRenderingHandler($text, \%map)=
3845   * =renderFormFieldForEditHandler($name, $type, $size, $value, $attributes, $possibleValues) -> $html=
3846   * =renderWikiWordHandler($linkText, $hasExplicitLinkLabel, $web, $topic) -> $linkText=
3847
3848   * <strike> =endRenderingHandler($text)= </strike>
3849   * <strike> =startRenderingHandler($text, $web)= </strike>
3850---++++ Func.pm
3851   * =addToHEAD($id, $header)=
3852   * =attachmentExists($web, $topic, $attachment) -> $boolean=
3853   * =clearSessionValue($key) -> $boolean=
3854   * =checkDependencies($moduleName, $dependenciesRef) -> $error=
3855   * =createWeb($newWeb, $baseWeb, $opts)=
3856   * =expandTemplate($def ) -> $string=
3857   * =expandVariablesOnTopicCreation($text) -> $text=
3858   * =getContext() -> \%hash=
3859   * =getListOfWebs($filter) -> @webs=
3860   * =getScriptUrl($web, $topic, $script, @params) -> $url=
3861   * =getRevisionAtTime($web, $topic, $time) -> $rev=
3862   * =getWorkArea($pluginName) -> $directorypath=
3863   * =isValidWikiWord($text) -> $boolean=
3864   * =loadTemplate($name, $skin, $web) -> $text=
3865   * =moveAttachment($web, $topic, $attachment, $newWeb, $newTopic, $newAttachment)=
3866   * =moveTopic($web, $topic, $newWeb, $newTopic)=
3867   * =moveWeb($oldName, $newName)=
3868   * =normalizeWebTopicName($web, $topic) ->($web, $topic)=
3869   * =readAttachment($web, $topic, $name, $rev) -> $data=
3870   * =registerRESTHandler($alias, \&fn,)=
3871   * =registerTagHandler($var, \&fn, $syntax)=
3872   * =saveAttachment($web, $topic, $attachment, $opts)=
3873   * =saveTopic($web, $topic, $meta, $text, $options) -> $error=
3874   * =searchInWebContent($searchString, $web, \@topics, \%options) -> \%map=
3875   * =sendEmail($text, $retries) -> $error=
3876   * =wikiToEmail($wikiName) -> $email=
3877   * =writeHeader($query, $contentLength)=
3878
3879   * <strike> =checkDependencies($moduleName, $dependenciesRef) -> $error= </strike>
3880   * <strike> =formatGmTime($time, $format) -> $text= </strike>
3881   * <strike> =getDataDir() -> $dir= </strike>
3882   * <strike> =getOopsUrl( $web, $topic, $template, @params ) -> $url= </strike>
3883   * <strike> =getPubDir() -> $dir= </strike>
3884   * <strike> =getPublicWebList() -> @webs= </strike>
3885   * <strike> =getScriptUrlPath() -> $path= </strike>
3886
3887---+++ TWiki-4.0.1 (Dakar Patch Release)
3888$TWiki::Plugins::VERSION 1.1
3889---++++ EmptyPlugin.pm
3890   * =afterSaveHandler($text, $topic, $web, $error, $meta)=
3891   * =beforeSaveHandler($text, $topic, $web, $meta)=
3892---++++ Func.pm
3893
3894---+++ TWiki-4.1 (Edinburgh Release)
3895$TWiki::Plugins::VERSION 1.11
3896---++++ EmptyPlugin.pm
3897   * =afterRenameHandler($oldWeb, $oldTopic, $oldAttachment, $newWeb, $newTopic, $newAttachment)=
3898---++++ Func.pm
3899No changes
3900
3901---+++ TWiki-4.2 (Freetown Release)
3902$TWiki::Plugins::VERSION 1.2
3903---++++ EmptyPlugin.pm
3904   * =afterCommonTagsHandler($text, $topic, $web, $meta)=
3905   * =beforeCommonTagsHandler($text, $topic, $web, $meta)=
3906   * =commonTagsHandler($text, $topic, $web, $included, $meta)=
3907---++++ Func.pm
3908   * =decodeFormatTokens($str) -> $unencodedString=
3909   * =eachChangeSince($web, $time) -> $iterator=
3910   * =eachGroup() -> $iterator=
3911   * =eachGroupMember($group) -> $iterator=
3912   * =eachMembership($wikiname) -> $iterator=
3913   * =eachUser() -> $iterator=
3914   * =emailToWikiNames($email, $dontAddWeb) -> @wikiNames=
3915   * =expandCommonVariables($text, $topic, $web, $meta) -> $text=
3916   * =getCanonicalUserID( $user ) -> $cUID=
3917   * =getExternalResource($url) -> $response=
3918   * =getSessionKeys() -> @keys=
3919   * =isAnAdmin($login) -> $boolean=
3920   * =isGroup($group) -> $boolean=
3921   * =isGroupMember($group, $login) -> $boolean=
3922   * =isTrue($value, $default) -> $boolean=
3923   * =popTopicContext()=
3924   * =pushTopicContext($web, $topic)=
3925   * =sanitizeAttachmentName($fname) -> ($fileName, $origName)=
3926   * =setPreferencesValue($name, $val)=
3927   * =spaceOutWikiWord($word, $sep) -> $text=
3928   * =wikiNameToEmails($wikiname) -> @emails=
3929   * <strike> =permissionsSet($web) -> $boolean= </strike>
3930   * <strike> =getOopsUrl( $web, $topic, $template, $param1, $param2, $param3, $param4 ) -> $url= </strike>
3931
3932---+++ TWiki-4.3 (Georgetown Release)
3933$TWiki::Plugins::VERSION 1.2
3934---++++ EmptyPlugin.pm
3935No changes
3936---++++ Func.pm
3937No changes
3938
3939---+++ TWiki-5.0 (Helsinki Release)
3940$TWiki::Plugins::VERSION 1.3
3941---++++ EmptyPlugin.pm
3942No changes
3943---++++ Func.pm
3944   * =buildWikiWord( $text ) -> $text=
3945
3946---+++ TWiki-5.1 (Istanbul Release)
3947$TWiki::Plugins::VERSION 1.4
3948---++++ EmptyPlugin.pm
3949   * Callback function registered by =registerTagHandler= has two new parameters =$meta= and =$textRef=
3950---++++ Func.pm
3951   * =afterAttachmentSaveHandler(\%attrHash, $topic, $web, $error, $meta)= -- added =$meta=
3952   * =beforeAttachmentSaveHandler(\%attrHash, $topic, $web, $meta)= == added =$meta=
3953   * =writeLog( $action, $extra, $web, $topic, $user )=
3954
3955---+++ TWiki-6.0 (Jerusalem Release)
3956$TWiki::Plugins::VERSION 6.00
3957---++++ EmptyPlugin.pm
3958   * =viewRedirectHandler( $session, $web, $topic )=
3959---++++ Func.pm
3960   * =isAnAdmin( $user, $topic, $web ) -> $boolean= -- added =$topic= and =$web=
3961   * =expandVariablesOnTopicCreation( $text, $web, $topic ) -> $text= -- added =$web= and =$topic=
3962   * =postExternalResource( $url, $text, \@headers, \%params ) -> $response=
3963   * =registerExternalHTTPHandler( \&fn )=
3964   * =getContentMode( $web ) -> $contentMode=
3965   * =webWritable( $web ) -> $boolean=
3966   * =getDiskList() -> @diskIDs=
3967   * =getDiskInfo($web, $siteName) -> ($dataDir, $pubDir, $diskID)=
3968   * =trashWebName(web => $web | disk => $diskID) -> $trashWebName=
3969   * =entityEncode( $text, $extra ) -> $text=
3970   * =entityDecode( $text ) -> $text=
3971   * =urlEncode( $text ) -> $text=
3972   * =urlDecode( $text ) -> $text=
3973
3974---+++ TWiki-6.0.2
3975$TWiki::Plugins::VERSION 6.02
3976---++++ EmptyPlugin.pm
3977   * =viewFileRedirectHandler( $session, $web, $topic )=
3978   * =topicTitleHandler( $session, $web, $topic, $titleRef )=
3979
3980---+++ TWiki-6.0.3
3981$TWiki::Plugins::VERSION 6.10
3982---++++ Func.pm
3983   * =getDebugFilePath() -> $path=
3984
3985=cut
3986
39871;
3988