Changeset 5810 for trunk/core/lib/Foswiki.pm
- Timestamp:
- 12/17/09 19:28:55 (2 years ago)
- File:
-
- 1 edited
-
trunk/core/lib/Foswiki.pm (modified) (42 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/core/lib/Foswiki.pm
r5791 r5810 61 61 use Foswiki::Store (); 62 62 use Foswiki::Users (); 63 use Foswiki::PageCache ();64 63 65 64 require 5.005; # For regex objects and internationalisation 66 65 67 66 # Site configuration constants 68 use vars qw( %cfg ); 69 70 # Uncomment this and the __END__ to enable AutoLoader 71 #use AutoLoader 'AUTOLOAD'; 72 # You then need to autosplit Foswiki.pm: 73 # cd lib 74 # perl -e 'use AutoSplit; autosplit("Foswiki.pm", "auto")' 67 our %cfg; 75 68 76 69 # Other computed constants 77 70 our $foswikiLibDir; 78 71 our %regex; 79 our % functionTags;72 our %macros; 80 73 our %contextFreeSyntax; 81 74 our $VERSION; … … 84 77 our $FALSE = 0; 85 78 our $engine; 86 our $ifParser; 87 88 # Token character that must not occur in any normal text - converted 89 # to a flag character if it ever does occur (very unlikely) 90 # Foswiki uses $TranslationToken to mark points in the text. This is 91 # normally \0, which is not a useful character in any 8-bit character 92 # set we can find, nor in UTF-8. But if you *do* encounter problems 93 # with it, the workaround is to change $TranslationToken to something 94 # longer that is unlikely to occur in your text - for example 95 # muRfleFli5ble8leep (do *not* use punctuation characters or whitspace 96 # in the string!) 97 # See Codev.NationalCharTokenClash for more. 98 our $TranslationToken = "\0"; 79 our $TranslationToken = "\0"; # unused, but maintained for compatibility 80 81 # Used by takeOut/putBack blocks 82 our $BLOCKID = 0; 83 our $OC = "<!--\0"; 84 our $CC = "\0-->"; 99 85 100 86 # Returns the full path of the directory containing Foswiki.pm … … 145 131 146 132 BEGIN { 133 #Monitor::MARK("Start of BEGIN block in Foswiki.pm"); 147 134 if (DEBUG) { 148 135 … … 170 157 171 158 # Default handlers for different %TAGS% 172 %functionTags = ( 173 ADDTOHEAD => \&ADDTOHEAD, 174 ALLVARIABLES => \&ALLVARIABLES, 175 ATTACHURL => \&ATTACHURL, 176 ATTACHURLPATH => \&ATTACHURLPATH, 177 COREPOD => \&COREPOD, 178 DATE => \&DATE, 179 DISPLAYTIME => \&DISPLAYTIME, 180 ENCODE => \&ENCODE, 181 ENV => \&ENV, 182 FORMFIELD => \&FORMFIELD, 183 FOREACH => \&FOREACH, 184 GMTIME => \&GMTIME, 185 GROUPINFO => \&GROUPINFO, 186 GROUPS => \&GROUPS, 187 HTTP_HOST => \&HTTP_HOST_deprecated, 188 HTTP => \&HTTP, 189 HTTPS => \&HTTPS, 190 ICON => \&ICON, 191 ICONURL => \&ICONURL, 192 ICONURLPATH => \&ICONURLPATH, 193 IF => \&IF, 194 INCLUDE => \&INCLUDE, 195 INTURLENCODE => \&INTURLENCODE_deprecated, 196 LANGUAGES => \&LANGUAGES, 197 MAKETEXT => \&MAKETEXT, 198 META => \&META, 199 METASEARCH => \&METASEARCH, 200 NOP => \&NOP, 201 PLUGINVERSION => \&PLUGINVERSION, 202 PUBURL => \&PUBURL, 203 PUBURLPATH => \&PUBURLPATH, 204 QUERYPARAMS => \&QUERYPARAMS, 205 QUERYSTRING => \&QUERYSTRING, 206 RELATIVETOPICPATH => \&RELATIVETOPICPATH, 207 REMOTE_ADDR => \&REMOTE_ADDR_deprecated, 208 REMOTE_PORT => \&REMOTE_PORT_deprecated, 209 REMOTE_USER => \&REMOTE_USER_deprecated, 210 REVINFO => \&REVINFO, 211 REVTITLE => \&REVTITLE, 212 REVARG => \&REVARG, 213 SCRIPTNAME => \&SCRIPTNAME, 214 SCRIPTURL => \&SCRIPTURL, 215 SCRIPTURLPATH => \&SCRIPTURLPATH, 216 SEARCH => \&SEARCH, 217 SEP => \&SEP, 218 SERVERTIME => \&SERVERTIME, 219 SHOWPREFERENCE => \&SHOWPREFERENCE, 220 SPACEDTOPIC => \&SPACEDTOPIC_deprecated, 221 SPACEOUT => \&SPACEOUT, 222 'TMPL:P' => \&TMPLP, 223 TOPICLIST => \&TOPICLIST, 224 URLENCODE => \&ENCODE, 225 URLPARAM => \&URLPARAM, 226 LANGUAGE => \&LANGUAGE, 227 USERINFO => \&USERINFO, 228 USERNAME => \&USERNAME_deprecated, 229 VAR => \&VAR, 230 WEBLIST => \&WEBLIST, 231 WIKINAME => \&WIKINAME_deprecated, 232 WIKIUSERNAME => \&WIKIUSERNAME_deprecated, 233 DISPLAYDEPENDENCIES => \&DISPLAYDEPENDENCIES, 159 # Where an entry is set as 'undef', the tag will be demand-loaded 160 # from Foswiki::Macros, if it is used. This tactic is used to reduce 161 # the load time of this module, especially when it is used from 162 # REST handlers. 163 %macros = ( 164 ADDTOHEAD => undef, 165 ALLVARIABLES => 166 sub { $_[0]->{prefs}->stringify() }, 167 ATTACHURL => 168 sub { return $_[0]->getPubUrl( 1, $_[2]->web, $_[2]->topic ); }, 169 ATTACHURLPATH => 170 sub { return $_[0]->getPubUrl( 0, $_[2]->web, $_[2]->topic ); }, 171 DATE => sub { 172 Foswiki::Time::formatTime( 173 time(), 174 $Foswiki::cfg{DefaultDateFormat}, 175 $Foswiki::cfg{DisplayTimeValues} 176 ); 177 }, 178 DISPLAYTIME => sub { 179 Foswiki::Time::formatTime( 180 time(), 181 $_[1]->{_DEFAULT} || '', 182 $Foswiki::cfg{DisplayTimeValues} 183 ); 184 }, 185 ENCODE => undef, 186 ENV => undef, 187 FORMFIELD => undef, 188 FOREACH => undef, 189 GMTIME => sub { 190 Foswiki::Time::formatTime( 191 time(), $_[1]->{_DEFAULT} || '', 'gmtime' ); 192 }, 193 GROUPINFO => undef, 194 GROUPS => undef, 195 HTTP_HOST => 196 #deprecated functionality, now implemented using %ENV% 197 sub { $_[0]->{request}->header('Host') || '' }, 198 HTTP => undef, 199 HTTPS => undef, 200 ICON => undef, 201 ICONURL => 202 sub { $_[0]->getIconUrl( 1, $_[1]->{_DEFAULT} || '' ); }, 203 ICONURLPATH => 204 sub { $_[0]->getIconUrl( 0, $_[1]->{_DEFAULT} || '' ); }, 205 IF => undef, 206 INCLUDE => undef, 207 INTURLENCODE => undef, 208 LANGUAGE => sub { $_[0]->i18n->language(); }, 209 LANGUAGES => undef, 210 MAKETEXT => undef, 211 META => undef, 212 METASEARCH => sub { 213 # DEPRECATED 214 $_[0]->search->searchMetaData($_[1]); 215 }, 216 NOP => 217 # Remove NOP tag in template topics but show content. 218 # Used in template _topics_ (not templates, per se, but 219 # topics used as templates for new topics) 220 sub { $_[1]->{_RAW} ? $_[1]->{_RAW} : '<nop>' }, 221 PLUGINVERSION => sub { 222 $_[0]->{plugins}->getPluginVersion( $_[1]->{_DEFAULT} ); 223 }, 224 PUBURL => 225 sub { $_[0]->getPubUrl(1) }, 226 PUBURLPATH => 227 sub { $_[0]->getPubUrl(0) }, 228 QUERYPARAMS => undef, 229 QUERYSTRING => 230 sub { $_[0]->{request}->queryString() }, 231 RELATIVETOPICPATH => undef, 232 REMOTE_ADDR => 233 # DEPRECATED, now implemented using %ENV% 234 #move to compatibility plugin in Foswiki 2.0 235 sub { $_[0]->{request}->remoteAddress() || ''; }, 236 REMOTE_PORT => 237 # DEPRECATED 238 # CGI/1.1 (RFC 3875) doesn't specify REMOTE_PORT, 239 # but some webservers implement it. However, since 240 # it's not RFC compliant, Foswiki should not rely on 241 # it. So we get more portability. 242 sub { '' }, 243 REMOTE_USER => 244 # DEPRECATED 245 sub { $_[0]->{request}->remoteUser() || '' }, 246 REVINFO => undef, 247 REVTITLE => undef, 248 REVARG => undef, 249 SCRIPTNAME => sub { $_[0]->{request}->action() }, 250 SCRIPTURL => 251 sub { $_[0]->getScriptUrl( 1, $_[1]->{_DEFAULT} || '' ) }, 252 SCRIPTURLPATH => 253 sub { $_[0]->getScriptUrl( 0, $_[1]->{_DEFAULT} || '' ) }, 254 SEARCH => undef, 255 SEP => 256 # Shortcut to %TMPL:P{"sep"}% 257 sub { $_[0]->templates->expandTemplate('sep') }, 258 SERVERTIME => sub { 259 Foswiki::Time::formatTime( 260 time(), $_[1]->{_DEFAULT} || '', 'servertime' ); 261 }, 262 SHOWPREFERENCE => undef, 263 SPACEDTOPIC => undef, 264 SPACEOUT => undef, 265 'TMPL:P' => sub { $_[0]->templates->tmplP($_[1]) }, 266 TOPICLIST => undef, 267 URLENCODE => undef, 268 URLPARAM => undef, 269 USERINFO => undef, 270 USERNAME => undef, 271 VAR => undef, 272 WEBLIST => undef, 273 WIKINAME => undef, 274 WIKIUSERNAME => undef, 275 DISPLAYDEPENDENCIES => undef, 234 276 235 277 # Constant tag strings _not_ dependent on config. These get nicely … … 292 334 293 335 # Constant tags dependent on the config 294 $ functionTags{ALLOWLOGINNAME} =336 $macros{ALLOWLOGINNAME} = 295 337 sub { $Foswiki::cfg{Register}{AllowLoginName} || 0 }; 296 $ functionTags{AUTHREALM} = sub { $Foswiki::cfg{AuthRealm} };297 $ functionTags{DEFAULTURLHOST} = sub { $Foswiki::cfg{DefaultUrlHost} };298 $ functionTags{HOMETOPIC} = sub { $Foswiki::cfg{HomeTopicName} };299 $ functionTags{LOCALSITEPREFS} = sub { $Foswiki::cfg{LocalSitePreferences} };300 $ functionTags{NOFOLLOW} =338 $macros{AUTHREALM} = sub { $Foswiki::cfg{AuthRealm} }; 339 $macros{DEFAULTURLHOST} = sub { $Foswiki::cfg{DefaultUrlHost} }; 340 $macros{HOMETOPIC} = sub { $Foswiki::cfg{HomeTopicName} }; 341 $macros{LOCALSITEPREFS} = sub { $Foswiki::cfg{LocalSitePreferences} }; 342 $macros{NOFOLLOW} = 301 343 sub { $Foswiki::cfg{NoFollow} ? 'rel=' . $Foswiki::cfg{NoFollow} : '' }; 302 $ functionTags{NOTIFYTOPIC} = sub { $Foswiki::cfg{NotifyTopicName} };303 $ functionTags{SCRIPTSUFFIX} = sub { $Foswiki::cfg{ScriptSuffix} };304 $ functionTags{STATISTICSTOPIC} = sub { $Foswiki::cfg{Stats}{TopicName} };305 $ functionTags{SYSTEMWEB} = sub { $Foswiki::cfg{SystemWebName} };306 $ functionTags{TRASHWEB} = sub { $Foswiki::cfg{TrashWebName} };307 $ functionTags{WIKIADMINLOGIN} = sub { $Foswiki::cfg{AdminUserLogin} };308 $ functionTags{USERSWEB} = sub { $Foswiki::cfg{UsersWebName} };309 $ functionTags{WEBPREFSTOPIC} = sub { $Foswiki::cfg{WebPrefsTopicName} };310 $ functionTags{WIKIPREFSTOPIC} = sub { $Foswiki::cfg{SitePrefsTopicName} };311 $ functionTags{WIKIUSERSTOPIC} = sub { $Foswiki::cfg{UsersTopicName} };312 $ functionTags{WIKIWEBMASTER} = sub { $Foswiki::cfg{WebMasterEmail} };313 $ functionTags{WIKIWEBMASTERNAME} = sub { $Foswiki::cfg{WebMasterName} };344 $macros{NOTIFYTOPIC} = sub { $Foswiki::cfg{NotifyTopicName} }; 345 $macros{SCRIPTSUFFIX} = sub { $Foswiki::cfg{ScriptSuffix} }; 346 $macros{STATISTICSTOPIC} = sub { $Foswiki::cfg{Stats}{TopicName} }; 347 $macros{SYSTEMWEB} = sub { $Foswiki::cfg{SystemWebName} }; 348 $macros{TRASHWEB} = sub { $Foswiki::cfg{TrashWebName} }; 349 $macros{WIKIADMINLOGIN} = sub { $Foswiki::cfg{AdminUserLogin} }; 350 $macros{USERSWEB} = sub { $Foswiki::cfg{UsersWebName} }; 351 $macros{WEBPREFSTOPIC} = sub { $Foswiki::cfg{WebPrefsTopicName} }; 352 $macros{WIKIPREFSTOPIC} = sub { $Foswiki::cfg{SitePrefsTopicName} }; 353 $macros{WIKIUSERSTOPIC} = sub { $Foswiki::cfg{UsersTopicName} }; 354 $macros{WIKIWEBMASTER} = sub { $Foswiki::cfg{WebMasterEmail} }; 355 $macros{WIKIWEBMASTERNAME} = sub { $Foswiki::cfg{WebMasterName} }; 314 356 315 357 # locale setup … … 336 378 } 337 379 338 $ functionTags{CHARSET} = sub {380 $macros{CHARSET} = sub { 339 381 $Foswiki::cfg{Site}{CharSet} 340 382 || 'iso-8859-1'; 341 383 }; 342 384 343 $ functionTags{LANG} = sub {385 $macros{LANG} = sub { 344 386 $Foswiki::cfg{Site}{Locale} =~ m/^([a-z]+_[a-z]+)/i ? $1 : 'en_US'; 345 387 }; … … 499 541 die $@ if $@; 500 542 501 Monitor::MARK('Static configuration loaded'); 502 } 503 504 =begin TML 505 506 ---++ ObjectMethod UTF82SiteCharSet( $utf8 ) -> $ascii 507 508 Auto-detect UTF-8 vs. site charset in string, and convert UTF-8 into site 509 charset. 510 511 =cut 543 #Monitor::MARK('End of BEGIN block in Foswiki.pm'); 544 } 512 545 513 546 sub UTF82SiteCharSet { … … 710 743 $this->generateHTTPHeaders( $pageType, $contentType, $text, $cachedPage ); 711 744 712 # SMELL: null operation. the http headers are written out during Foswiki::Engine::finalize 713 #$hdr = $this->{response}->printHeaders; 745 # SMELL: null operation. the http headers are written out 746 # during Foswiki::Engine::finalize 747 # $hdr = $this->{response}->printHeaders; 714 748 715 749 $this->{response}->print($text); … … 926 960 sub redirectto { 927 961 my ( $this, $url ) = @_; 928 ASSERT($url) ;962 ASSERT($url) if DEBUG; 929 963 930 964 my $redirecturl = $this->{request}->param('redirectto'); … … 986 1020 sub redirect { 987 1021 my ( $this, $url, $passthru ) = @_; 988 ASSERT( defined $url ) ;1022 ASSERT( defined $url ) if DEBUG; 989 1023 990 1024 return unless $this->{request}; … … 1098 1132 1099 1133 sub getCGISession { 1100 my $this = shift; 1101 return $this->{users}->getCGISession(); 1134 $_[0]->{users}->getCGISession(); 1102 1135 } 1103 1136 … … 1112 1145 1113 1146 sub getLoginManager { 1114 my $this = shift; 1115 ASSERT($this->{users}) if DEBUG; 1116 return $this->{users}->getLoginManager(); 1147 $_[0]->{users}->getLoginManager(); 1117 1148 } 1118 1149 … … 1146 1177 return 1 if ( $name =~ m/^$regex{topicNameRegex}$/o ); 1147 1178 return 0 unless $nonww; 1148 return 0 if $name =~ /$ Foswiki::cfg{NameFilter}/;1179 return 0 if $name =~ /$cfg{NameFilter}/o; 1149 1180 return 1; 1150 1181 } … … 1181 1212 # Note: must work on tainted names. 1182 1213 sub isValidEmailAddress { 1183 my ($name) = @_;1184 return $name =~ /^$regex{emailAddrRegex}$/ ;1214 my $name = shift || ''; 1215 return $name =~ /^$regex{emailAddrRegex}$/o; 1185 1216 } 1186 1217 … … 1413 1444 =begin TML 1414 1445 1415 ---++ ObjectMethod mapToIconFileName( $fileName, $default ) -> $fileName1416 1417 Maps from a filename (or just the extension) to the name of the1418 file that contains the image for that file type.1419 1420 =cut1421 1422 sub mapToIconFileName {1423 my ( $this, $fileName, $default ) = @_;1424 1425 my @bits = ( split( /\./, $fileName ) );1426 my $fileExt = lc( $bits[$#bits] );1427 1428 unless ( $this->{_ICONMAP} ) {1429 my $iconTopic = $this->{prefs}->getPreference('ICONTOPIC');1430 if ( defined($iconTopic) ) {1431 my ( $web, $topic ) =1432 $this->normalizeWebTopicName( $this->{webName}, $iconTopic );1433 my $topicObject = Foswiki::Meta->new( $this, $web, $topic );1434 local $/;1435 try {1436 my $icons =1437 $topicObject->openAttachment( '_filetypes.txt', '<' );1438 %{ $this->{_ICONMAP} } = split( /\s+/, <$icons> );1439 $icons->close();1440 }1441 catch Error with {1442 ASSERT( 0, $_[0] ) if DEBUG;1443 %{ $this->{_ICONMAP} } = ();1444 };1445 }1446 else {1447 return $default || $fileName;1448 }1449 }1450 1451 return $this->{_ICONMAP}->{$fileExt} || $default || 'else';1452 }1453 1454 =begin TML1455 1456 1446 ---++ ObjectMethod normalizeWebTopicName( $web, $topic ) -> ( $web, $topic ) 1457 1447 … … 1526 1516 sub new { 1527 1517 my ( $class, $defaultUser, $query, $initialContext ) = @_; 1528 ASSERT( !$query || UNIVERSAL::isa( $query, 'Foswiki::Request' ));1529 Monitor::MARK("Static compilation complete");1518 Monitor::MARK("Static init over; make Foswiki object"); 1519 ASSERT( !$query || UNIVERSAL::isa( $query, 'Foswiki::Request' ) ) if DEBUG; 1530 1520 1531 1521 # Compatibility; not used except maybe in plugins … … 1558 1548 $this->{context} = $initialContext; 1559 1549 1560 $this->{cache} = new Foswiki::PageCache( $this ) 1561 if ($Foswiki::cfg{Cache}{Enabled}); 1550 if ($Foswiki::cfg{Cache}{Enabled}) { 1551 require Foswiki::PageCache; 1552 $this->{cache} = new Foswiki::PageCache( $this ) 1553 } 1562 1554 my $prefs = new Foswiki::Prefs($this); 1563 1555 $this->{prefs} = $prefs; … … 1568 1560 $this->{store} = $Foswiki::cfg{Store}{Implementation}->new(); 1569 1561 1562 #Monitor::MARK("Created store"); 1563 1570 1564 $this->{users} = new Foswiki::Users($this); 1565 1566 #Monitor::MARK("Created users object"); 1571 1567 1572 1568 # Load (or create) the CGI session … … 1716 1712 $prefs->loadDefaultPreferences(); 1717 1713 1714 #Monitor::MARK("Loaded default prefs"); 1715 1718 1716 # SMELL: what happens if we move this into the Foswiki::Users::new? 1719 1717 $this->{user} = $this->{users}->initialiseUser( $this->{remoteUser} ); 1718 1719 #Monitor::MARK("Initialised user"); 1720 1720 1721 1721 # Static session variables that can be expanded in topics when they … … 1745 1745 $prefs->pushTopicContext( $this->{webName}, $this->{topicName} ); 1746 1746 1747 #Monitor::MARK("Preferences all set up"); 1748 1747 1749 # Finish plugin initialization - register handlers 1748 1750 $this->{plugins}->enable(); 1749 1751 1750 Monitor::MARK("Foswiki sessioncreated");1752 Monitor::MARK("Foswiki object created"); 1751 1753 1752 1754 return $this; … … 1918 1920 my $this = shift; 1919 1921 1922 # Print any macros that are never loaded 1923 #print STDERR "NEVER USED\n"; 1924 #for my $i (keys %macros) { 1925 # print STDERR "\t$i\n" unless defined $macros{$i}; 1926 #} 1920 1927 $_->finish() foreach values %{ $this->{forms} }; 1921 1928 $this->{plugins}->finish() if $this->{plugins}; … … 2018 2025 } 2019 2026 2020 # Add a web reference to a [[...][...]] link in an included topic2021 sub _fixIncludeLink {2022 my ( $web, $link, $label ) = @_;2023 2024 # Detect absolute and relative URLs and web-qualified wikinames2025 if ( $link =~2026 m#^($regex{webNameRegex}\.|$regex{defaultWebNameRegex}\.|$regex{linkProtocolPattern}:|/)#o2027 )2028 {2029 if ($label) {2030 return "[[$link][$label]]";2031 }2032 else {2033 return "[[$link]]";2034 }2035 }2036 elsif ( !$label ) {2037 2038 # Must be wikiword or spaced-out wikiword (or illegal link :-/)2039 $label = $link;2040 }2041 2042 # If link is only an anchor, leave it as is (Foswikitask:Item771)2043 return "[[$link][$label]]" if $link =~ /^#/;2044 return "[[$web.$link][$label]]";2045 }2046 2047 # Replace web references in a topic. Called from forEachLine, applying to2048 # each non-verbatim and non-literal line.2049 sub _fixupIncludedTopic {2050 my ( $text, $options ) = @_;2051 2052 my $fromWeb = $options->{web};2053 2054 unless ( $options->{in_noautolink} ) {2055 2056 # 'TopicName' to 'Web.TopicName'2057 $text =~2058 s#(?:^|(?<=[\s(]))($regex{wikiWordRegex})(?=\s|\)|$)#$fromWeb.$1#go;2059 }2060 2061 # Handle explicit [[]] everywhere2062 # '[[TopicName][...]]' to '[[Web.TopicName][...]]'2063 $text =~ s/\[\[([^]]+)\](?:\[([^]]+)\])?\]/2064 _fixIncludeLink( $fromWeb, $1, $2 )/geo;2065 2066 return $text;2067 }2068 2069 2027 =begin TML 2070 2028 … … 2085 2043 $pattern =~ s/(^|[^\\])([\$\@])/$1\\$2/g; 2086 2044 return $pattern; 2087 }2088 2089 =begin TML2090 2091 ---++ StaticMethod applyPatternToIncludedText( $text, $pattern ) -> $text2092 2093 Apply a pattern on included text to extract a subset2094 2095 =cut2096 2097 sub applyPatternToIncludedText {2098 my ( $text, $pattern ) = @_;2099 2100 $pattern = Foswiki::Sandbox::untaint( $pattern, \&validatePattern );2101 2102 try {2103 $text =~ s/$pattern/$1/is;2104 }2105 catch Error::Simple with {2106 $text = '';2107 };2108 return $text;2109 }2110 2111 #2112 # SMELL: this is _not_ a tag handler in the sense of other builtin tags,2113 # because it requires far more context information (the text of the topic)2114 # than any handler.2115 # SMELL: as a tag handler that also semi-renders the topic to extract the2116 # headings, this handler would be much better as a preRenderingHandler in2117 # a plugin (where head, script and verbatim sections are already protected)2118 #2119 # * $text : ref to the text of the current topic2120 # * $topic : the topic we are in2121 # * $web : the web we are in2122 # * $args : 'Topic' [web='Web'] [depth='N']2123 # Return value: $tableOfContents2124 # Handles %<nop>TOC{...}% syntax. Creates a table of contents2125 # using Foswiki bulleted2126 # list markup, linked to the section headings of a topic. A section heading is2127 # entered in one of the following forms:2128 # * $headingPatternSp : \t++... spaces section heading2129 # * $headingPatternDa : ---++... dashes section heading2130 # * $headingPatternHt : <h[1-6]> HTML section heading </h[1-6]>2131 sub _TOC {2132 my ( $this, $text, $topicObject, $args ) = @_;2133 2134 require Foswiki::Attrs;2135 my $params = new Foswiki::Attrs($args);2136 my $sameTopic = 1; # is the toc for this topic?2137 2138 my $tocTopic = $params->{_DEFAULT};2139 my $tocWeb = $params->{web};2140 2141 if ( $tocTopic || $tocWeb ) {2142 $tocWeb ||= $topicObject->web;2143 $tocTopic ||= $topicObject->topic;2144 ( $tocTopic, $tocWeb ) =2145 $this->normalizeWebTopicName( $tocTopic, $tocWeb );2146 2147 if ( $tocWeb eq $topicObject->web && $tocTopic eq $topicObject->topic )2148 {2149 $sameTopic = 1;2150 }2151 else {2152 2153 # Data for topic coming from another topic2154 $params->{differentTopic} = 1;2155 $topicObject = Foswiki::Meta->load( $this, $tocWeb, $tocTopic );2156 if ( !$topicObject->haveAccess('VIEW') ) {2157 return $this->inlineAlert( 'alerts', 'access_denied', $tocWeb,2158 $tocTopic );2159 }2160 $text = $topicObject->text;2161 $sameTopic = 0;2162 }2163 }2164 2165 return $this->renderer->renderTOC( $text, $topicObject, $params,2166 $sameTopic );2167 2045 } 2168 2046 … … 2687 2565 } 2688 2566 2567 =begin TML 2568 2569 ---++ StaticMethod takeOutBlocks( \$text, $tag, \%map ) -> $text 2570 * =$text= - Text to process 2571 * =$tag= - XML-style tag. 2572 * =\%map= - Reference to a hash to contain the removed blocks 2573 2574 Return value: $text with blocks removed 2575 2576 Searches through $text and extracts blocks delimited by an XML-style tag, 2577 storing the extracted block, and replacing with a token string which is 2578 not affected by TML rendering. The text after these substitutions is 2579 returned. 2580 2581 =cut 2582 2583 sub takeOutBlocks { 2584 my ( $intext, $tag, $map ) = @_; 2585 2586 return $intext unless ( $intext =~ m/<$tag\b/i ); 2587 2588 my $out = ''; 2589 my $depth = 0; 2590 my $scoop; 2591 my $tagParams; 2592 2593 foreach my $token ( split( /(<\/?$tag[^>]*>)/i, $intext )) { 2594 if ( $token =~ /<$tag\b([^>]*)?>/i ) { 2595 $depth++; 2596 if ( $depth eq 1 ) { 2597 $tagParams = $1; 2598 next; 2599 } 2600 } 2601 elsif ( $token =~ /<\/$tag>/i ) { 2602 if ( $depth > 0 ) { 2603 $depth--; 2604 if ( $depth eq 0 ) { 2605 my $placeholder = "$tag$BLOCKID"; 2606 $BLOCKID++; 2607 $map->{$placeholder}{text} = $scoop; 2608 $map->{$placeholder}{params} = $tagParams; 2609 $out .= "$OC$placeholder$CC"; 2610 $scoop = ''; 2611 next; 2612 } 2613 } 2614 } 2615 if ( $depth > 0 ) { 2616 $scoop .= $token; 2617 } 2618 else { 2619 $out .= $token; 2620 } 2621 } 2622 2623 # unmatched tags 2624 if ( defined($scoop) && ( $scoop ne '' ) ) { 2625 my $placeholder = "$tag$BLOCKID"; 2626 $BLOCKID++; 2627 $map->{$placeholder}{text} = $scoop; 2628 $map->{$placeholder}{params} = $tagParams; 2629 $out .= "$OC$placeholder$CC"; 2630 } 2631 2632 return $out; 2633 } 2634 2635 =begin TML 2636 2637 ---++ StaticMethod putBackBlocks( \$text, \%map, $tag, $newtag, $callBack ) -> $text 2638 2639 Return value: $text with blocks added back 2640 * =\$text= - reference to text to process 2641 * =\%map= - map placeholders to blocks removed by takeOutBlocks 2642 * =$tag= - Tag name processed by takeOutBlocks 2643 * =$newtag= - Tag name to use in output, in place of $tag. 2644 If undefined, uses $tag. 2645 * =$callback= - Reference to function to call on each block 2646 being inserted (optional) 2647 2648 Reverses the actions of takeOutBlocks. 2649 2650 Each replaced block is processed by the callback (if there is one) before 2651 re-insertion. 2652 2653 Parameters to the outermost cut block are replaced into the open tag, 2654 even if that tag is changed. This allows things like =<verbatim class=''>= 2655 to be changed to =<pre class=''>= 2656 2657 If you set $newtag to '', replaces the taken-out block with the contents 2658 of the block, not including the open/close. This is used for <literal>, 2659 for example. 2660 2661 =cut 2662 2663 sub putBackBlocks { 2664 my ( $text, $map, $tag, $newtag, $callback ) = @_; 2665 2666 $newtag = $tag if ( !defined($newtag) ); 2667 2668 foreach my $placeholder ( keys %$map ) { 2669 if ( $placeholder =~ /^$tag\d+$/ ) { 2670 my $params = $map->{$placeholder}{params} || ''; 2671 my $val = $map->{$placeholder}{text}; 2672 $val = &$callback($val) if ( defined($callback) ); 2673 if ( $newtag eq '' ) { 2674 $$text =~ s($OC$placeholder$CC)($val); 2675 } 2676 else { 2677 $$text =~ s($OC$placeholder$CC) 2678 (<$newtag$params>$val</$newtag>); 2679 } 2680 delete( $map->{$placeholder} ); 2681 } 2682 } 2683 } 2684 2689 2685 # Process Foswiki %TAGS{}% by parsing the input tokenised into 2690 2686 # % separated sections. The parser is a simple stack-based parse, … … 2701 2697 2702 2698 #no tags to process 2703 return $text unless ( $text =~ / (%)/ );2699 return $text unless ( $text =~ /%/ ); 2704 2700 2705 2701 unless ($depth) { … … 2714 2710 2715 2711 my $verbatim = {}; 2716 $text = $this->renderer->takeOutBlocks( $text, 'verbatim', 2717 $verbatim); 2712 $text = takeOutBlocks( $text, 'verbatim', $verbatim); 2718 2713 2719 2714 my $dirtyAreas = {}; 2720 $text = $this->renderer->takeOutBlocks( $text, 'dirtyarea', $dirtyAreas)2715 $text = takeOutBlocks( $text, 'dirtyarea', $dirtyAreas) 2721 2716 if $Foswiki::cfg{Cache}{Enabled}; 2722 2723 2724 # See Item14422725 #my $percent = ($TranslationToken x 3).'%'.($TranslationToken x 3);2726 2717 2727 2718 my @queue = split( /(%)/, $text ); … … 2748 2739 if ( $stackTop =~ /}$/s ) { 2749 2740 while ( scalar(@stack) 2750 && $stackTop !~ /^% ($regex{tagNameRegex}){.*}$/so )2741 && $stackTop !~ /^%$regex{tagNameRegex}\{.*}$/so ) 2751 2742 { 2752 2743 my $top = $stackTop; … … 2764 2755 2765 2756 #print STDERR ' ' x $tell,"POP $tag\n"; 2757 Monitor::MARK("Before $tag"); 2766 2758 my $e = &$tagf( $this, $tag, $args, $topicObject ); 2759 Monitor::MARK("After $tag"); 2767 2760 2768 2761 if ( defined($e) ) { … … 2770 2763 #print STDERR ' ' x $tell--,"EXPANDED $tag -> $e\n"; 2771 2764 $stackTop = pop(@stack); 2772 unless ( $e =~ /(%)/ ) { 2773 2774 #SMELL: this is a profiler speedup found by Sven on the last day of 4.2.1 2775 #TODO: I don't think this parser should be in this section - re-analysis desired. 2776 #print STDERR "no tags to recurse\n"; 2765 # Don't bother recursively expanding unless there are 2766 # unexpanded tags in the result. 2767 unless ( $e =~ /%$regex{tagNameRegex}(?:{.*})?%/o ) { 2777 2768 $stackTop .= $e; 2778 2769 next; … … 2784 2775 $depth - 1 ); 2785 2776 } 2786 else { # expansion failed2787 #print STDERR ' ' x $tell++,"EXPAND $tag FAILED\n";2788 # To handle %NOP2789 # correctly, we have to handle the %VAR% case differently2790 # to the %VAR{}% case when a variable expansion fails.2791 # This is so that recursively define variables e.g.2792 # %A%B%D% expand correctly, but at the same time we ensure2793 # that a mismatched }% can't accidentally close a context2794 # that was left open when a tag expansion failed.2795 # However Cairodidn't do this, so for compatibility2796 # we have to accept that %NOP can never be fixed. if it2797 # could, then we could uncomment the following:2777 else { 2778 #print STDERR ' ' x $tell++,"EXPAND $tag FAILED\n"; 2779 # To handle %NOP 2780 # correctly, we have to handle the %VAR% case differently 2781 # to the %VAR{}% case when a variable expansion fails. 2782 # This is so that recursively define variables e.g. 2783 # %A%B%D% expand correctly, but at the same time we ensure 2784 # that a mismatched }% can't accidentally close a context 2785 # that was left open when a tag expansion failed. 2786 # However TWiki didn't do this, so for compatibility 2787 # we have to accept that %NOP can never be fixed. if it 2788 # could, then we could uncomment the following: 2798 2789 2799 2790 #if( $stackTop =~ /}$/ ) { … … 2802 2793 # # onto the stack, but we don't want it to match the 2803 2794 # # tag expression again. So we protect the %'s 2804 # $stackTop = $percent.$expr.$percent;2795 # $stackTop = "%$expr%"; 2805 2796 #} else 2806 {2797 #{ 2807 2798 2808 2799 # %VAR% case. … … 2813 2804 push( @stack, $stackTop ); 2814 2805 $stackTop = '%'; # open new context 2815 }2806 #} 2816 2807 } 2817 2808 } … … 2834 2825 } 2835 2826 2836 #$stackTop =~ s/$percent/%/go; 2837 2838 $this->renderer->putBackBlocks( \$stackTop, $dirtyAreas, 'dirtyarea' ) 2827 putBackBlocks( \$stackTop, $dirtyAreas, 'dirtyarea' ) 2839 2828 if $Foswiki::cfg{Cache}{Enabled}; 2840 $this->renderer->putBackBlocks( \$stackTop, $verbatim, 'verbatim' );2829 putBackBlocks( \$stackTop, $verbatim, 'verbatim' ); 2841 2830 2842 2831 #print STDERR "FINAL $stackTop\n"; … … 2856 2845 my $e = $this->{prefs}->getPreference($tag); 2857 2846 unless ( defined($e) ) { 2858 if ( !defined($e) && defined( $functionTags{$tag} ) ) { 2859 $e = &{ $functionTags{$tag} }( 2847 if ( !defined($e) && exists( $macros{$tag} ) ) { 2848 unless ( defined( $macros{$tag} )) { 2849 # Demand-load the macro module 2850 die $tag unless $tag =~ /([A-Z_:]+)/i; 2851 $tag = $1; 2852 eval "require Foswiki::Macros::$tag"; 2853 die $@ if $@; 2854 $macros{$tag} = eval "\\&$tag"; 2855 die $@ if $@; 2856 } 2857 $e = &{ $macros{$tag} }( 2860 2858 $this, new Foswiki::Attrs( $args, $contextFreeSyntax{$tag} ), 2861 2859 $topicObject … … 2966 2964 sub registerTagHandler { 2967 2965 my ( $tag, $fnref, $syntax ) = @_; 2968 $ functionTags{$tag} = $fnref;2966 $macros{$tag} = $fnref; 2969 2967 if ( $syntax && $syntax eq 'context-free' ) { 2970 2968 $contextFreeSyntax{$tag} = 1; … … 3002 3000 #their verbatim blocks safetly. 3003 3001 my $verbatim={}; 3004 $text = $this->renderer->takeOutBlocks( $text, 'verbatim', 3005 $verbatim); 3002 $text = takeOutBlocks( $text, 'verbatim', $verbatim); 3006 3003 3007 3004 # take out dirty areas 3008 3005 my $dirtyAreas = {}; 3009 $text = $this->renderer->takeOutBlocks( $text, 'dirtyarea', $dirtyAreas )3006 $text = takeOutBlocks( $text, 'dirtyarea', $dirtyAreas ) 3010 3007 if $Foswiki::cfg{Cache}{Enabled}; 3011 3008 … … 3024 3021 $this->innerExpandMacros( \$text, $topicObject ); 3025 3022 3026 $text = $this->renderer->takeOutBlocks( $text, 'verbatim', $verbatim );3023 $text = takeOutBlocks( $text, 'verbatim', $verbatim ); 3027 3024 3028 3025 # Plugin Hook … … 3042 3039 # are complete, and has to reprocess the entire topic. 3043 3040 3044 $text =~ s/%TOC(?:{(.*?)})?%/$this->_TOC($text, $topicObject, $1)/ge; 3041 if ($text =~ /%TOC(?:{.*})?%/ ) { 3042 require Foswiki::Macros::TOC; 3043 $text =~ s/%TOC(?:{(.*?)})?%/$this->TOC($text, $topicObject, $1)/ge; 3044 } 3045 3045 3046 3046 # Codev.FormattedSearchWithConditionalOutput: remove <nop> lines, … … 3051 3051 3052 3052 # restore dirty areas 3053 $this->renderer->putBackBlocks( \$text, $dirtyAreas, 'dirtyarea' )3053 putBackBlocks( \$text, $dirtyAreas, 'dirtyarea' ) 3054 3054 if $Foswiki::cfg{Cache}{Enabled}; 3055 3055 3056 3057 $this->renderer->putBackBlocks( \$text, $verbatim, 'verbatim' ); 3056 putBackBlocks( \$text, $verbatim, 'verbatim' ); 3058 3057 3059 3058 # Foswiki Plugin Hook (for cache Plugins only) … … 3067 3066 =begin TML 3068 3067 3069 ---++ ObjectMethod addToH ead( $tag, $header, $requires, $topicObject )3068 ---++ ObjectMethod addToHEAD( $tag, $header, $requires, $topicObject ) 3070 3069 3071 3070 Add =$html= to the HEAD tag of the page currently being generated. … … 3196 3195 } 3197 3196 3198 # generate an include warning3199 # SMELL: varying number of parameters idiotic to handle for customized $warn3200 sub _includeWarning {3201 my $this = shift;3202 my $warn = shift;3203 my $message = shift;3204 3205 if ( $warn eq 'on' ) {3206 return $this->inlineAlert( 'alerts', $message, @_ );3207 }3208 elsif ( isTrue($warn) ) {3209 3210 # different inlineAlerts need different argument counts3211 my $argument = '';3212 if ( $message eq 'topic_not_found' ) {3213 my ( $web, $topic ) = @_;3214 $argument = "$web.$topic";3215 }3216 else {3217 $argument = shift;3218 }3219 $warn =~ s/\$topic/$argument/go if $argument;3220 return $warn;3221 } # else fail silently3222 return '';3223 }3224 3225 3197 =begin TML 3226 3198 … … 3283 3255 } 3284 3256 3285 #-------------------------------------------------------------------3286 # Tag Handlers3287 #-------------------------------------------------------------------3288 3289 sub ADDTOHEAD {3290 my ( $this, $args, $topicObject ) = @_;3291 3292 my $_DEFAULT = $args->{_DEFAULT};3293 my $text = $args->{text};3294 my $topic = $args->{topic};3295 my $requires = $args->{requires};3296 if ( defined $args->{topic} ) {3297 my ( $web, $topic ) =3298 $this->normalizeWebTopicName( $topicObject->web, $args->{topic} );3299 3300 # prevent deep recursion3301 $web =~ s/\//\./g;3302 unless ($this->{_addedToHEAD}{"$web.$topic"}) {3303 my $atom = Foswiki::Meta->new( $this, $web, $topic );3304 $text = $atom->text();3305 $this->{_addedToHEAD}{"$web.$topic"} = 1;3306 }3307 }3308 $text = $_DEFAULT unless defined $text;3309 $text = '' unless defined $text;3310 3311 $this->addToHEAD( $_DEFAULT, $text, $requires, $topicObject );3312 return '';3313 }3314 3315 sub FORMFIELD {3316 my ( $this, $args, $topicObject ) = @_;3317 if ( $args->{topic} ) {3318 my ( $web, $topic ) =3319 $this->normalizeWebTopicName( $topicObject->web, $args->{topic} );3320 $topicObject = new Foswiki::Meta( $this, $web, $topic );3321 }3322 else {3323 3324 # SMELL: horrible hack; assumes the current rev comes from the 'rev'3325 # parameter. There has to be a better way!3326 my $query = $this->{request};3327 $args->{rev} ||= $query->param('rev') if ($query);3328 }3329 return $this->renderer->renderFORMFIELD( $args, $topicObject );3330 }3331 3332 sub TMPLP {3333 my ( $this, $params ) = @_;3334 return $this->templates->tmplP($params);3335 }3336 3337 sub VAR {3338 my ( $this, $params, $topicObject ) = @_;3339 my $key = $params->{_DEFAULT};3340 return '' unless $key;3341 my $web = $params->{web} || $topicObject->web;3342 my $topic = $topicObject->topic;3343 3344 # handle %USERSWEB%-type cases3345 ( $web, $topic ) = $this->normalizeWebTopicName( $web, $topic );3346 3347 my $webObject = Foswiki::Meta->new( $this, $web );3348 3349 # always return a value, even when the key isn't defined3350 return $webObject->getPreference($key) || '';3351 }3352 3353 sub PLUGINVERSION {3354 my ( $this, $params ) = @_;3355 $this->{plugins}->getPluginVersion( $params->{_DEFAULT} );3356 }3357 3358 sub IF {3359 my ( $this, $params, $topicObject ) = @_;3360 3361 unless ($ifParser) {3362 require Foswiki::If::Parser;3363 $ifParser = new Foswiki::If::Parser();3364 }3365 3366 my $texpr = $params->{_DEFAULT};3367 my $expr;3368 my $result;3369 3370 # Recursion block.3371 $this->{evaluating_if} ||= {};3372 3373 # Block after 5 levels.3374 if ( $this->{evaluating_if}->{$texpr}3375 && $this->{evaluating_if}->{$texpr} > 5 )3376 {3377 delete $this->{evaluating_if}->{$texpr};3378 return '';3379 }3380 $this->{evaluating_if}->{$texpr}++;3381 try {3382 $expr = $ifParser->parse($texpr);3383 if ( $expr->evaluate( tom => $topicObject, data => $topicObject ) ) {3384 $params->{then} = '' unless defined $params->{then};3385 $result = expandStandardEscapes( $params->{then} );3386 }3387 else {3388 $params->{else} = '' unless defined $params->{else};3389 $result = expandStandardEscapes( $params->{else} );3390 }3391 }3392 catch Foswiki::Infix::Error with {3393 my $e = shift;3394 $result =3395 $this->inlineAlert( 'alerts', 'generic', 'IF{', $params->stringify(),3396 '}:', $e->{-text} );3397 }3398 finally {3399 delete $this->{evaluating_if}->{$texpr};3400 };3401 return $result;3402 }3403 3404 # Processes a specific instance %<nop>INCLUDE{...}% syntax.3405 # Returns the text to be inserted in place of the INCLUDE command.3406 # $includingTopicObject should be for the immediate parent topic in the3407 # include hierarchy. Works for both URLs and absolute server paths.3408 sub INCLUDE {3409 my ( $this, $params, $includingTopicObject ) = @_;3410 3411 # remember args for the key before mangling the params3412 my $args = $params->stringify();3413 3414 # Remove params, so they don't get expanded in the included page3415 my %control;3416 for my $p qw(_DEFAULT pattern rev section raw warn) {3417 $control{$p} = $params->remove($p);3418 }3419 3420 $control{warn} ||= $this->{prefs}->getPreference('INCLUDEWARNING');3421 3422 # make sure we have something to include. If we don't do this, then3423 # normalizeWebTopicName will default to WebHome. TWikibug:Item2209.3424 unless ( $control{_DEFAULT} ) {3425 return $this->_includeWarning( $control{warn}, 'bad_include_path', '' );3426 }3427 3428 # Filter out '..' from path to prevent includes of '../../file'3429 if ( $Foswiki::cfg{DenyDotDotInclude} && $control{_DEFAULT} =~ /\.\./ ) {3430 return $this->_includeWarning( $control{warn}, 'bad_include_path',3431 $control{_DEFAULT} );3432 }3433 3434 # no sense in considering an empty string as an unfindable section3435 delete $control{section}3436 if ( defined( $control{section} ) && $control{section} eq '' );3437 $control{raw} ||= '';3438 $control{inWeb} = $includingTopicObject->web;3439 $control{inTopic} = $includingTopicObject->topic;3440 3441 # Protocol links e.g. http:, https:, doc:3442 if ( $control{_DEFAULT} =~ /^([a-z]+):/ ) {3443 my $handler = $1;3444 eval 'use Foswiki::IncludeHandlers::' . $handler;3445 die $@ if ($@);3446 unless ($@) {3447 $handler = 'Foswiki::IncludeHandlers::' . $handler;3448 return $handler->INCLUDE( $this, \%control, $params );3449 }3450 }3451 3452 # No protocol handler; must be a topic reference3453 3454 my $text = '';3455 my $includedWeb;3456 my $includedTopic = $control{_DEFAULT};3457 $includedTopic =~ s/\.txt$//; # strip optional (undocumented) .txt3458 3459 ( $includedWeb, $includedTopic ) =3460 $this->normalizeWebTopicName( $includingTopicObject->web,3461 $includedTopic );3462 3463 # See Codev.FailedIncludeWarning for the history.3464 unless ( $this->{store}->topicExists( $includedWeb, $includedTopic ) ) {3465 return _includeWarning( $this, $control{warn}, 'topic_not_found',3466 $includedWeb, $includedTopic );3467 }3468 3469 # prevent recursive includes. Note that the inclusion of a topic into3470 # itself is not blocked; however subsequent attempts to include the3471 # topic will fail. There is a hard block of 99 on any recursive include.3472 my $key = $includingTopicObject->web . '.' . $includingTopicObject->topic;3473 my $count = grep( $key, keys %{ $this->{_INCLUDES} } );3474 $key .= $args;3475 if ( $this->{_INCLUDES}->{$key} || $count > 99 ) {3476 return _includeWarning( $this, $control{warn}, 'already_included',3477 "$includedWeb.$includedTopic", '' );3478 }3479 3480 # Push the topic context to the included topic, so we can create3481 # local (SESSION) macro definitions without polluting the including3482 # topic namespace.3483 $this->{prefs}->pushTopicContext( $this->{webName}, $this->{topicName} );3484 3485 $this->{_INCLUDES}->{$key} = 1;3486 3487 my $includedTopicObject =3488 Foswiki::Meta->load( $this, $includedWeb, $includedTopic, $control{rev} );3489 unless ( $includedTopicObject->haveAccess('VIEW') ) {3490 if ( isTrue( $control{warn} ) ) {3491 return $this->inlineAlert( 'alerts', 'access_denied',3492 "[[$includedWeb.$includedTopic]]" );3493 } # else fail silently3494 return '';3495 }3496 my $memWeb = $this->{prefs}->getPreference('INCLUDINGWEB');3497 my $memTopic = $this->{prefs}->getPreference('INCLUDINGTOPIC');3498 3499 my $dirtyAreas = {};3500 try {3501 3502 # Copy params into session level preferences. That way finalisation3503 # will apply to them. These preferences will be popped when the topic3504 # context is restored after the include.3505 $this->{prefs}->setSessionPreferences(%$params);3506 3507 # Set preferences that finalisation does *not* apply to3508 $this->{prefs}->setInternalPreferences(3509 INCLUDINGWEB => $includingTopicObject->web,3510 INCLUDINGTOPIC => $includingTopicObject->topic3511 );3512 3513 $text = $includedTopicObject->text;3514 3515 # Simplify leading, and remove trailing, newlines. If we don't remove3516 # trailing, it becomes impossible to %INCLUDE a topic into a table.3517 $text =~ s/^[\r\n]+/\n/;3518 $text =~ s/[\r\n]+$//;3519 3520 # remove everything before and after the default include block unless3521 # a section is explicitly defined3522 if ( !$control{section} ) {3523 $text =~ s/.*?%STARTINCLUDE%//s;3524 $text =~ s/%STOPINCLUDE%.*//s;3525 }3526 3527 # prevent dirty areas in included topics from being parsed3528 $text = $this->renderer->takeOutBlocks( $text, 'dirtyarea', $dirtyAreas)3529 if $Foswiki::cfg{Cache}{Enabled};3530 3531 # handle sections3532 my ( $ntext, $sections ) = parseSections($text);3533 3534 my $interesting = ( defined $control{section} );3535 if ( $interesting || scalar(@$sections) ) {3536 3537 # Rebuild the text from the interesting sections3538 $text = '';3539 foreach my $s (@$sections) {3540 if ( $control{section}3541 && $s->{type} eq 'section'3542 && $s->{name} eq $control{section} )3543 {3544 $text .=3545 substr( $ntext, $s->{start}, $s->{end} - $s->{start} );3546 $interesting = 1;3547 last;3548 }3549 elsif ( $s->{type} eq 'include' && !$control{section} ) {3550 $text .=3551 substr( $ntext, $s->{start}, $s->{end} - $s->{start} );3552 $interesting = 1;3553 }3554 }3555 }3556 3557 if ( $interesting and ( length($text) eq 0 ) ) {3558 $text =3559 _includeWarning( $this, $control{warn}, 'topic_section_not_found',3560 $includedWeb, $includedTopic, $control{section} );3561 }3562 else {3563 3564 # If there were no interesting sections, restore the whole text3565 $text = $ntext unless $interesting;3566 3567 $text = applyPatternToIncludedText( $text, $control{pattern} )3568 if ( $control{pattern} );3569 3570 # Do not show TOC in included topic if TOC_HIDE_IF_INCLUDED3571 # preference has been set3572 if ( isTrue( $this->{prefs}->getPreference('TOC_HIDE_IF_INCLUDED') )3573 )3574 {3575 $text =~ s/%TOC(?:{(.*?)})?%//g;3576 }3577 3578 $this->innerExpandMacros( \$text, $includedTopicObject );3579 3580 # 4th parameter tells plugin that its called for an included file3581 $this->{plugins}3582 ->dispatch( 'commonTagsHandler', $text, $includedTopic,3583 $includedWeb, 1, $includedTopicObject );3584 3585 # We have to expand tags again, because a plugin may have inserted3586 # additional tags.3587 $this->innerExpandMacros( \$text, $includedTopicObject );3588 3589 # If needed, fix all 'TopicNames' to 'Web.TopicNames' to get the3590 # right context so that links continue to work properly3591 if ( $includedWeb ne $includingTopicObject->web ) {3592 my $removed = {};3593 3594 $text = $this->renderer->forEachLine(3595 $text,3596 \&_fixupIncludedTopic,3597 {3598 web => $includedWeb,3599 pre => 1,3600 noautolink => 13601 }3602 );3603 3604 # handle tags again because of plugin hook3605 innerExpandMacros( $this, \$text, $includedTopicObject );3606 }3607 }3608 }3609 finally {3610 3611 # always restore the context, even in the event of an error3612 delete $this->{_INCLUDES}->{$key};3613 3614 $this->{prefs}->setInternalPreferences(3615 INCLUDINGWEB => $memWeb,3616 INCLUDINGTOPIC => $memTopic3617 );3618 3619 # restoring dirty areas3620 $this->renderer->putBackBlocks( \$text, $dirtyAreas, 'dirtyarea' )3621 if $Foswiki::cfg{Cache}{Enabled};3622 3623 ( $this->{webName}, $this->{topicName} ) =3624 $this->{prefs}->popTopicContext();3625 };3626 3627 return $text;3628 }3629 3630 sub HTTP {3631 my ( $this, $params ) = @_;3632 my $res;3633 if ( $params->{_DEFAULT} ) {3634 $res = $this->{request}->http( $params->{_DEFAULT} );3635 }3636 $res = '' unless defined($res);3637 return $res;3638 }3639 3640 sub HTTPS {3641 my ( $this, $params ) = @_;3642 my $res;3643 if ( $params->{_DEFAULT} ) {3644 $res = $this->{request}->https( $params->{_DEFAULT} );3645 }3646 $res = '' unless defined($res);3647 return $res;3648 }3649 3650 #deprecated functionality, now implemented using %ENV%3651 #move to compatibility plugin in Foswiki 2.03652 sub HTTP_HOST_deprecated {3653 return $_[0]->{request}->header('Host') || '';3654 }3655 3656 #deprecated functionality, now implemented using %ENV%3657 #move to compatibility plugin in Foswiki 2.03658 sub REMOTE_ADDR_deprecated {3659 return $_[0]->{request}->remoteAddress() || '';3660 }3661 3662 #deprecated functionality, now implemented using %ENV%3663 #move to compatibility plugin in Foswiki 2.03664 sub REMOTE_PORT_deprecated {3665 3666 # CGI/1.1 (RFC 3875) doesn't specify REMOTE_PORT,3667 # but some webservers implement it. However, since3668 # it's not RFC compliant, Foswiki should not rely on3669 # it. So we get more portability.3670 return '';3671 }3672 3673 #deprecated functionality, now implemented using %ENV%3674 #move to compatibility plugin in Foswiki3675 sub REMOTE_USER_deprecated {3676 return $_[0]->{request}->remoteUser() || '';3677 }3678 3679 # Only does simple search for topicmoved at present, can be expanded when required3680 # SMELL: this violates encapsulation of Store and Meta, by exporting3681 # the assumption that meta-data is stored embedded inside topic3682 # text.3683 sub METASEARCH {3684 my ( $this, $params ) = @_;3685 3686 return $this->search->searchMetaData($params);3687 }3688 3689 sub DATE {3690 my $this = shift;3691 return Foswiki::Time::formatTime(3692 time(),3693 $Foswiki::cfg{DefaultDateFormat},3694 $Foswiki::cfg{DisplayTimeValues}3695 );3696 }3697 3698 sub GMTIME {3699 my ( $this, $params ) = @_;3700 return Foswiki::Time::formatTime( time(), $params->{_DEFAULT} || '',3701 'gmtime' );3702 }3703 3704 sub SERVERTIME {3705 my ( $this, $params ) = @_;3706 return Foswiki::Time::formatTime( time(), $params->{_DEFAULT} || '',3707 'servertime' );3708 }3709 3710 sub DISPLAYTIME {3711 my ( $this, $params ) = @_;3712 return Foswiki::Time::formatTime(3713 time(),3714 $params->{_DEFAULT} || '',3715 $Foswiki::cfg{DisplayTimeValues}3716 );3717 }3718 3719 #| $web | web and |3720 #| $topic | topic to display the name for |3721 #| $formatString | format string (like in search) |3722 sub REVINFO {3723 my ( $this, $params, $topicObject ) = @_;3724 my $format = $params->{_DEFAULT} || $params->{format};3725 my $web = $params->{web} || $topicObject->web;3726 my $topic = $params->{topic} || $topicObject->topic;3727 my $cgiQuery = $this->{request};3728 my $cgiRev = '';3729 $cgiRev = $cgiQuery->param('rev') if ($cgiQuery);3730 my $rev = $params->{rev} || $cgiRev || '';3731 3732 ( $web, $topic ) = $this->normalizeWebTopicName( $web, $topic );3733 if ( $web ne $topicObject->web || $topic ne $topicObject->topic ) {3734 $topicObject = Foswiki::Meta->new( $this, $web, $topic );3735 unless ( $topicObject->haveAccess('VIEW') ) {3736 return $this->inlineAlert( 'alerts', 'access_denied', $web,3737 $topic );3738 }3739 }3740 3741 return $this->renderer->renderRevisionInfo( $topicObject, $rev, $format );3742 }3743 3744 sub REVTITLE {3745 my ( $this, $params, $topicObject ) = @_;3746 my $request = $this->{request};3747 my $out = '';3748 if ($request) {3749 my $rev = $request->param('rev');3750 $out = '(r' . $rev . ')' if ($rev);3751 }3752 return $out;3753 }3754 3755 sub REVARG {3756 my ( $this, $params ) = @_;3757 my $request = $this->{request};3758 my $out = '';3759 if ($request) {3760 my $rev = $request->param('rev');3761 $out = '&rev=' . $rev if ($rev);3762 }3763 return $out;3764 }3765 3766 sub ENCODE {3767 my ( $this, $params ) = @_;3768 my $type = $params->{type} || 'url';3769 3770 # Value 0 can be valid input so we cannot use simple = || ''3771 my $text = defined( $params->{_DEFAULT} ) ? $params->{_DEFAULT} : '';3772 return _encode( $type, $text );3773 }3774 3775 sub _encode {3776 my ( $type, $text ) = @_;3777 3778 if ( $type =~ /^entit(y|ies)$/i ) {3779 return entityEncode($text);3780 }3781 elsif ( $type =~ /^html$/i ) {3782 return entityEncode( $text, "\n\r" );3783 }3784 elsif ( $type =~ /^quotes?$/i ) {3785 3786 # escape quotes with backslash (Bugs:Item3383 fix)3787 $text =~ s/\"/\\"/go;3788 return $text;3789 }3790 elsif ( $type =~ /^url$/i ) {3791 $text =~ s/\r*\n\r*/<br \/>/; # Legacy.3792 return urlEncode($text);3793 }3794 elsif ( $type =~ /^(off|none)$/i ) {3795 3796 # no encoding3797 return $text;3798 }3799 else { # safe or default3800 # entity encode ' " < > and %3801 $text =~ s/([<>%'"])/'&#'.ord($1).';'/ge;3802 return $text;3803 }3804 }3805 3806 sub ENV {3807 my ( $this, $params ) = @_;3808 3809 my $key = $params->{_DEFAULT};3810 return ''3811 unless $key3812 && defined $Foswiki::cfg{AccessibleENV}3813 && $key =~ /$Foswiki::cfg{AccessibleENV}/o;3814 my $val;3815 if ( $key =~ /^HTTPS?_(\w+)/ ) {3816 $val = $this->{request}->header($1);3817 }3818 elsif ( $key eq 'REQUEST_METHOD' ) {3819 $val = $this->{request}->method;3820 }3821 elsif ( $key eq 'REMOTE_USER' ) {3822 $val = $this->{request}->remoteUser;3823 }3824 elsif ( $key eq 'REMOTE_ADDR' ) {3825 $val = $this->{request}->remoteAddress;3826 }3827 else {3828 3829 # TSA SMELL: Foswiki::Request doesn't support3830 # SERVER_\w+, REMOTE_HOST and REMOTE_IDENT.3831 # Use %ENV as fallback, but for ones above3832 # wil probably not behave as expected if3833 # running with non-CGI engine.3834 $val = $ENV{$key};3835 }3836 return defined $val ? $val : 'not set';3837 }3838 3839 sub SEARCH {3840 my ( $this, $params, $topicObject ) = @_;3841 3842 # pass on all attrs, and add some more3843 #$params->{_callback} = undef;3844 $params->{inline} = 1;3845 $params->{baseweb} = $topicObject->web;3846 $params->{basetopic} = $topicObject->topic;3847 $params->{search} = $params->{_DEFAULT} if defined $params->{_DEFAULT};3848 $params->{type} = $this->{prefs}->getPreference('SEARCHVARDEFAULTTYPE')3849 unless ( $params->{type} );3850 my $s;3851 try {3852 $s = $this->search->searchWeb(%$params);3853 }3854 catch Error::Simple with {3855 my $message = (DEBUG) ? shift->stringify() : shift->{-text};3856 3857 # Block recursions kicked off by the text being repeated in the3858 # error message3859 $message =~ s/%([A-Z]*[{%])/%<nop>$1/g;3860 $s = $this->inlineAlert( 'alerts', 'bad_search', $message );3861 };3862 3863 return $s;3864 }3865 3866 sub FOREACH {3867 my ( $this, $params, $topicObject ) = @_;3868 3869 # pass on all attrs, and add some more3870 #$params->{_callback} = undef;3871 $params->{inline} = 1;3872 $params->{baseweb} = $topicObject->web;3873 $params->{basetopic} = $topicObject->topic;3874 $params->{search} = $params->{_DEFAULT} if defined $params->{_DEFAULT};3875 $params->{type} = $this->{prefs}->getPreference('SEARCHVARDEFAULTTYPE')3876 unless ( $params->{type} );3877 # $params->{format} = '$topic' unless ( defined($params->{format}) );3878 #TODO: change to $n some time.3879 $params->{separator} = "\n" unless ( defined($params->{separator}) );3880 # $params->{header} = '' unless ( $params->{header} );3881 # $params->{footer} = '' unless ( $params->{footer} );3882 my $s;3883 try {3884 my $webObject = Foswiki::Meta->new( $this, $params->{baseweb} );3885 my $topicString = $params->{_DEFAULT} || '';3886 #from Search::_makeTopicPattern (plus an added . to allow web.topic)3887 my @topics = map { s/[^\*\_\-\+\.$Foswiki::regex{mixedAlphaNum}]//go; s/\*/\.\*/go; $_ }3888 split( /,\s*/, $topicString );3889 3890 my $query; #query node3891 my $searchString = '';3892 require Foswiki::Search::InfoCache;3893 my $infoCache = new Foswiki::Search::InfoCache($this, $params->{baseweb}, \@topics);3894 my ( $ttopics, $searchResult, $tmplTail ) = $this->search->formatResults($webObject, $query, $searchString, $infoCache, $params);3895 $s = $searchResult;3896 }3897 catch Error::Simple with {3898 my $message = (DEBUG) ? shift->stringify() : shift->{-text};3899 3900 # Block recursions kicked off by the text being repeated in the3901 # error message3902 $message =~ s/%([A-Z]*[{%])/%<nop>$1/g;3903 $s = $this->inlineAlert( 'alerts', 'bad_search', $message );3904 };3905 return $s;3906 }3907 3908 sub WEBLIST {3909 my ( $this, $params ) = @_;3910 3911 # List of webs to consider; default is all public webs3912 my $webs = $params->{webs} || 'public';3913 my @webslist = split( /,\s*/, $webs );3914 3915 # Modifier on "public" and "webtemplate" pseudo-webs3916 my $rootWeb = $params->{subwebs};3917 3918 # the web= parameter, *not* the web being listed3919 my $web = $params->{web} || '';3920 $web =~ s#\.#/#go;3921 3922 # Output format3923 my $format = $params->{_DEFAULT} || $params->{'format'} || '$name';3924 $format ||= '$name';3925 3926 my $separator = $params->{separator} || "\n";3927 $separator =~ s/\$n/\n/;3928 3929 my $selection = $params->{selection} || '';3930 $selection =~ s/\,/ /g;3931 $selection = " $selection ";3932 3933 my $marker = $params->{marker} || 'selected="selected"';3934 3935 my @list = ();3936 foreach my $aweb (@webslist) {3937 if ( $aweb =~ /^(public|webtemplate)$/ ) {3938 require Foswiki::WebFilter;3939 my $filter;3940 if ( $aweb eq 'public' ) {3941 $filter = new Foswiki::WebFilter('user,public,allowed');3942 }3943 elsif ( $aweb eq 'webtemplate' ) {3944 $filter = new Foswiki::WebFilter('template,allowed');3945 }3946 push( @list, $this->deepWebList( $filter, $rootWeb ) );3947 }3948 else {3949 push( @list, $aweb ) if ( $this->webExists($aweb) );3950 }3951 }3952 3953 my @items;3954 foreach my $item (@list) {3955 my $line = $format;3956 $line =~ s/\$web\b/$web/g;3957 $line =~ s/\$name\b/$item/g;3958 $line =~ s/\$qname/"$item"/g;3959 my $indenteditem = $item;3960 $indenteditem =~ s#/$##g;3961 $indenteditem =~ s#\w+/#%TMPL:P{"webListIndent"}%#g;3962 $line =~ s/\$indentedname/$indenteditem/g;3963 my $mark = ( $selection =~ / \Q$item\E / ) ? $marker : '';3964 $line =~ s/\$marker/$mark/g;3965 push( @items, $line );3966 }3967 return join( $separator, @items );3968 }3969 3970 sub TOPICLIST {3971 my ( $this, $params ) = @_;3972 my $format = $params->{_DEFAULT} || $params->{'format'} || '$topic';3973 my $separator = $params->{separator} || "\n";3974 $separator =~ s/\$n/\n/;3975 my $selection = $params->{selection} || '';3976 $selection =~ s/\,/ /g;3977 $selection = " $selection ";3978 my $marker = $params->{marker} || 'selected="selected"';3979 3980 my $web = $params->{web} || $this->{webName};3981 $web =~ s#\.#/#go;3982 3983 my $webObject = Foswiki::Meta->new( $this, $web );3984 return ''3985 if $web ne $this->{webName}3986 && $webObject->getPreference('NOSEARCHALL');3987 3988 my @items;3989 my $it = $webObject->eachTopic();3990 while ( $it->hasNext() ) {3991 my $item = $it->next();3992 my $line = $format;3993 $line =~ s/\$web\b/$web/g;3994 $line =~ s/\$topic\b/$item/g;3995 $line =~ s/\$name\b/$item/g; # Undocumented, DO NOT REMOVE3996 $line =~ s/\$qname/"$item"/g; # Undocumented, DO NOT REMOVE3997 my $mark = ( $selection =~ / \Q$item\E / ) ? $marker : '';3998 $line =~ s/\$marker/$mark/g;3999 $line = expandStandardEscapes($line);4000 push( @items, $line );4001 }4002 return join( $separator, @items );4003 }4004 4005 sub QUERYSTRING {4006 my $this = shift;4007 return $this->{request}->queryString();4008 }4009 4010 sub QUERYPARAMS {4011 my ( $this, $params ) = @_;4012 return '' unless $this->{request};4013 my $format =4014 defined $params->{format}4015 ? $params->{format}4016 : '$name=$value';4017 my $separator = defined $params->{separator} ? $params->{separator} : "\n";4018 my $encoding = $params->{encoding} || 'safe';4019 4020 my @list;4021 foreach my $name ( $this->{request}->param() ) {4022 4023 # Issues multi-valued parameters as separate hiddens4024 my $value = $this->{request}->param($name);4025 $value = '' unless defined $value;4026 $name = _encode( $encoding, $name );4027 $value = _encode( $encoding, $value );4028 4029 my $entry = $format;4030 $entry =~ s/\$name/$name/g;4031 $entry =~ s/\$value/$value/;4032 push( @list, $entry );4033 }4034 return join( $separator, @list );4035 }4036 4037 sub URLPARAM {4038 my ( $this, $params ) = @_;4039 my $param = $params->{_DEFAULT} || '';4040 my $newLine = $params->{newline};4041 my $encode = $params->{encode} || 'safe';4042 my $multiple = $params->{multiple};4043 my $separator = $params->{separator};4044 $separator = "\n" unless ( defined $separator );4045 4046 my $value;4047 if ( $this->{request} ) {4048 if ( Foswiki::isTrue($multiple) ) {4049 my @valueArray = $this->{request}->param($param);4050 if (@valueArray) {4051 4052 # join multiple values properly4053 unless ( $multiple =~ m/^on$/i ) {4054 my $item = '';4055 @valueArray = map {4056 $item = $_;4057 $_ = $multiple;4058 $_ .= $item unless (s/\$item/$item/go);4059 $_4060 } @valueArray;4061 }4062 $value = join( $separator, @valueArray );4063 }4064 }4065 else {4066 $value = $this->{request}->param($param);4067 }4068 }4069 if ( defined $value ) {4070 $value =~ s/\r?\n/$newLine/go if ( defined $newLine );4071 if ( $encode =~ /^entit(y|ies)$/i ) {4072 $value = entityEncode($value);4073 }4074 elsif ( $encode =~ /^quotes?$/i ) {4075 $value =~4076 s/\"/\\"/go; # escape quotes with backslash (Bugs:Item3383 fix)4077 }4078 elsif ( $encode =~ /^(off|none)$/i ) {4079 4080 # no encoding4081 }4082 elsif ( $encode =~ /^url$/i ) {4083 $value =~ s/\r*\n\r*/<br \/>/; # Legacy4084 $value = urlEncode($value);4085 }4086 else { # safe or default4087 # entity encode ' " < > and %4088 $value =~ s/([<>%'"])/'&#'.ord($1).';'/ge;4089 }4090 }4091 unless ( defined $value ) {4092 $value = $params->{default};4093 $value = '' unless defined $value;4094 }4095 4096 # Block expansion of %URLPARAM in the value to prevent recursion4097 $value =~ s/%URLPARAM{/%<nop>URLPARAM{/g;4098 return $value;4099 }4100 4101 # This routine was introduced to URL encode Mozilla UTF-8 POST URLs in the4102 # TWiki Feb2003 release - encoding is no longer needed since UTF-URLs are now4103 # directly supported, but it is provided for backward compatibility with4104 # skins that may still be using the deprecated %INTURLENCODE%.4105 sub INTURLENCODE_deprecated {4106 my ( $this, $params ) = @_;4107 4108 # Just strip double quotes, no URL encoding - Mozilla UTF-8 URLs4109 # directly supported now4110 return $params->{_DEFAULT} || '';4111 }4112 4113 # This routine is deprecated as of DakarRelease,4114 # and is maintained only for backward compatibility.4115 # Spacing of WikiWords is now done with %SPACEOUT%4116 # (and the private routine _SPACEOUT).4117 # Move to compatibility module in Foswiki 2.04118 sub SPACEDTOPIC_deprecated {4119 my ( $this, $params, $topicObject ) = @_;4120 my $topic = spaceOutWikiWord( $topicObject->topic );4121 $topic =~ s/ / */g;4122 return urlEncode($topic);4123 }4124 4125 sub SPACEOUT {4126 my ( $this, $params ) = @_;4127 my $spaceOutTopic = $params->{_DEFAULT};4128 my $sep = $params->{'separator'};4129 $spaceOutTopic = spaceOutWikiWord( $spaceOutTopic, $sep );4130 return $spaceOutTopic;4131 }4132 4133 sub ICON {4134 my ( $this, $params ) = @_;4135 my $file = $params->{_DEFAULT} || '';4136 4137 # Try to map the file name to see if there is a matching filetype image4138 # If no mapping could be found, use the file name that was passed4139 my $iconFileName = $this->mapToIconFileName( $file, $file );4140 return '' unless $iconFileName;4141 return $this->renderer->renderIconImage(4142 $this->getIconUrl( 0, $iconFileName ),4143 $iconFileName );4144 }4145 4146 sub ICONURL {4147 my ( $this, $params ) = @_;4148 my $file = ( $params->{_DEFAULT} || '' );4149 4150 return $this->getIconUrl( 1, $file );4151 }4152 4153 sub ICONURLPATH {4154 my ( $this, $params ) = @_;4155 my $file = ( $params->{_DEFAULT} || '' );4156 4157 return $this->getIconUrl( 0, $file );4158 }4159 4160 sub RELATIVETOPICPATH {4161 my ( $this, $params, $topicObject ) = @_;4162 my $topic = $params->{_DEFAULT};4163 4164 return '' unless $topic;4165 4166 my $relPath;4167 4168 # if there is no dot in $topicObject->topic, no web has been specified4169 if ( index( $topic, '.' ) == -1 ) {4170 4171 # add local web4172 $relPath = $topicObject->web() . '/' . $topic;4173 }4174 else {4175 $relPath = $topic; #including dot4176 }4177 4178 # replace dot by slash is not necessary; System.MyTopic is a valid url4179 # add ../ if not already present to make a relative file reference4180 if ( $relPath !~ m!^../! ) {4181 $relPath = "../$relPath";4182 }4183 return $relPath;4184 }4185 4186 sub ATTACHURLPATH {4187 my ( $this, $params, $topicObject ) = @_;4188 return $this->getPubUrl( 0, $topicObject->web, $topicObject->topic );4189 }4190 4191 sub ATTACHURL {4192 my ( $this, $params, $topicObject ) = @_;4193 return $this->getPubUrl( 1, $topicObject->web, $topicObject->topic );4194 }4195 4196 sub LANGUAGE {4197 my $this = shift;4198 return $this->i18n->language();4199 }4200 4201 sub LANGUAGES {4202 my ( $this, $params ) = @_;4203 my $format = $params->{format} || " * \$langname";4204 my $separator = $params->{separator} || "\n";4205 $separator =~ s/\\n/\n/g;4206 my $selection = $params->{selection} || '';4207 $selection =~ s/\,/ /g;4208 $selection = " $selection ";4209 my $marker = $params->{marker} || 'selected="selected"';4210 4211 # $languages is a hash reference:4212 my $languages = $this->i18n->enabled_languages();4213 4214 my @tags = sort( keys( %{$languages} ) );4215 4216 my $result = '';4217 my $i = 0;4218 foreach my $lang (@tags) {4219 my $item = $format;4220 my $name = ${$languages}{$lang};4221 $item =~ s/\$langname/$name/g;4222 $item =~ s/\$langtag/$lang/g;4223 my $mark = ( $selection =~ / \Q$lang\E / ) ? $marker : '';4224 $item =~ s/\$marker/$mark/g;4225 $result .= $separator if $i;4226 $result .= $item;4227 $i++;4228 }4229 4230 return $result;4231 }4232 4233 sub MAKETEXT {4234 my ( $this, $params ) = @_;4235 4236 my $str = $params->{_DEFAULT} || $params->{string} || "";4237 return "" unless $str;4238 4239 # escape everything:4240 $str =~ s/\[/~[/g;4241 $str =~ s/\]/~]/g;4242 4243 # restore already escaped stuff:4244 $str =~ s/~~\[/~[/g;4245 $str =~ s/~~\]/~]/g;4246 4247 # unescape parameters and calculate highest parameter number:4248 my $max = 0;4249 $str =~ s/~\[(\_(\d+))~\]/ $max = $2 if ($2 > $max); "[$1]"/ge;4250 $str =~4251 s/~\[(\*,\_(\d+),[^,]+(,([^,]+))?)~\]/ $max = $2 if ($2 > $max); "[$1]"/ge;4252 4253 # get the args to be interpolated.4254 my $argsStr = $params->{args} || "";4255 4256 my @args = split( /\s*,\s*/, $argsStr );4257 4258 # fill omitted args with zeros4259 while ( ( scalar @args ) < $max ) {4260 push( @args, 0 );4261 }4262 4263 # do the magic:4264 my $result = $this->i18n->maketext( $str, @args );4265 4266 # replace accesskeys:4267 $result =~4268 s#(^|[^&])&([a-zA-Z])#$1<span class='foswikiAccessKey'>$2</span>#g;4269 4270 # replace escaped amperstands:4271 $result =~ s/&&/\&/g;4272 4273 return $result;4274 }4275 4276 sub SCRIPTNAME {4277 return $_[0]->{request}->action;4278 }4279 4280 sub SCRIPTURL {4281 my ( $this, $params ) = @_;4282 my $script = $params->{_DEFAULT} || '';4283 4284 return $this->getScriptUrl( 1, $script );4285 }4286 4287 sub SCRIPTURLPATH {4288 my ( $this, $params ) = @_;4289 my $script = $params->{_DEFAULT} || '';4290 4291 return $this->getScriptUrl( 0, $script );4292 }4293 4294 sub PUBURL {4295 my $this = shift;4296 return $this->getPubUrl(1);4297 }4298 4299 sub PUBURLPATH {4300 my $this = shift;4301 return $this->getPubUrl(0);4302 }4303 4304 sub ALLVARIABLES {4305 return shift->{prefs}->stringify();4306 }4307 4308 # Poor-man's content access.4309 sub META {4310 my ( $this, $params, $topicObject ) = @_;4311 4312 $topicObject->reload() unless $topicObject->getLoadedRev();4313 4314 my $option = $params->{_DEFAULT} || '';4315 4316 if ( $option eq 'form' ) {4317 4318 # META:FORM and META:FIELD4319 return $topicObject->renderFormForDisplay();4320 }4321 elsif ( $option eq 'formfield' ) {4322 4323 # a formfield from within topic text4324 return $topicObject->renderFormFieldForDisplay( $params->get('name'),4325 '$value', $params );4326 }4327 elsif ( $option eq 'attachments' ) {4328 4329 # renders attachment tables4330 return $this->attach->renderMetaData( $topicObject, $params );4331 }4332 elsif ( $option eq 'moved' ) {4333 return $this->renderer->renderMoved( $topicObject, $params );4334 }4335 elsif ( $option eq 'parent' ) {4336 4337 # Only parent parameter has the format option and should do std escapes4338 return expandStandardEscapes(4339 $this->renderer->renderParent( $topicObject, $params ) );4340 }4341 4342 # return nothing if invalid parameter4343 return '';4344 }4345 4346 # Remove NOP tag in template topics but show content. Used in template4347 # _topics_ (not templates, per se, but topics used as templates for new4348 # topics)4349 sub NOP {4350 my ( $this, $params ) = @_;4351 4352 return '<nop>' unless $params->{_RAW};4353 4354 return $params->{_RAW};4355 }4356 4357 # Shortcut to %TMPL:P{"sep"}%4358 sub SEP {4359 my $this = shift;4360 return $this->templates->expandTemplate('sep');4361 }4362 4363 #deprecated functionality, now implemented using %USERINFO%4364 #move to compatibility plugin in Foswiki 2.04365 sub WIKINAME_deprecated {4366 my ( $this, $params ) = @_;4367 4368 $params->{format} = $this->{prefs}->getPreference('WIKINAME')4369 || '$wikiname';4370 4371 return $this->USERINFO($params);4372 }4373 4374 #deprecated functionality, now implemented using %USERINFO%4375 #move to compatibility plugin in Foswiki 2.04376 sub USERNAME_deprecated {4377 my ( $this, $params ) = @_;4378 4379 $params->{format} = $this->{prefs}->getPreference('USERNAME')4380 || '$username';4381 4382 return $this->USERINFO($params);4383 }4384 4385 #deprecated functionality, now implemented using %USERINFO%4386 #move to compatibility plugin in Foswiki 2.04387 sub WIKIUSERNAME_deprecated {4388 my ( $this, $params ) = @_;4389 4390 $params->{format} = $this->{prefs}->getPreference('WIKIUSERNAME')4391 || '$wikiusername';4392 4393 return $this->USERINFO($params);4394 }4395 4396 sub USERINFO {4397 my ( $this, $params ) = @_;4398 my $format = $params->{format} || '$username, $wikiusername, $emails';4399 4400 my $user = $this->{user};4401 4402 if ( $params->{_DEFAULT} ) {4403 $user = $params->{_DEFAULT};4404 return '' if !$user;4405 4406 # map wikiname to a login name4407 $user = $this->{users}->getCanonicalUserID($user);4408 return '' unless $user;4409 return ''4410 if ( $Foswiki::cfg{AntiSpam}{HideUserDetails}4411 && !$this->{users}->isAdmin( $this->{user} )4412 && $user ne $this->{user} );4413 }4414 4415 return '' unless $user;4416 4417 my $info = $format;4418 4419 if ( $info =~ /\$username/ ) {4420 my $username = $this->{users}->getLoginName($user);4421 $username = 'unknown' unless defined $username;4422 $info =~ s/\$username/$username/g;4423 }4424 if ( $info =~ /\$wikiname/ ) {4425 my $wikiname = $this->{users}->getWikiName($user);4426 $wikiname = 'UnknownUser' unless defined $wikiname;4427 $info =~ s/\$wikiname/$wikiname/g;4428 }4429 if ( $info =~ /\$wikiusername/ ) {4430 my $wikiusername = $this->{users}->webDotWikiName($user);4431 $wikiusername = "$Foswiki::cfg{UsersWebName}.UnknownUser"4432 unless defined $wikiusername;4433 $info =~ s/\$wikiusername/$wikiusername/g;4434 }4435 if ( $info =~ /\$emails/ ) {4436 my $emails = join( ', ', $this->{users}->getEmails($user) );4437 $info =~ s/\$emails/$emails/g;4438 }4439 if ( $info =~ /\$groups/ ) {4440 my @groupNames;4441 my $it = $this->{users}->eachMembership($user);4442 while ( $it->hasNext() ) {4443 my $group = $it->next();4444 push( @groupNames, $group );4445 }4446 my $groups = join( ', ', @groupNames );4447 $info =~ s/\$groups/$groups/g;4448 }4449 if ( $info =~ /\$cUID/ ) {4450 my $cUID = $user;4451 $info =~ s/\$cUID/$cUID/g;4452 }4453 if ( $info =~ /\$admin/ ) {4454 my $admin = $this->{users}->isAdmin($user) ? 'true' : 'false';4455 $info =~ s/\$admin/$admin/g;4456 }4457 4458 return $info;4459 }4460 4461 sub GROUPINFO {4462 my ( $this, $params ) = @_;4463 4464 my $group = $params->{_DEFAULT};4465 my $format = $params->{format};4466 my $sep = $params->{separator}; $sep = ', ' unless defined $sep;4467 my $limit = $params->{limit} || 100000000;4468 my $limited = $params->{limited}; $limited = '' unless defined $limited;4469 my $header = $params->{header}; $header = '' unless defined $header;4470 my $footer = $params->{footer}; $footer = '' unless defined $footer;4471 4472 my $it;#erator4473 my @rows;4474 if ($group) {4475 $it = $this->{users}->eachGroupMember($group);4476 $format = '$wikiusername' unless defined $format;4477 } else {4478 $it = $this->{users}->eachGroup();4479 $format = '$name' unless defined $format;4480 }4481 while ($it->hasNext()) {4482 my $cUID = $it->next();4483 my $row = $format;4484 if ($group) {4485 next unless($this->{users}->groupAllowsView( $group ));4486 my $wname = $this->{users}->getWikiName( $cUID );4487 my $uname = $this->{users}->getLoginName( $cUID );4488 my $wuname = $this->{users}->webDotWikiName( $cUID );4489 my $change = $this->{users}->groupAllowsChange( $group );4490 4491 $row =~ s/\$wikiname/$wname/ge;4492 $row =~ s/\$username/$uname/ge;4493 $row =~ s/\$wikiusername/$wuname/ge;4494 $row =~ s/\$name/$group/g;4495 $row =~ s/\$allowschange/$change/ge;4496 } else {4497 # all groups4498 next unless($this->{users}->groupAllowsView( $cUID ));4499 my $change = $this->{users}->groupAllowsChange( $cUID );4500 4501 $row =~ s/\$name/$cUID/g;4502 $row =~ s/\$allowschange/$change/ge;4503 }4504 push(@rows, $row);4505 last if (--$limit == 0);4506 }4507 $footer = $limited.$footer if $limit == 0;4508 return expandStandardEscapes($header.join($sep, @rows).$footer);4509 }4510 4511 # Legacy4512 sub GROUPS {4513 my ( $this, $params ) = @_;4514 4515 my $groups = $this->{users}->eachGroup();4516 my @table;4517 while ( $groups->hasNext() ) {4518 my $group = $groups->next();4519 4520 # Nop it to prevent wikiname expansion unless the topic exists.4521 my $groupLink = "<nop>$group";4522 $groupLink = '[[' . $Foswiki::cfg{UsersWebName} . ".$group][$group]]"4523 if ( $this->topicExists( $Foswiki::cfg{UsersWebName}, $group ) );4524 my $descr = "| $groupLink |";4525 my $it = $this->{users}->eachGroupMember($group);4526 my $limit_output = 32;4527 while ( $it->hasNext() ) {4528 my $user = $it->next();4529 $descr .= ' [['4530 . $this->{users}->webDotWikiName($user) . ']['4531 . $this->{users}->getWikiName($user) . ']]';4532 if ( $limit_output == 0 ) {4533 $descr .= '<div>%MAKETEXT{"user list truncated"}%</div>';4534 last;4535 }4536 $limit_output--;4537 }4538 push( @table, "$descr |" );4539 }4540 4541 return '| *Group* | *Members* |' . "\n" . join( "\n", sort @table );4542 }4543 4544 sub DISPLAYDEPENDENCIES {4545 my ( $this, $params ) = @_;4546 4547 my $web = $params->{web} || $this->{webName};4548 my $topic = $params->{topic} || $this->{topicName};4549 my $header = $params->{header} || '';4550 my $footer = $params->{footer} || '';4551 my $format = $params->{format} || ' 1 [[$web.$topic]]';4552 my $separator = $params->{sep} || $params->{separator} || "\n";4553 my $exclude = $params->{exclude};4554 4555 ($web, $topic) = $this->normalizeWebTopicName($web, $topic);4556 4557 my $deps = $this->{cache}->getDependencies($web, $topic);4558 my @lines;4559 my $thisWeb;4560 my $thisTopic;4561 foreach my $dep (sort @$deps) {4562 next if $exclude && $dep =~ /$exclude/;4563 $dep =~ /^(.*)[\.\/](.*?)$/;4564 $thisWeb = $1;4565 $thisTopic = $2;4566 my $text = $format;4567 $text =~ s/\$web/$thisWeb/g;4568 $text =~ s/\$topic/$thisTopic/g;4569 push @lines, $text;4570 }4571 return '' unless @lines;4572 return expandStandardEscapes($header.join($separator, @lines).$footer);4573 }4574 4575 sub SHOWPREFERENCE {4576 my ( $this, $params ) = @_;4577 my $tml = '';4578 if ( $params->{_DEFAULT} ) {4579 foreach my $preference ( split( /[, ]+/, $params->{_DEFAULT} ) ) {4580 $tml .= $this->{prefs}->stringify($preference);4581 }4582 }4583 else {4584 $tml = $this->{prefs}->stringify();4585 }4586 return $tml;4587 }4588 4589 3257 1; 4590 3258 __DATA__ 4591 #Foswiki - The Free and Open Source Wiki, http://foswiki.org/4592 # 4593 # Copyright (C) 2008Foswiki Contributors. Foswiki Contributors4594 #are listed in the AUTHORS file in the root of this distribution.4595 #NOTE: Please extend that file, not this notice.4596 # 4597 #Additional copyrights apply to some or all of the code in this4598 #file as follows:4599 # 4600 #Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org4601 #and TWiki Contributors. All Rights Reserved. TWiki Contributors4602 #are listed in the AUTHORS file in the root of this distribution.4603 #Based on parts of Ward Cunninghams original Wiki and JosWiki.4604 #Copyright (C) 1998 Markus Peter - SPiN GmbH (warpi@spin.de)4605 #Some changes by Dave Harris (drh@bhresearch.co.uk) incorporated4606 # 4607 #This program is free software; you can redistribute it and/or4608 #modify it under the terms of the GNU General Public License4609 #as published by the Free Software Foundation; either version 24610 #of the License, or (at your option) any later version. For4611 #more details read LICENSE in the root of this distribution.4612 # 4613 #This program is distributed in the hope that it will be useful,4614 #but WITHOUT ANY WARRANTY; without even the implied warranty of4615 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.4616 # 4617 #As per the GPL, removal of this notice is prohibited.3259 Foswiki - The Free and Open Source Wiki, http://foswiki.org/ 3260 3261 Copyright (C) 2008-2009 Foswiki Contributors. Foswiki Contributors 3262 are listed in the AUTHORS file in the root of this distribution. 3263 NOTE: Please extend that file, not this notice. 3264 3265 Additional copyrights apply to some or all of the code in this 3266 file as follows: 3267 3268 Copyright (C) 1999-2007 Peter Thoeny, peter@thoeny.org 3269 and TWiki Contributors. All Rights Reserved. TWiki Contributors 3270 are listed in the AUTHORS file in the root of this distribution. 3271 Based on parts of Ward Cunninghams original Wiki and JosWiki. 3272 Copyright (C) 1998 Markus Peter - SPiN GmbH (warpi@spin.de) 3273 Some changes by Dave Harris (drh@bhresearch.co.uk) incorporated 3274 3275 This program is free software; you can redistribute it and/or 3276 modify it under the terms of the GNU General Public License 3277 as published by the Free Software Foundation; either version 2 3278 of the License, or (at your option) any later version. For 3279 more details read LICENSE in the root of this distribution. 3280 3281 This program is distributed in the hope that it will be useful, 3282 but WITHOUT ANY WARRANTY; without even the implied warranty of 3283 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 3284 3285 As per the GPL, removal of this notice is prohibited.
Note: See TracChangeset
for help on using the changeset viewer.
