Changeset 8849
- Timestamp:
- 09/02/10 09:47:02 (21 months ago)
- Location:
- trunk/core
- Files:
-
- 3 edited
-
data/System/DevelopingPlugins.txt (modified) (1 diff)
-
data/System/WebHome.txt (modified) (1 diff)
-
lib/Foswiki/Func.pm (modified) (14 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/core/data/System/DevelopingPlugins.txt
r8688 r8849 4 4 %TOC% 5 5 The 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 9 Foswiki 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 11 The 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 13 Foswiki 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 21 Once all the engines have run, the output is sent to the browser. 22 23 There 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. 6 27 7 28 ---++ APIs available to Extensions -
trunk/core/data/System/WebHome.txt
r8561 r8849 17 17 * [[ReleaseNotes01x01][Release Notes for 1.1]] - describes what's new in this release 18 18 19 * [[http://foswiki.org/Support][Getting support]] - if you get stuck 20 19 21 ---++ <nop>%WEB% Web Utilities 20 22 * WebTopicList - all topics in alphabetical order -
trunk/core/lib/Foswiki/Func.pm
r8839 r8849 466 466 ( $session->{webName}, $session->{topicName} ) = 467 467 $session->{prefs}->popTopicContext(); 468 } 469 470 =begin TML 471 472 ---++ Registering extensions 473 474 Plugins work either by using handlers to manipulate the text being processed, 475 or 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 483 Should only be called from initPlugin. 484 485 Register a function to handle a simple variable. Handles both %<nop>VAR% and 486 %<nop>VAR{...}%. Registered variables are treated the same as internal macros, 487 and 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 499 The variable handler function must be of the form: 500 <verbatim> 501 sub handler(\%session, \%params, $topic, $web, $topicObject) 502 </verbatim> 503 where: 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 509 for example, to execute an arbitrary command on the server, you might do this: 510 <verbatim> 511 sub initPlugin{ 512 Foswiki::Func::registerTagHandler('EXEC', \&boo); 513 } 514 515 sub 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> 525 would let you do this: 526 =%<nop>EXEC{"ps -Af" silent="on"}%= 527 528 Registered 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 536 sub 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 584 Should only be called from initPlugin. 585 586 Adds 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 590 The handler function must be of the form: 591 <verbatim> 592 sub handler(\%session) 593 </verbatim> 594 where: 595 * =\%session= - a reference to the Foswiki session object (may be ignored) 596 597 From the REST interface, the name of the plugin must be used 598 as the subject of the invokation. 599 600 Additional options are set in the =%options= hash. These options are important 601 to ensuring that requests to your handler can't be used in cross-scripting 602 attacks, 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 623 The 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 629 This adds the =restExample= function to the REST dispatch table 630 for the EmptyPlugin under the 'example' alias, and allows it 631 to be invoked using the URL 632 633 =http://server:port/bin/rest/EmptyPlugin/example= 634 635 note 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 642 The =rest= script allows handlers to be invoked from the command line. The 643 script is invoked passing the parameters as described in CommandAndCGIScripts. 644 If the handler requires authentication ( =authenticate=>1= ) then this can 645 be passed in the username and =password= parameters. 646 647 For example, 648 649 =perl -wT rest /EmptyPlugin/example -username HughPugh -password trumpton= 650 651 =cut 652 653 sub 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 679 Foswiki supports embedding meta-data into topics. For example, 680 681 =%<nop>META:BOOK{title="Transit" author="Edmund Cooper" isbn="0-571-05724-1"}%= 682 683 This meta-data is validated when it is read from the store. Meta-data 684 that is not registered, or doesn't pass validation, is ignored. This 685 function 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 687 validate the fields of the datum. 688 689 The following checks are supported: 690 691 =function=>\&fn= In this case the function =fn= will be called when the 692 datum is encountered, passing in the name of the macro and the 693 argument hash. The function must return a non-zero/undef value if the tag 694 is acceptable, or 0 otherwise. For example: 695 <verbatim> 696 registerMETA('BOOK', function => sub { 697 my ($name, $args) = @_; 698 # $name will be BOOK 699 return defined $args->{title}; 700 } 701 </verbatim> 702 can 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 705 the tag. For example, 706 <verbatim> 707 registerMETA('BOOK', require => [ 'title', 'author' ]); 708 </verbatim> 709 can be used to check that both =title= and =author= are present. 710 711 =allow=>[]= lets you specify other optional parameters that are allowed 712 on the tag. If you specify =allow= then the validation will fail if the 713 tag contains any parameters that are _not_ in the =allow= or =require= lists. 714 If you don't specify =allow= then all parameters will be allowed. 715 716 Checks are cumulative, so if you: 717 <verbatim> 718 registerMETA('BOOK', 719 function => \&checkParameters, 720 require => [ 'title' ], 721 allow => [ 'author', 'isbn' ]); 722 </verbatim> 723 then all these conditions will be tested. Note that =require= and =allow= 724 are tested _after_ =function= is called, to give the function a chance to 725 rewrite the parameter list. 726 727 If no checker is registered for a META tag, then it will automatically 728 be accepted into the topic meta-data. 729 730 Note that the checker only verifies the *presence* of parameters, and 731 not their *values*. 732 733 =cut 734 735 sub registerMETA { 736 my ( $macro, $spec ) = @_; 737 Foswiki::Meta::registerMETA( $macro, $spec ); 468 738 } 469 739 … … 1142 1412 =begin TML 1143 1413 1144 ---++ Webs, Topics and Attachments1414 ---++ Traversing 1145 1415 1146 1416 =cut … … 1183 1453 =begin TML 1184 1454 1455 ---+++ isValidWebName( $name [, $system] ) -> $boolean 1456 1457 Check for a valid web name. If $system is true, then 1458 system web names are considered valid (names starting with _) 1459 otherwise only user web names are valid 1460 1461 If $Foswiki::cfg{EnableHierarchicalWebs} is off, it will also return false 1462 when a nested web name is passed to it. 1463 1464 =cut 1465 1466 sub isValidWebName { 1467 return Foswiki::isValidWebName(@_); 1468 } 1469 1470 =begin TML 1471 1185 1472 ---+++ webExists( $web ) -> $boolean 1186 1473 … … 1197 1484 return $Foswiki::Plugins::SESSION->webExists($web); 1198 1485 } 1486 =begin TML 1487 1488 ---+++ getTopicList( $web ) -> @topics 1489 1490 Get list of all topics in a web 1491 * =$web= - Web name, required, e.g. ='Sandbox'= 1492 Return: =@topics= Topic list, e.g. =( 'WebChanges', 'WebHome', 'WebIndex', 'WebNotify' )= 1493 1494 =cut 1495 1496 sub 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 1509 Check for a valid topic name. 1510 * =$name= - topic name 1511 * =$allowNonWW= - true to allow non-wikiwords 1512 1513 =cut 1514 1515 sub isValidTopicName { 1516 return Foswiki::isValidTopicName(@_); 1517 } 1518 1519 =begin TML 1520 1521 ---+++ topicExists( $web, $topic ) -> $boolean 1522 1523 Test 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=. 1528 Specifically, the %USERSWEB% is used if $web is not specified and $topic has no web specifier. 1529 To get an expected behaviour it is recommened to specify the current web for $web; don't leave it empty. 1530 1531 =cut 1532 1533 sub 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 1544 Read 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) 1548 Return: =( $meta, $text )= Meta data object and topic text 1549 1550 =$meta= is a perl 'object' of class =Foswiki::Meta=. This class is 1551 fully documented in the source code documentation shipped with the 1552 release, or can be inspected in the =lib/Foswiki/Meta.pm= file. 1553 1554 This method *ignores* topic access permissions. You should be careful to use 1555 =checkAccessPermission= to ensure the current user has read access to the 1556 topic. 1557 1558 =cut 1559 1560 sub 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 1575 Get 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 1580 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", )= 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 1586 NOTE: if you are trying to get revision info for a topic, use 1587 =$meta->getRevisionInfo= instead if you can - it is significantly 1588 more efficient. 1589 1590 =cut 1591 1592 sub 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 1620 Get 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 1624 Return: 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 1629 sub 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 1641 Get a list of the attachments on the given topic. 1642 1643 *Since:* 31 Mar 2009 1644 1645 =cut 1646 1647 sub 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 1660 Test 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 1668 sub 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) 1685 Read an attachment from the store for a topic, and return it as a string. The 1686 names of attachments on a topic can be recovered from the meta-data returned 1687 by =readTopic=. If the attachment does not exist, or cannot be read, undef 1688 will be returned. If the revision is not specified, the latest version will 1689 be returned. 1690 1691 View permission on the topic is required for the 1692 read to be successful. Access control violations are flagged by a 1693 Foswiki::AccessControlException. Permissions are checked for the current user. 1694 1695 <verbatim> 1696 my( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic ); 1697 my @attachments = $meta->find( 'FILEATTACHMENT' ); 1698 foreach 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 1707 This is the way 99% of extensions will access attachments. 1708 See =Foswiki::Meta::openAttachment= for a lower level interface that does 1709 not check access controls. 1710 1711 =cut 1712 1713 sub 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 1199 1747 1200 1748 =begin TML … … 1296 1844 } 1297 1845 1298 =begin TML1299 1300 ---+++ eachChangeSince($web, $time) -> $iterator1301 1302 Get an iterator over the list of all the changes in the given web between1303 =$time= and now. $time is a time in seconds since 1st Jan 1970, and is not1304 guaranteed to return any changes that occurred before (now -1305 {Store}{RememberChangesFor}). {Store}{RememberChangesFor}) is a1306 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 days1313 while ($iterator->hasNext()) {1314 my $change = $iterator->next();1315 # $change is a perl hash that contains the following fields:1316 # topic => topic name1317 # user => wikiname - wikiname of user who made the change1318 # time => time of the change1319 # revision => revision number *after* the change1320 # more => more info about the change (e.g. 'minor')1321 }1322 </verbatim>1323 1324 =cut1325 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 mapped1335 # to wikinames per the Foswiki::Func 'spec' (changes used to be stored1336 # 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_CUID1344 unless defined $n->{user};1345 $n->{user} =1346 $Foswiki::Plugins::SESSION->{users}->getWikiName( $n->{user} );1347 return $n;1348 }1349 );1350 }1351 1352 =begin TML1353 1354 ---++ summariseChanges($web, $topic, $orev, $nrev, $tml) -> $text1355 Generate a summary of the changes between rev $orev and rev $nrev of the1356 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 blank1370 text.1371 1372 *Since* 2009-03-061373 1374 =cut1375 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 TML1387 1388 ---+++ getTopicList( $web ) -> @topics1389 1390 Get list of all topics in a web1391 * =$web= - Web name, required, e.g. ='Sandbox'=1392 Return: =@topics= Topic list, e.g. =( 'WebChanges', 'WebHome', 'WebIndex', 'WebNotify' )=1393 1394 =cut1395 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 TML1406 1407 ---+++ topicExists( $web, $topic ) -> $boolean1408 1409 Test if topic exists1410 * =$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 =cut1418 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 }1425 1846 1426 1847 =begin TML … … 1631 2052 1632 2053 $from->move($to); 1633 }1634 1635 =begin TML1636 1637 ---+++ getRevisionInfo($web, $topic, $rev, $attachment ) -> ( $date, $user, $rev, $comment )1638 1639 Get revision info of a topic or attachment1640 * =$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 filename1644 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, use1651 =$meta->getRevisionInfo= instead if you can - it is significantly1652 more efficient.1653 1654 =cut1655 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 TML1681 1682 ---+++ getRevisionAtTime( $web, $topic, $time ) -> $rev1683 1684 Get the revision number of a topic at a specific time.1685 * =$web= - web for topic1686 * =$topic= - topic1687 * =$time= - time (in epoch secs) for the rev1688 Return: Single-digit revision number, or undef if it couldn't be determined1689 (either because the topic isn't that old, or there was a problem)1690 1691 =cut1692 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 TML1703 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 text1711 1712 =$meta= is a perl 'object' of class =Foswiki::Meta=. This class is1713 fully documented in the source code documentation shipped with the1714 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 use1717 =checkAccessPermission= to ensure the current user has read access to the1718 topic.1719 1720 =cut1721 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 TML1734 1735 ---+++ getAttachmentList( $web, $topic ) -> @list1736 Get a list of the attachments on the given topic.1737 1738 *Since:* 31 Mar 20091739 1740 =cut1741 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 TML1752 1753 ---+++ attachmentExists( $web, $topic, $attachment ) -> $boolean1754 1755 Test if attachment exists1756 * =$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 =cut1762 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 TML1773 1774 ---+++ readAttachment( $web, $topic, $name, $rev ) -> $data1775 1776 * =$web= - web for topic - must not be tainted1777 * =$topic= - topic - must not be tainted1778 * =$name= - attachment name - must not be tainted1779 * =$rev= - revision to read (default latest)1780 Read an attachment from the store for a topic, and return it as a string. The1781 names of attachments on a topic can be recovered from the meta-data returned1782 by =readTopic=. If the attachment does not exist, or cannot be read, undef1783 will be returned. If the revision is not specified, the latest version will1784 be returned.1785 1786 View permission on the topic is required for the1787 read to be successful. Access control violations are flagged by a1788 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 does1804 not check access controls.1805 1806 =cut1807 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;1834 2054 } 1835 2055 … … 2053 2273 =begin TML 2054 2274 2055 ---++ Assembling Pages 2275 ---++ Finding changes 2276 2277 =cut 2278 2279 =begin TML 2280 2281 ---+++ eachChangeSince($web, $time) -> $iterator 2282 2283 Get 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 2285 guaranteed to return any changes that occurred before (now - 2286 {Store}{RememberChangesFor}). {Store}{RememberChangesFor}) is a 2287 setting in =configure=. Changes are returned in *most-recent-first* 2288 order. 2289 2290 Use 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 2307 sub 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 2336 Generate a summary of the changes between rev $orev and rev $nrev of the 2337 given 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) 2342 Generate a (max 3 line) summary of the differences between the revs. 2343 2344 If there is only one rev, a topic summary will be returned. 2345 2346 If =$tml= is not set, all HTML will be removed. 2347 2348 In non-tml, lines are truncated to 70 characters. Differences are shown using + and - to indicate added and removed text. 2349 2350 If access is denied to either revision, then it will be treated as blank 2351 text. 2352 2353 *Since* 2009-03-06 2354 2355 =cut 2356 2357 sub 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 2056 2370 2057 2371 =cut … … 2128 2442 =begin TML 2129 2443 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 2222 2447 2223 2448 =begin TML … … 2246 2471 return $meta->expandMacros($text); 2247 2472 } 2248 2249 =begin TML2250 2251 ---+++ renderText( $text, $web, $topic ) -> $text2252 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 missing2256 * =$topic= - topic name, optional, defaults to web home2257 Return: =$text= XHTML text, e.g. ='<b>bold</b> and <code>fixed font</code>'=2258 2259 =cut2260 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 TML2273 2274 ---+++ internalLink( $pre, $web, $topic, $label, $anchor, $createLink ) -> $text2275 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, optional2278 * =$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 topics2283 Return: =$text= XHTML anchor, e.g. ='<a href='/cgi-bin/view/Main/WebNotify#Jump'>notify</a>'=2284 2285 =cut2286 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 TML2296 2297 ---++ E-mail2298 2299 ---+++ sendEmail ( $text, $retries ) -> $error2300 2301 * =$text= - text of the mail, including MIME headers2302 * =$retries= - number of times to retry the send (default 1)2303 Send an e-mail specified as MIME format content. To specify MIME2304 format mails, you create a string that contains a set of header2305 lines that contain field definitions and a message body such as:2306 <verbatim>2307 To: liz@windsor.gov.uk2308 From: serf@hovel.net2309 CC: george@whitehouse.gov2310 Subject: Revolution2311 2312 Dear Liz,2313 2314 Please abolish the monarchy (with King George's permission, of course)2315 2316 Thanks,2317 2318 A. Peasant2319 </verbatim>2320 Leave a blank line between the last header field and the message body.2321 2322 =cut2323 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 TML2332 2333 ---++ Creating New Topics2334 2335 =cut2336 2473 2337 2474 =begin TML … … 2373 2510 =begin TML 2374 2511 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 2514 Render 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 2518 Return: =$text= XHTML text, e.g. ='<b>bold</b> and <code>fixed font</code>'= 2519 2520 =cut 2521 2522 sub 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 2537 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()= 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 2544 Return: =$text= XHTML anchor, e.g. ='<a href='/cgi-bin/view/Main/WebNotify#Jump'>notify</a>'= 2545 2546 =cut 2547 2548 sub 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 2560 Direct 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 2568 All 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 2572 calling =addToZone()= from a rendering or macro/tag-related plugin handler 2573 </blockquote> 2574 2575 Examples: 2576 <verbatim> 2577 Foswiki::Func::addToZone( 'head', 'PATTERN_STYLE', 2578 '<link rel="stylesheet" type="text/css" href="%PUBURL%/Foswiki/PatternSkin/layout.css" media="all" />'); 2579 2580 Foswiki::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 2587 sub 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 2606 Prints a basic content-type HTML header for text/html to standard out. 2607 2608 =cut 2609 2610 sub 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 2619 Redirect 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 2624 Return: none 2625 2626 Print output to STDOUT that will cause a 302 redirect to a new URL. 2627 Nothing more should be printed to STDOUT after this method has been called. 2628 2629 The =$passthru= parameter allows you to pass the parameters that were passed 2630 to the current query on to the target URL, as long as it is another URL on the 2631 same installation. If =$passthru= is set to a true value, then Foswiki 2632 will save the current URL parameters, and then try to restore them on the 2633 other side of the redirect. Parameters are stored on the server in a cache 2634 file. 2635 2636 Note that if =$passthru= is set, then any parameters in =$url= will be lost 2637 when the old parameters are restored. if you want to change any parameter 2638 values, you will need to do that in the current CGI query before redirecting 2639 e.g. 2640 <verbatim> 2641 my $query = Foswiki::Func::getRequestObject(); 2642 $query->param(-name => 'text', -value => 'Different text'); 2643 Foswiki::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 2647 Foswiki installation. 2648 2649 =cut 2650 2651 sub 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 2667 Gets a private directory for Plugin use. The Plugin is entirely responsible 2668 for managing this directory; Foswiki will not read from it, or write to it. 2669 2670 The directory is guaranteed to exist, and to be writable by the webserver 2671 user. By default it will *not* be web accessible. 2672 2673 The directory and its contents are permanent, so Plugins must be careful 2674 to keep their areas tidy. 2675 2676 =cut 2677 2678 sub 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 2688 Read file, low level. Used for Plugin workarea. 2689 * =$filename= - Full path name of file 2690 Return: =$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 2696 sub 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 2712 Save file, low level. Used for Plugin workarea. 2713 * =$filename= - Full path name of file 2714 * =$text= - Text to save 2715 Return: 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 2721 sub 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 2741 Parse 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. 2744 Return: 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 2756 Note that hierarchical web names (Web.SubWeb) are only available if hierarchical webs are enabled in =configure=. 2757 2758 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: 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 2766 sub normalizeWebTopicName { 2767 2768 #my( $web, $topic ) = @_; 2769 ASSERT($Foswiki::Plugins::SESSION) if DEBUG; 2770 return $Foswiki::Plugins::SESSION->normalizeWebTopicName(@_); 2771 } 2675 2772 2676 2773 =begin TML … … 2731 2828 =begin TML 2732 2829 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 2832 Foswiki has an informal standard set of tokens used in =format= 2833 parameters that are used to block evaluation of paramater strings. 2834 For example, if you were to write 2835 2836 =%<nop>MYTAG{format="%<nop>WURBLE%"}%= 2837 2838 then %<nop>WURBLE would be expanded *before* %<NOP>MYTAG is evaluated. To avoid 2839 this Foswiki uses escapes in the format string. For example: 2840 2841 =%<nop>MYTAG{format="$percentWURBLE$percent"}%= 2842 2843 This lets you enter arbitrary strings into parameters without worrying that 2844 Foswiki will expand them before your plugin gets a chance to deal with them 2845 properly. Once you have processed your tag, you will want to expand these 2846 tokens to their proper value. That's what this function does. 2847 2848 The set of tokens that is expanded is described in System.FormatTokens. 2849 2850 =cut 2851 2852 sub decodeFormatTokens { 2853 return Foswiki::expandStandardEscapes(@_); 2845 2854 } 2846 2855 … … 2885 2894 =begin TML 2886 2895 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}=. These2895 plugins must be modified to use =writeEvent= and =eachEventSince= instead.2896 2897 To maintain compatibility with older Foswiki releases, you can write2898 conditional code as follows:2899 <verbatim>2900 if (defined &Foswiki::Func::writeEvent) {2901 # use writeEvent and eachEventSince2902 } else {2903 # old code using {LogFileName}2904 }2905 </verbatim>2906 2907 Note that the ability to read/write =$Foswiki::cfg{LogFileName}= is2908 maintained for compatibility but is *deprecated* (should not be used2909 in new code intended to work only with Foswiki 1.1 and later) and will2910 not work with any installation that stores logs in a database.2911 2912 =cut2913 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 TML2924 2925 ---++ eachEventSince($time, $level) -> $iterator2926 * =$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 level2930 between =$time= and now. Events are written to the event log using2931 =writeEvent=. The Foswiki core will write other events that will2932 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 event2939 1 the event name (the $action passed to =writeEvent=)2940 1 the Web.Topic that the event applied to2941 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 =cut2956 2957 sub eachEventSince {2958 my $time = shift;2959 return $Foswiki::Plugins::SESSION->logger->eachEventSince( $time, 'info' );2960 }2961 2962 =begin TML2963 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 added2968 2969 =cut2970 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 TML2978 2979 ---+++ writeDebug( $text )2980 2981 Log debug message to the debug log2982 * =$text= - Text to write; timestamp gets added2983 2984 =cut2985 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 TML2994 2995 2896 ---+++ isTrue( $value, $default ) -> $boolean 2996 2897 … … 3023 2924 sub isValidWikiWord { 3024 2925 return Foswiki::isValidWikiWord(@_); 3025 }3026 3027 =begin TML3028 3029 ---+++ isValidWebName( $name [, $system] ) -> $boolean3030 3031 Check for a valid web name. If $system is true, then3032 system web names are considered valid (names starting with _)3033 otherwise only user web names are valid3034 3035 If $Foswiki::cfg{EnableHierarchicalWebs} is off, it will also return false3036 when a nested web name is passed to it.3037 3038 =cut3039 3040 sub isValidWebName {3041 return Foswiki::isValidWebName(@_);3042 }3043 3044 =begin TML3045 3046 ---++ isValidTopicName( $name [, $allowNonWW] ) -> $boolean3047 3048 Check for a valid topic name.3049 * =$name= - topic name3050 * =$allowNonWW= - true to allow non-wikiwords3051 3052 =cut3053 3054 sub isValidTopicName {3055 return Foswiki::isValidTopicName(@_);3056 2926 } 3057 2927 … … 3114 2984 =begin TML 3115 2985 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) 2990 Send an e-mail specified as MIME format content. To specify MIME 2991 format mails, you create a string that contains a set of header 2992 lines that contain field definitions and a message body such as: 2993 <verbatim> 2994 To: liz@windsor.gov.uk 2995 From: serf@hovel.net 2996 CC: george@whitehouse.gov 2997 Subject: Revolution 2998 2999 Dear Liz, 3000 3001 Please abolish the monarchy (with King George's permission, of course) 3002 3003 Thanks, 3004 3005 A. Peasant 3006 </verbatim> 3007 Leave a blank line between the last header field and the message body. 3008 3009 =cut 3010 3011 sub 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 3028 Log an event. 3029 * =$action= - name of the event (keep them unique!) 3030 * =$extra= - arbitrary extra information to add to the log. 3031 You can enumerate the contents of the log using the =eachEventSince= function. 3032 3033 *NOTE:* Older plugins may use =$Foswiki::cfg{LogFileName}=. These 3034 plugins must be modified to use =writeEvent= and =eachEventSince= instead. 3035 3036 To maintain compatibility with older Foswiki releases, you can write 3037 conditional code as follows: 3038 <verbatim> 3039 if (defined &Foswiki::Func::writeEvent) { 3040 # use writeEvent and eachEventSince 3041 } else { 3042 # old code using {LogFileName} 3043 } 3044 </verbatim> 3045 3046 Note that the ability to read/write =$Foswiki::cfg{LogFileName}= is 3047 maintained for compatibility but is *deprecated* (should not be used 3048 in new code intended to work only with Foswiki 1.1 and later) and will 3049 not work with any installation that stores logs in a database. 3050 3051 =cut 3052 3053 sub 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 3066 Log 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 3071 sub 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 3081 Log debug message to the debug log 3082 * =$text= - Text to write; timestamp gets added 3083 3084 =cut 3085 3086 sub 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 3099 Get an iterator over the list of all the events at the given level 3100 between =$time= and now. Events are written to the event log using 3101 =writeEvent=. The Foswiki core will write other events that will 3102 also be returned. 3103 3104 Events are returned in *oldest-first* order. 3105 3106 Each 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 3114 Use the iterator like this: 3115 <verbatim> 3116 my $it = Foswiki::Func::eachEventSince(Foswiki::Time::parseTime("1 Apr 2010")); 3117 while ($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 3127 sub eachEventSince { 3128 my $time = shift; 3129 return $Foswiki::Plugins::SESSION->logger->eachEventSince( $time, 'info' ); 3130 } 3131 3132 =begin TML 3133 3116 3134 ---++ Deprecated functions 3117 3135
Note: See TracChangeset
for help on using the changeset viewer.
