Changeset 8849


Ignore:
Timestamp:
09/02/10 09:47:02 (21 months ago)
Author:
CrawfordCurrie
Message:

Item8841: improve links to support; reorder functions in Func for a more logical TOC

Location:
trunk/core
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/core/data/System/DevelopingPlugins.txt

    r8688 r8849  
    44%TOC% 
    55The usual way Foswiki is extended is by writing a _Plugin_. Plugins extend Foswiki by providing functions that 'listen' to events in the Foswiki core, and handling these events. These functions are called "Plugin Handlers" and they are described in depth in [[%SCRIPTURL{view}%/%SYSTEMWEB%/PerlDoc?module=Foswiki::Plugins::EmptyPlugin][EmptyPlugin]] ( =lib/Foswiki/Plugins/EmptyPlugin.pm= ). 
     6 
     7---++ The 3048m view of how Foswiki works 
     8 
     9Foswiki is a web application that runs inside a web server. When the web server receives a request that it recognises as being for Foswiki, it calls one of the perl scripts in the Foswiki =bin= directory. Each of the scripts has a specific function, as described in [[command and CGI scripts]]. 
     10 
     11The scripts are responsible for interpreting the parameters passed in the request, and generating a response that is sent back to the browser, usually in the form of an HTML page. 
     12 
     13Foswiki contains three _engines_ that are used by the scripts; the _template engine_, the _macro engine_, and the _TML engine_. 
     14 
     15   1 The *template engine* reads predefined templates from files on the server. These templates contain directives that are expanded by the engine to create the output HTML skeleton. One of these directives expands to the topic text. 
     16   1 The *macro engine* then expands the [[macros]] in the skeleton. This is also where macros registered by plugins are expanded. 
     17      * Macros, including those registered by plugins, are processed in a strict left-right-inside-out processing order. See [[macros]] for more details. 
     18      * Macros include things like searches, so this is usually the slowest part of generating a page.  
     19   1 The *TML (Topic Markup Language) engine* now processes the expanded text, looking for TML constructs such as bulleted lists and tables. It generates HTML for these constructs. 
     20 
     21Once all the engines have run, the output is sent to the browser. 
     22 
     23There are several ways plugins can interact with this process. 
     24   1 They can *register macros* that are expanded by the macro engine. This is the simplest kind of plugin. 
     25   1 The can interact with various points in the rendering pipeline by implementing *handlers* (callbacks). 
     26   1 They can *register REST handlers* that are invoked via the =rest= script to support some form of transaction outside those supported by the standard scripts. 
    627 
    728---++ APIs available to Extensions 
  • trunk/core/data/System/WebHome.txt

    r8561 r8849  
    1717   * [[ReleaseNotes01x01][Release Notes for 1.1]] - describes what's new in this release 
    1818 
     19   * [[http://foswiki.org/Support][Getting support]] - if you get stuck 
     20 
    1921---++ <nop>%WEB% Web Utilities 
    2022   * WebTopicList - all topics in alphabetical order 
  • trunk/core/lib/Foswiki/Func.pm

    r8839 r8849  
    466466    ( $session->{webName}, $session->{topicName} ) = 
    467467      $session->{prefs}->popTopicContext(); 
     468} 
     469 
     470=begin TML 
     471 
     472---++ Registering extensions 
     473 
     474Plugins work either by using handlers to manipulate the text being processed, 
     475or by registering extensions, such as new macros, scripts, or meta-data types. 
     476 
     477=cut 
     478 
     479=begin TML= 
     480 
     481---+++ registerTagHandler( $var, \&fn, $syntax ) 
     482 
     483Should only be called from initPlugin. 
     484 
     485Register a function to handle a simple variable. Handles both %<nop>VAR% and  
     486%<nop>VAR{...}%. Registered variables are treated the same as internal macros,  
     487and are expanded at the same time. This is a _lot_ more efficient than using the =commonTagsHandler=. 
     488   * =$var= - The name of the variable, i.e. the 'MYVAR' part of %<nop>MYVAR%.  
     489   The variable name *must* match /^[A-Z][A-Z0-9_]*$/ or it won't work. 
     490   * =\&fn= - Reference to the handler function. 
     491   * =$syntax= can be 'classic' (the default) or 'context-free'. (context-free may be removed in future) 
     492   'classic' syntax is appropriate where you want the variable to support classic syntax  
     493   i.e. to accept the standard =%<nop>MYVAR{ "unnamed" param1="value1" param2="value2" }%= syntax,  
     494   as well as an unquoted default parameter, such as =%<nop>MYVAR{unquoted parameter}%=.  
     495   If your variable will only use named parameters, you can use 'context-free' syntax,  
     496   which supports a more relaxed syntax. For example,  
     497   %MYVAR{param1=value1, value 2, param3="value 3", param4='value 5"}% 
     498 
     499The variable handler function must be of the form: 
     500<verbatim> 
     501sub handler(\%session, \%params, $topic, $web, $topicObject) 
     502</verbatim> 
     503where: 
     504   * =\%session= - a reference to the session object (may be ignored) 
     505   * =\%params= - a reference to a Foswiki::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. 
     506   * =$topic= - name of the topic in the query 
     507   * =$web= - name of the web in the query 
     508   * =$topicObject= - is the Foswiki::Meta object for the topic *Since* 2009-03-06 
     509for example, to execute an arbitrary command on the server, you might do this: 
     510<verbatim> 
     511sub initPlugin{ 
     512   Foswiki::Func::registerTagHandler('EXEC', \&boo); 
     513} 
     514 
     515sub boo { 
     516    my( $session, $params, $topic, $web, $topicObject ) = @_; 
     517    my $cmd = $params->{_DEFAULT}; 
     518 
     519    return "NO COMMAND SPECIFIED" unless $cmd; 
     520 
     521    my $result = `$cmd 2>&1`; 
     522    return $params->{silent} ? '' : $result; 
     523} 
     524</verbatim> 
     525would let you do this: 
     526=%<nop>EXEC{"ps -Af" silent="on"}%= 
     527 
     528Registered tags differ from tags implemented using the old approach (text substitution in =commonTagsHandler=) in the following ways: 
     529   * 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). 
     530   * registered tag names can only contain alphanumerics and _ (underscore) 
     531   * registering a tag =FRED= defines both =%<nop>FRED{...}%= *and also* =%FRED%=. 
     532   * registered tag handlers *cannot* return another tag as their only result (e.g. =return '%<nop>SERVERTIME%';=). It won't work. 
     533 
     534=cut 
     535 
     536sub registerTagHandler { 
     537    my ( $tag, $function, $syntax ) = @_; 
     538    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     539 
     540    # $pluginContext is undefined if a contrib registers a tag handler. 
     541    my $pluginContext; 
     542    if ( caller =~ m/^Foswiki::Plugins::(\w+)/ ) { 
     543        $pluginContext = $1 . 'Enabled'; 
     544    } 
     545 
     546    # Use an anonymous function so it gets inlined at compile time. 
     547    # Make sure we don't mangle the session reference. 
     548    Foswiki::registerTagHandler( 
     549        $tag, 
     550        sub { 
     551            my ( $session, $params, $topicObject ) = @_; 
     552            my $record = $Foswiki::Plugins::SESSION; 
     553            $Foswiki::Plugins::SESSION = $_[0]; 
     554 
     555            # $pluginContext is defined for all plugins 
     556            # but never defined for contribs. 
     557            # This is convenient, because contribs cannot be disabled 
     558            # at run-time, either. 
     559            if ( defined $pluginContext ) { 
     560 
     561                # Registered tag handlers should only be called if the plugin 
     562                # is enabled. Disabled plugins can still have tag handlers 
     563                # registered in persistent environments (e.g. modperl) 
     564                # and also for rest handlers that disable plugins. 
     565                # See Item1871 
     566                return unless $session->inContext($pluginContext); 
     567            } 
     568 
     569            # Compatibility; expand $topicObject to the topic and web 
     570            my $result = 
     571              &$function( $session, $params, $topicObject->topic, 
     572                $topicObject->web, $topicObject ); 
     573            $Foswiki::Plugins::SESSION = $record; 
     574            return $result; 
     575        }, 
     576        $syntax 
     577    ); 
     578} 
     579 
     580=begin TML= 
     581 
     582---+++ registerRESTHandler( $alias, \&fn, %options ) 
     583 
     584Should only be called from initPlugin. 
     585 
     586Adds a function to the dispatch table of the REST interface  
     587   * =$alias= - The name . 
     588   * =\&fn= - Reference to the function. 
     589   * =%options= - additional options affecting the handler 
     590The handler function must be of the form: 
     591<verbatim> 
     592sub handler(\%session) 
     593</verbatim> 
     594where: 
     595   * =\%session= - a reference to the Foswiki session object (may be ignored) 
     596 
     597From the REST interface, the name of the plugin must be used 
     598as the subject of the invokation. 
     599 
     600Additional options are set in the =%options= hash. These options are important 
     601to ensuring that requests to your handler can't be used in cross-scripting 
     602attacks, or used for phishing. 
     603   * =authenticate= - use this boolean option to require authentication for the 
     604     handler. If this is set, then an authenticated session must be in place 
     605     or the REST call will be rejected with a 401 (Unauthorized) status code. 
     606     By default, rest handlers do *not* require authentication. 
     607   * =validate= - use this boolean option to require validation of any requests 
     608     made to this handler. Validation is the process by which a secret key 
     609     is passed to the server so it can identify the origin of the request. 
     610     By default, requests made to REST handlers are not validated. 
     611   * =http_allow= use this option to specify that the HTTP methods that can 
     612     be used to invoke the handler. For example, =http_allow=>'POST,GET'= will 
     613     constrain the handler to be invoked using POST and GET, but not other 
     614     HTTP methods, such as DELETE. Normally you will use http_allow=>'POST'. 
     615     Together with authentication this is an important security tool. 
     616     Handlers that can be invoked using GET are vulnerable to being called 
     617     in the =src= parameter of =img= tags, a common method for cross-site 
     618     request forgery (CSRF) attacks. This option is set automatically if 
     619     =authenticate= is specified. 
     620 
     621---++++ Example 
     622 
     623The EmptyPlugin has the following call in the initPlugin handler: 
     624<verbatim> 
     625   Foswiki::Func::registerRESTHandler('example', \&restExample, 
     626     http_allow=>'GET,POST'); 
     627</verbatim> 
     628 
     629This adds the =restExample= function to the REST dispatch table 
     630for the EmptyPlugin under the 'example' alias, and allows it 
     631to be invoked using the URL 
     632 
     633=http://server:port/bin/rest/EmptyPlugin/example= 
     634 
     635note that the URL 
     636 
     637=http://server:port/bin/rest/EmptyPlugin/restExample= 
     638 
     639(ie, with the name of the function instead of the alias) will not work. 
     640 
     641---++++ Calling REST handlers from the command-line 
     642The =rest= script allows handlers to be invoked from the command line. The 
     643script is invoked passing the parameters as described in CommandAndCGIScripts. 
     644If the handler requires authentication ( =authenticate=>1= ) then this can 
     645be passed in the username and =password= parameters. 
     646 
     647For example, 
     648 
     649=perl -wT rest /EmptyPlugin/example -username HughPugh -password trumpton= 
     650 
     651=cut 
     652 
     653sub registerRESTHandler { 
     654    my ( $alias, $function, %options ) = @_; 
     655    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     656    my $plugin = caller; 
     657    $plugin =~ s/.*:://;    # strip off Foswiki::Plugins:: prefix 
     658 
     659    # Use an anonymous function so it gets inlined at compile time. 
     660    # Make sure we don't mangle the session reference. 
     661    require Foswiki::UI::Rest; 
     662    Foswiki::UI::Rest::registerRESTHandler( 
     663        $plugin, $alias, 
     664        sub { 
     665            my $record = $Foswiki::Plugins::SESSION; 
     666            $Foswiki::Plugins::SESSION = $_[0]; 
     667            my $result = &$function(@_); 
     668            $Foswiki::Plugins::SESSION = $record; 
     669            return $result; 
     670        }, 
     671        %options 
     672    ); 
     673} 
     674 
     675=begin TML 
     676 
     677---+++ registerMETA($name, %syntax) 
     678 
     679Foswiki supports embedding meta-data into topics. For example, 
     680 
     681=%<nop>META:BOOK{title="Transit" author="Edmund Cooper" isbn="0-571-05724-1"}%= 
     682 
     683This meta-data is validated when it is read from the store. Meta-data 
     684that is not registered, or doesn't pass validation, is ignored. This 
     685function allows you to register a new META datum, passing the name in 
     686=$name=. =%syntax= is a set of optional checks that describe how to 
     687validate the fields of the datum. 
     688 
     689The following checks are supported: 
     690 
     691=function=>\&fn= In this case the function =fn= will be called when the 
     692datum is encountered, passing in the name of the macro and the 
     693argument hash. The function must return a non-zero/undef value if the tag 
     694is acceptable, or 0 otherwise. For example: 
     695<verbatim> 
     696registerMETA('BOOK', function => sub { 
     697    my ($name, $args) = @_; 
     698    # $name will be BOOK 
     699    return defined $args->{title}; 
     700} 
     701</verbatim> 
     702can be used to check that =%META:BOOK{}= contains a title. 
     703 
     704=require=>[]= is used to check that a list of named parameters are present on 
     705the tag. For example, 
     706<verbatim> 
     707registerMETA('BOOK', require => [ 'title', 'author' ]); 
     708</verbatim> 
     709can be used to check that both =title= and =author= are present. 
     710 
     711=allow=>[]= lets you specify other optional parameters that are allowed 
     712on the tag. If you specify =allow= then the validation will fail if the 
     713tag contains any parameters that are _not_ in the =allow= or =require= lists. 
     714If you don't specify =allow= then all parameters will be allowed. 
     715 
     716Checks are cumulative, so if you: 
     717<verbatim> 
     718registerMETA('BOOK', 
     719    function => \&checkParameters, 
     720    require => [ 'title' ], 
     721    allow => [ 'author', 'isbn' ]); 
     722</verbatim> 
     723then all these conditions will be tested. Note that =require= and =allow= 
     724are tested _after_ =function= is called, to give the function a chance to 
     725rewrite the parameter list. 
     726 
     727If no checker is registered for a META tag, then it will automatically 
     728be accepted into the topic meta-data. 
     729 
     730Note that the checker only verifies the *presence* of parameters, and 
     731not their *values*. 
     732 
     733=cut 
     734 
     735sub registerMETA { 
     736    my ( $macro, $spec ) = @_; 
     737    Foswiki::Meta::registerMETA( $macro, $spec ); 
    468738} 
    469739 
     
    11421412=begin TML 
    11431413 
    1144 ---++ Webs, Topics and Attachments 
     1414---++ Traversing 
    11451415 
    11461416=cut 
     
    11831453=begin TML 
    11841454 
     1455---+++ isValidWebName( $name [, $system] ) -> $boolean 
     1456 
     1457Check for a valid web name. If $system is true, then 
     1458system web names are considered valid (names starting with _) 
     1459otherwise only user web names are valid 
     1460 
     1461If $Foswiki::cfg{EnableHierarchicalWebs} is off, it will also return false 
     1462when a nested web name is passed to it. 
     1463 
     1464=cut 
     1465 
     1466sub isValidWebName { 
     1467    return Foswiki::isValidWebName(@_); 
     1468} 
     1469 
     1470=begin TML 
     1471 
    11851472---+++ webExists( $web ) -> $boolean 
    11861473 
     
    11971484    return $Foswiki::Plugins::SESSION->webExists($web); 
    11981485} 
     1486=begin TML 
     1487 
     1488---+++ getTopicList( $web ) -> @topics 
     1489 
     1490Get list of all topics in a web 
     1491   * =$web= - Web name, required, e.g. ='Sandbox'= 
     1492Return: =@topics= Topic list, e.g. =( 'WebChanges',  'WebHome', 'WebIndex', 'WebNotify' )= 
     1493 
     1494=cut 
     1495 
     1496sub getTopicList { 
     1497 
     1498    my ($web) = _validateWTA(@_); 
     1499 
     1500    my $webObject = Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web ); 
     1501    my $it = $webObject->eachTopic(); 
     1502    return $it->all(); 
     1503} 
     1504 
     1505=begin TML 
     1506 
     1507---+++ isValidTopicName( $name [, $allowNonWW] ) -> $boolean 
     1508 
     1509Check for a valid topic name. 
     1510   * =$name= - topic name 
     1511   * =$allowNonWW= - true to allow non-wikiwords 
     1512 
     1513=cut 
     1514 
     1515sub isValidTopicName { 
     1516    return Foswiki::isValidTopicName(@_); 
     1517} 
     1518 
     1519=begin TML 
     1520 
     1521---+++ topicExists( $web, $topic ) -> $boolean 
     1522 
     1523Test if topic exists 
     1524   * =$web=   - Web name, optional, e.g. ='Main'=. 
     1525   * =$topic= - Topic name, required, e.g. ='TokyoOffice'=, or ="Main.TokyoOffice"= 
     1526 
     1527$web and $topic are parsed as described in the documentation for =normalizeWebTopicName=. 
     1528Specifically, the %USERSWEB% is used if $web is not specified and $topic has no web specifier. 
     1529To get an expected behaviour it is recommened to specify the current web for $web; don't leave it empty. 
     1530 
     1531=cut 
     1532 
     1533sub topicExists { 
     1534    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     1535    my ( $web, $topic ) = _checkWTA(@_); 
     1536    return 0 unless defined $web && defined $topic; 
     1537    return $Foswiki::Plugins::SESSION->topicExists( $web, $topic ); 
     1538} 
     1539 
     1540=begin TML 
     1541 
     1542---+++ readTopic( $web, $topic, $rev ) -> ( $meta, $text ) 
     1543 
     1544Read topic text and meta data, regardless of access permissions. 
     1545   * =$web= - Web name, required, e.g. ='Main'= 
     1546   * =$topic= - Topic name, required, e.g. ='TokyoOffice'= 
     1547   * =$rev= - revision to read (default latest) 
     1548Return: =( $meta, $text )= Meta data object and topic text 
     1549 
     1550=$meta= is a perl 'object' of class =Foswiki::Meta=. This class is 
     1551fully documented in the source code documentation shipped with the 
     1552release, or can be inspected in the =lib/Foswiki/Meta.pm= file. 
     1553 
     1554This method *ignores* topic access permissions. You should be careful to use 
     1555=checkAccessPermission= to ensure the current user has read access to the 
     1556topic. 
     1557 
     1558=cut 
     1559 
     1560sub readTopic { 
     1561 
     1562    my( $web, $topic, $rev ) = @_; 
     1563    ($web, $topic) = _validateWTA($web, $topic); 
     1564    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     1565 
     1566    my $meta = Foswiki::Meta->load( 
     1567        $Foswiki::Plugins::SESSION, $web, $topic, $rev ); 
     1568    return ( $meta, $meta->text() ); 
     1569} 
     1570 
     1571=begin TML 
     1572 
     1573---+++ getRevisionInfo($web, $topic, $rev, $attachment ) -> ( $date, $user, $rev, $comment )  
     1574 
     1575Get revision info of a topic or attachment 
     1576   * =$web= - Web name, optional, e.g. ='Main'= 
     1577   * =$topic=   - Topic name, required, e.g. ='TokyoOffice'= 
     1578   * =$rev=     - revsion number, or tag name (can be in the format 1.2, or just the minor number) 
     1579   * =$attachment=                 -attachment filename 
     1580Return: =( $date, $user, $rev, $comment )= List with: ( last update date, login name of last user, minor part of top revision number, comment of attachment if attachment ), e.g. =( 1234561, 'phoeny', "5",  )= 
     1581| $date | in epochSec | 
     1582| $user | Wiki name of the author (*not* login name) | 
     1583| $rev | actual rev number | 
     1584| $comment | comment given for uploaded attachment | 
     1585 
     1586NOTE: if you are trying to get revision info for a topic, use 
     1587=$meta->getRevisionInfo= instead if you can - it is significantly 
     1588more efficient. 
     1589 
     1590=cut 
     1591 
     1592sub getRevisionInfo { 
     1593    my ( $web, $topic, $rev, $attachment ) = @_; 
     1594 
     1595    ($web, $topic) = _validateWTA($web, $topic); 
     1596 
     1597    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     1598 
     1599    my $topicObject; 
     1600    my $info; 
     1601    if ($attachment) { 
     1602        $topicObject = 
     1603          Foswiki::Meta->load( $Foswiki::Plugins::SESSION, $web, $topic ); 
     1604        $info = $topicObject->getAttachmentRevisionInfo( $attachment, $rev ); 
     1605    } 
     1606    else { 
     1607        $topicObject = 
     1608          Foswiki::Meta->load( $Foswiki::Plugins::SESSION, $web, $topic, $rev ); 
     1609        $info = $topicObject->getRevisionInfo(); 
     1610    } 
     1611    return ( $info->{date}, 
     1612        $Foswiki::Plugins::SESSION->{users}->getWikiName( $info->{author} ), 
     1613        $info->{version}, $info->{comment} ); 
     1614} 
     1615 
     1616=begin TML 
     1617 
     1618---+++ getRevisionAtTime( $web, $topic, $time ) -> $rev 
     1619 
     1620Get the revision number of a topic at a specific time. 
     1621   * =$web= - web for topic 
     1622   * =$topic= - topic 
     1623   * =$time= - time (in epoch secs) for the rev 
     1624Return: Single-digit revision number, or undef if it couldn't be determined 
     1625(either because the topic isn't that old, or there was a problem) 
     1626 
     1627=cut 
     1628 
     1629sub getRevisionAtTime { 
     1630    my ( $web, $topic, $time ) = @_; 
     1631    ($web, $topic) = _validateWTA($web, $topic); 
     1632    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     1633    my $topicObject = 
     1634      Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
     1635    return $topicObject->getRevisionAtTime($time); 
     1636} 
     1637 
     1638=begin TML 
     1639 
     1640---+++ getAttachmentList( $web, $topic ) -> @list 
     1641Get a list of the attachments on the given topic. 
     1642 
     1643*Since:* 31 Mar 2009 
     1644 
     1645=cut 
     1646 
     1647sub getAttachmentList { 
     1648    my ( $web, $topic ) = @_; 
     1649    ($web, $topic) = _validateWTA($web, $topic); 
     1650    my $topicObject = 
     1651      Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
     1652    my $it = $topicObject->eachAttachment(); 
     1653    return sort $it->all(); 
     1654} 
     1655 
     1656=begin TML 
     1657 
     1658---+++ attachmentExists( $web, $topic, $attachment ) -> $boolean 
     1659 
     1660Test if attachment exists 
     1661   * =$web=   - Web name, optional, e.g. =Main=. 
     1662   * =$topic= - Topic name, required, e.g. =TokyoOffice=, or =Main.TokyoOffice= 
     1663   * =$attachment= - attachment name, e.g.=logo.gif= 
     1664$web and $topic are parsed as described in the documentation for =normalizeWebTopicName=. 
     1665 
     1666=cut 
     1667 
     1668sub attachmentExists { 
     1669    my ( $web, $topic, $attachment ) = _checkWTA(@_); 
     1670    return 0 unless defined $web && defined $topic && defined $attachment; 
     1671 
     1672    my $topicObject = 
     1673      Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
     1674    return $topicObject->hasAttachment($attachment); 
     1675} 
     1676 
     1677=begin TML 
     1678 
     1679---+++ readAttachment( $web, $topic, $name, $rev ) -> $data 
     1680 
     1681   * =$web= - web for topic - must not be tainted 
     1682   * =$topic= - topic - must not be tainted 
     1683   * =$name= - attachment name - must not be tainted 
     1684   * =$rev= - revision to read (default latest) 
     1685Read an attachment from the store for a topic, and return it as a string. The 
     1686names of attachments on a topic can be recovered from the meta-data returned 
     1687by =readTopic=. If the attachment does not exist, or cannot be read, undef 
     1688will be returned. If the revision is not specified, the latest version will 
     1689be returned. 
     1690 
     1691View permission on the topic is required for the 
     1692read to be successful.  Access control violations are flagged by a 
     1693Foswiki::AccessControlException. Permissions are checked for the current user. 
     1694 
     1695<verbatim> 
     1696my( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic ); 
     1697my @attachments = $meta->find( 'FILEATTACHMENT' ); 
     1698foreach my $a ( @attachments ) { 
     1699   try { 
     1700       my $data = Foswiki::Func::readAttachment( $web, $topic, $a->{name} ); 
     1701       ... 
     1702   } catch Foswiki::AccessControlException with { 
     1703   }; 
     1704} 
     1705</verbatim> 
     1706 
     1707This is the way 99% of extensions will access attachments. 
     1708See =Foswiki::Meta::openAttachment= for a lower level interface that does 
     1709not check access controls. 
     1710 
     1711=cut 
     1712 
     1713sub readAttachment { 
     1714    my ( $web, $topic, $attachment, $rev ) = @_; 
     1715 
     1716    ( $web, $topic, $attachment ) = _validateWTA($web, $topic, $attachment); 
     1717 
     1718    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     1719    my $result; 
     1720 
     1721    my $topicObject = 
     1722      Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
     1723    unless ( $topicObject->haveAccess('VIEW') ) { 
     1724        throw Foswiki::AccessControlException( 'VIEW', 
     1725            $Foswiki::Plugins::SESSION->{user}, 
     1726            $web, $topic, $Foswiki::Meta::reason ); 
     1727    } 
     1728    my $fh; 
     1729    try { 
     1730        $fh = $topicObject->openAttachment( $attachment, '<', version => $rev ); 
     1731    } 
     1732    catch Error::Simple with { 
     1733        $fh = undef; 
     1734    }; 
     1735    return undef unless $fh; 
     1736    local $/; 
     1737    my $data = <$fh>; 
     1738    return $data; 
     1739} 
     1740 
     1741=begin TML 
     1742 
     1743---++ Manipulating 
     1744 
     1745=cut 
     1746 
    11991747 
    12001748=begin TML 
     
    12961844} 
    12971845 
    1298 =begin TML 
    1299  
    1300 ---+++ eachChangeSince($web, $time) -> $iterator 
    1301  
    1302 Get an iterator over the list of all the changes in the given web between 
    1303 =$time= and now. $time is a time in seconds since 1st Jan 1970, and is not 
    1304 guaranteed to return any changes that occurred before (now -  
    1305 {Store}{RememberChangesFor}). {Store}{RememberChangesFor}) is a 
    1306 setting in =configure=. Changes are returned in *most-recent-first* 
    1307 order. 
    1308  
    1309 Use it as follows: 
    1310 <verbatim> 
    1311     my $iterator = Foswiki::Func::eachChangeSince( 
    1312         $web, time() - 7 * 24 * 60 * 60); # the last 7 days 
    1313     while ($iterator->hasNext()) { 
    1314         my $change = $iterator->next(); 
    1315         # $change is a perl hash that contains the following fields: 
    1316         # topic => topic name 
    1317         # user => wikiname - wikiname of user who made the change 
    1318         # time => time of the change 
    1319         # revision => revision number *after* the change 
    1320         # more => more info about the change (e.g. 'minor') 
    1321     } 
    1322 </verbatim> 
    1323  
    1324 =cut 
    1325  
    1326 sub eachChangeSince { 
    1327     my ( $web, $time ) = @_; 
    1328     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    1329     ($web) = _validateWTA($web); 
    1330     ASSERT( $Foswiki::Plugins::SESSION->webExists($web) ) if DEBUG; 
    1331  
    1332     my $webObject = Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web ); 
    1333  
    1334     # eachChange returns changes with cUIDs. these have to be mapped 
    1335     # to wikinames per the Foswiki::Func 'spec' (changes used to be stored 
    1336     # with wikinames) 
    1337     require Foswiki::Iterator::ProcessIterator; 
    1338     require Foswiki::Users::BaseUserMapping; 
    1339     return new Foswiki::Iterator::ProcessIterator( 
    1340         $webObject->eachChange($time), 
    1341         sub { 
    1342             my $n = shift; 
    1343             $n->{user} = $Foswiki::Users::BaseUserMapping::UNKNOWN_USER_CUID 
    1344               unless defined $n->{user}; 
    1345             $n->{user} = 
    1346               $Foswiki::Plugins::SESSION->{users}->getWikiName( $n->{user} ); 
    1347             return $n; 
    1348         } 
    1349     ); 
    1350 } 
    1351  
    1352 =begin TML 
    1353  
    1354 ---++ summariseChanges($web, $topic, $orev, $nrev, $tml) -> $text 
    1355 Generate a summary of the changes between rev $orev and rev $nrev of the 
    1356 given topic. 
    1357    * =$web=, =$topic= - topic (required) 
    1358    * =$orev= - older rev (required) 
    1359    * =$nrev= - later rev (may be undef for the latest) 
    1360    * =$tml= - if true will generate renderable TML (i.e. HTML with NOPs. if false will generate a summary suitable for use in plain text (mail, for example) 
    1361 Generate a (max 3 line) summary of the differences between the revs. 
    1362  
    1363 If there is only one rev, a topic summary will be returned. 
    1364  
    1365 If =$tml= is not set, all HTML will be removed. 
    1366  
    1367 In non-tml, lines are truncated to 70 characters. Differences are shown using + and - to indicate added and removed text. 
    1368  
    1369 If access is denied to either revision, then it will be treated as blank 
    1370 text. 
    1371  
    1372 *Since* 2009-03-06 
    1373  
    1374 =cut 
    1375  
    1376 sub summariseChanges { 
    1377     my ( $web, $topic, $orev, $nrev, $tml ) = @_; 
    1378     ($web, $topic) = _validateWTA($web, $topic); 
    1379  
    1380     my $topicObject = 
    1381       Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
    1382     return $topicObject->summariseChanges( Foswiki::Store::cleanUpRevID($orev), 
    1383         Foswiki::Store::cleanUpRevID($nrev), $tml ); 
    1384 } 
    1385  
    1386 =begin TML 
    1387  
    1388 ---+++ getTopicList( $web ) -> @topics 
    1389  
    1390 Get list of all topics in a web 
    1391    * =$web= - Web name, required, e.g. ='Sandbox'= 
    1392 Return: =@topics= Topic list, e.g. =( 'WebChanges',  'WebHome', 'WebIndex', 'WebNotify' )= 
    1393  
    1394 =cut 
    1395  
    1396 sub getTopicList { 
    1397  
    1398     my ($web) = _validateWTA(@_); 
    1399  
    1400     my $webObject = Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web ); 
    1401     my $it = $webObject->eachTopic(); 
    1402     return $it->all(); 
    1403 } 
    1404  
    1405 =begin TML 
    1406  
    1407 ---+++ topicExists( $web, $topic ) -> $boolean 
    1408  
    1409 Test if topic exists 
    1410    * =$web=   - Web name, optional, e.g. ='Main'=. 
    1411    * =$topic= - Topic name, required, e.g. ='TokyoOffice'=, or ="Main.TokyoOffice"= 
    1412  
    1413 $web and $topic are parsed as described in the documentation for =normalizeWebTopicName=. 
    1414 Specifically, the %USERSWEB% is used if $web is not specified and $topic has no web specifier. 
    1415 To get an expected behaviour it is recommened to specify the current web for $web; don't leave it empty. 
    1416  
    1417 =cut 
    1418  
    1419 sub topicExists { 
    1420     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    1421     my ( $web, $topic ) = _checkWTA(@_); 
    1422     return 0 unless defined $web && defined $topic; 
    1423     return $Foswiki::Plugins::SESSION->topicExists( $web, $topic ); 
    1424 } 
    14251846 
    14261847=begin TML 
     
    16312052 
    16322053    $from->move($to); 
    1633 } 
    1634  
    1635 =begin TML 
    1636  
    1637 ---+++ getRevisionInfo($web, $topic, $rev, $attachment ) -> ( $date, $user, $rev, $comment )  
    1638  
    1639 Get revision info of a topic or attachment 
    1640    * =$web= - Web name, optional, e.g. ='Main'= 
    1641    * =$topic=   - Topic name, required, e.g. ='TokyoOffice'= 
    1642    * =$rev=     - revsion number, or tag name (can be in the format 1.2, or just the minor number) 
    1643    * =$attachment=                 -attachment filename 
    1644 Return: =( $date, $user, $rev, $comment )= List with: ( last update date, login name of last user, minor part of top revision number, comment of attachment if attachment ), e.g. =( 1234561, 'phoeny', "5",  )= 
    1645 | $date | in epochSec | 
    1646 | $user | Wiki name of the author (*not* login name) | 
    1647 | $rev | actual rev number | 
    1648 | $comment | comment given for uploaded attachment | 
    1649  
    1650 NOTE: if you are trying to get revision info for a topic, use 
    1651 =$meta->getRevisionInfo= instead if you can - it is significantly 
    1652 more efficient. 
    1653  
    1654 =cut 
    1655  
    1656 sub getRevisionInfo { 
    1657     my ( $web, $topic, $rev, $attachment ) = @_; 
    1658  
    1659     ($web, $topic) = _validateWTA($web, $topic); 
    1660  
    1661     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    1662  
    1663     my $topicObject; 
    1664     my $info; 
    1665     if ($attachment) { 
    1666         $topicObject = 
    1667           Foswiki::Meta->load( $Foswiki::Plugins::SESSION, $web, $topic ); 
    1668         $info = $topicObject->getAttachmentRevisionInfo( $attachment, $rev ); 
    1669     } 
    1670     else { 
    1671         $topicObject = 
    1672           Foswiki::Meta->load( $Foswiki::Plugins::SESSION, $web, $topic, $rev ); 
    1673         $info = $topicObject->getRevisionInfo(); 
    1674     } 
    1675     return ( $info->{date}, 
    1676         $Foswiki::Plugins::SESSION->{users}->getWikiName( $info->{author} ), 
    1677         $info->{version}, $info->{comment} ); 
    1678 } 
    1679  
    1680 =begin TML 
    1681  
    1682 ---+++ getRevisionAtTime( $web, $topic, $time ) -> $rev 
    1683  
    1684 Get the revision number of a topic at a specific time. 
    1685    * =$web= - web for topic 
    1686    * =$topic= - topic 
    1687    * =$time= - time (in epoch secs) for the rev 
    1688 Return: Single-digit revision number, or undef if it couldn't be determined 
    1689 (either because the topic isn't that old, or there was a problem) 
    1690  
    1691 =cut 
    1692  
    1693 sub getRevisionAtTime { 
    1694     my ( $web, $topic, $time ) = @_; 
    1695     ($web, $topic) = _validateWTA($web, $topic); 
    1696     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    1697     my $topicObject = 
    1698       Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
    1699     return $topicObject->getRevisionAtTime($time); 
    1700 } 
    1701  
    1702 =begin TML 
    1703  
    1704 ---+++ readTopic( $web, $topic, $rev ) -> ( $meta, $text ) 
    1705  
    1706 Read topic text and meta data, regardless of access permissions. 
    1707    * =$web= - Web name, required, e.g. ='Main'= 
    1708    * =$topic= - Topic name, required, e.g. ='TokyoOffice'= 
    1709    * =$rev= - revision to read (default latest) 
    1710 Return: =( $meta, $text )= Meta data object and topic text 
    1711  
    1712 =$meta= is a perl 'object' of class =Foswiki::Meta=. This class is 
    1713 fully documented in the source code documentation shipped with the 
    1714 release, or can be inspected in the =lib/Foswiki/Meta.pm= file. 
    1715  
    1716 This method *ignores* topic access permissions. You should be careful to use 
    1717 =checkAccessPermission= to ensure the current user has read access to the 
    1718 topic. 
    1719  
    1720 =cut 
    1721  
    1722 sub readTopic { 
    1723  
    1724     my( $web, $topic, $rev ) = @_; 
    1725     ($web, $topic) = _validateWTA($web, $topic); 
    1726     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    1727  
    1728     my $meta = Foswiki::Meta->load( 
    1729         $Foswiki::Plugins::SESSION, $web, $topic, $rev ); 
    1730     return ( $meta, $meta->text() ); 
    1731 } 
    1732  
    1733 =begin TML 
    1734  
    1735 ---+++ getAttachmentList( $web, $topic ) -> @list 
    1736 Get a list of the attachments on the given topic. 
    1737  
    1738 *Since:* 31 Mar 2009 
    1739  
    1740 =cut 
    1741  
    1742 sub getAttachmentList { 
    1743     my ( $web, $topic ) = @_; 
    1744     ($web, $topic) = _validateWTA($web, $topic); 
    1745     my $topicObject = 
    1746       Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
    1747     my $it = $topicObject->eachAttachment(); 
    1748     return sort $it->all(); 
    1749 } 
    1750  
    1751 =begin TML 
    1752  
    1753 ---+++ attachmentExists( $web, $topic, $attachment ) -> $boolean 
    1754  
    1755 Test if attachment exists 
    1756    * =$web=   - Web name, optional, e.g. =Main=. 
    1757    * =$topic= - Topic name, required, e.g. =TokyoOffice=, or =Main.TokyoOffice= 
    1758    * =$attachment= - attachment name, e.g.=logo.gif= 
    1759 $web and $topic are parsed as described in the documentation for =normalizeWebTopicName=. 
    1760  
    1761 =cut 
    1762  
    1763 sub attachmentExists { 
    1764     my ( $web, $topic, $attachment ) = _checkWTA(@_); 
    1765     return 0 unless defined $web && defined $topic && defined $attachment; 
    1766  
    1767     my $topicObject = 
    1768       Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
    1769     return $topicObject->hasAttachment($attachment); 
    1770 } 
    1771  
    1772 =begin TML 
    1773  
    1774 ---+++ readAttachment( $web, $topic, $name, $rev ) -> $data 
    1775  
    1776    * =$web= - web for topic - must not be tainted 
    1777    * =$topic= - topic - must not be tainted 
    1778    * =$name= - attachment name - must not be tainted 
    1779    * =$rev= - revision to read (default latest) 
    1780 Read an attachment from the store for a topic, and return it as a string. The 
    1781 names of attachments on a topic can be recovered from the meta-data returned 
    1782 by =readTopic=. If the attachment does not exist, or cannot be read, undef 
    1783 will be returned. If the revision is not specified, the latest version will 
    1784 be returned. 
    1785  
    1786 View permission on the topic is required for the 
    1787 read to be successful.  Access control violations are flagged by a 
    1788 Foswiki::AccessControlException. Permissions are checked for the current user. 
    1789  
    1790 <verbatim> 
    1791 my( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic ); 
    1792 my @attachments = $meta->find( 'FILEATTACHMENT' ); 
    1793 foreach my $a ( @attachments ) { 
    1794    try { 
    1795        my $data = Foswiki::Func::readAttachment( $web, $topic, $a->{name} ); 
    1796        ... 
    1797    } catch Foswiki::AccessControlException with { 
    1798    }; 
    1799 } 
    1800 </verbatim> 
    1801  
    1802 This is the way 99% of extensions will access attachments. 
    1803 See =Foswiki::Meta::openAttachment= for a lower level interface that does 
    1804 not check access controls. 
    1805  
    1806 =cut 
    1807  
    1808 sub readAttachment { 
    1809     my ( $web, $topic, $attachment, $rev ) = @_; 
    1810  
    1811     ( $web, $topic, $attachment ) = _validateWTA($web, $topic, $attachment); 
    1812  
    1813     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    1814     my $result; 
    1815  
    1816     my $topicObject = 
    1817       Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
    1818     unless ( $topicObject->haveAccess('VIEW') ) { 
    1819         throw Foswiki::AccessControlException( 'VIEW', 
    1820             $Foswiki::Plugins::SESSION->{user}, 
    1821             $web, $topic, $Foswiki::Meta::reason ); 
    1822     } 
    1823     my $fh; 
    1824     try { 
    1825         $fh = $topicObject->openAttachment( $attachment, '<', version => $rev ); 
    1826     } 
    1827     catch Error::Simple with { 
    1828         $fh = undef; 
    1829     }; 
    1830     return undef unless $fh; 
    1831     local $/; 
    1832     my $data = <$fh>; 
    1833     return $data; 
    18342054} 
    18352055 
     
    20532273=begin TML 
    20542274 
    2055 ---++ Assembling Pages 
     2275---++ Finding changes 
     2276 
     2277=cut 
     2278 
     2279=begin TML 
     2280 
     2281---+++ eachChangeSince($web, $time) -> $iterator 
     2282 
     2283Get an iterator over the list of all the changes in the given web between 
     2284=$time= and now. $time is a time in seconds since 1st Jan 1970, and is not 
     2285guaranteed to return any changes that occurred before (now -  
     2286{Store}{RememberChangesFor}). {Store}{RememberChangesFor}) is a 
     2287setting in =configure=. Changes are returned in *most-recent-first* 
     2288order. 
     2289 
     2290Use it as follows: 
     2291<verbatim> 
     2292    my $iterator = Foswiki::Func::eachChangeSince( 
     2293        $web, time() - 7 * 24 * 60 * 60); # the last 7 days 
     2294    while ($iterator->hasNext()) { 
     2295        my $change = $iterator->next(); 
     2296        # $change is a perl hash that contains the following fields: 
     2297        # topic => topic name 
     2298        # user => wikiname - wikiname of user who made the change 
     2299        # time => time of the change 
     2300        # revision => revision number *after* the change 
     2301        # more => more info about the change (e.g. 'minor') 
     2302    } 
     2303</verbatim> 
     2304 
     2305=cut 
     2306 
     2307sub eachChangeSince { 
     2308    my ( $web, $time ) = @_; 
     2309    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     2310    ($web) = _validateWTA($web); 
     2311    ASSERT( $Foswiki::Plugins::SESSION->webExists($web) ) if DEBUG; 
     2312 
     2313    my $webObject = Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web ); 
     2314 
     2315    # eachChange returns changes with cUIDs. these have to be mapped 
     2316    # to wikinames per the Foswiki::Func 'spec' (changes used to be stored 
     2317    # with wikinames) 
     2318    require Foswiki::Iterator::ProcessIterator; 
     2319    require Foswiki::Users::BaseUserMapping; 
     2320    return new Foswiki::Iterator::ProcessIterator( 
     2321        $webObject->eachChange($time), 
     2322        sub { 
     2323            my $n = shift; 
     2324            $n->{user} = $Foswiki::Users::BaseUserMapping::UNKNOWN_USER_CUID 
     2325              unless defined $n->{user}; 
     2326            $n->{user} = 
     2327              $Foswiki::Plugins::SESSION->{users}->getWikiName( $n->{user} ); 
     2328            return $n; 
     2329        } 
     2330    ); 
     2331} 
     2332 
     2333=begin TML 
     2334 
     2335---+++ summariseChanges($web, $topic, $orev, $nrev, $tml) -> $text 
     2336Generate a summary of the changes between rev $orev and rev $nrev of the 
     2337given topic. 
     2338   * =$web=, =$topic= - topic (required) 
     2339   * =$orev= - older rev (required) 
     2340   * =$nrev= - later rev (may be undef for the latest) 
     2341   * =$tml= - if true will generate renderable TML (i.e. HTML with NOPs. if false will generate a summary suitable for use in plain text (mail, for example) 
     2342Generate a (max 3 line) summary of the differences between the revs. 
     2343 
     2344If there is only one rev, a topic summary will be returned. 
     2345 
     2346If =$tml= is not set, all HTML will be removed. 
     2347 
     2348In non-tml, lines are truncated to 70 characters. Differences are shown using + and - to indicate added and removed text. 
     2349 
     2350If access is denied to either revision, then it will be treated as blank 
     2351text. 
     2352 
     2353*Since* 2009-03-06 
     2354 
     2355=cut 
     2356 
     2357sub summariseChanges { 
     2358    my ( $web, $topic, $orev, $nrev, $tml ) = @_; 
     2359    ($web, $topic) = _validateWTA($web, $topic); 
     2360 
     2361    my $topicObject = 
     2362      Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
     2363    return $topicObject->summariseChanges( Foswiki::Store::cleanUpRevID($orev), 
     2364        Foswiki::Store::cleanUpRevID($nrev), $tml ); 
     2365} 
     2366 
     2367=begin TML 
     2368 
     2369---++ Templates 
    20562370 
    20572371=cut 
     
    21282442=begin TML 
    21292443 
    2130 ---+++ writeHeader() 
    2131  
    2132 Prints a basic content-type HTML header for text/html to standard out. 
    2133  
    2134 =cut 
    2135  
    2136 sub writeHeader { 
    2137     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2138     $Foswiki::Plugins::SESSION->generateHTTPHeaders(); 
    2139 } 
    2140  
    2141 =begin TML 
    2142  
    2143 ---+++ redirectCgiQuery( $query, $url, $passthru ) 
    2144  
    2145 Redirect to URL 
    2146    * =$query= - CGI query object. Ignored, only there for compatibility. The session CGI query object is used instead. 
    2147    * =$url=   - URL to redirect to 
    2148    * =$passthru= - enable passthrough. 
    2149  
    2150 Return:             none 
    2151  
    2152 Print output to STDOUT that will cause a 302 redirect to a new URL. 
    2153 Nothing more should be printed to STDOUT after this method has been called. 
    2154  
    2155 The =$passthru= parameter allows you to pass the parameters that were passed 
    2156 to the current query on to the target URL, as long as it is another URL on the 
    2157 same installation. If =$passthru= is set to a true value, then Foswiki 
    2158 will save the current URL parameters, and then try to restore them on the 
    2159 other side of the redirect. Parameters are stored on the server in a cache 
    2160 file. 
    2161  
    2162 Note that if =$passthru= is set, then any parameters in =$url= will be lost 
    2163 when the old parameters are restored. if you want to change any parameter 
    2164 values, you will need to do that in the current CGI query before redirecting 
    2165 e.g. 
    2166 <verbatim> 
    2167 my $query = Foswiki::Func::getRequestObject(); 
    2168 $query->param(-name => 'text', -value => 'Different text'); 
    2169 Foswiki::Func::redirectCgiQuery( 
    2170   undef, Foswiki::Func::getScriptUrl($web, $topic, 'edit'), 1); 
    2171 </verbatim> 
    2172 =$passthru= does nothing if =$url= does not point to a script in the current 
    2173 Foswiki installation. 
    2174  
    2175 =cut 
    2176  
    2177 sub redirectCgiQuery { 
    2178     my ( $query, $url, $passthru ) = @_; 
    2179     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2180     return $Foswiki::Plugins::SESSION->redirect( $url, $passthru ); 
    2181 } 
    2182  
    2183 =begin TML 
    2184  
    2185 ---+++ addToZone( $zone, $id, $data, $requires ) 
    2186  
    2187 Direct interface to %<nop>ADDTOZONE (see %SYSTEMWEB%.VarADDTOZONE) 
    2188  
    2189    * =$zone= - name of the zone 
    2190    * =$id= - unique ID 
    2191    * =$data= - the content. 
    2192    * =requires= optional, comma-separated list of =$id= identifiers that should 
    2193      precede the content 
    2194  
    2195 All macros present in =$data= will be expanded before being inserted into the =<head>= section. 
    2196  
    2197 <blockquote class="foswikiHelp">%X% 
    2198 *Note:* Read the developer supplement at Foswiki:Development.AddToZoneFromPluginHandlers if you are 
    2199 calling =addToZone()= from a rendering or macro/tag-related plugin handler 
    2200 </blockquote> 
    2201  
    2202 Examples: 
    2203 <verbatim> 
    2204 Foswiki::Func::addToZone( 'head', 'PATTERN_STYLE', 
    2205    '<link rel="stylesheet" type="text/css" href="%PUBURL%/Foswiki/PatternSkin/layout.css" media="all" />'); 
    2206  
    2207 Foswiki::Func::addToZone( 'body', 'MY_JQUERY', 
    2208    '<script type="text/javascript" src="%PUBURL%/Myweb/MyJQuery/myjquery.js"></scipt>', 
    2209    'JQUERYPLUGIN::FOSWIKI'); 
    2210 </verbatim> 
    2211  
    2212 =cut= 
    2213  
    2214 sub addToZone { 
    2215  
    2216     #my ( $zone, $tag, $data, $requires ) = @_; 
    2217     my $session = $Foswiki::Plugins::SESSION; 
    2218     ASSERT($session) if DEBUG; 
    2219  
    2220     $session->addToZone(@_); 
    2221 } 
     2444---++ Rendering 
     2445 
     2446=cut 
    22222447 
    22232448=begin TML 
     
    22462471    return $meta->expandMacros($text); 
    22472472} 
    2248  
    2249 =begin TML 
    2250  
    2251 ---+++ renderText( $text, $web, $topic ) -> $text 
    2252  
    2253 Render text from TML into XHTML as defined in [[%SYSTEMWEB%.TextFormattingRules]] 
    2254    * =$text= - Text to render, e.g. ='*bold* text and =fixed font='= 
    2255    * =$web=  - Web name, optional, e.g. ='Main'=. The current web is taken if missing 
    2256    * =$topic= - topic name, optional, defaults to web home 
    2257 Return: =$text=    XHTML text, e.g. ='&lt;b>bold&lt;/b> and &lt;code>fixed font&lt;/code>'= 
    2258  
    2259 =cut 
    2260  
    2261 sub renderText { 
    2262  
    2263     my ( $text, $web, $topic ) = @_; 
    2264     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2265     $web   ||= $Foswiki::Plugins::SESSION->{webName}; 
    2266     $topic ||= $Foswiki::cfg{HomeTopicName}; 
    2267     my $webObject = 
    2268       Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
    2269     return $webObject->renderTML($text); 
    2270 } 
    2271  
    2272 =begin TML 
    2273  
    2274 ---+++ internalLink( $pre, $web, $topic, $label, $anchor, $createLink ) -> $text 
    2275  
    2276 Render topic name and link label into an XHTML link. Normally you do not need to call this funtion, it is called internally by =renderText()= 
    2277    * =$pre=        - Text occuring before the link syntax, optional 
    2278    * =$web=        - Web name, required, e.g. ='Main'= 
    2279    * =$topic=      - Topic name to link to, required, e.g. ='WebNotify'= 
    2280    * =$label=      - Link label, required. Usually the same as =$topic=, e.g. ='notify'= 
    2281    * =$anchor=     - Anchor, optional, e.g. ='#Jump'= 
    2282    * =$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 
    2283 Return: =$text=          XHTML anchor, e.g. ='&lt;a href='/cgi-bin/view/Main/WebNotify#Jump'>notify&lt;/a>'= 
    2284  
    2285 =cut 
    2286  
    2287 sub internalLink { 
    2288     my $pre = shift; 
    2289     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2290  
    2291     #   my( $web, $topic, $label, $anchor, $anchor, $createLink ) = @_; 
    2292     return $pre . $Foswiki::Plugins::SESSION->renderer->internalLink(@_); 
    2293 } 
    2294  
    2295 =begin TML 
    2296  
    2297 ---++ E-mail 
    2298  
    2299 ---+++ sendEmail ( $text, $retries ) -> $error 
    2300  
    2301    * =$text= - text of the mail, including MIME headers 
    2302    * =$retries= - number of times to retry the send (default 1) 
    2303 Send an e-mail specified as MIME format content. To specify MIME 
    2304 format mails, you create a string that contains a set of header 
    2305 lines that contain field definitions and a message body such as: 
    2306 <verbatim> 
    2307 To: liz@windsor.gov.uk 
    2308 From: serf@hovel.net 
    2309 CC: george@whitehouse.gov 
    2310 Subject: Revolution 
    2311  
    2312 Dear Liz, 
    2313  
    2314 Please abolish the monarchy (with King George's permission, of course) 
    2315  
    2316 Thanks, 
    2317  
    2318 A. Peasant 
    2319 </verbatim> 
    2320 Leave a blank line between the last header field and the message body. 
    2321  
    2322 =cut 
    2323  
    2324 sub sendEmail { 
    2325  
    2326     #my( $text, $retries ) = @_; 
    2327     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2328     return $Foswiki::Plugins::SESSION->net->sendEmail(@_); 
    2329 } 
    2330  
    2331 =begin TML 
    2332  
    2333 ---++ Creating New Topics 
    2334  
    2335 =cut 
    23362473 
    23372474=begin TML 
     
    23732510=begin TML 
    23742511 
    2375 ---++ Special handlers 
    2376  
    2377 Special handlers can be defined to make functions in plugins behave as if they were built-in. 
    2378  
    2379 =cut 
    2380  
    2381 =begin TML= 
    2382  
    2383 ---+++ registerTagHandler( $var, \&fn, $syntax ) 
    2384  
    2385 Should only be called from initPlugin. 
    2386  
    2387 Register a function to handle a simple variable. Handles both %<nop>VAR% and  
    2388 %<nop>VAR{...}%. Registered variables are treated the same as internal macros,  
    2389 and are expanded at the same time. This is a _lot_ more efficient than using the =commonTagsHandler=. 
    2390    * =$var= - The name of the variable, i.e. the 'MYVAR' part of %<nop>MYVAR%.  
    2391    The variable name *must* match /^[A-Z][A-Z0-9_]*$/ or it won't work. 
    2392    * =\&fn= - Reference to the handler function. 
    2393    * =$syntax= can be 'classic' (the default) or 'context-free'. (context-free may be removed in future) 
    2394    'classic' syntax is appropriate where you want the variable to support classic syntax  
    2395    i.e. to accept the standard =%<nop>MYVAR{ "unnamed" param1="value1" param2="value2" }%= syntax,  
    2396    as well as an unquoted default parameter, such as =%<nop>MYVAR{unquoted parameter}%=.  
    2397    If your variable will only use named parameters, you can use 'context-free' syntax,  
    2398    which supports a more relaxed syntax. For example,  
    2399    %MYVAR{param1=value1, value 2, param3="value 3", param4='value 5"}% 
    2400  
    2401 The variable handler function must be of the form: 
    2402 <verbatim> 
    2403 sub handler(\%session, \%params, $topic, $web, $topicObject) 
    2404 </verbatim> 
    2405 where: 
    2406    * =\%session= - a reference to the session object (may be ignored) 
    2407    * =\%params= - a reference to a Foswiki::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. 
    2408    * =$topic= - name of the topic in the query 
    2409    * =$web= - name of the web in the query 
    2410    * =$topicObject= - is the Foswiki::Meta object for the topic *Since* 2009-03-06 
    2411 for example, to execute an arbitrary command on the server, you might do this: 
    2412 <verbatim> 
    2413 sub initPlugin{ 
    2414    Foswiki::Func::registerTagHandler('EXEC', \&boo); 
    2415 } 
    2416  
    2417 sub boo { 
    2418     my( $session, $params, $topic, $web, $topicObject ) = @_; 
    2419     my $cmd = $params->{_DEFAULT}; 
    2420  
    2421     return "NO COMMAND SPECIFIED" unless $cmd; 
    2422  
    2423     my $result = `$cmd 2>&1`; 
    2424     return $params->{silent} ? '' : $result; 
    2425 } 
    2426 </verbatim> 
    2427 would let you do this: 
    2428 =%<nop>EXEC{"ps -Af" silent="on"}%= 
    2429  
    2430 Registered tags differ from tags implemented using the old approach (text substitution in =commonTagsHandler=) in the following ways: 
    2431    * 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). 
    2432    * registered tag names can only contain alphanumerics and _ (underscore) 
    2433    * registering a tag =FRED= defines both =%<nop>FRED{...}%= *and also* =%FRED%=. 
    2434    * registered tag handlers *cannot* return another tag as their only result (e.g. =return '%<nop>SERVERTIME%';=). It won't work. 
    2435  
    2436 =cut 
    2437  
    2438 sub registerTagHandler { 
    2439     my ( $tag, $function, $syntax ) = @_; 
    2440     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2441  
    2442     # $pluginContext is undefined if a contrib registers a tag handler. 
    2443     my $pluginContext; 
    2444     if ( caller =~ m/^Foswiki::Plugins::(\w+)/ ) { 
    2445         $pluginContext = $1 . 'Enabled'; 
    2446     } 
    2447  
    2448     # Use an anonymous function so it gets inlined at compile time. 
    2449     # Make sure we don't mangle the session reference. 
    2450     Foswiki::registerTagHandler( 
    2451         $tag, 
    2452         sub { 
    2453             my ( $session, $params, $topicObject ) = @_; 
    2454             my $record = $Foswiki::Plugins::SESSION; 
    2455             $Foswiki::Plugins::SESSION = $_[0]; 
    2456  
    2457             # $pluginContext is defined for all plugins 
    2458             # but never defined for contribs. 
    2459             # This is convenient, because contribs cannot be disabled 
    2460             # at run-time, either. 
    2461             if ( defined $pluginContext ) { 
    2462  
    2463                 # Registered tag handlers should only be called if the plugin 
    2464                 # is enabled. Disabled plugins can still have tag handlers 
    2465                 # registered in persistent environments (e.g. modperl) 
    2466                 # and also for rest handlers that disable plugins. 
    2467                 # See Item1871 
    2468                 return unless $session->inContext($pluginContext); 
    2469             } 
    2470  
    2471             # Compatibility; expand $topicObject to the topic and web 
    2472             my $result = 
    2473               &$function( $session, $params, $topicObject->topic, 
    2474                 $topicObject->web, $topicObject ); 
    2475             $Foswiki::Plugins::SESSION = $record; 
    2476             return $result; 
    2477         }, 
    2478         $syntax 
    2479     ); 
    2480 } 
    2481  
    2482 =begin TML= 
    2483  
    2484 ---+++ registerRESTHandler( $alias, \&fn, %options ) 
    2485  
    2486 Should only be called from initPlugin. 
    2487  
    2488 Adds a function to the dispatch table of the REST interface  
    2489    * =$alias= - The name . 
    2490    * =\&fn= - Reference to the function. 
    2491    * =%options= - additional options affecting the handler 
    2492 The handler function must be of the form: 
    2493 <verbatim> 
    2494 sub handler(\%session) 
    2495 </verbatim> 
    2496 where: 
    2497    * =\%session= - a reference to the Foswiki session object (may be ignored) 
    2498  
    2499 From the REST interface, the name of the plugin must be used 
    2500 as the subject of the invokation. 
    2501  
    2502 Additional options are set in the =%options= hash. These options are important 
    2503 to ensuring that requests to your handler can't be used in cross-scripting 
    2504 attacks, or used for phishing. 
    2505    * =authenticate= - use this boolean option to require authentication for the 
    2506      handler. If this is set, then an authenticated session must be in place 
    2507      or the REST call will be rejected with a 401 (Unauthorized) status code. 
    2508      By default, rest handlers do *not* require authentication. 
    2509    * =validate= - use this boolean option to require validation of any requests 
    2510      made to this handler. Validation is the process by which a secret key 
    2511      is passed to the server so it can identify the origin of the request. 
    2512      By default, requests made to REST handlers are not validated. 
    2513    * =http_allow= use this option to specify that the HTTP methods that can 
    2514      be used to invoke the handler. For example, =http_allow=>'POST,GET'= will 
    2515      constrain the handler to be invoked using POST and GET, but not other 
    2516      HTTP methods, such as DELETE. Normally you will use http_allow=>'POST'. 
    2517      Together with authentication this is an important security tool. 
    2518      Handlers that can be invoked using GET are vulnerable to being called 
    2519      in the =src= parameter of =img= tags, a common method for cross-site 
    2520      request forgery (CSRF) attacks. This option is set automatically if 
    2521      =authenticate= is specified. 
    2522  
    2523 ---++++ Example 
    2524  
    2525 The EmptyPlugin has the following call in the initPlugin handler: 
    2526 <verbatim> 
    2527    Foswiki::Func::registerRESTHandler('example', \&restExample, 
    2528      http_allow=>'GET,POST'); 
    2529 </verbatim> 
    2530  
    2531 This adds the =restExample= function to the REST dispatch table 
    2532 for the EmptyPlugin under the 'example' alias, and allows it 
    2533 to be invoked using the URL 
    2534  
    2535 =http://server:port/bin/rest/EmptyPlugin/example= 
    2536  
    2537 note that the URL 
    2538  
    2539 =http://server:port/bin/rest/EmptyPlugin/restExample= 
    2540  
    2541 (ie, with the name of the function instead of the alias) will not work. 
    2542  
    2543 ---++++ Calling REST handlers from the command-line 
    2544 The =rest= script allows handlers to be invoked from the command line. The 
    2545 script is invoked passing the parameters as described in CommandAndCGIScripts. 
    2546 If the handler requires authentication ( =authenticate=>1= ) then this can 
    2547 be passed in the username and =password= parameters. 
    2548  
    2549 For example, 
    2550  
    2551 =perl -wT rest /EmptyPlugin/example -username HughPugh -password trumpton= 
    2552  
    2553 =cut 
    2554  
    2555 sub registerRESTHandler { 
    2556     my ( $alias, $function, %options ) = @_; 
    2557     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2558     my $plugin = caller; 
    2559     $plugin =~ s/.*:://;    # strip off Foswiki::Plugins:: prefix 
    2560  
    2561     # Use an anonymous function so it gets inlined at compile time. 
    2562     # Make sure we don't mangle the session reference. 
    2563     require Foswiki::UI::Rest; 
    2564     Foswiki::UI::Rest::registerRESTHandler( 
    2565         $plugin, $alias, 
    2566         sub { 
    2567             my $record = $Foswiki::Plugins::SESSION; 
    2568             $Foswiki::Plugins::SESSION = $_[0]; 
    2569             my $result = &$function(@_); 
    2570             $Foswiki::Plugins::SESSION = $record; 
    2571             return $result; 
    2572         }, 
    2573         %options 
    2574     ); 
    2575 } 
    2576  
    2577 =begin TML 
    2578  
    2579 ---++ registerMETA($name, %syntax) 
    2580  
    2581 Foswiki supports embedding meta-data into topics. For example, 
    2582  
    2583 =%<nop>META:BOOK{title="Transit" author="Edmund Cooper" isbn="0-571-05724-1"}%= 
    2584  
    2585 This meta-data is validated when it is read from the store. Meta-data 
    2586 that is not registered, or doesn't pass validation, is ignored. This 
    2587 function allows you to register a new META datum, passing the name in 
    2588 =$name=. =%syntax= is a set of optional checks that describe how to 
    2589 validate the fields of the datum. 
    2590  
    2591 The following checks are supported: 
    2592  
    2593 =function=>\&fn= In this case the function =fn= will be called when the 
    2594 datum is encountered, passing in the name of the macro and the 
    2595 argument hash. The function must return a non-zero/undef value if the tag 
    2596 is acceptable, or 0 otherwise. For example: 
    2597 <verbatim> 
    2598 registerMETA('BOOK', function => sub { 
    2599     my ($name, $args) = @_; 
    2600     # $name will be BOOK 
    2601     return defined $args->{title}; 
    2602 } 
    2603 </verbatim> 
    2604 can be used to check that =%META:BOOK{}= contains a title. 
    2605  
    2606 =require=>[]= is used to check that a list of named parameters are present on 
    2607 the tag. For example, 
    2608 <verbatim> 
    2609 registerMETA('BOOK', require => [ 'title', 'author' ]); 
    2610 </verbatim> 
    2611 can be used to check that both =title= and =author= are present. 
    2612  
    2613 =allow=>[]= lets you specify other optional parameters that are allowed 
    2614 on the tag. If you specify =allow= then the validation will fail if the 
    2615 tag contains any parameters that are _not_ in the =allow= or =require= lists. 
    2616 If you don't specify =allow= then all parameters will be allowed. 
    2617  
    2618 Checks are cumulative, so if you: 
    2619 <verbatim> 
    2620 registerMETA('BOOK', 
    2621     function => \&checkParameters, 
    2622     require => [ 'title' ], 
    2623     allow => [ 'author', 'isbn' ]); 
    2624 </verbatim> 
    2625 then all these conditions will be tested. Note that =require= and =allow= 
    2626 are tested _after_ =function= is called, to give the function a chance to 
    2627 rewrite the parameter list. 
    2628  
    2629 If no checker is registered for a META tag, then it will automatically 
    2630 be accepted into the topic meta-data. 
    2631  
    2632 Note that the checker only verifies the *presence* of parameters, and 
    2633 not their *values*. 
    2634  
    2635 =cut 
    2636  
    2637 sub registerMETA { 
    2638     my ( $macro, $spec ) = @_; 
    2639     Foswiki::Meta::registerMETA( $macro, $spec ); 
    2640 } 
    2641  
    2642 =begin TML 
    2643  
    2644 ---+++ decodeFormatTokens($str) -> $unencodedString 
    2645  
    2646 Foswiki has an informal standard set of tokens used in =format= 
    2647 parameters that are used to block evaluation of paramater strings. 
    2648 For example, if you were to write 
    2649  
    2650 =%<nop>MYTAG{format="%<nop>WURBLE%"}%= 
    2651  
    2652 then %<nop>WURBLE would be expanded *before* %<NOP>MYTAG is evaluated. To avoid 
    2653 this Foswiki uses escapes in the format string. For example: 
    2654  
    2655 =%<nop>MYTAG{format="$percentWURBLE$percent"}%= 
    2656  
    2657 This lets you enter arbitrary strings into parameters without worrying that 
    2658 Foswiki will expand them before your plugin gets a chance to deal with them 
    2659 properly. Once you have processed your tag, you will want to expand these 
    2660 tokens to their proper value. That's what this function does. 
    2661  
    2662 The set of tokens that is expanded is described in System.FormatTokens. 
    2663  
    2664 =cut 
    2665  
    2666 sub decodeFormatTokens { 
    2667     return Foswiki::expandStandardEscapes(@_); 
    2668 } 
    2669  
    2670 =begin TML 
    2671  
    2672 ---++ Searching 
    2673  
    2674 =cut 
     2512---+++ renderText( $text, $web, $topic ) -> $text 
     2513 
     2514Render text from TML into XHTML as defined in [[%SYSTEMWEB%.TextFormattingRules]] 
     2515   * =$text= - Text to render, e.g. ='*bold* text and =fixed font='= 
     2516   * =$web=  - Web name, optional, e.g. ='Main'=. The current web is taken if missing 
     2517   * =$topic= - topic name, optional, defaults to web home 
     2518Return: =$text=    XHTML text, e.g. ='&lt;b>bold&lt;/b> and &lt;code>fixed font&lt;/code>'= 
     2519 
     2520=cut 
     2521 
     2522sub renderText { 
     2523 
     2524    my ( $text, $web, $topic ) = @_; 
     2525    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     2526    $web   ||= $Foswiki::Plugins::SESSION->{webName}; 
     2527    $topic ||= $Foswiki::cfg{HomeTopicName}; 
     2528    my $webObject = 
     2529      Foswiki::Meta->new( $Foswiki::Plugins::SESSION, $web, $topic ); 
     2530    return $webObject->renderTML($text); 
     2531} 
     2532 
     2533=begin TML 
     2534 
     2535---+++ internalLink( $pre, $web, $topic, $label, $anchor, $createLink ) -> $text 
     2536 
     2537Render topic name and link label into an XHTML link. Normally you do not need to call this funtion, it is called internally by =renderText()= 
     2538   * =$pre=        - Text occuring before the link syntax, optional 
     2539   * =$web=        - Web name, required, e.g. ='Main'= 
     2540   * =$topic=      - Topic name to link to, required, e.g. ='WebNotify'= 
     2541   * =$label=      - Link label, required. Usually the same as =$topic=, e.g. ='notify'= 
     2542   * =$anchor=     - Anchor, optional, e.g. ='#Jump'= 
     2543   * =$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 
     2544Return: =$text=          XHTML anchor, e.g. ='&lt;a href='/cgi-bin/view/Main/WebNotify#Jump'>notify&lt;/a>'= 
     2545 
     2546=cut 
     2547 
     2548sub internalLink { 
     2549    my $pre = shift; 
     2550    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     2551 
     2552    #   my( $web, $topic, $label, $anchor, $anchor, $createLink ) = @_; 
     2553    return $pre . $Foswiki::Plugins::SESSION->renderer->internalLink(@_); 
     2554} 
     2555 
     2556=begin TML 
     2557 
     2558---+++ addToZone( $zone, $id, $data, $requires ) 
     2559 
     2560Direct interface to %<nop>ADDTOZONE (see %SYSTEMWEB%.VarADDTOZONE) 
     2561 
     2562   * =$zone= - name of the zone 
     2563   * =$id= - unique ID 
     2564   * =$data= - the content. 
     2565   * =requires= optional, comma-separated list of =$id= identifiers that should 
     2566     precede the content 
     2567 
     2568All macros present in =$data= will be expanded before being inserted into the =<head>= section. 
     2569 
     2570<blockquote class="foswikiHelp">%X% 
     2571*Note:* Read the developer supplement at Foswiki:Development.AddToZoneFromPluginHandlers if you are 
     2572calling =addToZone()= from a rendering or macro/tag-related plugin handler 
     2573</blockquote> 
     2574 
     2575Examples: 
     2576<verbatim> 
     2577Foswiki::Func::addToZone( 'head', 'PATTERN_STYLE', 
     2578   '<link rel="stylesheet" type="text/css" href="%PUBURL%/Foswiki/PatternSkin/layout.css" media="all" />'); 
     2579 
     2580Foswiki::Func::addToZone( 'body', 'MY_JQUERY', 
     2581   '<script type="text/javascript" src="%PUBURL%/Myweb/MyJQuery/myjquery.js"></scipt>', 
     2582   'JQUERYPLUGIN::FOSWIKI'); 
     2583</verbatim> 
     2584 
     2585=cut= 
     2586 
     2587sub addToZone { 
     2588 
     2589    #my ( $zone, $tag, $data, $requires ) = @_; 
     2590    my $session = $Foswiki::Plugins::SESSION; 
     2591    ASSERT($session) if DEBUG; 
     2592 
     2593    $session->addToZone(@_); 
     2594} 
     2595 
     2596=begin TML 
     2597 
     2598---++ Controlling page output 
     2599 
     2600=cut 
     2601 
     2602=begin TML 
     2603 
     2604---+++ writeHeader() 
     2605 
     2606Prints a basic content-type HTML header for text/html to standard out. 
     2607 
     2608=cut 
     2609 
     2610sub writeHeader { 
     2611    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     2612    $Foswiki::Plugins::SESSION->generateHTTPHeaders(); 
     2613} 
     2614 
     2615=begin TML 
     2616 
     2617---+++ redirectCgiQuery( $query, $url, $passthru ) 
     2618 
     2619Redirect to URL 
     2620   * =$query= - CGI query object. Ignored, only there for compatibility. The session CGI query object is used instead. 
     2621   * =$url=   - URL to redirect to 
     2622   * =$passthru= - enable passthrough. 
     2623 
     2624Return:             none 
     2625 
     2626Print output to STDOUT that will cause a 302 redirect to a new URL. 
     2627Nothing more should be printed to STDOUT after this method has been called. 
     2628 
     2629The =$passthru= parameter allows you to pass the parameters that were passed 
     2630to the current query on to the target URL, as long as it is another URL on the 
     2631same installation. If =$passthru= is set to a true value, then Foswiki 
     2632will save the current URL parameters, and then try to restore them on the 
     2633other side of the redirect. Parameters are stored on the server in a cache 
     2634file. 
     2635 
     2636Note that if =$passthru= is set, then any parameters in =$url= will be lost 
     2637when the old parameters are restored. if you want to change any parameter 
     2638values, you will need to do that in the current CGI query before redirecting 
     2639e.g. 
     2640<verbatim> 
     2641my $query = Foswiki::Func::getRequestObject(); 
     2642$query->param(-name => 'text', -value => 'Different text'); 
     2643Foswiki::Func::redirectCgiQuery( 
     2644  undef, Foswiki::Func::getScriptUrl($web, $topic, 'edit'), 1); 
     2645</verbatim> 
     2646=$passthru= does nothing if =$url= does not point to a script in the current 
     2647Foswiki installation. 
     2648 
     2649=cut 
     2650 
     2651sub redirectCgiQuery { 
     2652    my ( $query, $url, $passthru ) = @_; 
     2653    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     2654    return $Foswiki::Plugins::SESSION->redirect( $url, $passthru ); 
     2655} 
     2656 
     2657=begin TML 
     2658 
     2659---++ Plugin-specific file handling 
     2660 
     2661=cut 
     2662 
     2663=begin TML 
     2664 
     2665---+++ getWorkArea( $pluginName ) -> $directorypath 
     2666 
     2667Gets a private directory for Plugin use. The Plugin is entirely responsible 
     2668for managing this directory; Foswiki will not read from it, or write to it. 
     2669 
     2670The directory is guaranteed to exist, and to be writable by the webserver 
     2671user. By default it will *not* be web accessible. 
     2672 
     2673The directory and its contents are permanent, so Plugins must be careful 
     2674to keep their areas tidy. 
     2675 
     2676=cut 
     2677 
     2678sub getWorkArea { 
     2679    my ($plugin) = @_; 
     2680    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     2681    return $Foswiki::Plugins::SESSION->getWorkArea($plugin); 
     2682} 
     2683 
     2684=begin TML 
     2685 
     2686---+++ readFile( $filename ) -> $text 
     2687 
     2688Read file, low level. Used for Plugin workarea. 
     2689   * =$filename= - Full path name of file 
     2690Return: =$text= Content of file, empty if not found 
     2691 
     2692__NOTE:__ Use this function only for the Plugin workarea, *not* for topics and attachments. Use the appropriate functions to manipulate topics and attachments. 
     2693 
     2694=cut 
     2695 
     2696sub readFile { 
     2697    my $name = shift; 
     2698    my $data = ''; 
     2699    my $IN_FILE; 
     2700    open( $IN_FILE, '<', $name ) || return ''; 
     2701    local $/ = undef;    # set to read to EOF 
     2702    $data = <$IN_FILE>; 
     2703    close($IN_FILE); 
     2704    $data = '' unless $data;    # no undefined 
     2705    return $data; 
     2706} 
     2707 
     2708=begin TML 
     2709 
     2710---+++ saveFile( $filename, $text ) 
     2711 
     2712Save file, low level. Used for Plugin workarea. 
     2713   * =$filename= - Full path name of file 
     2714   * =$text=     - Text to save 
     2715Return:                none 
     2716 
     2717__NOTE:__ Use this function only for the Plugin workarea, *not* for topics and attachments. Use the appropriate functions to manipulate topics and attachments. 
     2718 
     2719=cut 
     2720 
     2721sub saveFile { 
     2722    my ( $name, $text ) = @_; 
     2723    my $FILE; 
     2724    unless ( open( $FILE, '>', $name ) ) { 
     2725        die "Can't create file $name - $!\n"; 
     2726    } 
     2727    print $FILE $text; 
     2728    close($FILE); 
     2729} 
     2730 
     2731=begin TML 
     2732 
     2733---++ General Utilities 
     2734 
     2735=cut 
     2736 
     2737=begin TML 
     2738 
     2739---+++ normalizeWebTopicName($web, $topic) -> ($web, $topic) 
     2740 
     2741Parse a web and topic name, supplying defaults as appropriate. 
     2742   * =$web= - Web name, identifying variable, or empty string 
     2743   * =$topic= - Topic name, may be a web.topic string, required. 
     2744Return: the parsed Web/Topic pair 
     2745 
     2746| *Input*                               | *Return*  | 
     2747| <tt>( 'Web', 'Topic' ) </tt>          | <tt>( 'Web', 'Topic' ) </tt>  | 
     2748| <tt>( '', 'Topic' ) </tt>             | <tt>( 'Main', 'Topic' ) </tt>  | 
     2749| <tt>( '', '' ) </tt>                  | <tt>( 'Main', 'WebHome' ) </tt>  | 
     2750| <tt>( '', 'Web/Topic' ) </tt>         | <tt>( 'Web', 'Topic' ) </tt>  | 
     2751| <tt>( '', 'Web/Subweb/Topic' ) </tt>  | <tt>( 'Web/Subweb', 'Topic' ) </tt>  | 
     2752| <tt>( '', 'Web.Topic' ) </tt>         | <tt>( 'Web', 'Topic' ) </tt>  | 
     2753| <tt>( '', 'Web.Subweb.Topic' ) </tt>  | <tt>( 'Web/Subweb', 'Topic' ) </tt>  | 
     2754| <tt>( 'Web1', 'Web2.Topic' )</tt>     | <tt>( 'Web2', 'Topic' ) </tt>  | 
     2755 
     2756Note that hierarchical web names (Web.SubWeb) are only available if hierarchical webs are enabled in =configure=. 
     2757 
     2758The 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: 
     2759| *Input*                               | *Return* | 
     2760| <tt>( '%<nop>USERSWEB%', 'Topic' )</tt>     | <tt>( 'Main', 'Topic' ) </tt>  | 
     2761| <tt>( '%<nop>SYSTEMWEB%', 'Topic' )</tt>    | <tt>( 'System', 'Topic' ) </tt>  | 
     2762| <tt>( '', '%<nop>DOCWEB%.Topic' )</tt>    | <tt>( 'System', 'Topic' ) </tt>  | 
     2763 
     2764=cut 
     2765 
     2766sub normalizeWebTopicName { 
     2767 
     2768    #my( $web, $topic ) = @_; 
     2769    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     2770    return $Foswiki::Plugins::SESSION->normalizeWebTopicName(@_); 
     2771} 
    26752772 
    26762773=begin TML 
     
    27312828=begin TML 
    27322829 
    2733 ---++ Plugin-specific file handling 
    2734  
    2735 =cut 
    2736  
    2737 =begin TML 
    2738  
    2739 ---+++ getWorkArea( $pluginName ) -> $directorypath 
    2740  
    2741 Gets a private directory for Plugin use. The Plugin is entirely responsible 
    2742 for managing this directory; Foswiki will not read from it, or write to it. 
    2743  
    2744 The directory is guaranteed to exist, and to be writable by the webserver 
    2745 user. By default it will *not* be web accessible. 
    2746  
    2747 The directory and its contents are permanent, so Plugins must be careful 
    2748 to keep their areas tidy. 
    2749  
    2750 =cut 
    2751  
    2752 sub getWorkArea { 
    2753     my ($plugin) = @_; 
    2754     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2755     return $Foswiki::Plugins::SESSION->getWorkArea($plugin); 
    2756 } 
    2757  
    2758 =begin TML 
    2759  
    2760 ---+++ readFile( $filename ) -> $text 
    2761  
    2762 Read file, low level. Used for Plugin workarea. 
    2763    * =$filename= - Full path name of file 
    2764 Return: =$text= Content of file, empty if not found 
    2765  
    2766 __NOTE:__ Use this function only for the Plugin workarea, *not* for topics and attachments. Use the appropriate functions to manipulate topics and attachments. 
    2767  
    2768 =cut 
    2769  
    2770 sub readFile { 
    2771     my $name = shift; 
    2772     my $data = ''; 
    2773     my $IN_FILE; 
    2774     open( $IN_FILE, '<', $name ) || return ''; 
    2775     local $/ = undef;    # set to read to EOF 
    2776     $data = <$IN_FILE>; 
    2777     close($IN_FILE); 
    2778     $data = '' unless $data;    # no undefined 
    2779     return $data; 
    2780 } 
    2781  
    2782 =begin TML 
    2783  
    2784 ---+++ saveFile( $filename, $text ) 
    2785  
    2786 Save file, low level. Used for Plugin workarea. 
    2787    * =$filename= - Full path name of file 
    2788    * =$text=     - Text to save 
    2789 Return:                none 
    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 =cut 
    2794  
    2795 sub saveFile { 
    2796     my ( $name, $text ) = @_; 
    2797     my $FILE; 
    2798     unless ( open( $FILE, '>', $name ) ) { 
    2799         die "Can't create file $name - $!\n"; 
    2800     } 
    2801     print $FILE $text; 
    2802     close($FILE); 
    2803 } 
    2804  
    2805 =begin TML 
    2806  
    2807 ---++ General Utilities 
    2808  
    2809 =cut 
    2810  
    2811 =begin TML 
    2812  
    2813 ---+++ normalizeWebTopicName($web, $topic) -> ($web, $topic) 
    2814  
    2815 Parse a web and topic name, supplying defaults as appropriate. 
    2816    * =$web= - Web name, identifying variable, or empty string 
    2817    * =$topic= - Topic name, may be a web.topic string, required. 
    2818 Return: the parsed Web/Topic pair 
    2819  
    2820 | *Input*                               | *Return*  | 
    2821 | <tt>( 'Web', 'Topic' ) </tt>          | <tt>( 'Web', 'Topic' ) </tt>  | 
    2822 | <tt>( '', 'Topic' ) </tt>             | <tt>( 'Main', 'Topic' ) </tt>  | 
    2823 | <tt>( '', '' ) </tt>                  | <tt>( 'Main', 'WebHome' ) </tt>  | 
    2824 | <tt>( '', 'Web/Topic' ) </tt>         | <tt>( 'Web', 'Topic' ) </tt>  | 
    2825 | <tt>( '', 'Web/Subweb/Topic' ) </tt>  | <tt>( 'Web/Subweb', 'Topic' ) </tt>  | 
    2826 | <tt>( '', 'Web.Topic' ) </tt>         | <tt>( 'Web', 'Topic' ) </tt>  | 
    2827 | <tt>( '', 'Web.Subweb.Topic' ) </tt>  | <tt>( 'Web/Subweb', 'Topic' ) </tt>  | 
    2828 | <tt>( 'Web1', 'Web2.Topic' )</tt>     | <tt>( 'Web2', 'Topic' ) </tt>  | 
    2829  
    2830 Note that hierarchical web names (Web.SubWeb) are only available if hierarchical webs are enabled in =configure=. 
    2831  
    2832 The 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: 
    2833 | *Input*                               | *Return* | 
    2834 | <tt>( '%<nop>USERSWEB%', 'Topic' )</tt>     | <tt>( 'Main', 'Topic' ) </tt>  | 
    2835 | <tt>( '%<nop>SYSTEMWEB%', 'Topic' )</tt>    | <tt>( 'System', 'Topic' ) </tt>  | 
    2836 | <tt>( '', '%<nop>DOCWEB%.Topic' )</tt>    | <tt>( 'System', 'Topic' ) </tt>  | 
    2837  
    2838 =cut 
    2839  
    2840 sub normalizeWebTopicName { 
    2841  
    2842     #my( $web, $topic ) = @_; 
    2843     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2844     return $Foswiki::Plugins::SESSION->normalizeWebTopicName(@_); 
     2830---+++ decodeFormatTokens($str) -> $unencodedString 
     2831 
     2832Foswiki has an informal standard set of tokens used in =format= 
     2833parameters that are used to block evaluation of paramater strings. 
     2834For example, if you were to write 
     2835 
     2836=%<nop>MYTAG{format="%<nop>WURBLE%"}%= 
     2837 
     2838then %<nop>WURBLE would be expanded *before* %<NOP>MYTAG is evaluated. To avoid 
     2839this Foswiki uses escapes in the format string. For example: 
     2840 
     2841=%<nop>MYTAG{format="$percentWURBLE$percent"}%= 
     2842 
     2843This lets you enter arbitrary strings into parameters without worrying that 
     2844Foswiki will expand them before your plugin gets a chance to deal with them 
     2845properly. Once you have processed your tag, you will want to expand these 
     2846tokens to their proper value. That's what this function does. 
     2847 
     2848The set of tokens that is expanded is described in System.FormatTokens. 
     2849 
     2850=cut 
     2851 
     2852sub decodeFormatTokens { 
     2853    return Foswiki::expandStandardEscapes(@_); 
    28452854} 
    28462855 
     
    28852894=begin TML 
    28862895 
    2887 ---+++ writeEvent( $action, $extra ) 
    2888  
    2889 Log an event. 
    2890    * =$action= - name of the event (keep them unique!) 
    2891    * =$extra= - arbitrary extra information to add to the log. 
    2892 You can enumerate the contents of the log using the =eachEventSince= function. 
    2893  
    2894 *NOTE:* Older plugins may use =$Foswiki::cfg{LogFileName}=. These 
    2895 plugins must be modified to use =writeEvent= and =eachEventSince= instead. 
    2896  
    2897 To maintain compatibility with older Foswiki releases, you can write 
    2898 conditional code as follows: 
    2899 <verbatim> 
    2900 if (defined &Foswiki::Func::writeEvent) { 
    2901    # use writeEvent and eachEventSince 
    2902 } else { 
    2903    # old code using {LogFileName} 
    2904 } 
    2905 </verbatim> 
    2906  
    2907 Note that the ability to read/write =$Foswiki::cfg{LogFileName}= is 
    2908 maintained for compatibility but is *deprecated* (should not be used 
    2909 in new code intended to work only with Foswiki 1.1 and later) and will 
    2910 not work with any installation that stores logs in a database. 
    2911  
    2912 =cut 
    2913  
    2914 sub writeEvent { 
    2915     my ( $action, $extra ) = @_; 
    2916     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2917     my $webTopic = 
    2918         $Foswiki::Plugins::SESSION->{webName} . '.' 
    2919       . $Foswiki::Plugins::SESSION->{topicName}; 
    2920     return $Foswiki::Plugins::SESSION->logEvent( $action, $webTopic, $extra ); 
    2921 } 
    2922  
    2923 =begin TML 
    2924  
    2925 ---++ eachEventSince($time, $level) -> $iterator 
    2926    * =$time= - a time in the past (seconds since the epoch) 
    2927    * =$level= - log level to return events for. 
    2928  
    2929 Get an iterator over the list of all the events at the given level 
    2930 between =$time= and now. Events are written to the event log using 
    2931 =writeEvent=. The Foswiki core will write other events that will 
    2932 also be returned. 
    2933  
    2934 Events are returned in *oldest-first* order. 
    2935  
    2936 Each event is returned as a reference to an array. The elements are: 
    2937    1 date of the event (seconds since the epoch) 
    2938    1 login name of the user who triggered the event 
    2939    1 the event name (the $action passed to =writeEvent=) 
    2940    1 the Web.Topic that the event applied to 
    2941    1 Extras (the $extra passed to =writeEvent=) 
    2942    1 The IP address that was the source of the event (if known) 
    2943  
    2944 Use the iterator like this: 
    2945 <verbatim> 
    2946 my $it = Foswiki::Func::eachEventSince(Foswiki::Time::parseTime("1 Apr 2010")); 
    2947 while ($it->hasNext()) { 
    2948    my $entry = $it->next(); 
    2949    my $date = $entry->[0]; 
    2950    my $loginName = $entry->[1]; 
    2951    ... 
    2952 } 
    2953 </verbatim> 
    2954  
    2955 =cut 
    2956  
    2957 sub eachEventSince { 
    2958     my $time = shift; 
    2959     return $Foswiki::Plugins::SESSION->logger->eachEventSince( $time, 'info' ); 
    2960 } 
    2961  
    2962 =begin TML 
    2963  
    2964 ---+++ writeWarning( $text ) 
    2965  
    2966 Log a warning that may require admin intervention to the warnings log (=data/warn*.txt=) 
    2967    * =$text= - Text to write; timestamp gets added 
    2968  
    2969 =cut 
    2970  
    2971 sub writeWarning { 
    2972     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2973     return $Foswiki::Plugins::SESSION->logger->log( 'warning', 
    2974         scalar( caller() ), @_ ); 
    2975 } 
    2976  
    2977 =begin TML 
    2978  
    2979 ---+++ writeDebug( $text ) 
    2980  
    2981 Log debug message to the debug log  
    2982    * =$text= - Text to write; timestamp gets added 
    2983  
    2984 =cut 
    2985  
    2986 sub writeDebug { 
    2987  
    2988     #   my( $text ) = @_; 
    2989     ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
    2990     return $Foswiki::Plugins::SESSION->logger->log( 'debug', @_ ); 
    2991 } 
    2992  
    2993 =begin TML 
    2994  
    29952896---+++ isTrue( $value, $default ) -> $boolean 
    29962897 
     
    30232924sub isValidWikiWord { 
    30242925    return Foswiki::isValidWikiWord(@_); 
    3025 } 
    3026  
    3027 =begin TML 
    3028  
    3029 ---+++ isValidWebName( $name [, $system] ) -> $boolean 
    3030  
    3031 Check for a valid web name. If $system is true, then 
    3032 system web names are considered valid (names starting with _) 
    3033 otherwise only user web names are valid 
    3034  
    3035 If $Foswiki::cfg{EnableHierarchicalWebs} is off, it will also return false 
    3036 when a nested web name is passed to it. 
    3037  
    3038 =cut 
    3039  
    3040 sub isValidWebName { 
    3041     return Foswiki::isValidWebName(@_); 
    3042 } 
    3043  
    3044 =begin TML 
    3045  
    3046 ---++ isValidTopicName( $name [, $allowNonWW] ) -> $boolean 
    3047  
    3048 Check for a valid topic name. 
    3049    * =$name= - topic name 
    3050    * =$allowNonWW= - true to allow non-wikiwords 
    3051  
    3052 =cut 
    3053  
    3054 sub isValidTopicName { 
    3055     return Foswiki::isValidTopicName(@_); 
    30562926} 
    30572927 
     
    31142984=begin TML 
    31152985 
     2986---+++ sendEmail ( $text, $retries ) -> $error 
     2987 
     2988   * =$text= - text of the mail, including MIME headers 
     2989   * =$retries= - number of times to retry the send (default 1) 
     2990Send an e-mail specified as MIME format content. To specify MIME 
     2991format mails, you create a string that contains a set of header 
     2992lines that contain field definitions and a message body such as: 
     2993<verbatim> 
     2994To: liz@windsor.gov.uk 
     2995From: serf@hovel.net 
     2996CC: george@whitehouse.gov 
     2997Subject: Revolution 
     2998 
     2999Dear Liz, 
     3000 
     3001Please abolish the monarchy (with King George's permission, of course) 
     3002 
     3003Thanks, 
     3004 
     3005A. Peasant 
     3006</verbatim> 
     3007Leave a blank line between the last header field and the message body. 
     3008 
     3009=cut 
     3010 
     3011sub sendEmail { 
     3012 
     3013    #my( $text, $retries ) = @_; 
     3014    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     3015    return $Foswiki::Plugins::SESSION->net->sendEmail(@_); 
     3016} 
     3017 
     3018=begin TML 
     3019 
     3020---++ Logging 
     3021 
     3022=cut 
     3023 
     3024=begin TML 
     3025 
     3026---+++ writeEvent( $action, $extra ) 
     3027 
     3028Log an event. 
     3029   * =$action= - name of the event (keep them unique!) 
     3030   * =$extra= - arbitrary extra information to add to the log. 
     3031You can enumerate the contents of the log using the =eachEventSince= function. 
     3032 
     3033*NOTE:* Older plugins may use =$Foswiki::cfg{LogFileName}=. These 
     3034plugins must be modified to use =writeEvent= and =eachEventSince= instead. 
     3035 
     3036To maintain compatibility with older Foswiki releases, you can write 
     3037conditional code as follows: 
     3038<verbatim> 
     3039if (defined &Foswiki::Func::writeEvent) { 
     3040   # use writeEvent and eachEventSince 
     3041} else { 
     3042   # old code using {LogFileName} 
     3043} 
     3044</verbatim> 
     3045 
     3046Note that the ability to read/write =$Foswiki::cfg{LogFileName}= is 
     3047maintained for compatibility but is *deprecated* (should not be used 
     3048in new code intended to work only with Foswiki 1.1 and later) and will 
     3049not work with any installation that stores logs in a database. 
     3050 
     3051=cut 
     3052 
     3053sub writeEvent { 
     3054    my ( $action, $extra ) = @_; 
     3055    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     3056    my $webTopic = 
     3057        $Foswiki::Plugins::SESSION->{webName} . '.' 
     3058      . $Foswiki::Plugins::SESSION->{topicName}; 
     3059    return $Foswiki::Plugins::SESSION->logEvent( $action, $webTopic, $extra ); 
     3060} 
     3061 
     3062=begin TML 
     3063 
     3064---+++ writeWarning( $text ) 
     3065 
     3066Log a warning that may require admin intervention to the warnings log (=data/warn*.txt=) 
     3067   * =$text= - Text to write; timestamp gets added 
     3068 
     3069=cut 
     3070 
     3071sub writeWarning { 
     3072    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     3073    return $Foswiki::Plugins::SESSION->logger->log( 'warning', 
     3074        scalar( caller() ), @_ ); 
     3075} 
     3076 
     3077=begin TML 
     3078 
     3079---+++ writeDebug( $text ) 
     3080 
     3081Log debug message to the debug log  
     3082   * =$text= - Text to write; timestamp gets added 
     3083 
     3084=cut 
     3085 
     3086sub writeDebug { 
     3087 
     3088    #   my( $text ) = @_; 
     3089    ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 
     3090    return $Foswiki::Plugins::SESSION->logger->log( 'debug', @_ ); 
     3091} 
     3092 
     3093=begin TML 
     3094 
     3095---+++ eachEventSince($time, $level) -> $iterator 
     3096   * =$time= - a time in the past (seconds since the epoch) 
     3097   * =$level= - log level to return events for. 
     3098 
     3099Get an iterator over the list of all the events at the given level 
     3100between =$time= and now. Events are written to the event log using 
     3101=writeEvent=. The Foswiki core will write other events that will 
     3102also be returned. 
     3103 
     3104Events are returned in *oldest-first* order. 
     3105 
     3106Each event is returned as a reference to an array. The elements are: 
     3107   1 date of the event (seconds since the epoch) 
     3108   1 login name of the user who triggered the event 
     3109   1 the event name (the $action passed to =writeEvent=) 
     3110   1 the Web.Topic that the event applied to 
     3111   1 Extras (the $extra passed to =writeEvent=) 
     3112   1 The IP address that was the source of the event (if known) 
     3113 
     3114Use the iterator like this: 
     3115<verbatim> 
     3116my $it = Foswiki::Func::eachEventSince(Foswiki::Time::parseTime("1 Apr 2010")); 
     3117while ($it->hasNext()) { 
     3118   my $entry = $it->next(); 
     3119   my $date = $entry->[0]; 
     3120   my $loginName = $entry->[1]; 
     3121   ... 
     3122} 
     3123</verbatim> 
     3124 
     3125=cut 
     3126 
     3127sub eachEventSince { 
     3128    my $time = shift; 
     3129    return $Foswiki::Plugins::SESSION->logger->eachEventSince( $time, 'info' ); 
     3130} 
     3131 
     3132=begin TML 
     3133 
    31163134---++ Deprecated functions 
    31173135 
Note: See TracChangeset for help on using the changeset viewer.