← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 19:05:14 2015
Reported on Fri Jul 31 19:08:10 2015

Filename/var/www/foswiki11/lib/Foswiki/Render.pm
StatementsExecuted 7057 statements in 38.6ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
51119.6ms61.4msFoswiki::Render::::getRenderedVersionFoswiki::Render::getRenderedVersion
5111.61ms4.67msFoswiki::Render::::forEachLineFoswiki::Render::forEachLine
221311.33ms1.33msFoswiki::Render::::_addListItemFoswiki::Render::_addListItem
30611.12ms1.26msFoswiki::Render::::_takeOutProtectedFoswiki::Render::_takeOutProtected
18111.03ms7.11msFoswiki::Render::::_handleSquareBracketedLinkFoswiki::Render::_handleSquareBracketedLink
111948µs1.02msFoswiki::Render::::BEGIN@19Foswiki::Render::BEGIN@19
3061656µs708µsFoswiki::Render::::_putBackProtectedFoswiki::Render::_putBackProtected
1711520µs5.23msFoswiki::Render::::internalLinkFoswiki::Render::internalLink
1711484µs2.80msFoswiki::Render::::_renderExistingWikiWordFoswiki::Render::_renderExistingWikiWord
1811320µs320µsFoswiki::Render::::_escapeAutoLinksFoswiki::Render::_escapeAutoLinks
1711218µs4.56msFoswiki::Render::::_renderWikiWordFoswiki::Render::_renderWikiWord
311218µs878µsFoswiki::Render::::renderRevisionInfoFoswiki::Render::renderRevisionInfo
2011137µs137µsFoswiki::Render::::_replaceBlockFoswiki::Render::_replaceBlock
171194µs134µsFoswiki::Render::::_linkToolTipInfoFoswiki::Render::_linkToolTipInfo
181192µs92µsFoswiki::Render::::_isImageLinkFoswiki::Render::_isImageLink
51169µs109µsFoswiki::Render::::getAnchorNamesFoswiki::Render::getAnchorNames
141135µs35µsFoswiki::Render::::_filterLiteralFoswiki::Render::_filterLiteral
121127µs27µsFoswiki::Render::::_filterScriptFoswiki::Render::_filterScript
11126µs44µsFoswiki::Render::::renderParentFoswiki::Render::renderParent
11119µs24µsFoswiki::Render::::BEGIN@1795Foswiki::Render::BEGIN@1795
11118µs34µsFoswiki::Render::::BEGIN@12Foswiki::Render::BEGIN@12
11118µs82µsFoswiki::Render::::BEGIN@81Foswiki::Render::BEGIN@81
11114µs23µsFoswiki::Render::::BEGIN@92Foswiki::Render::BEGIN@92
11113µs18µsFoswiki::Render::::BEGIN@1797Foswiki::Render::BEGIN@1797
11112µs12µsFoswiki::Render::::newFoswiki::Render::new
11111µs18µsFoswiki::Render::::BEGIN@13Foswiki::Render::BEGIN@13
11110µs26µsFoswiki::Render::::BEGIN@14Foswiki::Render::BEGIN@14
11110µs140µsFoswiki::Render::::BEGIN@15Foswiki::Render::BEGIN@15
1118µs8µsFoswiki::Render::::_externalLinkFoswiki::Render::_externalLink
1118µs8µsFoswiki::Render::::finishFoswiki::Render::finish
1115µs5µsFoswiki::Render::::BEGIN@17Foswiki::Render::BEGIN@17
1114µs4µsFoswiki::Render::::BEGIN@18Foswiki::Render::BEGIN@18
0000s0sFoswiki::Render::::TML2PlainTextFoswiki::Render::TML2PlainText
0000s0sFoswiki::Render::::_addTHEADandTFOOTFoswiki::Render::_addTHEADandTFOOT
0000s0sFoswiki::Render::::_emitTRFoswiki::Render::_emitTR
0000s0sFoswiki::Render::::_escapeFoswiki::Render::_escape
0000s0sFoswiki::Render::::_fixedFontTextFoswiki::Render::_fixedFontText
0000s0sFoswiki::Render::::_handleWikiWordFoswiki::Render::_handleWikiWord
0000s0sFoswiki::Render::::_mailLinkFoswiki::Render::_mailLink
0000s0sFoswiki::Render::::_makeAnchorHeadingFoswiki::Render::_makeAnchorHeading
0000s0sFoswiki::Render::::_newLinkFormatFoswiki::Render::_newLinkFormat
0000s0sFoswiki::Render::::_renderNonExistingWikiWordFoswiki::Render::_renderNonExistingWikiWord
0000s0sFoswiki::Render::::breakNameFoswiki::Render::breakName
0000s0sFoswiki::Render::::getReferenceREFoswiki::Render::getReferenceRE
0000s0sFoswiki::Render::::makeTopicSummaryFoswiki::Render::makeTopicSummary
0000s0sFoswiki::Render::::protectFormFieldValueFoswiki::Render::protectFormFieldValue
0000s0sFoswiki::Render::::protectPlainTextFoswiki::Render::protectPlainText
0000s0sFoswiki::Render::::renderFORMFIELDFoswiki::Render::renderFORMFIELD
0000s0sFoswiki::Render::::renderIconImageFoswiki::Render::renderIconImage
0000s0sFoswiki::Render::::renderMovedFoswiki::Render::renderMoved
0000s0sFoswiki::Render::::verbatimCallBackFoswiki::Render::verbatimCallBack
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2package Foswiki::Render;
3
4=begin TML
5
6---+ package Foswiki::Render
7
8This module provides most of the actual HTML rendering code in Foswiki.
9
10=cut
11
12235µs250µs
# spent 34µs (18+16) within Foswiki::Render::BEGIN@12 which was called: # once (18µs+16µs) by Foswiki::renderer at line 12
use strict;
# spent 34µs making 1 call to Foswiki::Render::BEGIN@12 # spent 16µs making 1 call to strict::import
13230µs224µs
# spent 18µs (11+7) within Foswiki::Render::BEGIN@13 which was called: # once (11µs+7µs) by Foswiki::renderer at line 13
use warnings;
# spent 18µs making 1 call to Foswiki::Render::BEGIN@13 # spent 6µs making 1 call to warnings::import
14232µs242µs
# spent 26µs (10+16) within Foswiki::Render::BEGIN@14 which was called: # once (10µs+16µs) by Foswiki::renderer at line 14
use Assert;
# spent 26µs making 1 call to Foswiki::Render::BEGIN@14 # spent 16µs making 1 call to Assert::import
15235µs2270µs
# spent 140µs (10+130) within Foswiki::Render::BEGIN@15 which was called: # once (10µs+130µs) by Foswiki::renderer at line 15
use Error qw(:try);
# spent 140µs making 1 call to Foswiki::Render::BEGIN@15 # spent 130µs making 1 call to Error::import
16
17224µs15µs
# spent 5µs within Foswiki::Render::BEGIN@17 which was called: # once (5µs+0s) by Foswiki::renderer at line 17
use Foswiki::Time ();
# spent 5µs making 1 call to Foswiki::Render::BEGIN@17
18222µs14µs
# spent 4µs within Foswiki::Render::BEGIN@18 which was called: # once (4µs+0s) by Foswiki::renderer at line 18
use Foswiki::Sandbox ();
# spent 4µs making 1 call to Foswiki::Render::BEGIN@18
192314µs11.02ms
# spent 1.02ms (948µs+73µs) within Foswiki::Render::BEGIN@19 which was called: # once (948µs+73µs) by Foswiki::renderer at line 19
use Foswiki::Render::Anchors ();
# spent 1.02ms making 1 call to Foswiki::Render::BEGIN@19
20
21# Counter used to generate unique placeholders for when we lift blocks
22# (such as <verbatim> out of the text during rendering.
231600nsour $placeholderMarker = 0;
24
25# Limiting lookbehind and lookahead for wikiwords and emphasis.
26# use like \b
2714µsour $STARTWW = qr/^|(?<=[\s\(])/m;
2811µsour $ENDWW = qr/$|(?=[\s,.;:!?)])/m;
29
30# Note: the following marker sequences are used in text to mark things that
31# have been hoisted or need to be marked for further processing. The strings
32# are carefully chosen so that they (1) are not normally present in written
33# text and (2) they do not combine with other characters to form valid
34# wide-byte characters. A subset of the 7-bit control characters is used
35# (codepoint < 0x07). Warning: the RENDERZONE_MARKER in Foswiki.pm uses \3
36
37# Marker used to indicate the start of a table
381300nsour $TABLEMARKER = "\0\1\2TABLE\2\1\0";
39
40# Marker used to indicate table rows that are valid header/footer rows
411300nsour $TRMARK = "is\1all\1th";
42
43# General purpose marker used to mark escapes inthe text; for example, we
44# use it to mark hoisted blocks, such as verbatim blocks.
451300nsour $REMARKER = "\0";
46
47# Optional End marker for escapes where the default end character ; also
48# must be removed. Used for email anti-spam encoding.
491200nsour $REEND = "\1";
50
51# Characters that need to be %XX escaped in mailto URIs.
52113µsour %ESCAPED = (
53 '<' => '%3C',
54 '>' => '%3E',
55 '#' => '%23',
56 '"' => '%22',
57 '%' => '%25',
58 "'" => '%27',
59 '{' => '%7B',
60 '}' => '%7D',
61 '|' => '%7C',
62 '\\\\' => '%5C',
63 '^' => '%5E',
64 '~' => '%7E',
65 '`' => '%60',
66 '?' => '%3F',
67 '&' => '%26',
68 '=' => '%3D',
69);
70
71# Temporary marker for <nop> tags. They are used as follows:
72# - Hide all <nop>
73# - Take out <input ..> tags
74# - Restore all <nop>
75# - ... do other rendering
76# - Put back all <input ...> tags
77# - Remove any extraneous <nop> markers.
781300nsour $NOPMARK = "\2";
79
80# Default format for a link to a non-existant topic
81113µs164µs
# spent 82µs (18+64) within Foswiki::Render::BEGIN@81 which was called: # once (18µs+64µs) by Foswiki::renderer at line 83
use constant DEFAULT_NEWLINKFORMAT => <<'NLF';
# spent 64µs making 1 call to constant::import
82<span class="foswikiNewLink">$text<a href="%SCRIPTURLPATH{"edit"}%/$web/$topic?topicparent=%WEB%.%TOPIC%" rel="nofollow" title="%MAKETEXT{"Create this topic"}%">?</a></span>
83171µs182µsNLF
# spent 82µs making 1 call to Foswiki::Render::BEGIN@81
84
8513µsmy %list_types = (
86 A => 'upper-alpha',
87 a => 'lower-alpha',
88 i => 'lower-roman',
89 I => 'upper-roman'
90);
91
92
# spent 23µs (14+9) within Foswiki::Render::BEGIN@92 which was called: # once (14µs+9µs) by Foswiki::renderer at line 99
BEGIN {
93
94 # Do a dynamic 'use locale' for this module
9515µs if ( $Foswiki::cfg{UseLocale} ) {
961500ns require locale;
9716µs19µs import locale();
# spent 9µs making 1 call to locale::import
98 }
9918.87ms123µs}
# spent 23µs making 1 call to Foswiki::Render::BEGIN@92
100
101=begin TML
102
103---++ ClassMethod new ($session)
104
105Creates a new renderer
106
107=cut
108
109
# spent 12µs within Foswiki::Render::new which was called: # once (12µs+0s) by Foswiki::renderer at line 1996 of /var/www/foswiki11/lib/Foswiki.pm
sub new {
1101900ns my ( $class, $session ) = @_;
11118µs my $this = bless( { session => $session }, $class );
112
11317µs return $this;
114}
115
116=begin TML
117
118---++ ObjectMethod finish()
119Break circular references.
120
121=cut
122
123# Note to developers; please undef *all* fields in the object explicitly,
124# whether they are references or not. That way this method is "golden
125# documentation" of the live fields in the object.
126
# spent 8µs within Foswiki::Render::finish which was called: # once (8µs+0s) by Foswiki::finish at line 2163 of /var/www/foswiki11/lib/Foswiki.pm
sub finish {
1271600ns my $this = shift;
1281700ns undef $this->{NEWLINKFORMAT};
12911µs undef $this->{LINKTOOLTIPINFO};
1301800ns undef $this->{LIST};
1311900ns undef $this->{ffCache};
13214µs undef $this->{session};
133}
134
135sub _newLinkFormat {
136 my $this = shift;
137 unless ( $this->{NEWLINKFORMAT} ) {
138 $this->{NEWLINKFORMAT} =
139 $this->{session}->{prefs}->getPreference('NEWLINKFORMAT')
140 || DEFAULT_NEWLINKFORMAT;
141 }
142 return $this->{NEWLINKFORMAT};
143}
144
145=begin TML
146
147---++ ObjectMethod renderParent($topicObject, $params) -> $text
148
149Render parent meta-data
150
151=cut
152
153
# spent 44µs (26+18) within Foswiki::Render::renderParent which was called: # once (26µs+18µs) by Foswiki::META at line 19 of /var/www/foswiki11/lib/Foswiki/Macros/META.pm
sub renderParent {
1541700ns my ( $this, $topicObject, $ah ) = @_;
1551800ns my $dontRecurse = $ah->{dontrecurse} || 0;
1561300ns my $depth = $ah->{depth} || 0;
1571600ns my $noWebHome = $ah->{nowebhome} || 0;
1581400ns my $prefix = $ah->{prefix} || '';
1591400ns my $suffix = $ah->{suffix} || '';
1601500ns my $usesep = $ah->{separator} || ' &gt; ';
1611300ns my $format = $ah->{format} || '[[$web.$topic][$topic]]';
162
16314µs25µs my ( $web, $topic ) = ( $topicObject->web, $topicObject->topic );
# spent 3µs making 1 call to Foswiki::Meta::web # spent 2µs making 1 call to Foswiki::Meta::topic
1641300ns return '' unless $web && $topic;
165
1661200ns my %visited;
16712µs $visited{ $web . '.' . $topic } = 1;
168
1691300ns my $pWeb = $web;
1701100ns my $pTopic;
1711300ns my $text = '';
17214µs113µs my $parentMeta = $topicObject->get('TOPICPARENT');
# spent 13µs making 1 call to Foswiki::Meta::get
1731100ns my $parent;
174
1751200ns $parent = $parentMeta->{name} if $parentMeta;
176
1771300ns my @stack;
1781200ns my $currentDepth = 0;
1791100ns $depth = 1 if $dontRecurse;
180
1811500ns while ($parent) {
182 $currentDepth++;
183 ( $pWeb, $pTopic ) =
184 $this->{session}->normalizeWebTopicName( $pWeb, $parent );
185 $parent = $pWeb . '.' . $pTopic;
186 last
187 if ( $noWebHome && ( $pTopic eq $Foswiki::cfg{HomeTopicName} )
188 || $visited{$parent} );
189 $visited{$parent} = 1;
190 $text = $format;
191 $text =~ s/\$web/$pWeb/g;
192 $text =~ s/\$topic/$pTopic/g;
193
194 if ( !$depth or $currentDepth == $depth ) {
195 unshift( @stack, $text );
196 }
197 last if $currentDepth == $depth;
198
199 # Compromise; rather than supporting a hack in the store to support
200 # rapid access to parent meta (as in TWiki) accept the hit
201 # of reading the whole topic.
202 my $topicObject =
203 Foswiki::Meta->load( $this->{session}, $pWeb, $pTopic );
204 my $parentMeta = $topicObject->get('TOPICPARENT');
205 $parent = $parentMeta->{name} if $parentMeta;
206 }
20711µs $text = join( $usesep, @stack );
208
2091200ns if ($text) {
210 $text = $prefix . $text if ($prefix);
211 $text .= $suffix if ($suffix);
212 }
213
21415µs return $text;
215}
216
217=begin TML
218
219---++ ObjectMethod renderMoved($topicObject, $params) -> $text
220
221Render moved meta-data
222
223=cut
224
225sub renderMoved {
226 my ( $this, $topicObject, $params ) = @_;
227 my $text = '';
228 my $moved = $topicObject->get('TOPICMOVED');
229 my $prefix = $params->{prefix} || '';
230 my $suffix = $params->{suffix} || '';
231
232 if ($moved) {
233 my ( $fromWeb, $fromTopic ) =
234 $this->{session}
235 ->normalizeWebTopicName( $topicObject->web, $moved->{from} );
236 my ( $toWeb, $toTopic ) =
237 $this->{session}
238 ->normalizeWebTopicName( $topicObject->web, $moved->{to} );
239 my $by = $moved->{by};
240 my $u = $by;
241 my $users = $this->{session}->{users};
242 $by = $users->webDotWikiName($u) if $u;
243 my $date = Foswiki::Time::formatTime( $moved->{date}, '', 'gmtime' );
244
245 # Only allow put back if current web and topic match
246 # stored information
247 my $putBack = '';
248 if ( $topicObject->web eq $toWeb && $topicObject->topic eq $toTopic ) {
249 $putBack = ' - '
250 . CGI::a(
251 {
252 title => (
253 $this->{session}->i18n->maketext(
254'Click to move topic back to previous location, with option to change references.'
255 )
256 ),
257 href => $this->{session}->getScriptUrl(
258 0, 'rename', $topicObject->web, $topicObject->topic
259 ),
260 rel => 'nofollow'
261 },
262 $this->{session}->i18n->maketext('Put it back...')
263 );
264 }
265 $text = $this->{session}->i18n->maketext(
266 "[_1] was renamed or moved from [_2] on [_3] by [_4]",
267 "<nop>$toWeb.<nop>$toTopic", "<nop>$fromWeb.<nop>$fromTopic",
268 $date, $by
269 ) . $putBack;
270 }
271 $text = "$prefix$text$suffix" if $text;
272 return $text;
273}
274
275# Add a list item, of the given type and indent depth. The list item may
276# cause the opening or closing of lists currently being handled.
277
# spent 1.33ms within Foswiki::Render::_addListItem which was called 221 times, avg 6µs/call: # 156 times (654µs+0s) by Foswiki::Render::getRenderedVersion at line 1380, avg 4µs/call # 60 times (656µs+0s) by Foswiki::Render::getRenderedVersion at line 1340, avg 11µs/call # 5 times (20µs+0s) by Foswiki::Render::getRenderedVersion at line 1392, avg 4µs/call
sub _addListItem {
278221193µs my ( $this, $result, $type, $element, $indent ) = @_;
279
280221142µs $indent =~ s/ /\t/g;
28122163µs my $depth = length($indent);
282
283221104µs my $size = scalar( @{ $this->{LIST} } );
284
285 # The whitespaces either side of the tags are required for the
286 # emphasis REs to work.
28722173µs if ( $size < $depth ) {
28872µs my $firstTime = 1;
28975µs while ( $size < $depth ) {
290719µs push( @{ $this->{LIST} }, { type => $type, element => $element } );
29172µs push @$result, ' <' . $element . ">\n" unless ($firstTime);
29278µs push @$result, ' <' . $type . ">\n";
29371µs $firstTime = 0;
29474µs $size++;
295 }
296 }
297 else {
29821493µs while ( $size > $depth ) {
29976µs my $tags = pop( @{ $this->{LIST} } );
300712µs push @$result,
301 "\n</" . $tags->{element} . '></' . $tags->{type} . '> ';
302711µs $size--;
303 }
304214148µs if ($size) {
305 push @$result,
306 "\n</" . $this->{LIST}->[ $size - 1 ]->{element} . '> ';
307 }
308 else {
30916181µs push @$result, "\n";
310 }
311 }
312
313221584µs if ($size) {
3146038µs my $oldt = $this->{LIST}->[ $size - 1 ];
3156043µs if ( $oldt->{type} ne $type ) {
316 push @$result, ' </' . $oldt->{type} . '><' . $type . ">\n";
317 pop( @{ $this->{LIST} } );
318 push( @{ $this->{LIST} }, { type => $type, element => $element } );
319 }
320 }
321}
322
323# Given that we have just seen the end of a table, work out the thead,
324# tbody and tfoot sections
325sub _addTHEADandTFOOT {
326 my ($lines) = @_;
327
328 # scan back to the head of the table
329 my $i = scalar(@$lines) - 1;
330 my @thRows;
331 my $inFoot = 1;
332 my $footLines = 0;
333 my $headLines = 0;
334
335 while ( $i >= 0 && $lines->[$i] ne $TABLEMARKER ) {
336 if ( $lines->[$i] =~ /^\s*$/ ) {
337
338 # Remove blank lines in tables; they generate spurious <p>'s
339 splice( @$lines, $i, 1 );
340 }
341 elsif ( $lines->[$i] =~ s/$TRMARK=(["'])(.*?)\1//i ) {
342 if ($2) {
343
344 # In head or foot
345 if ($inFoot) {
346
347 #print STDERR "FOOT: $lines->[$i]\n";
348 $footLines++;
349 }
350 else {
351
352 #print STDERR "HEAD: $lines->[$i]\n";
353 $headLines++;
354 }
355 }
356 else {
357
358 # In body
359 #print STDERR "BODY: $lines->[$i]\n";
360 $inFoot = 0;
361 $headLines = 0;
362 }
363 }
364 $i--;
365 }
366 $lines->[ $i++ ] = CGI::start_table(
367 {
368 class => 'foswikiTable',
369 border => 1,
370 cellspacing => 0,
371 cellpadding => 0
372 }
373 );
374
375 if ($headLines) {
376 splice( @$lines, $i++, 0, '<thead>' );
377 splice( @$lines, $i + $headLines, 0, '</thead>' );
378 $i += $headLines + 1;
379 }
380
381 if ($footLines) {
382
383 # Extract the foot and stick it in the table after the head (if any)
384 # WRC says browsers prefer this
385 my $firstFoot = scalar(@$lines) - $footLines;
386 my @foot = splice( @$lines, $firstFoot, $footLines );
387 unshift( @foot, '<tfoot>' );
388 push( @foot, '</tfoot>' );
389 splice( @$lines, $i, 0, @foot );
390 $i += scalar(@foot);
391 }
392 splice( @$lines, $i, 0, '<tbody>' );
393 push( @$lines, '</tbody>' );
394}
395
396sub _emitTR {
397 my ( $this, $row ) = @_;
398
399 $row =~ s/\t/ /g; # change tabs to space
400 $row =~ s/\s*$//; # remove trailing spaces
401 # calc COLSPAN
402 $row =~ s/(\|\|+)/
403 'colspan'.$REMARKER.length($1).'|'/ge;
404 my $cells = '';
405 my $containsTableHeader;
406 my $isAllTH = 1;
407 foreach ( split( /\|/, $row ) ) {
408 my %attr;
409
410 # Avoid matching single columns
411 if (s/colspan$REMARKER([0-9]+)//o) {
412 $attr{colspan} = $1;
413 }
414 s/^\s+$/ &nbsp; /;
415 my ( $l1, $l2 ) = ( 0, 0 );
416 if (/^(\s*).*?(\s*)$/) {
417 $l1 = length($1);
418 $l2 = length($2);
419 }
420 if ( $l1 >= 2 ) {
421 if ( $l2 <= 1 ) {
422 $attr{align} = 'right';
423 }
424 else {
425 $attr{align} = 'center';
426 }
427 }
428
429 # implicit untaint is OK, because we are just taking topic data
430 # and rendering it; no security step is bypassed.
431 if (/^\s*\*(.*)\*\s*$/) {
432 $cells .= CGI::th( \%attr, CGI::strong( {}, " $1 " ) ) . "\n";
433 }
434 else {
435 $cells .= CGI::td( \%attr, " $_ " ) . "\n";
436 $isAllTH = 0;
437 }
438 }
439 return CGI::Tr( { $TRMARK => $isAllTH }, $cells );
440}
441
442sub _fixedFontText {
443 my ( $text, $embolden ) = @_;
444
445 # preserve white space, so replace it by '&nbsp; ' patterns
446 $text =~ s/\t/ /g;
447 $text =~ s|((?:[\s]{2})+)([^\s])|'&nbsp; ' x (length($1) / 2) . $2|eg;
448 $text = CGI->b($text) if $embolden;
449 return CGI->code($text);
450}
451
452# Build an HTML &lt;Hn> element with suitable anchor for linking
453# from %<nop>TOC%
454sub _makeAnchorHeading {
455 my ( $this, $text, $level, $anchors ) = @_;
456
457 # - Build '<nop><h1><a name='atext'></a> heading </h1>' markup
458 # - Initial '<nop>' is needed to prevent subsequent matches.
459 # filter '!!', '%NOTOC%'
460 $text =~ s/$Foswiki::regex{headerPatternNoTOC}//o;
461
462 my $html =
463 '<nop><h'
464 . $level . '>'
465 . $anchors->makeHTMLTarget($text) . ' '
466 . $text . ' </h'
467 . $level . '>';
468
469 return $html;
470}
471
472# Returns =title='...'= tooltip info if the LINKTOOLTIPINFO preference
473# is set. Warning: Slower performance if enabled.
474
# spent 134µs (94+40) within Foswiki::Render::_linkToolTipInfo which was called 17 times, avg 8µs/call: # 17 times (94µs+40µs) by Foswiki::Render::_renderExistingWikiWord at line 693, avg 8µs/call
sub _linkToolTipInfo {
4751714µs my ( $this, $web, $topic ) = @_;
4761710µs unless ( defined( $this->{LINKTOOLTIPINFO} ) ) {
47712µs140µs $this->{LINKTOOLTIPINFO} =
# spent 40µs making 1 call to Foswiki::Prefs::getPreference
478 $this->{session}->{prefs}->getPreference('LINKTOOLTIPINFO')
479 || '';
48012µs $this->{LINKTOOLTIPINFO} = '$username - $date - r$rev: $summary'
481 if ( 'on' eq lc( $this->{LINKTOOLTIPINFO} ) );
482 }
483175µs return '' unless ( $this->{LINKTOOLTIPINFO} );
4841785µs return '' if ( $this->{LINKTOOLTIPINFO} =~ /^off$/i );
485 return '' unless ( $this->{session}->inContext('view') );
486
487 # These are safe to untaint blindly because this method is only
488 # called when a regex matches a valid wikiword
489 $web = Foswiki::Sandbox::untaintUnchecked($web);
490 $topic = Foswiki::Sandbox::untaintUnchecked($topic);
491
492 # FIXME: This is slow, it can be improved by caching topic rev
493 # info and summary
494 my $users = $this->{session}->{users};
495
496 my $topicObject = Foswiki::Meta->new( $this->{session}, $web, $topic );
497 my $info = $topicObject->getRevisionInfo();
498 my $tooltip = $this->{LINKTOOLTIPINFO};
499 $tooltip =~ s/\$web/<nop>$web/g;
500 $tooltip =~ s/\$topic/<nop>$topic/g;
501 $tooltip =~ s/\$rev/1.$info->{version}/g;
502 $tooltip =~ s/\$date/Foswiki::Time::formatTime( $info->{date} )/ge;
503 $tooltip =~ s/\$username/
504 $users->getLoginName($info->{author}) || $info->{author}/ge;
505 $tooltip =~ s/\$wikiname/
506 $users->getWikiName($info->{author}) || $info->{author}/ge;
507 $tooltip =~ s/\$wikiusername/
508 $users->webDotWikiName($info->{author}) || $info->{author}/ge;
509
510 if ( $tooltip =~ /\$summary/ ) {
511 my $summary;
512 if ( $topicObject->haveAccess('VIEW') ) {
513 $summary = $topicObject->text || '';
514 }
515 else {
516 $summary =
517 $this->{session}
518 ->inlineAlert( 'alerts', 'access_denied', "$web.$topic" );
519 }
520 $summary = $topicObject->summariseText();
521 $summary =~
522 s/[\"\']/<nop>/g; # remove quotes (not allowed in title attribute)
523 $tooltip =~ s/\$summary/$summary/g;
524 }
525 return $tooltip;
526}
527
528=begin TML
529
530---++ ObjectMethod internalLink ( $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix, $hasExplicitLinkLabel ) -> $html
531
532Generate a link.
533
534Note: Topic names may be spaced out. Spaced out names are converted
535to <nop>WikWords, for example, "spaced topic name" points to "SpacedTopicName".
536 * =$web= - the web containing the topic
537 * =$topic= - the topic to be link
538 * =$linkText= - text to use for the link
539 * =$anchor= - the link anchor, if any
540 * =$linkIfAbsent= - boolean: false means suppress link for
541 non-existing pages
542 * =$keepWebPrefix= - boolean: true to keep web prefix (for
543 non existing Web.TOPIC)
544 * =$hasExplicitLinkLabel= - boolean: true if
545 [[link][explicit link label]]
546
547Called from outside the package by Func::internalLink
548
549Calls _renderWikiWord, which in turn will use Plurals.pm to match fold
550plurals to equivalency with their singular form
551
552SMELL: why is this available to Func?
553
554=cut
555
556
# spent 5.23ms (520µs+4.71) within Foswiki::Render::internalLink which was called 17 times, avg 308µs/call: # 17 times (520µs+4.71ms) by Foswiki::Render::_handleSquareBracketedLink at line 905, avg 308µs/call
sub internalLink {
5571726µs my ( $this, $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix,
558 $hasExplicitLinkLabel, $params )
559 = @_;
560
561 # SMELL - shouldn't it be callable by Foswiki::Func as well?
562
563 #PN: Webname/Subweb/ -> Webname/Subweb
5641710µs $web =~ s/\/\Z//o;
565
566177µs if ( $linkText eq $web ) {
567 $linkText =~ s/\//\./go;
568 }
569
570 #WebHome links to tother webs render as the WebName
5711710µs if ( ( $linkText eq $Foswiki::cfg{HomeTopicName} )
572 && ( $web ne $this->{session}->{webName} ) )
573 {
574 $linkText = $web;
575 }
576
577 # Get rid of leading/trailing spaces in topic name
5781726µs $topic =~ s/^\s*//o;
5791765µs $topic =~ s/\s*$//o;
580
581 # Allow spacing out, etc.
582 # Plugin authors use $hasExplicitLinkLabel to determine if the link label
583 # should be rendered differently even if the topic author has used a
584 # specific link label.
5851751µs1773µs $linkText =
# spent 73µs making 17 calls to Foswiki::Plugins::dispatch, avg 4µs/call
586 $this->{session}->{plugins}
587 ->dispatch( 'renderWikiWordHandler', $linkText, $hasExplicitLinkLabel,
588 $web, $topic )
589 || $linkText;
590
591 # Turn spaced-out names into WikiWords - upper case first letter of
592 # whole link, and first of each word. TODO: Try to turn this off,
593 # avoiding spaces being stripped elsewhere
5941711µs $topic = ucfirst($topic);
5951727µs $topic =~ s/\s([$Foswiki::regex{mixedAlphaNum}])/\U$1/go;
596
597 # If locales are in effect, the above conversions will taint the topic
598 # name (Foswiki:Tasks:Item2091)
5991730µs1776µs $topic = Foswiki::Sandbox::untaintUnchecked($topic);
# spent 76µs making 17 calls to Foswiki::Sandbox::untaintUnchecked, avg 4µs/call
600
601 # Add <nop> before WikiWord inside link text to prevent double links
60217102µs $linkText =~ s/(?<=[\s\(])([$Foswiki::regex{upperAlpha}])/<nop>$1/go;
6031782µs174.56ms return _renderWikiWord( $this, $web, $topic, $linkText, $anchor,
# spent 4.56ms making 17 calls to Foswiki::Render::_renderWikiWord, avg 268µs/call
604 $linkIfAbsent, $keepWebPrefix, $params );
605}
606
607# TODO: this should be overridable by plugins.
608
# spent 4.56ms (218µs+4.35) within Foswiki::Render::_renderWikiWord which was called 17 times, avg 268µs/call: # 17 times (218µs+4.35ms) by Foswiki::Render::internalLink at line 603, avg 268µs/call
sub _renderWikiWord {
6091725µs my ( $this, $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix,
610 $params )
611 = @_;
6121711µs my $session = $this->{session};
6131739µs171.55ms my $topicExists = $session->topicExists( $web, $topic );
# spent 1.55ms making 17 calls to Foswiki::topicExists, avg 91µs/call
614
615176µs my $singular = '';
616174µs unless ($topicExists) {
617
618 # topic not found - try to singularise
619 require Foswiki::Plurals;
620 $singular = Foswiki::Plurals::singularForm( $web, $topic );
621 if ($singular) {
622 $topicExists = $session->topicExists( $web, $singular );
623 $topic = $singular if $topicExists;
624 }
625 }
626
627176µs if ($topicExists) {
628
629 # add a dependency so that the page gets invalidated as soon as the
630 # topic is deleted
6311715µs $this->{session}->{cache}->addDependency( $web, $topic )
632 if $Foswiki::cfg{Cache}{Enabled};
633
6341798µs172.80ms return _renderExistingWikiWord( $this, $web, $topic, $linkText, $anchor,
# spent 2.80ms making 17 calls to Foswiki::Render::_renderExistingWikiWord, avg 165µs/call
635 $params );
636 }
637 if ($linkIfAbsent) {
638
639 # CDot: disabled until SuggestSingularNotPlural is resolved
640 # if ($singular && $singular ne $topic) {
641 # #unshift( @topics, $singular);
642 # }
643
644 # add a dependency so that the page gets invalidated as soon as the
645 # WikiWord comes into existance
646 # Note we *ignore* the params if the target topic does not exist
647 $this->{session}->{cache}->addDependency( $web, $topic )
648 if $Foswiki::cfg{Cache}{Enabled};
649
650 return _renderNonExistingWikiWord( $this, $web, $topic, $linkText );
651 }
652 if ($keepWebPrefix) {
653 return $web . '.' . $linkText;
654 }
655
656 return $linkText;
657}
658
659
# spent 2.80ms (484µs+2.32) within Foswiki::Render::_renderExistingWikiWord which was called 17 times, avg 165µs/call: # 17 times (484µs+2.32ms) by Foswiki::Render::_renderWikiWord at line 634, avg 165µs/call
sub _renderExistingWikiWord {
6601722µs my ( $this, $web, $topic, $text, $anchor, $params ) = @_;
661
662175µs my @cssClasses;
6631720µs push( @cssClasses, 'foswikiCurrentWebHomeLink' )
664 if ( ( $web eq $this->{session}->{webName} )
665 && ( $topic eq $Foswiki::cfg{HomeTopicName} ) );
666
667175µs my $inCurrentTopic = 0;
668
6691712µs if ( ( $web eq $this->{session}->{webName} )
670 && ( $topic eq $this->{session}->{topicName} ) )
671 {
672 push( @cssClasses, 'foswikiCurrentTopicLink' );
673 $inCurrentTopic = 1;
674 }
675
676174µs my %attrs;
6771743µs17835µs my $href = $this->{session}->getScriptUrl( 0, 'view', $web, $topic );
# spent 835µs making 17 calls to Foswiki::getScriptUrl, avg 49µs/call
678174µs if ($params) {
679 $href .= $params;
680 }
681
682173µs if ($anchor) {
683 $anchor = Foswiki::Render::Anchors::make($anchor);
684 $anchor = Foswiki::urlEncode($anchor);
685
686 # No point in trying to make it unique; just aim at the first
687 # occurrence
688 # Item8556 - drop path if same topic
689 $href = $inCurrentTopic ? "#$anchor" : "$href#$anchor";
690 }
6911714µs $attrs{class} = join( ' ', @cssClasses ) if ( $#cssClasses >= 0 );
6921725µs $attrs{href} = $href;
6931737µs17134µs my $tooltip = _linkToolTipInfo( $this, $web, $topic );
# spent 134µs making 17 calls to Foswiki::Render::_linkToolTipInfo, avg 8µs/call
694173µs $attrs{title} = $tooltip if $tooltip;
695
6961735µs17375µs my $aFlag = CGI::autoEscape(0);
# spent 276µs making 16 calls to CGI::autoEscape, avg 17µs/call # spent 100µs making 1 call to CGI::AUTOLOAD
6971744µs17709µs my $link = CGI::a( \%attrs, $text );
# spent 709µs making 17 calls to CGI::a, avg 42µs/call
6981726µs17249µs CGI::autoEscape($aFlag);
# spent 249µs making 17 calls to CGI::autoEscape, avg 15µs/call
699
700 # When we pass the tooltip text to CGI::a it may contain
701 # <nop>s, and CGI::a will convert the < to &lt;. This is a
702 # basic problem with <nop>.
703 #$link =~ s/&lt;nop&gt;/<nop>/g;
7041786µs return $link;
705}
706
707sub _renderNonExistingWikiWord {
708 my ( $this, $web, $topic, $text ) = @_;
709
710 my $ans = $this->_newLinkFormat;
711 $ans =~ s/\$web/$web/g;
712 $ans =~ s/\$topic/$topic/g;
713 $ans =~ s/\$text/$text/g;
714 my $topicObject = Foswiki::Meta->new(
715 $this->{session},
716 $this->{session}->{webName},
717 $this->{session}->{topicName}
718 );
719 return $topicObject->expandMacros($ans);
720}
721
722# _handleWikiWord is called for a wiki word that needs linking.
723# Handle the various link constructions. e.g.:
724# WikiWord
725# Web.WikiWord
726# Web.WikiWord#anchor
727#
728# This routine adds missing parameters before passing off to internallink
729sub _handleWikiWord {
730 my ( $this, $topicObject, $web, $topic, $anchor ) = @_;
731
732 my $linkIfAbsent = 1;
733 my $keepWeb = 0;
734 my $text;
735
736 # For some strange reason, $web doesn't get untainted by the regex
737 # that invokes this function. We can untaint it safely, because it's
738 # validated by the RE.
739 $web = Foswiki::Sandbox::untaintUnchecked($web);
740
741 $web = $topicObject->web() unless ( defined($web) );
742 if ( defined($anchor) ) {
743 ASSERT( ( $anchor =~ m/\#.*/ ) ) if DEBUG; # must include a hash.
744 }
745 else {
746 $anchor = '';
747 }
748
749 if ( defined($anchor) ) {
750
751 # 'Web.TopicName#anchor' or 'Web.ABBREV#anchor' link
752 $text = $topic . $anchor;
753 }
754 else {
755 $anchor = '';
756
757 # 'Web.TopicName' or 'Web.ABBREV' link:
758 if ( $topic eq $Foswiki::cfg{HomeTopicName}
759 && $web ne $this->{session}->{webName} )
760 {
761 $text = $web;
762 }
763 else {
764 $text = $topic;
765 }
766 }
767
768 # true to keep web prefix for non-existing Web.TOPIC
769 # Have to leave "web part" of ABR.ABR.ABR intact if topic not found
770 $keepWeb =
771 ( $topic =~ /^$Foswiki::regex{abbrevRegex}$/o
772 && $web ne $this->{session}->{webName} );
773
774 # false means suppress link for non-existing pages
775 $linkIfAbsent = ( $topic !~ /^$Foswiki::regex{abbrevRegex}$/o );
776
777 return $this->internalLink( $web, $topic, $text, $anchor, $linkIfAbsent,
778 $keepWeb, undef );
779}
780
781# Protect WikiWords, TLAs and URLs from further rendering with <nop>
782
# spent 320µs within Foswiki::Render::_escapeAutoLinks which was called 18 times, avg 18µs/call: # 18 times (320µs+0s) by Foswiki::Render::_handleSquareBracketedLink at line 824, avg 18µs/call
sub _escapeAutoLinks {
783189µs my $text = shift;
784
785188µs if ($text) {
786
787 # WikiWords, TLAs, and email addresses
78818221µs $text =~ s/(?<=[\s\(])
789 (
790 (?:
791 (?:($Foswiki::regex{webNameRegex})\.)?
792 (?: $Foswiki::regex{wikiWordRegex}
793 | $Foswiki::regex{abbrevRegex} )
794 )
795 | $Foswiki::regex{emailAddrRegex}
796 )/<nop>$1/gox;
797
798 # Explicit links
7991846µs $text =~ s/($Foswiki::regex{linkProtocolPattern}):(?=\S)/$1<nop>:/go;
800 }
8011867µs return $text;
802}
803
804# Handle SquareBracketed links mentioned on page $web.$topic
805# format: [[$link]]
806# format: [[$link][$text]]
807
# spent 7.11ms (1.03+6.08) within Foswiki::Render::_handleSquareBracketedLink which was called 18 times, avg 395µs/call: # 18 times (1.03ms+6.08ms) by Foswiki::Render::getRenderedVersion at line 1413, avg 395µs/call
sub _handleSquareBracketedLink {
8081854µs my ( $this, $topicObject, $link, $text ) = @_;
809
810 # Strip leading/trailing spaces
8111824µs $link =~ s/^\s+//;
8121815µs $link =~ s/\s+$//;
813
814186µs my $hasExplicitLinkLabel = 0;
815
8161811µs if ( defined($text) ) {
817
818 # [[$link][$text]]
819183µs $hasExplicitLinkLabel = 1;
8201848µs1892µs if ( my $img = $this->_isImageLink($text) ) {
# spent 92µs making 18 calls to Foswiki::Render::_isImageLink, avg 5µs/call
821 $text = $img;
822 }
823 else {
8241839µs18320µs $text = _escapeAutoLinks($text);
# spent 320µs making 18 calls to Foswiki::Render::_escapeAutoLinks, avg 18µs/call
825 }
826 }
827
8281866µs if ( $link =~ m#^($Foswiki::regex{linkProtocolPattern}:|/)# ) {
829
830 # Explicit external [[http://$link]] or [[http://$link][$text]]
831 # or explicit absolute [[/$link]] or [[/$link][$text]]
8321600ns if ( !defined($text) && $link =~ /^(\S+)\s+(.*)$/ ) {
833
834 my $candidateLink = $1;
835 my $candidateText = $2;
836
837 # If the URL portion contains a ? indicating query parameters then
838 # the spaces are possibly embedded in the query string, so don't
839 # use the legacy format.
840 if ( $candidateLink !~ m/\?/ ) {
841
842 # Legacy case of '[[URL anchor display text]]' link
843 # implicit untaint is OK as we are just recycling topic content
844 $link = $candidateLink;
845 $text = _escapeAutoLinks($candidateText);
846 }
847 }
84816µs18µs return $this->_externalLink( $link, $text );
# spent 8µs making 1 call to Foswiki::Render::_externalLink
849 }
850
851 # Extract '?params'
852 # $link =~ s/(\?.*?)(?>#|$)//;
853178µs my $params = '';
854179µs if ( $link =~ s/(\?.*$)// ) {
855 $params = $1;
856 }
857
858174µs $text = _escapeAutoLinks($link) unless defined $text;
8591736µs $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/_fixedFontText($1,1)/gem;
8601727µs $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S)
861 __$ENDWW/<strong><em>$1<\/em><\/strong>/gmx;
8621725µs $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/<strong>$1<\/strong>/gm;
8631723µs $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/<em>$1<\/em>/gm;
8641721µs $text =~ s/${STARTWW}\=(\S+?|\S[^\n]*?\S)\=$ENDWW/_fixedFontText($1,0)/gem;
865
866 # Extract '#anchor'
867 # $link =~ s/(\#[a-zA-Z_0-9\-]*$)//;
868174µs my $anchor = '';
8691729µs if ( $link =~ s/($Foswiki::regex{anchorRegex}$)// ) {
870 $anchor = $1;
871
872 #$text =~ s/#$anchor//;
873 }
874
875 # filter out &any; entities (legacy)
8761711µs $link =~ s/\&[a-z]+\;//gi;
877
878 # filter out &#123; entities (legacy)
879177µs $link =~ s/\&\#[0-9]+\;//g;
880
881 # Filter junk
8821749µs $link =~ s/$Foswiki::cfg{NameFilter}+/ /g;
883
8841737µs1736µs ASSERT( UNTAINTED($link) ) if DEBUG;
# spent 36µs making 17 calls to Assert::ASSERTS_OFF, avg 2µs/call
885
886 # Capitalise first word
8871714µs $link = ucfirst($link);
888
889 # Collapse spaces and capitalise following letter
8901728µs $link =~ s/\s([$Foswiki::regex{mixedAlphaNum}])/\U$1/go;
891
892 # Get rid of remaining spaces, i.e. spaces in front of -'s and ('s
893178µs $link =~ s/\s//go;
894
895 # The link is used in the topic name, and if locales are in effect,
896 # the above conversions will taint the name (Foswiki:Tasks:Item2091)
8971735µs1794µs $link = Foswiki::Sandbox::untaintUnchecked($link);
# spent 94µs making 17 calls to Foswiki::Sandbox::untaintUnchecked, avg 6µs/call
898
899174µs $link ||= $topicObject->topic;
900
901 # Topic defaults to the current topic
9021791µs34299µs my ( $web, $topic ) =
# spent 247µs making 17 calls to Foswiki::normalizeWebTopicName, avg 15µs/call # spent 52µs making 17 calls to Foswiki::Meta::web, avg 3µs/call
903 $this->{session}->normalizeWebTopicName( $topicObject->web, $link );
904
9051794µs175.23ms return $this->internalLink( $web, $topic, $text, $anchor, 1, undef,
# spent 5.23ms making 17 calls to Foswiki::Render::internalLink, avg 308µs/call
906 $hasExplicitLinkLabel, $params );
907}
908
909# Check if text is an image # (as indicated by the file type)
910# return an img tag, otherwise nothing
911
# spent 92µs within Foswiki::Render::_isImageLink which was called 18 times, avg 5µs/call: # 18 times (92µs+0s) by Foswiki::Render::_handleSquareBracketedLink at line 820, avg 5µs/call
sub _isImageLink {
9121812µs my ( $this, $url ) = @_;
913
9141816µs return if $url =~ /<nop>/;
9151813µs $url =~ s/^\s+//;
9161814µs $url =~ s/\s+$//;
917188µs if ( $url =~ m#^https?://[^?]*\.(?:gif|jpg|jpeg|png)$#i ) {
918 my $filename = $url;
919 $filename =~ s@.*/@@;
920 return CGI::img( { src => $url, alt => $filename } );
921 }
9221875µs return;
923}
924
925# Handle an external link typed directly into text. If it's an image
926# and no text is specified, then use an img tag, otherwise generate a link.
927
# spent 8µs within Foswiki::Render::_externalLink which was called: # once (8µs+0s) by Foswiki::Render::_handleSquareBracketedLink at line 848
sub _externalLink {
92811µs my ( $this, $url, $text ) = @_;
929
9301200ns if ( !$text && ( my $img = $this->_isImageLink($url) ) ) {
931 return $img;
932 }
9331400ns my $opt = '';
93411µs if ( $url =~ /^mailto:/i ) {
935 if ( $Foswiki::cfg{AntiSpam}{EmailPadding} ) {
936 $url =~ s/(\@[\w\_\-\+]+)(\.)
937 /$1$Foswiki::cfg{AntiSpam}{EmailPadding}$2/x;
938 if ($text) {
939 $text =~ s/(\@[\w\_\-\+]+)(\.)
940 /$1$Foswiki::cfg{AntiSpam}{EmailPadding}$2/x;
941 }
942 }
943 if ( $Foswiki::cfg{AntiSpam}{EntityEncode} ) {
944
945 # Much harder obfuscation scheme. For link text we only encode '@'
946 # See also http://develop.twiki.org/~twiki4/cgi-bin/view/Bugs/Item2928
947 # and http://develop.twiki.org/~twiki4/cgi-bin/view/Bugs/Item3430
948 # before touching this
949 # Note: & is already encoded, so don't encode any entities
950 # See http://foswiki.org/Tasks/Item10905
951 $url =~ s/&(\w+);/$REMARKER$1$REEND/g; # "&abc;"
952 $url =~ s/&(#x?[0-9a-f]+);/$REMARKER$1$REEND/gi; # "&#123;"
953 $url =~ s/([^\w$REMARKER$REEND])/'&#'.ord($1).';'/ge;
954 $url =~ s/$REMARKER(#x?[0-9a-f]+)$REEND/&$1;/goi;
955 $url =~ s/$REMARKER(\w+)$REEND/&$1;/go;
956 if ($text) {
957 $text =~ s/\@/'&#'.ord('@').';'/ge;
958 }
959 }
960 }
961 else {
9621600ns $opt = ' target="_top"';
963 }
9641200ns $text ||= $url;
965
966 # Item5787: if a URL has spaces, escape them so the URL has less
967 # chance of being broken by later rendering.
9681500ns $url =~ s/ /%20/g;
969
970 # SMELL: Can't use CGI::a here, because it encodes ampersands in
971 # the link, and those have already been encoded once in the
972 # rendering loop (they are identified as "stand-alone"). One
973 # encoding works; two is too many. None would be better for everyone!
97416µs return '<a href="' . $url . '"' . $opt . '>' . $text . '</a>';
975}
976
977# Generate a "mailTo" link
978sub _mailLink {
979 my ( $this, $text ) = @_;
980
981 my $url = $text;
982 return $text if $url =~ /^(?:!|\<nop\>)/;
983
984#use Email::Valid ();
985#my $tmpEmail = $url;
986#$tmpEmail =~ s/^mailto://;
987#my $errtxt = '';
988#$errtxt = "<b>INVALID</b> $tmpEmail " unless (Email::Valid->address($tmpEmail));
989
990 # Any special characters in the user portion must be %hex escaped.
991 $url =~ s/^((?:mailto\:)?)?(.*?)(@.*?)$/'mailto:'._escape( $2 ).$3/msiex;
992 my $lenLeft = length($2);
993 my $lenRight = length($3);
994
995# Per RFC 3696 Errata, length restricted to 254 overall per RFC 2821 RCPT limits
996 return $text
997 if ( $lenLeft > 64 || $lenRight > 254 || $lenLeft + $lenRight > 254 );
998
999 $url = 'mailto:' . $url unless $url =~ /^mailto:/i;
1000 return _externalLink( $this, $url, $text );
1001}
1002
1003sub _escape {
1004 my $txt = shift;
1005
1006 my $chars = join( '', keys(%ESCAPED) );
1007 $txt =~ s/([$chars])/$ESCAPED{$1}/g;
1008 $txt =~ s/[\s]/%20/g; # Any folding white space
1009 return $txt;
1010}
1011
1012=begin TML
1013
1014---++ ObjectMethod renderFORMFIELD ( %params, $topic, $web ) -> $html
1015
1016Returns the fully rendered expansion of a %FORMFIELD{}% tag.
1017
1018=cut
1019
1020sub renderFORMFIELD {
1021 my ( $this, $params, $topicObject ) = @_;
1022
1023 my $formField = $params->{_DEFAULT};
1024 return '' unless defined $formField;
1025 my $altText = $params->{alttext};
1026 my $default = $params->{default};
1027 my $rev = $params->{rev} || '';
1028 my $format = $params->{format};
1029
1030 $altText = '' unless defined $altText;
1031 $default = '' unless defined $default;
1032
1033 unless ( defined $format ) {
1034 $format = '$value';
1035 }
1036
1037 # SMELL: this local creation of a cache looks very suspicious. Suspect
1038 # this may have been a one-off optimisation.
1039 my $formTopicObject = $this->{ffCache}{ $topicObject->getPath() . $rev };
1040 unless ($formTopicObject) {
1041 undef $rev unless $rev;
1042 $formTopicObject =
1043 Foswiki::Meta->load( $this->{session}, $topicObject->web,
1044 $topicObject->topic, $rev );
1045 unless ( $formTopicObject->haveAccess('VIEW') ) {
1046
1047 # Access violation, create dummy meta with empty text, so
1048 # it looks like it was already loaded.
1049 $formTopicObject =
1050 Foswiki::Meta->new( $this->{session}, $topicObject->web,
1051 $topicObject->topic, '' );
1052 }
1053 $this->{ffCache}{ $formTopicObject->getPath() . ( $rev || 0 ) } =
1054 $formTopicObject;
1055 }
1056
1057 my $text = $format;
1058 my $found = 0;
1059 my $title = '';
1060 my @fields = $formTopicObject->find('FIELD');
1061 foreach my $field (@fields) {
1062 my $name = $field->{name};
1063 $title = $field->{title} || $name;
1064 if ( $title eq $formField || $name eq $formField ) {
1065 $found = 1;
1066 my $value = $field->{value};
1067 $text = $default if !length($value);
1068 $text =~ s/\$title/$title/go;
1069
1070 # raw field value
1071 $text =~ s/\$value/$value/go;
1072 $text =~ s/\$name/$name/g;
1073 if ( $text =~ m/\$form/ ) {
1074 my @defform = $formTopicObject->find('FORM');
1075 my $form = $defform[0]; # only one form per topic
1076 my $fname = $form->{name};
1077 $text =~ s/\$form/$fname/g;
1078 }
1079
1080 last; # one hit suffices
1081 }
1082 }
1083
1084 unless ($found) {
1085 $text = $altText || '';
1086 }
1087
1088 $text = Foswiki::expandStandardEscapes($text);
1089
1090 # render nop exclamation marks before words as <nop>
1091 $text =~ s/!(\w+)/<nop>$1/gs;
1092
1093 return $text;
1094}
1095
1096=begin TML
1097
1098---++ ObjectMethod getRenderedVersion ( $text, $topicObject ) -> $html
1099
1100The main rendering function.
1101
1102=cut
1103
1104
# spent 61.4ms (19.6+41.8) within Foswiki::Render::getRenderedVersion which was called 5 times, avg 12.3ms/call: # 5 times (19.6ms+41.8ms) by Foswiki::Meta::renderTML at line 3117 of /var/www/foswiki11/lib/Foswiki/Meta.pm, avg 12.3ms/call
sub getRenderedVersion {
1105526µs my ( $this, $text, $topicObject ) = @_;
110657µs55µs ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG;
# spent 5µs making 5 calls to Assert::ASSERTS_OFF, avg 1µs/call
1107
110852µs return '' unless defined $text; # nothing to do
1109
111054µs my $session = $this->{session};
111152µs my $plugins = $session->{plugins};
111252µs my $prefs = $session->{prefs};
1113
111454µs @{ $this->{LIST} } = ();
1115
1116 # Initial cleanup
1117549µs $text =~ s/\r//g;
1118
1119 # whitespace before <! tag (if it is the first thing) is illegal
1120512µs $text =~ s/^\s+(<![a-z])/$1/i;
1121
1122 # clutch to enforce correct rendering at end of doc
1123540µs $text =~ s/\n?$/\n<nop>\n/s;
1124
1125 # Maps of placeholders to tag parameters and text
112654µs my $removed = {};
1127
1128 # verbatim before literal - see Item3431
1129511µs5169µs $text = Foswiki::takeOutBlocks( $text, 'verbatim', $removed );
# spent 169µs making 5 calls to Foswiki::takeOutBlocks, avg 34µs/call
113059µs5485µs $text = Foswiki::takeOutBlocks( $text, 'literal', $removed );
# spent 485µs making 5 calls to Foswiki::takeOutBlocks, avg 97µs/call
113156µs $text = Foswiki::takeOutBlocks( $text, 'dirtyarea', $removed )
1132 if $Foswiki::cfg{Cache}{Enabled};
1133
1134540µs5142µs $text =
# spent 142µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 28µs/call
1135 $this->_takeOutProtected( $text, qr/<\?([^?]*)\?>/s, 'comment',
1136 $removed );
1137521µs5207µs $text =
# spent 207µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 41µs/call
1138 $this->_takeOutProtected( $text, qr/<!DOCTYPE([^<>]*)>?/mi, 'comment',
1139 $removed );
1140520µs5179µs $text =
# spent 179µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 36µs/call
1141 $this->_takeOutProtected( $text, qr/<head.*?<\/head>/si, 'head',
1142 $removed );
1143518µs5157µs $text = $this->_takeOutProtected( $text, qr/<textarea\b.*?<\/textarea>/si,
# spent 157µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 31µs/call
1144 'textarea', $removed );
1145517µs5265µs $text =
# spent 265µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 53µs/call
1146 $this->_takeOutProtected( $text, qr/<script\b.*?<\/script>/si, 'script',
1147 $removed );
1148
1149 # Remove the sticky tags (used in WysiwygPlugin's TML2HTML conversion)
1150 # since they could potentially break a browser.
1151 # They are removed here and not in the plugin because the plugin might
1152 # not be installed but the sticky tags are standard markup.
1153533µs $text =~ s#</?sticky>##g;
1154
1155 # DEPRECATED startRenderingHandler before PRE removed
1156 # SMELL: could parse more efficiently if this wasn't
1157 # here.
1158528µs159.77ms $plugins->dispatch( 'startRenderingHandler', $text, $topicObject->web,
# spent 9.75ms making 5 calls to Foswiki::Plugins::dispatch, avg 1.95ms/call # spent 15µs making 5 calls to Foswiki::Meta::web, avg 3µs/call # spent 11µs making 5 calls to Foswiki::Meta::topic, avg 2µs/call
1159 $topicObject->topic );
1160
1161511µs5145µs $text = Foswiki::takeOutBlocks( $text, 'pre', $removed );
# spent 145µs making 5 calls to Foswiki::takeOutBlocks, avg 29µs/call
1162
1163 # Join lines ending in '\' (don't need \r?, it was removed already)
1164530µs $text =~ s/\\\n//gs;
1165
1166510µs518.6ms $plugins->dispatch( 'preRenderingHandler', $text, $removed );
# spent 18.6ms making 5 calls to Foswiki::Plugins::dispatch, avg 3.71ms/call
1167
1168520µs522µs if ( $plugins->haveHandlerFor('insidePREHandler') ) {
# spent 22µs making 5 calls to Foswiki::Plugins::haveHandlerFor, avg 4µs/call
1169 foreach my $region ( sort keys %$removed ) {
1170 next unless ( $region =~ /^pre\d+$/i );
1171 my @lines = split( /\r?\n/, $removed->{$region}{text} );
1172 my $rt = '';
1173 while ( scalar(@lines) ) {
1174 my $line = shift(@lines);
1175 $plugins->dispatch( 'insidePREHandler', $line );
1176 if ( $line =~ /\n/ ) {
1177 unshift( @lines, split( /\r?\n/, $line ) );
1178 next;
1179 }
1180 $rt .= $line . "\n";
1181 }
1182 $removed->{$region}{text} = $rt;
1183 }
1184 }
1185
118659µs510µs if ( $plugins->haveHandlerFor('outsidePREHandler') ) {
# spent 10µs making 5 calls to Foswiki::Plugins::haveHandlerFor, avg 2µs/call
1187
1188 # DEPRECATED - this is the one call preventing
1189 # effective optimisation of the TML processing loop,
1190 # as it exposes the concept of a 'line loop' to plugins,
1191 # but HTML is not a line-oriented language (though TML is).
1192 # But without it, a lot of processing could be moved
1193 # outside the line loop.
1194 my @lines = split( /\r?\n/, $text );
1195 my $rt = '';
1196 while ( scalar(@lines) ) {
1197 my $line = shift(@lines);
1198 $plugins->dispatch( 'outsidePREHandler', $line );
1199 if ( $line =~ /\n/ ) {
1200 unshift( @lines, split( /\r?\n/, $line ) );
1201 next;
1202 }
1203 $rt .= $line . "\n";
1204 }
1205
1206 $text = $rt;
1207 }
1208
1209 # Remove input fields: Item11480
1210581µs $text =~ s/<nop>/N$NOPMARK/g;
1211526µs5310µs $text =
# spent 310µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 62µs/call
1212 $this->_takeOutProtected( $text, qr/<input\b.*?>/si, 'input', $removed );
1213594µs $text =~ s/N$NOPMARK/<nop>/g;
1214
1215 # Escape rendering: Change ' !AnyWord' to ' <nop>AnyWord',
1216 # for final ' AnyWord' output
1217577µs $text =~ s/$STARTWW\!(?=[\w\*\=])/<nop>/gm;
1218
1219 # Blockquoted email (indented with '> ')
1220 # Could be used to provide different colours for different numbers of '>'
1221566µs $text =~ s/^>(.*?)$/'&gt;'.CGI::cite( {}, $1 ).CGI::br()/gem;
1222
1223 # locate isolated < and > and translate to entities
1224 # Protect isolated <!-- and -->
1225598µs $text =~ s/<!--/{$REMARKER!--/g;
1226592µs $text =~ s/-->/--}$REMARKER/g;
1227
1228 # SMELL: this next fragment does not handle the case where HTML tags
1229 # are embedded in the values provided to other tags. The only way to
1230 # do this correctly is to parse the HTML (bleagh!). So we just assume
1231 # they have been escaped.
12325728µs $text =~ s/<(\/?\w+(:\w+)?)>/{$REMARKER$1}$REMARKER/g;
123351.87ms $text =~ s/<(\w+(:\w+)?(\s+.*?|\/)?)>/{$REMARKER$1}$REMARKER/g;
1234
1235 # XML processing instruction only valid at start of text
1236525µs $text =~ s/^<(\?\w.*?\?)>/{$REMARKER$1}$REMARKER/g;
1237
1238 # entitify lone < and >, praying that we haven't screwed up :-(
1239 # Item1985: CDATA sections are not lone < and >
1240534µs $text =~ s/<(?!\!\[CDATA\[)/&lt\;/g;
1241531µs $text =~ s/(?<!\]\])>/&gt\;/g;
12425261µs $text =~ s/{$REMARKER/</go;
12435259µs $text =~ s/}$REMARKER/>/go;
1244
1245 # other entities
12465166µs $text =~ s/&(\w+);/$REMARKER$1;/g; # "&abc;"
1247592µs $text =~ s/&(#x?[0-9a-f]+);/$REMARKER$1;/gi; # "&#123;"
1248538µs $text =~ s/&/&amp;/g; # escape standalone "&"
12495114µs $text =~ s/$REMARKER(#x?[0-9a-f]+;)/&$1/goi;
12505173µs $text =~ s/$REMARKER(\w+;)/&$1/go;
1251
1252 # clear the set of unique anchornames in order to inhibit
1253 # the 'relabeling' of anchor names if the same topic is processed
1254 # more than once, cf. explanation in expandMacros()
1255523µs5109µs my $anchors = $this->getAnchorNames($topicObject);
# spent 109µs making 5 calls to Foswiki::Render::getAnchorNames, avg 22µs/call
1256521µs521µs $anchors->clear();
# spent 21µs making 5 calls to Foswiki::Render::Anchors::clear, avg 4µs/call
1257
1258 # '#WikiName' anchors. Don't attempt to make these unique; renaming
1259 # user-defined anchors is not sensible.
1260566µs $text =~ s/^(\#$Foswiki::regex{wikiWordRegex})/
126115µs286µs CGI::a({
# spent 44µs making 1 call to Foswiki::Render::Anchors::add # spent 42µs making 1 call to CGI::a
1262 name => $anchors->add( $1 )
1263 }, '')/geom;
1264
1265 # Headings
1266 # '<h6>...</h6>' HTML rule
1267562µs $text =~ s/$Foswiki::regex{headerPatternHt}/
1268 _makeAnchorHeading($this, $2, $1, $anchors)/geo;
1269
1270 # '----+++++++' rule
1271571µs $text =~ s/$Foswiki::regex{headerPatternDa}/
1272 _makeAnchorHeading($this, $2, length($1), $anchors)/geo;
1273
1274 # Horizontal rule
1275520µs5292µs my $hr = CGI::hr();
# spent 208µs making 1 call to CGI::AUTOLOAD # spent 84µs making 4 calls to CGI::hr, avg 21µs/call
1276586µs $text =~ s/^---+/$hr/gm;
1277
1278 # Now we really _do_ need a line loop, to process TML
1279 # line-oriented stuff.
128053µs my $isList = 0; # True when within a list
128151µs my $tableRow = 0;
12825900ns my @result;
128352µs my $isFirst = 1;
1284
12855207µs foreach my $line ( split( /\r?\n/, $text ) ) {
1286
1287 # Table: | cell | cell |
1288 # allow trailing white space after the last |
1289223124µs if ( $line =~ m/^(\s*)\|.*\|\s*$/ ) {
1290
1291 if ($isList) {
1292
1293 # Table start should terminate previous list
1294 _addListItem( $this, \@result, '', '', '' );
1295 $isList = 0;
1296 }
1297
1298 unless ($tableRow) {
1299
1300 # mark the head of the table
1301 push( @result, $TABLEMARKER );
1302 }
1303 $line =~ s/^(\s*)\|(.*)/$1._emitTR( $this, $2 )/e;
1304 $tableRow++;
1305 }
1306 elsif ($tableRow) {
1307 _addTHEADandTFOOT( \@result );
1308 push( @result, '</table>' );
1309 $tableRow = 0;
1310 }
1311
1312 # Lists and paragraphs
1313223425µs if ( $line =~ m/^\s*$/ ) {
13145214µs unless ( $tableRow || $isFirst ) {
1315 $line = '<p></p>';
1316 }
13175210µs $isList = 0;
1318 }
1319 elsif ( $line =~ m/^\S/ ) {
1320 $isList = 0;
1321 }
1322 elsif ( $line =~ m/^(\t| )+\S/ ) {
132362296µs if ( $line =~
1324 s/^((\t| )+)\$\s(([^:]+|:[^\s]+)+?):\s/<dt> $3 <\/dt><dd> / )
1325 {
1326
1327 # Definition list
1328 _addListItem( $this, \@result, 'dl', 'dd', $1 );
1329 $isList = 1;
1330 }
1331 elsif ( $line =~ s/^((\t| )+)(\S+?):\s/<dt> $3<\/dt><dd> /o ) {
1332
1333 # Definition list
1334 _addListItem( $this, \@result, 'dl', 'dd', $1 );
1335 $isList = 1;
1336 }
1337 elsif ( $line =~ s/^((\t| )+)\* /<li> /o ) {
1338
1339 # Unnumbered list
134060111µs60656µs _addListItem( $this, \@result, 'ul', 'li', $1 );
# spent 656µs making 60 calls to Foswiki::Render::_addListItem, avg 11µs/call
13416025µs $isList = 1;
1342 }
1343 elsif ( $line =~ m/^((\t| )+)([1AaIi]\.|\d+\.?) ?/ ) {
1344
1345 # Numbered list
1346 my $ot = $3;
1347 $ot =~ s/^(.).*/$1/;
1348 if ( $ot !~ /^\d$/ ) {
1349
1350 # Use style="list-type-type:"
1351 $ot = ' style="list-style-type:' . $list_types{$ot} . '"';
1352 }
1353 else {
1354 $ot = '';
1355 }
1356 $line =~ s/^((\t| )+)([1AaIi]\.|\d+\.?) ?/<li$ot> /;
1357 _addListItem( $this, \@result, 'ol', 'li', $1 );
1358 $isList = 1;
1359 }
1360 elsif ( $isList && $line =~ /^(\t| )+\s*\S/ ) {
1361
1362 # indented line extending prior list item
136322µs push( @result, $line );
136421µs next;
1365 }
1366 else {
1367 $isList = 0;
1368 }
1369 }
1370 elsif ( $isList && $line =~ /^(\t| )+\s*\S/ ) {
1371
1372 # indented line extending prior list item; case where indent
1373 # starts with is at least 3 spaces or a tab, but may not be a
1374 # multiple of 3.
1375 push( @result, $line );
1376 next;
1377 }
1378
1379 # Finish the list
1380221234µs156654µs unless ( $isList || $isFirst ) {
# spent 654µs making 156 calls to Foswiki::Render::_addListItem, avg 4µs/call
1381 _addListItem( $this, \@result, '', '', '' );
1382 }
1383
1384221123µs push( @result, $line );
1385221101µs $isFirst = 0;
1386 }
1387
13885800ns if ($tableRow) {
1389 _addTHEADandTFOOT( \@result );
1390 push( @result, '</table>' );
1391 }
139259µs520µs _addListItem( $this, \@result, '', '', '' );
# spent 20µs making 5 calls to Foswiki::Render::_addListItem, avg 4µs/call
1393
1394553µs $text = join( '', @result );
1395
1396 # SMELL: use of $STARTWW and $ENDWW really limit the number of places
1397 # emphasis can happen. But it's a tradeoff between that and excessive
1398 # greed.
1399
1400547µs $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/_fixedFontText($1,1)/gem;
1401538µs $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S)
1402 __$ENDWW/<strong><em>$1<\/em><\/strong>/gmx;
14035112µs $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/<strong>$1<\/strong>/gm;
1404554µs $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/<em>$1<\/em>/gm;
14055166µs $text =~ s/${STARTWW}\=(\S+?|\S[^\n]*?\S)\=$ENDWW/_fixedFontText($1,0)/gem;
1406
1407 # Handle [[][] and [[]] links
1408 # Change ' ![[...' to ' [<nop>[...' to protect from further rendering
1409557µs $text =~ s/(^|\s)\!\[\[/$1\[<nop>\[/gm;
1410
1411 # Spaced-out Wiki words with alternative link text
1412 # i.e. [[$1][$3]]
141323231µs187.11ms $text =~ s(\[\[([^\]\[\n]+)\](\[([^\]\n]+)\])?\])
# spent 7.11ms making 18 calls to Foswiki::Render::_handleSquareBracketedLink, avg 395µs/call
1414 (_handleSquareBracketedLink( $this,$topicObject,$1,$3))ge;
1415
1416 # URI - don't apply if the URI is surrounded by url() to avoid naffing
1417 # CSS
141855.99ms $text =~ s/(^|(?<!url)[-*\s(|])
1419 ($Foswiki::regex{linkProtocolPattern}:
1420 ([^\s<>"]+[^\s*.,!?;:)<|]))/
1421 $1._externalLink( $this,$2)/geox;
1422
1423 # Normal mailto:foo@example.com ('mailto:' part optional)
142455.18ms $text =~ s/$STARTWW((mailto\:)?
1425 $Foswiki::regex{emailAddrRegex})$ENDWW/
1426 _mailLink( $this, $1 )/gemx;
1427
1428521µs10317µs unless ( Foswiki::isTrue( $prefs->getPreference('NOAUTOLINK') ) ) {
# spent 267µs making 5 calls to Foswiki::Prefs::getPreference, avg 53µs/call # spent 50µs making 5 calls to Foswiki::isTrue, avg 10µs/call
1429
1430 # Handle WikiWords
1431 $text = Foswiki::takeOutBlocks( $text, 'noautolink', $removed );
1432 $text =~ s($STARTWW
1433 (?:($Foswiki::regex{webNameRegex})\.)?
1434 ($Foswiki::regex{wikiWordRegex}|
1435 $Foswiki::regex{abbrevRegex})
1436 ($Foswiki::regex{anchorRegex})?)
1437 (_handleWikiWord( $this, $topicObject, $1, $2, $3))gexom;
1438 Foswiki::putBackBlocks( \$text, $removed, 'noautolink' );
1439 }
1440
1441 # Restore input fields before calling the end/post handlers
1442527µs5280µs $this->_putBackProtected( \$text, 'input', $removed );
# spent 280µs making 5 calls to Foswiki::Render::_putBackProtected, avg 56µs/call
1443529µs $text =~ s/N$NOPMARK//g;
1444
1445512µs558µs Foswiki::putBackBlocks( \$text, $removed, 'pre' );
# spent 58µs making 5 calls to Foswiki::putBackBlocks, avg 12µs/call
1446
1447 # DEPRECATED plugins hook after PRE re-inserted
1448510µs516µs $plugins->dispatch( 'endRenderingHandler', $text );
# spent 16µs making 5 calls to Foswiki::Plugins::dispatch, avg 3µs/call
1449
1450 # replace verbatim with pre in the final output
1451517µs548µs Foswiki::putBackBlocks( \$text, $removed, 'verbatim', 'pre',
# spent 48µs making 5 calls to Foswiki::putBackBlocks, avg 10µs/call
1452 \&verbatimCallBack );
1453519µs $text =~ s|\n?<nop>\n$||o; # clean up clutch
1454
1455515µs5270µs $this->_putBackProtected( \$text, 'script', $removed, \&_filterScript );
# spent 270µs making 5 calls to Foswiki::Render::_putBackProtected, avg 54µs/call
1456513µs5311µs Foswiki::putBackBlocks( \$text, $removed, 'literal', '', \&_filterLiteral );
# spent 311µs making 5 calls to Foswiki::putBackBlocks, avg 62µs/call
1457510µs534µs $this->_putBackProtected( \$text, 'literal', $removed );
# spent 34µs making 5 calls to Foswiki::Render::_putBackProtected, avg 7µs/call
145855µs Foswiki::putBackBlocks( \$text, $removed, 'dirtyarea' )
1459 if $Foswiki::cfg{Cache}{Enabled};
146058µs551µs $this->_putBackProtected( \$text, 'comment', $removed );
# spent 51µs making 5 calls to Foswiki::Render::_putBackProtected, avg 10µs/call
146158µs548µs $this->_putBackProtected( \$text, 'head', $removed );
# spent 48µs making 5 calls to Foswiki::Render::_putBackProtected, avg 10µs/call
146258µs526µs $this->_putBackProtected( \$text, 'textarea', $removed );
# spent 26µs making 5 calls to Foswiki::Render::_putBackProtected, avg 5µs/call
1463
1464542µs10110µs $this->{session}->getLoginManager()->endRenderingHandler($text);
# spent 55µs making 5 calls to Foswiki::LoginManager::endRenderingHandler, avg 11µs/call # spent 55µs making 5 calls to Foswiki::getLoginManager, avg 11µs/call
1465
146658µs5864µs $plugins->dispatch( 'postRenderingHandler', $text );
# spent 864µs making 5 calls to Foswiki::Plugins::dispatch, avg 173µs/call
1467568µs return $text;
1468}
1469
1470=begin TML
1471
1472---++ StaticMethod verbatimCallBack
1473
1474Callback for use with putBackBlocks that replaces &lt; and >
1475by their HTML entities &amp;lt; and &amp;gt;
1476
1477=cut
1478
1479sub verbatimCallBack {
1480 my $val = shift;
1481
1482 # SMELL: A shame to do this, but been in Foswiki.org have converted
1483 # 3 spaces to tabs since day 1
1484 $val =~ s/\t/ /g;
1485
1486 return Foswiki::entityEncode($val);
1487}
1488
1489# Only put script and literal sections back if they are allowed by options
1490
# spent 35µs within Foswiki::Render::_filterLiteral which was called 14 times, avg 3µs/call: # 14 times (35µs+0s) by Foswiki::putBackBlocks at line 2948 of /var/www/foswiki11/lib/Foswiki.pm, avg 3µs/call
sub _filterLiteral {
14911410µs my $val = shift;
14921450µs return $val if ( $Foswiki::cfg{AllowInlineScript} );
1493 return CGI::comment(
1494'<literal> is not allowed on this site - denied by deprecated {AllowInlineScript} setting'
1495 );
1496}
1497
1498
# spent 27µs within Foswiki::Render::_filterScript which was called 12 times, avg 2µs/call: # 12 times (27µs+0s) by Foswiki::Render::_putBackProtected at line 1706, avg 2µs/call
sub _filterScript {
1499125µs my $val = shift;
15001241µs return $val if ( $Foswiki::cfg{AllowInlineScript} );
1501 return CGI::comment(
1502'<script> is not allowed on this site - denied by deprecated {AllowInlineScript} setting'
1503 );
1504}
1505
1506=begin TML
1507
1508---++ ObjectMethod TML2PlainText( $text, $topicObject, $opts ) -> $plainText
1509
1510Strip TML markup from text for display as plain text without
1511pushing it through the full rendering pipeline. Intended for
1512generation of topic and change summaries. Adds nop tags to
1513prevent subsequent rendering; nops get removed at the very end.
1514
1515$opts:
1516 * showvar - shows !%VAR% names if not expanded
1517 * expandvar - expands !%VARS%
1518 * nohead - strips ---+ headings at the top of the text
1519 * showmeta - does not filter meta-data
1520
1521=cut
1522
1523sub TML2PlainText {
1524 my ( $this, $text, $topicObject, $opts ) = @_;
1525 $opts ||= '';
1526
1527 return '' unless defined $text;
1528
1529 $text =~ s/\r//g; # SMELL, what about OS10?
1530
1531 if ( $opts =~ /showmeta/ ) {
1532 $text =~ s/%META:/%<nop>META:/g;
1533 }
1534 else {
1535 $text =~ s/%META:[A-Z].*?}%//g;
1536 }
1537
1538 if ( $opts =~ /expandvar/ ) {
1539 $text =~ s/(\%)(SEARCH){/$1<nop>$2/g; # prevent recursion
1540 $topicObject = Foswiki::Meta->new( $this->{session} )
1541 unless $topicObject;
1542 $text = $topicObject->expandMacros($text);
1543 }
1544 else {
1545 $text =~ s/%WEB%/$topicObject->web() || ''/ge;
1546 $text =~ s/%TOPIC%/$topicObject->topic() || ''/ge;
1547 my $wtn = $this->{session}->{prefs}->getPreference('WIKITOOLNAME')
1548 || '';
1549 $text =~ s/%WIKITOOLNAME%/$wtn/g;
1550 if ( $opts =~ /showvar/ ) {
1551 $text =~ s/%(\w+({.*?}))%/$1/g; # defuse
1552 }
1553 else {
1554 $text =~ s/%$Foswiki::regex{tagNameRegex}({.*?})?%//g; # remove
1555 }
1556 }
1557
1558 # Format e-mail to add spam padding (HTML tags removed later)
1559 $text =~ s/$STARTWW(
1560 (mailto\:)?
1561 [a-zA-Z0-9-_.+]+@[a-zA-Z0-9-_.]+\.[a-zA-Z0-9-_]+
1562 )$ENDWW
1563 /_mailLink( $this, $1 )/gemx;
1564 $text =~ s/<!--.*?-->//gs; # remove all HTML comments
1565 $text =~ s/<(?!nop)[^>]*>//g; # remove all HTML tags except <nop>
1566 $text =~ s/\&[a-z]+;/ /g; # remove entities
1567 if ( $opts =~ /nohead/ ) {
1568
1569 # skip headings on top
1570 while ( $text =~ s/^\s*\-\-\-+\+[^\n\r]*// ) { }; # remove heading
1571 }
1572
1573 # keep only link text of legacy [[prot://uri.tld/ link text]]
1574 $text =~ s/
1575 \[
1576 \[$Foswiki::regex{linkProtocolPattern}\:
1577 ([^\s<>"\]]+[^\s*.,!?;:)<|\]])
1578 \s+([^\[\]]*?)
1579 \]
1580 \]/$3/gx;
1581
1582 #keep only test portion of [[][]] links
1583 $text =~ s/\[\[([^\]]*\]\[)(.*?)\]\]/$2/g;
1584
1585 # SMELL: can't do this, it removes these characters even when they're
1586 # not for formatting
1587 #$text =~ s/[\[\]\*\|=_\&\<\>]/ /g;
1588
1589 $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/$1/gem;
1590 $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S)__$ENDWW/$1/gm;
1591 $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/$1/gm;
1592 $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/$1/gm;
1593 $text =~ s/${STARTWW}\=(\S+?|\S[^\n]*?\S)\=$ENDWW/$1/gem;
1594
1595 #SMELL: need to correct these too
1596 $text =~ s/[\[\]\|\&]/ /g; # remove remaining Wiki formatting chars
1597
1598 $text =~ s/^\-\-\-+\+*\s*\!*/ /gm; # remove heading formatting and hbar
1599 $text =~ s/[\+\-]+/ /g; # remove special chars
1600 $text =~ s/^\s+//; # remove leading whitespace
1601 $text =~ s/\s+$//; # remove trailing whitespace
1602 $text =~ s/!(\w+)/$1/gs; # remove all nop exclamation marks before words
1603 $text =~ s/[\r\n]+/\n/s;
1604 $text =~ s/[ \t]+/ /s;
1605
1606 # defuse "Web." prefix in "Web.TopicName" link
1607 $text =~ s{$STARTWW
1608 (($Foswiki::regex{webNameRegex})\.
1609 ($Foswiki::regex{wikiWordRegex}
1610 | $Foswiki::regex{abbrevRegex}))}
1611 {$2.<nop>$3}gx;
1612 $text =~ s/\<nop\>//g; # remove any remaining nops
1613 $text =~ s/[\<\>]/ /g; # remove any remaining formatting
1614
1615 return $text;
1616}
1617
1618=begin TML
1619
1620---++ ObjectMethod protectPlainText($text) -> $tml
1621
1622Protect plain text from expansions that would normally be done
1623duing rendering, such as wikiwords. Topic summaries, for example,
1624have to be protected this way.
1625
1626=cut
1627
1628sub protectPlainText {
1629 my ( $this, $text ) = @_;
1630
1631 # prevent text from getting rendered in inline search and link tool
1632 # tip text by escaping links (external, internal, Interwiki)
1633 $text =~ s/((($Foswiki::regex{webNameRegex})\.)?
1634 ($Foswiki::regex{wikiWordRegex}
1635 |$Foswiki::regex{abbrevRegex}))/<nop>$1/gx;
1636
1637 $text =~ s/([@%])/<nop>$1<nop>/g; # email address, macro
1638
1639 # Encode special chars into XML &#nnn; entities for use in RSS feeds
1640 # - no encoding for HTML pages, to avoid breaking international
1641 # characters. Only works for ISO-8859-1 sites, since the Unicode
1642 # encoding (&#nnn;) is identical for first 256 characters.
1643 # I18N TODO: Convert to Unicode from any site character set.
1644 if ( $this->{session}->inContext('rss')
1645 && defined( $Foswiki::cfg{Site}{CharSet} )
1646 && $Foswiki::cfg{Site}{CharSet} =~ /^iso-?8859-?1$/i )
1647 {
1648 $text =~ s/([\x7f-\xff])/"\&\#" . unpack( 'C', $1 ) .';'/ge;
1649 }
1650
1651 return $text;
1652}
1653
1654# DEPRECATED: retained for compatibility with various hack-job extensions
1655sub makeTopicSummary {
1656 my ( $this, $text, $topic, $web, $flags ) = @_;
1657 my $topicObject = Foswiki::Meta->new( $this->{session}, $web, $topic );
1658 return $topicObject->summariseText( '', $text );
1659}
1660
1661# _takeOutProtected( \$text, $re, $id, \%map ) -> $text
1662#
1663# * =$text= - Text to process
1664# * =$re= - Regular expression that matches tag expressions to remove
1665# * =\%map= - Reference to a hash to contain the removed blocks
1666#
1667# Return value: $text with blocks removed. Unlike takeOuBlocks, this
1668# *preserves* the tags.
1669#
1670# used to extract from $text comment type tags like &lt;!DOCTYPE blah>
1671#
1672# WARNING: if you want to take out &lt;!-- comments --> you _will_ need
1673# to re-write all the takeOuts to use a different placeholder
1674
# spent 1.26ms (1.12+137µs) within Foswiki::Render::_takeOutProtected which was called 30 times, avg 42µs/call: # 5 times (247µs+63µs) by Foswiki::Render::getRenderedVersion at line 1211, avg 62µs/call # 5 times (207µs+59µs) by Foswiki::Render::getRenderedVersion at line 1145, avg 53µs/call # 5 times (199µs+8µs) by Foswiki::Render::getRenderedVersion at line 1137, avg 41µs/call # 5 times (173µs+7µs) by Foswiki::Render::getRenderedVersion at line 1140, avg 36µs/call # 5 times (157µs+0s) by Foswiki::Render::getRenderedVersion at line 1143, avg 31µs/call # 5 times (142µs+0s) by Foswiki::Render::getRenderedVersion at line 1134, avg 28µs/call
sub _takeOutProtected {
16753045µs my ( $this, $intext, $re, $id, $map ) = @_;
1676
167750963µs20137µs $intext =~ s/($re)/_replaceBlock($1, $id, $map)/ge;
# spent 137µs making 20 calls to Foswiki::Render::_replaceBlock, avg 7µs/call
1678
167930143µs return $intext;
1680}
1681
1682
# spent 137µs within Foswiki::Render::_replaceBlock which was called 20 times, avg 7µs/call: # 20 times (137µs+0s) by Foswiki::Render::_takeOutProtected at line 1677, avg 7µs/call
sub _replaceBlock {
16832028µs my ( $scoop, $id, $map ) = @_;
1684205µs my $placeholder = $placeholderMarker;
1685204µs $placeholderMarker++;
16862058µs $map->{ $id . $placeholder }{text} = $scoop;
1687
16882075µs return '<!--' . $REMARKER . $id . $placeholder . $REMARKER . '-->';
1689}
1690
1691# _putBackProtected( \$text, $id, \%map, $callback ) -> $text
1692# Return value: $text with blocks added back
1693# * =\$text= - reference to text to process
1694# * =$id= - type of taken-out block e.g. 'verbatim'
1695# * =\%map= - map placeholders to blocks removed by takeOutBlocks
1696# * =$callback= - Reference to function to call on each block being inserted (optional)
1697#
1698#Reverses the actions of takeOutProtected.
1699
# spent 708µs (656+52) within Foswiki::Render::_putBackProtected which was called 30 times, avg 24µs/call: # 5 times (274µs+6µs) by Foswiki::Render::getRenderedVersion at line 1442, avg 56µs/call # 5 times (238µs+32µs) by Foswiki::Render::getRenderedVersion at line 1455, avg 54µs/call # 5 times (47µs+3µs) by Foswiki::Render::getRenderedVersion at line 1460, avg 10µs/call # 5 times (44µs+3µs) by Foswiki::Render::getRenderedVersion at line 1461, avg 10µs/call # 5 times (30µs+4µs) by Foswiki::Render::getRenderedVersion at line 1457, avg 7µs/call # 5 times (22µs+3µs) by Foswiki::Render::getRenderedVersion at line 1462, avg 5µs/call
sub _putBackProtected {
17003023µs my ( $this, $text, $id, $map, $callback ) = @_;
17013029µs3025µs ASSERT( ref($map) eq 'HASH' ) if DEBUG;
# spent 25µs making 30 calls to Assert::ASSERTS_OFF, avg 840ns/call
1702
170330136µs foreach my $placeholder ( keys %$map ) {
170467111µs next unless $placeholder =~ /^$id\d+$/;
17052025µs my $val = $map->{$placeholder}{text};
17062023µs1227µs $val = &$callback($val) if ( defined($callback) );
# spent 27µs making 12 calls to Foswiki::Render::_filterScript, avg 2µs/call
170720256µs $$text =~ s/<!--$REMARKER$placeholder$REMARKER-->/$val/;
17082034µs delete( $map->{$placeholder} );
1709 }
1710}
1711
1712=begin TML
1713
1714---++ ObjectMethod renderRevisionInfo($topicObject, $rev, $format) -> $string
1715
1716Obtain and render revision info for a topic.
1717 * =$topicObject= - the topic
1718 * =$rev= - the rev number, defaults to latest rev
1719 * =$format= - the render format, defaults to
1720 =$rev - $time - $wikiusername=. =$format= can contain
1721 the following keys for expansion:
1722 | =$web= | the web name |
1723 | =$topic= | the topic name |
1724 | =$rev= | the rev number |
1725 | =$username= | the login of the saving user |
1726 | =$wikiname= | the wikiname of the saving user |
1727 | =$wikiusername= | the web.wikiname of the saving user |
1728 | =$date= | the date of the rev (no time) |
1729 | =$time= | the time of the rev |
1730 | =$min=, =$sec=, etc. | Same date format qualifiers as GMTIME |
1731
1732=cut
1733
1734
# spent 878µs (218+660) within Foswiki::Render::renderRevisionInfo which was called 3 times, avg 293µs/call: # 3 times (218µs+660µs) by Foswiki::REVINFO at line 38 of /var/www/foswiki11/lib/Foswiki/Macros/REVINFO.pm, avg 293µs/call
sub renderRevisionInfo {
173535µs my ( $this, $topicObject, $rrev, $format ) = @_;
173631µs my $value = $format || 'r$rev - $date - $time - $wikiusername';
173735µs337µs $value = Foswiki::expandStandardEscapes($value);
# spent 37µs making 3 calls to Foswiki::expandStandardEscapes, avg 12µs/call
1738
1739 # nop if there are no format tokens
174038µs return $value
1741 unless $value =~
1742/\$(?:year|ye|wikiusername|wikiname|week|we|web|wday|username|tz|topic|time|seconds|sec|rev|rcs|month|mo|minutes|min|longdate|isotz|iso|http|hours|hou|epoch|email|dow|day|date)/x;
1743
174433µs my $users = $this->{session}->{users};
17453700ns if ($rrev) {
1746 my $loadedRev = $topicObject->getLoadedRev() || 0;
1747 unless ( $rrev == $loadedRev ) {
1748 $topicObject = Foswiki::Meta->new($topicObject);
1749 $topicObject = $topicObject->load($rrev);
1750 }
1751 }
1752311µs367µs my $info = $topicObject->getRevisionInfo();
# spent 67µs making 3 calls to Foswiki::Meta::getRevisionInfo, avg 22µs/call
1753
175432µs my $wun = '';
17553500ns my $wn = '';
175631µs my $un = '';
175732µs if ( $info->{author} ) {
175836µs395µs my $cUID = $users->getCanonicalUserID( $info->{author} );
# spent 95µs making 3 calls to Foswiki::Users::getCanonicalUserID, avg 32µs/call
1759
1760#pre-set cuid if author is the unknown user from the basemapper (ie, default value) to avoid further guesswork
176132µs $cUID = $info->{author}
1762 if ( $info->{author} eq
1763 $Foswiki::Users::BaseUserMapping::UNKNOWN_USER_CUID );
17643700ns if ( !$cUID ) {
1765 my $ln = $users->getLoginName( $info->{author} );
1766 $cUID = $info->{author}
1767 if ( defined($ln) and ( $ln ne 'unknown' ) );
1768 }
176931µs if ($cUID) {
177039µs3113µs $wun = $users->webDotWikiName($cUID);
# spent 113µs making 3 calls to Foswiki::Users::webDotWikiName, avg 38µs/call
177134µs37µs $wn = $users->getWikiName($cUID);
# spent 7µs making 3 calls to Foswiki::Users::getWikiName, avg 2µs/call
177237µs3105µs $un = $users->getLoginName($cUID);
# spent 105µs making 3 calls to Foswiki::Users::getLoginName, avg 35µs/call
1773 }
1774
1775 #only do the legwork if we really have to
177634µs if ( not( defined($wun) and defined($wn) and defined($un) )
1777 or ( ( $wun eq '' ) or ( $wn eq '' ) or ( $un eq '' ) ) )
1778 {
1779 my $user = $info->{author};
1780
1781 # If we are still unsure, then use whatever is saved in the meta.
1782 # But obscure it if the RenderLoggedInButUnknownUsers is enabled.
1783 if ( $Foswiki::cfg{RenderLoggedInButUnknownUsers} ) {
1784 $user = $info->{author} = 'unknown';
1785 }
1786 else {
1787
1788 #cUID's are forced to ascii by escaping other chars..
1789 #$cUID =~ s/([^a-zA-Z0-9])/'_'.sprintf('%02x', ord($1))/ge;
1790
1791#remove any SomeMapping_ prefix from the cuid - as that initial '_' is not escaped.
1792 $user =~ s/^[A-Z][A-Za-z]+Mapping_//;
1793
1794 #and then xform any escaped chars.
1795266µs229µs
# spent 24µs (19+5) within Foswiki::Render::BEGIN@1795 which was called: # once (19µs+5µs) by Foswiki::renderer at line 1795
use bytes;
# spent 24µs making 1 call to Foswiki::Render::BEGIN@1795 # spent 5µs making 1 call to bytes::import
1796 $user =~ s/_([0-9a-f][0-9a-f])/chr(hex($1))/ge;
179721.98ms224µs
# spent 18µs (13+5) within Foswiki::Render::BEGIN@1797 which was called: # once (13µs+5µs) by Foswiki::renderer at line 1797
no bytes;
# spent 18µs making 1 call to Foswiki::Render::BEGIN@1797 # spent 5µs making 1 call to bytes::unimport
1798 }
1799 $wun ||= $user;
1800 $wn ||= $user;
1801 $un ||= $user;
1802 }
1803 }
1804
180533µs $value =~ s/\$web/$topicObject->web() || ''/ge;
180632µs $value =~ s/\$topic\(([^\)]*)\)/
1807 Foswiki::Render::breakName( $topicObject->topic(), $1 )/ge;
180832µs $value =~ s/\$topic/$topicObject->topic() || ''/ge;
1809310µs $value =~ s/\$rev/$info->{version}/g;
181036µs141µs $value =~ s/\$time/
# spent 41µs making 1 call to Foswiki::Time::formatTime
1811 Foswiki::Time::formatTime($info->{date}, '$hour:$min:$sec')/ge;
1812316µs396µs $value =~ s/\$date/
# spent 96µs making 3 calls to Foswiki::Time::formatTime, avg 32µs/call
1813 Foswiki::Time::formatTime(
1814 $info->{date}, $Foswiki::cfg{DefaultDateFormat} )/ge;
1815319µs $value =~ s/(\$(rcs|longdate|isotz|iso|http|email|))/
1816813µs8100µs Foswiki::Time::formatTime($info->{date}, $1 )/ge;
# spent 100µs making 8 calls to Foswiki::Time::formatTime, avg 12µs/call
1817
181837µs if ( $value =~
1819/\$(?:year|ye|week|we|web|wday|username|tz|seconds|sec|rcs|month|mo|minutes|min|longdate|hours|hou|epoch|dow|day)/
1820 )
1821 {
1822 $value = Foswiki::Time::formatTime( $info->{date}, $value );
1823 }
182432µs $value =~ s/\$username/$un/g;
182538µs $value =~ s/\$wikiname/$wn/g;
182631µs $value =~ s/\$wikiusername/$wun/g;
1827
1828315µs return $value;
1829}
1830
1831=begin TML
1832
1833---++ ObjectMethod forEachLine( $text, \&fn, \%options ) -> $newText
1834
1835Iterate over each line, calling =\&fn= on each.
1836\%options may contain:
1837 * =pre= => true, will call fn for each line in pre blocks
1838 * =verbatim= => true, will call fn for each line in verbatim blocks
1839 * =literal= => true, will call fn for each line in literal blocks
1840 * =noautolink= => true, will call fn for each line in =noautolink= blocks
1841The spec of \&fn is =sub fn( $line, \%options ) -> $newLine=. The %options
1842hash passed into this function is passed down to the sub, and the keys
1843=in_literal=, =in_pre=, =in_verbatim= and =in_noautolink= are set boolean
1844TRUE if the line is from one (or more) of those block types.
1845
1846The return result replaces $line in $newText.
1847
1848=cut
1849
1850
# spent 4.67ms (1.61+3.06) within Foswiki::Render::forEachLine which was called 5 times, avg 934µs/call: # 5 times (1.61ms+3.06ms) by Foswiki::__ANON__[/var/www/foswiki11/lib/Foswiki/Macros/INCLUDE.pm:331] at line 317 of /var/www/foswiki11/lib/Foswiki/Macros/INCLUDE.pm, avg 934µs/call
sub forEachLine {
1851511µs my ( $this, $text, $fn, $options ) = @_;
1852
185352µs return '' unless defined $text;
1854
185557µs $options->{in_pre} = 0;
185652µs $options->{in_pre} = 0;
185754µs $options->{in_verbatim} = 0;
185854µs $options->{in_literal} = 0;
185953µs $options->{in_noautolink} = 0;
186054µs my $newText = '';
18615363µs foreach my $line ( split( /([\r\n]+)/, $text ) ) {
1862122354µs if ( $line =~ /[\r\n]/ ) {
18635913µs $newText .= $line;
18645916µs next;
1865 }
18666370µs $options->{in_verbatim}++ if ( $line =~ m|^\s*<verbatim\b[^>]*>\s*$|i );
18676331µs $options->{in_verbatim}-- if ( $line =~ m|^\s*</verbatim>\s*$|i );
18686330µs $options->{in_literal}++ if ( $line =~ m|^\s*<literal\b[^>]*>\s*$|i );
18696333µs $options->{in_literal}-- if ( $line =~ m|^\s*</literal>\s*$|i );
18706359µs unless ( ( $options->{in_verbatim} > 0 )
1871 || ( ( $options->{in_literal} > 0 ) ) )
1872 {
18736376µs $options->{in_pre}++ if ( $line =~ m|<pre\b|i );
18746376µs $options->{in_pre}-- if ( $line =~ m|</pre>|i );
18756335µs $options->{in_noautolink}++
1876 if ( $line =~ m|^\s*<noautolink\b[^>]*>\s*$|i );
18776331µs $options->{in_noautolink}--
1878 if ( $line =~ m|^\s*</noautolink>\s*|i );
1879 }
188063189µs633.06ms unless ( $options->{in_pre} > 0 && !$options->{pre}
# spent 3.06ms making 63 calls to Foswiki::_fixupIncludedTopic, avg 49µs/call
1881 || $options->{in_verbatim} > 0 && !$options->{verbatim}
1882 || $options->{in_literal} > 0 && !$options->{literal}
1883 || $options->{in_noautolink} > 0 && !$options->{noautolink} )
1884 {
1885 $line = &$fn( $line, $options );
1886 }
18876377µs $newText .= $line;
1888 }
1889533µs return $newText;
1890}
1891
1892=begin TML
1893
1894---++ StaticMethod getReferenceRE($web, $topic, %options) -> $re
1895
1896 * $web, $topic - specify the topic being referred to, or web if $topic is
1897 undef.
1898 * %options - the following options are available
1899 * =interweb= - if true, then fully web-qualified references are required.
1900 * =grep= - if true, generate a GNU-grep compatible RE instead of the
1901 default Perl RE.
1902 * =nosot= - If true, do not generate "Spaced out text" match
1903 * =template= - If true, match for template setting in Set/Local statement
1904 * =in_noautolink= - Only match explicit (squabbed) WikiWords. Used in <noautolink> blocks
1905 * =inMeta= - Re should match exact string. No delimiters needed.
1906 * =url= - if set, generates an expression that will match a Foswiki
1907 URL that points to the web/topic, instead of the default which
1908 matches topic links in plain text.
1909Generate a regular expression that can be used to match references to the
1910specified web/topic. Note that the resultant RE will only match fully
1911qualified (i.e. with web specifier) topic names and topic names that
1912are wikiwords in text. Works for spaced-out wikiwords for topic names.
1913
1914The RE returned is designed to be used with =s///=
1915
1916=cut
1917
1918sub getReferenceRE {
1919 my ( $web, $topic, %options ) = @_;
1920
1921 my $matchWeb = $web;
1922
1923 # Convert . and / to [./] (subweb separators) and quote
1924 # special characters
1925 $matchWeb =~ s#[./]#$REMARKER#g;
1926 $matchWeb = quotemeta($matchWeb);
1927
1928# SMELL: Item10176 - Adding doublequote as a WikiWord delimiter. This causes non-linking quoted
1929# WikiWords in tml to be incorrectly renamed. But does handle quoted topic names inside macro parameters.
1930# But this doesn't really fully fix the issue - $quotWikiWord for example.
1931 my $reSTARTWW = qr/^|(?<=[\s"\*=_\(])/m;
1932 my $reENDWW = qr/$|(?=[\s"\*#=_,.;:!?)])/m;
1933
1934 # $REMARKER is escaped by quotemeta so we need to match the escape
1935 $matchWeb =~ s#\\$REMARKER#[./]#go;
1936
1937 # Item1468/5791 - Quote special characters
1938 $topic = quotemeta($topic) if defined $topic;
1939
1940 # Note use of \b to match the empty string at the
1941 # edges of a word.
1942 my ( $bow, $eow, $forward, $back ) = ( '\b_?', '_?\b', '?=', '?<=' );
1943 if ( $options{grep} ) {
1944 $bow = '\b_?';
1945 $eow = '_?\b';
1946 $forward = '';
1947 $back = '';
1948 }
1949 my $squabo = "($back\\[\\[)";
1950 my $squabc = "($forward(?:#.*?)?\\][][])";
1951
1952 my $re = '';
1953
1954 if ( $options{url} ) {
1955
1956 # URL fragment. Assume / separator (while . is legal, it's
1957 # undocumented and is not common usage)
1958 $re = "/$web/";
1959 $re .= $topic . $eow if $topic;
1960 }
1961 else {
1962 if ( defined($topic) ) {
1963
1964 my $sot;
1965 unless ( $options{nosot} ) {
1966
1967 # Work out spaced-out version (allows lc first chars on words)
1968 $sot = Foswiki::spaceOutWikiWord( $topic, ' *' );
1969 if ( $sot ne $topic ) {
1970 $sot =~ s/\b([a-zA-Z])/'['.uc($1).lc($1).']'/ge;
1971 }
1972 else {
1973 $sot = undef;
1974 }
1975 }
1976
1977 if ( $options{interweb} ) {
1978
1979 # Require web specifier
1980 if ( $options{grep} ) {
1981 $re = "$bow$matchWeb\\.$topic$eow";
1982 }
1983 elsif ( $options{template} ) {
1984
1985# $1 is used in replace. Can't use lookbehind because of variable length restriction
1986 $re = '('
1987 . $Foswiki::regex{setRegex}
1988 . '(?:VIEW|EDIT)_TEMPLATE\s*=\s*)('
1989 . $matchWeb . '\\.'
1990 . $topic . ')\s*$';
1991 }
1992 elsif ( $options{in_noautolink} ) {
1993 $re = "$squabo$matchWeb\\.$topic$squabc";
1994 }
1995 else {
1996 $re = "$reSTARTWW$matchWeb\\.$topic$reENDWW";
1997 }
1998
1999 # Matching of spaced out topic names.
2000 if ($sot) {
2001
2002 # match spaced out in squabs only
2003 $re .= "|$squabo$matchWeb\\.$sot$squabc";
2004 }
2005 }
2006 else {
2007
2008 # Optional web specifier - but *only* if the topic name
2009 # is a wikiword
2010 if ( $topic =~ /$Foswiki::regex{wikiWordRegex}/ ) {
2011
2012 # Bit of jigger-pokery at the front to avoid matching
2013 # subweb specifiers
2014 if ( $options{grep} ) {
2015 $re = "(($back\[^./])|^)$bow($matchWeb\\.)?$topic$eow";
2016 }
2017 elsif ( $options{template} ) {
2018
2019# $1 is used in replace. Can't use lookbehind because of variable length restriction
2020 $re = '('
2021 . $Foswiki::regex{setRegex}
2022 . '(?:VIEW|EDIT)_TEMPLATE\s*=\s*)'
2023 . "($matchWeb\\.)?$topic" . '\s*$';
2024 }
2025 elsif ( $options{in_noautolink} ) {
2026 $re = "$squabo($matchWeb\\.)?$topic$squabc";
2027 }
2028 else {
2029 $re = "$reSTARTWW($matchWeb\\.)?$topic$reENDWW";
2030 }
2031
2032 if ($sot) {
2033
2034 # match spaced out in squabs only
2035 $re .= "|$squabo($matchWeb\\.)?$sot$squabc";
2036 }
2037 }
2038 else {
2039 if ( $options{inMeta} ) {
2040 $re = "^($matchWeb\\.)?$topic\$"
2041 ; # Updating a META item, Exact match, no delimiters
2042 }
2043 else {
2044
2045 # Non-wikiword; require web specifier or squabs
2046 $re = "$squabo$topic$squabc"; # Squabbed topic
2047 $re .= "|\"($matchWeb\\.)?$topic\""
2048 ; # Quoted string in Meta and Macros
2049 $re .= "|(($back\[^./])|^)$bow$matchWeb\\.$topic$eow"
2050 unless ( $options{in_noautolink} )
2051 ; # Web qualified topic outside of autolink blocks.
2052 }
2053 }
2054 }
2055 }
2056 else {
2057
2058 # Searching for a web
2059 # SMELL: Does this web search also need to allow for quoted
2060 # "Web.Topic" strings found in macros and META usage?
2061
2062 if ( $options{interweb} ) {
2063
2064 if ( $options{in_noautolink} ) {
2065
2066 # web name used to refer to a topic
2067 $re =
2068 $squabo
2069 . $matchWeb
2070 . "(\.[$Foswiki::regex{mixedAlphaNum}]+)"
2071 . $squabc;
2072 }
2073 else {
2074 $re =
2075 $bow
2076 . $matchWeb
2077 . "(\.[$Foswiki::regex{mixedAlphaNum}]+)"
2078 . $eow;
2079 }
2080 }
2081 else {
2082
2083 # most general search for a reference to a topic or subweb
2084 # note that Foswiki::UI::Rename::_replaceWebReferences()
2085 # uses $1 from this regex
2086 if ( $options{in_noautolink} ) {
2087 $re =
2088 $squabo
2089 . $matchWeb
2090 . "(([\/\.][$Foswiki::regex{upperAlpha}]"
2091 . "[$Foswiki::regex{mixedAlphaNum}_]*)+"
2092 . "\.[$Foswiki::regex{mixedAlphaNum}]*)"
2093 . $squabc;
2094 }
2095 else {
2096 $re =
2097 $bow
2098 . $matchWeb
2099 . "(([\/\.][$Foswiki::regex{upperAlpha}]"
2100 . "[$Foswiki::regex{mixedAlphaNum}_]*)+"
2101 . "\.[$Foswiki::regex{mixedAlphaNum}]*)"
2102 . $eow;
2103 }
2104 }
2105 }
2106 }
2107
2108#my $optsx = '';
2109#$optsx .= "NOSOT=$options{nosot} " if ($options{nosot});
2110#$optsx .= "GREP=$options{grep} " if ($options{grep});
2111#$optsx .= "URL=$options{url} " if ($options{url});
2112#$optsx .= "INNOAUTOLINK=$options{in_noautolink} " if ($options{in_noautolink});
2113#$optsx .= "INTERWEB=$options{interweb} " if ($options{interweb});
2114#print STDERR "ReferenceRE returns $re $optsx \n";
2115 return $re;
2116}
2117
2118=begin TML
2119
2120---++ StaticMethod breakName( $text, $args) -> $text
2121
2122 * =$text= - text to "break"
2123 * =$args= - string of format (\d+)([,\s*]\.\.\.)?)
2124Hyphenates $text every $1 characters, or if $2 is "..." then shortens to
2125$1 characters and appends "..." (making the final string $1+3 characters
2126long)
2127
2128_Moved from Search.pm because it was obviously unhappy there,
2129as it is a rendering function_
2130
2131=cut
2132
2133sub breakName {
2134 my ( $text, $args ) = @_;
2135
2136 my @params = split( /[\,\s]+/, $args, 2 );
2137 if (@params) {
2138 my $len = $params[0] || 1;
2139 $len = 1 if ( $len < 1 );
2140 my $sep = '- ';
2141 $sep = $params[1] if ( @params > 1 );
2142 if ( $sep =~ /^\.\.\./i ) {
2143
2144 # make name shorter like 'ThisIsALongTop...'
2145 $text =~ s/(.{$len})(.+)/$1.../s;
2146
2147 }
2148 else {
2149
2150 # split and hyphenate the topic like 'ThisIsALo- ngTopic'
2151 $text =~ s/(.{$len})/$1$sep/gs;
2152 $text =~ s/$sep$//;
2153 }
2154 }
2155 return $text;
2156}
2157
2158=begin TML
2159
2160---++ StaticMethod protectFormFieldValue($value, $attrs) -> $html
2161
2162Given the value of a form field, and a set of attributes that control how
2163to display that value, protect the value from further processing.
2164
2165The protected value is determined from the value of the field after:
2166 * newlines are replaced with &lt;br> or the value of $attrs->{newline}
2167 * processing through breakName if $attrs->{break} is defined
2168 * escaping of $vars if $attrs->{protectdollar} is defined
2169 * | is replaced with &amp;#124; or the value of $attrs->{bar} if defined
2170
2171=cut
2172
2173sub protectFormFieldValue {
2174 my ( $value, $attrs ) = @_;
2175
2176 $value = '' unless defined($value);
2177
2178 if ( $attrs && $attrs->{break} ) {
2179 $value =~ s/^\s*(.*?)\s*$/$1/g;
2180 $value = breakName( $value, $attrs->{break} );
2181 }
2182
2183 # Item3489, Item2837. Prevent $vars in formfields from
2184 # being expanded in formatted searches.
2185 if ( $attrs && $attrs->{protectdollar} ) {
2186 $value =~ s/\$(n|nop|quot|percnt|dollar)/\$<nop>$1/g;
2187 }
2188
2189 # change newlines
2190 my $newline = '<br />';
2191 if ( $attrs && defined $attrs->{newline} ) {
2192 $newline = $attrs->{newline};
2193 $newline =~ s/\$n/\n/gs;
2194 }
2195 $value =~ s/\r?\n/$newline/gs;
2196
2197 # change vbars
2198 my $bar = '&#124;';
2199 if ( $attrs && $attrs->{bar} ) {
2200 $bar = $attrs->{bar};
2201 }
2202 $value =~ s/\|/$bar/g;
2203
2204 return $value;
2205}
2206
2207=begin TML
2208
2209---++ ObjectMethod getAnchors( $topicObject ) -> $set
2210
2211Get the anchor name set generated for the given topic. This is so that the
2212same anchor names can be generated for each time the same topic is
2213%INCLUDEd (the same anchor target will be generated for each time the
2214topic is included.
2215
2216Note that anchor names generated this way are unique since the last time the
2217anchor set is cleared, which happens (1) whenever a new session is started
2218and (2) whenever a new %TOC macro is rendered (see Foswiki/Macros/TOC).
2219
2220Returns an object of type Foswiki::Render::Anchors.
2221
2222=cut
2223
2224
# spent 109µs (69+40) within Foswiki::Render::getAnchorNames which was called 5 times, avg 22µs/call: # 5 times (69µs+40µs) by Foswiki::Render::getRenderedVersion at line 1255, avg 22µs/call
sub getAnchorNames {
222556µs my ( $this, $topicObject ) = @_;
2226515µs531µs my $id = $topicObject->getPath();
# spent 31µs making 5 calls to Foswiki::Meta::getPath, avg 6µs/call
222756µs my $a = $this->{_anchorNames}{$id};
222852µs unless ($a) {
222914µs110µs $a = new Foswiki::Render::Anchors();
# spent 10µs making 1 call to Foswiki::Render::Anchors::new
223011µs $this->{_anchorNames}{$id} = $a;
2231 }
2232515µs return $a;
2233}
2234
2235=begin TML
2236
2237---++ ObjectMethod renderIconImage($url [, $alt]) -> $html
2238Generate the output for representing an 16x16 icon image. The source of
2239the image is taken from =$url=. The optional =$alt= specifies an alt string.
2240
2241re-written using TMPL:DEF{icon:image} in Foswiki.tmpl
2242%TMPL:DEF{"icon:image"}%<span class='foswikiIcon'><img src="%URL%" width="%WIDTH%" height="%HEIGHT%" alt="%ALT%" /></span>%TMPL:END%
2243see System.SkinTemplates:base.css for the default of .foswikiIcon img
2244
2245TODO: Sven's not sure this code belongs here - its only use appears to be the ICON macro
2246
2247=cut
2248
2249sub renderIconImage {
2250 my ( $this, $url, $alt ) = @_;
2251
2252 if ( !defined($alt) ) {
2253
2254 #yes, you really should have a useful alt text.
2255 $alt = $url;
2256 }
2257
2258 my $html = $this->{session}->templates->expandTemplate("icon:image");
2259 $html =~ s/%URL%/$url/ge;
2260 $html =~ s/%WIDTH%/16/g;
2261 $html =~ s/%HEIGHT%/16/g;
2262 $html =~ s/%ALT%/$alt/ge;
2263
2264 return $html;
2265}
2266
2267110µs1;
2268__END__