Filename | /var/www/foswiki11/lib/Foswiki/Render.pm |
Statements | Executed 7057 statements in 38.6ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
5 | 1 | 1 | 19.6ms | 61.4ms | getRenderedVersion | Foswiki::Render::
5 | 1 | 1 | 1.61ms | 4.67ms | forEachLine | Foswiki::Render::
221 | 3 | 1 | 1.33ms | 1.33ms | _addListItem | Foswiki::Render::
30 | 6 | 1 | 1.12ms | 1.26ms | _takeOutProtected | Foswiki::Render::
18 | 1 | 1 | 1.03ms | 7.11ms | _handleSquareBracketedLink | Foswiki::Render::
1 | 1 | 1 | 948µs | 1.02ms | BEGIN@19 | Foswiki::Render::
30 | 6 | 1 | 656µs | 708µs | _putBackProtected | Foswiki::Render::
17 | 1 | 1 | 520µs | 5.23ms | internalLink | Foswiki::Render::
17 | 1 | 1 | 484µs | 2.80ms | _renderExistingWikiWord | Foswiki::Render::
18 | 1 | 1 | 320µs | 320µs | _escapeAutoLinks | Foswiki::Render::
17 | 1 | 1 | 218µs | 4.56ms | _renderWikiWord | Foswiki::Render::
3 | 1 | 1 | 218µs | 878µs | renderRevisionInfo | Foswiki::Render::
20 | 1 | 1 | 137µs | 137µs | _replaceBlock | Foswiki::Render::
17 | 1 | 1 | 94µs | 134µs | _linkToolTipInfo | Foswiki::Render::
18 | 1 | 1 | 92µs | 92µs | _isImageLink | Foswiki::Render::
5 | 1 | 1 | 69µs | 109µs | getAnchorNames | Foswiki::Render::
14 | 1 | 1 | 35µs | 35µs | _filterLiteral | Foswiki::Render::
12 | 1 | 1 | 27µs | 27µs | _filterScript | Foswiki::Render::
1 | 1 | 1 | 26µs | 44µs | renderParent | Foswiki::Render::
1 | 1 | 1 | 19µs | 24µs | BEGIN@1795 | Foswiki::Render::
1 | 1 | 1 | 18µs | 34µs | BEGIN@12 | Foswiki::Render::
1 | 1 | 1 | 18µs | 82µs | BEGIN@81 | Foswiki::Render::
1 | 1 | 1 | 14µs | 23µs | BEGIN@92 | Foswiki::Render::
1 | 1 | 1 | 13µs | 18µs | BEGIN@1797 | Foswiki::Render::
1 | 1 | 1 | 12µs | 12µs | new | Foswiki::Render::
1 | 1 | 1 | 11µs | 18µs | BEGIN@13 | Foswiki::Render::
1 | 1 | 1 | 10µs | 26µs | BEGIN@14 | Foswiki::Render::
1 | 1 | 1 | 10µs | 140µs | BEGIN@15 | Foswiki::Render::
1 | 1 | 1 | 8µs | 8µs | _externalLink | Foswiki::Render::
1 | 1 | 1 | 8µs | 8µs | finish | Foswiki::Render::
1 | 1 | 1 | 5µs | 5µs | BEGIN@17 | Foswiki::Render::
1 | 1 | 1 | 4µs | 4µs | BEGIN@18 | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | TML2PlainText | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _addTHEADandTFOOT | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _emitTR | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _escape | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _fixedFontText | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _handleWikiWord | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _mailLink | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _makeAnchorHeading | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _newLinkFormat | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _renderNonExistingWikiWord | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | breakName | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | getReferenceRE | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | makeTopicSummary | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | protectFormFieldValue | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | protectPlainText | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | renderFORMFIELD | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | renderIconImage | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | renderMoved | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | verbatimCallBack | Foswiki::Render::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | package Foswiki::Render; | ||||
3 | |||||
4 | =begin TML | ||||
5 | |||||
6 | ---+ package Foswiki::Render | ||||
7 | |||||
8 | This module provides most of the actual HTML rendering code in Foswiki. | ||||
9 | |||||
10 | =cut | ||||
11 | |||||
12 | 2 | 35µs | 2 | 50µ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 # spent 34µs making 1 call to Foswiki::Render::BEGIN@12
# spent 16µs making 1 call to strict::import |
13 | 2 | 30µs | 2 | 24µ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 # spent 18µs making 1 call to Foswiki::Render::BEGIN@13
# spent 6µs making 1 call to warnings::import |
14 | 2 | 32µs | 2 | 42µ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 # spent 26µs making 1 call to Foswiki::Render::BEGIN@14
# spent 16µs making 1 call to Assert::import |
15 | 2 | 35µs | 2 | 270µ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 # spent 140µs making 1 call to Foswiki::Render::BEGIN@15
# spent 130µs making 1 call to Error::import |
16 | |||||
17 | 2 | 24µs | 1 | 5µs | # spent 5µs within Foswiki::Render::BEGIN@17 which was called:
# once (5µs+0s) by Foswiki::renderer at line 17 # spent 5µs making 1 call to Foswiki::Render::BEGIN@17 |
18 | 2 | 22µs | 1 | 4µs | # spent 4µs within Foswiki::Render::BEGIN@18 which was called:
# once (4µs+0s) by Foswiki::renderer at line 18 # spent 4µs making 1 call to Foswiki::Render::BEGIN@18 |
19 | 2 | 314µs | 1 | 1.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 # 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. | ||||
23 | 1 | 600ns | our $placeholderMarker = 0; | ||
24 | |||||
25 | # Limiting lookbehind and lookahead for wikiwords and emphasis. | ||||
26 | # use like \b | ||||
27 | 1 | 4µs | our $STARTWW = qr/^|(?<=[\s\(])/m; | ||
28 | 1 | 1µs | our $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 | ||||
38 | 1 | 300ns | our $TABLEMARKER = "\0\1\2TABLE\2\1\0"; | ||
39 | |||||
40 | # Marker used to indicate table rows that are valid header/footer rows | ||||
41 | 1 | 300ns | our $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. | ||||
45 | 1 | 300ns | our $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. | ||||
49 | 1 | 200ns | our $REEND = "\1"; | ||
50 | |||||
51 | # Characters that need to be %XX escaped in mailto URIs. | ||||
52 | 1 | 13µs | our %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. | ||||
78 | 1 | 300ns | our $NOPMARK = "\2"; | ||
79 | |||||
80 | # Default format for a link to a non-existant topic | ||||
81 | 1 | 13µs | 1 | 64µ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 # 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> | ||||
83 | 1 | 71µs | 1 | 82µs | NLF # spent 82µs making 1 call to Foswiki::Render::BEGIN@81 |
84 | |||||
85 | 1 | 3µs | my %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 | ||||
93 | |||||
94 | # Do a dynamic 'use locale' for this module | ||||
95 | 1 | 5µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
96 | 1 | 500ns | require locale; | ||
97 | 1 | 6µs | 1 | 9µs | import locale(); # spent 9µs making 1 call to locale::import |
98 | } | ||||
99 | 1 | 8.87ms | 1 | 23µs | } # spent 23µs making 1 call to Foswiki::Render::BEGIN@92 |
100 | |||||
101 | =begin TML | ||||
102 | |||||
103 | ---++ ClassMethod new ($session) | ||||
104 | |||||
105 | Creates 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 | ||||
110 | 1 | 900ns | my ( $class, $session ) = @_; | ||
111 | 1 | 8µs | my $this = bless( { session => $session }, $class ); | ||
112 | |||||
113 | 1 | 7µs | return $this; | ||
114 | } | ||||
115 | |||||
116 | =begin TML | ||||
117 | |||||
118 | ---++ ObjectMethod finish() | ||||
119 | Break 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 | ||||
127 | 1 | 600ns | my $this = shift; | ||
128 | 1 | 700ns | undef $this->{NEWLINKFORMAT}; | ||
129 | 1 | 1µs | undef $this->{LINKTOOLTIPINFO}; | ||
130 | 1 | 800ns | undef $this->{LIST}; | ||
131 | 1 | 900ns | undef $this->{ffCache}; | ||
132 | 1 | 4µs | undef $this->{session}; | ||
133 | } | ||||
134 | |||||
135 | sub _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 | |||||
149 | Render 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 | ||||
154 | 1 | 700ns | my ( $this, $topicObject, $ah ) = @_; | ||
155 | 1 | 800ns | my $dontRecurse = $ah->{dontrecurse} || 0; | ||
156 | 1 | 300ns | my $depth = $ah->{depth} || 0; | ||
157 | 1 | 600ns | my $noWebHome = $ah->{nowebhome} || 0; | ||
158 | 1 | 400ns | my $prefix = $ah->{prefix} || ''; | ||
159 | 1 | 400ns | my $suffix = $ah->{suffix} || ''; | ||
160 | 1 | 500ns | my $usesep = $ah->{separator} || ' > '; | ||
161 | 1 | 300ns | my $format = $ah->{format} || '[[$web.$topic][$topic]]'; | ||
162 | |||||
163 | 1 | 4µs | 2 | 5µ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 |
164 | 1 | 300ns | return '' unless $web && $topic; | ||
165 | |||||
166 | 1 | 200ns | my %visited; | ||
167 | 1 | 2µs | $visited{ $web . '.' . $topic } = 1; | ||
168 | |||||
169 | 1 | 300ns | my $pWeb = $web; | ||
170 | 1 | 100ns | my $pTopic; | ||
171 | 1 | 300ns | my $text = ''; | ||
172 | 1 | 4µs | 1 | 13µs | my $parentMeta = $topicObject->get('TOPICPARENT'); # spent 13µs making 1 call to Foswiki::Meta::get |
173 | 1 | 100ns | my $parent; | ||
174 | |||||
175 | 1 | 200ns | $parent = $parentMeta->{name} if $parentMeta; | ||
176 | |||||
177 | 1 | 300ns | my @stack; | ||
178 | 1 | 200ns | my $currentDepth = 0; | ||
179 | 1 | 100ns | $depth = 1 if $dontRecurse; | ||
180 | |||||
181 | 1 | 500ns | 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 | } | ||||
207 | 1 | 1µs | $text = join( $usesep, @stack ); | ||
208 | |||||
209 | 1 | 200ns | if ($text) { | ||
210 | $text = $prefix . $text if ($prefix); | ||||
211 | $text .= $suffix if ($suffix); | ||||
212 | } | ||||
213 | |||||
214 | 1 | 5µs | return $text; | ||
215 | } | ||||
216 | |||||
217 | =begin TML | ||||
218 | |||||
219 | ---++ ObjectMethod renderMoved($topicObject, $params) -> $text | ||||
220 | |||||
221 | Render moved meta-data | ||||
222 | |||||
223 | =cut | ||||
224 | |||||
225 | sub 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 | ||||
278 | 221 | 193µs | my ( $this, $result, $type, $element, $indent ) = @_; | ||
279 | |||||
280 | 221 | 142µs | $indent =~ s/ /\t/g; | ||
281 | 221 | 63µs | my $depth = length($indent); | ||
282 | |||||
283 | 221 | 104µs | my $size = scalar( @{ $this->{LIST} } ); | ||
284 | |||||
285 | # The whitespaces either side of the tags are required for the | ||||
286 | # emphasis REs to work. | ||||
287 | 221 | 73µs | if ( $size < $depth ) { | ||
288 | 7 | 2µs | my $firstTime = 1; | ||
289 | 7 | 5µs | while ( $size < $depth ) { | ||
290 | 7 | 19µs | push( @{ $this->{LIST} }, { type => $type, element => $element } ); | ||
291 | 7 | 2µs | push @$result, ' <' . $element . ">\n" unless ($firstTime); | ||
292 | 7 | 8µs | push @$result, ' <' . $type . ">\n"; | ||
293 | 7 | 1µs | $firstTime = 0; | ||
294 | 7 | 4µs | $size++; | ||
295 | } | ||||
296 | } | ||||
297 | else { | ||||
298 | 214 | 93µs | while ( $size > $depth ) { | ||
299 | 7 | 6µs | my $tags = pop( @{ $this->{LIST} } ); | ||
300 | 7 | 12µs | push @$result, | ||
301 | "\n</" . $tags->{element} . '></' . $tags->{type} . '> '; | ||||
302 | 7 | 11µs | $size--; | ||
303 | } | ||||
304 | 214 | 148µs | if ($size) { | ||
305 | push @$result, | ||||
306 | "\n</" . $this->{LIST}->[ $size - 1 ]->{element} . '> '; | ||||
307 | } | ||||
308 | else { | ||||
309 | 161 | 81µs | push @$result, "\n"; | ||
310 | } | ||||
311 | } | ||||
312 | |||||
313 | 221 | 584µs | if ($size) { | ||
314 | 60 | 38µs | my $oldt = $this->{LIST}->[ $size - 1 ]; | ||
315 | 60 | 43µ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 | ||||
325 | sub _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 | |||||
396 | sub _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+$/ /; | ||||
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 | |||||
442 | sub _fixedFontText { | ||||
443 | my ( $text, $embolden ) = @_; | ||||
444 | |||||
445 | # preserve white space, so replace it by ' ' patterns | ||||
446 | $text =~ s/\t/ /g; | ||||
447 | $text =~ s|((?:[\s]{2})+)([^\s])|' ' x (length($1) / 2) . $2|eg; | ||||
448 | $text = CGI->b($text) if $embolden; | ||||
449 | return CGI->code($text); | ||||
450 | } | ||||
451 | |||||
452 | # Build an HTML <Hn> element with suitable anchor for linking | ||||
453 | # from %<nop>TOC% | ||||
454 | sub _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 | ||||
475 | 17 | 14µs | my ( $this, $web, $topic ) = @_; | ||
476 | 17 | 10µs | unless ( defined( $this->{LINKTOOLTIPINFO} ) ) { | ||
477 | 1 | 2µs | 1 | 40µs | $this->{LINKTOOLTIPINFO} = # spent 40µs making 1 call to Foswiki::Prefs::getPreference |
478 | $this->{session}->{prefs}->getPreference('LINKTOOLTIPINFO') | ||||
479 | || ''; | ||||
480 | 1 | 2µs | $this->{LINKTOOLTIPINFO} = '$username - $date - r$rev: $summary' | ||
481 | if ( 'on' eq lc( $this->{LINKTOOLTIPINFO} ) ); | ||||
482 | } | ||||
483 | 17 | 5µs | return '' unless ( $this->{LINKTOOLTIPINFO} ); | ||
484 | 17 | 85µ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 | |||||
532 | Generate a link. | ||||
533 | |||||
534 | Note: Topic names may be spaced out. Spaced out names are converted | ||||
535 | to <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 | |||||
547 | Called from outside the package by Func::internalLink | ||||
548 | |||||
549 | Calls _renderWikiWord, which in turn will use Plurals.pm to match fold | ||||
550 | plurals to equivalency with their singular form | ||||
551 | |||||
552 | SMELL: 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 | ||||
557 | 17 | 26µ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 | ||||
564 | 17 | 10µs | $web =~ s/\/\Z//o; | ||
565 | |||||
566 | 17 | 7µs | if ( $linkText eq $web ) { | ||
567 | $linkText =~ s/\//\./go; | ||||
568 | } | ||||
569 | |||||
570 | #WebHome links to tother webs render as the WebName | ||||
571 | 17 | 10µ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 | ||||
578 | 17 | 26µs | $topic =~ s/^\s*//o; | ||
579 | 17 | 65µ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. | ||||
585 | 17 | 51µs | 17 | 73µ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 | ||||
594 | 17 | 11µs | $topic = ucfirst($topic); | ||
595 | 17 | 27µ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) | ||||
599 | 17 | 30µs | 17 | 76µ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 | ||||
602 | 17 | 102µs | $linkText =~ s/(?<=[\s\(])([$Foswiki::regex{upperAlpha}])/<nop>$1/go; | ||
603 | 17 | 82µs | 17 | 4.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 | ||||
609 | 17 | 25µs | my ( $this, $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix, | ||
610 | $params ) | ||||
611 | = @_; | ||||
612 | 17 | 11µs | my $session = $this->{session}; | ||
613 | 17 | 39µs | 17 | 1.55ms | my $topicExists = $session->topicExists( $web, $topic ); # spent 1.55ms making 17 calls to Foswiki::topicExists, avg 91µs/call |
614 | |||||
615 | 17 | 6µs | my $singular = ''; | ||
616 | 17 | 4µ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 | |||||
627 | 17 | 6µs | if ($topicExists) { | ||
628 | |||||
629 | # add a dependency so that the page gets invalidated as soon as the | ||||
630 | # topic is deleted | ||||
631 | 17 | 15µs | $this->{session}->{cache}->addDependency( $web, $topic ) | ||
632 | if $Foswiki::cfg{Cache}{Enabled}; | ||||
633 | |||||
634 | 17 | 98µs | 17 | 2.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 | ||||
660 | 17 | 22µs | my ( $this, $web, $topic, $text, $anchor, $params ) = @_; | ||
661 | |||||
662 | 17 | 5µs | my @cssClasses; | ||
663 | 17 | 20µs | push( @cssClasses, 'foswikiCurrentWebHomeLink' ) | ||
664 | if ( ( $web eq $this->{session}->{webName} ) | ||||
665 | && ( $topic eq $Foswiki::cfg{HomeTopicName} ) ); | ||||
666 | |||||
667 | 17 | 5µs | my $inCurrentTopic = 0; | ||
668 | |||||
669 | 17 | 12µs | if ( ( $web eq $this->{session}->{webName} ) | ||
670 | && ( $topic eq $this->{session}->{topicName} ) ) | ||||
671 | { | ||||
672 | push( @cssClasses, 'foswikiCurrentTopicLink' ); | ||||
673 | $inCurrentTopic = 1; | ||||
674 | } | ||||
675 | |||||
676 | 17 | 4µs | my %attrs; | ||
677 | 17 | 43µs | 17 | 835µs | my $href = $this->{session}->getScriptUrl( 0, 'view', $web, $topic ); # spent 835µs making 17 calls to Foswiki::getScriptUrl, avg 49µs/call |
678 | 17 | 4µs | if ($params) { | ||
679 | $href .= $params; | ||||
680 | } | ||||
681 | |||||
682 | 17 | 3µ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 | } | ||||
691 | 17 | 14µs | $attrs{class} = join( ' ', @cssClasses ) if ( $#cssClasses >= 0 ); | ||
692 | 17 | 25µs | $attrs{href} = $href; | ||
693 | 17 | 37µs | 17 | 134µs | my $tooltip = _linkToolTipInfo( $this, $web, $topic ); # spent 134µs making 17 calls to Foswiki::Render::_linkToolTipInfo, avg 8µs/call |
694 | 17 | 3µs | $attrs{title} = $tooltip if $tooltip; | ||
695 | |||||
696 | 17 | 35µs | 17 | 375µ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 |
697 | 17 | 44µs | 17 | 709µs | my $link = CGI::a( \%attrs, $text ); # spent 709µs making 17 calls to CGI::a, avg 42µs/call |
698 | 17 | 26µs | 17 | 249µ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 <. This is a | ||||
702 | # basic problem with <nop>. | ||||
703 | #$link =~ s/<nop>/<nop>/g; | ||||
704 | 17 | 86µs | return $link; | ||
705 | } | ||||
706 | |||||
707 | sub _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 | ||||
729 | sub _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 | ||||
783 | 18 | 9µs | my $text = shift; | ||
784 | |||||
785 | 18 | 8µs | if ($text) { | ||
786 | |||||
787 | # WikiWords, TLAs, and email addresses | ||||
788 | 18 | 221µ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 | ||||
799 | 18 | 46µs | $text =~ s/($Foswiki::regex{linkProtocolPattern}):(?=\S)/$1<nop>:/go; | ||
800 | } | ||||
801 | 18 | 67µ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 | ||||
808 | 18 | 54µs | my ( $this, $topicObject, $link, $text ) = @_; | ||
809 | |||||
810 | # Strip leading/trailing spaces | ||||
811 | 18 | 24µs | $link =~ s/^\s+//; | ||
812 | 18 | 15µs | $link =~ s/\s+$//; | ||
813 | |||||
814 | 18 | 6µs | my $hasExplicitLinkLabel = 0; | ||
815 | |||||
816 | 18 | 11µs | if ( defined($text) ) { | ||
817 | |||||
818 | # [[$link][$text]] | ||||
819 | 18 | 3µs | $hasExplicitLinkLabel = 1; | ||
820 | 18 | 48µs | 18 | 92µ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 { | ||||
824 | 18 | 39µs | 18 | 320µs | $text = _escapeAutoLinks($text); # spent 320µs making 18 calls to Foswiki::Render::_escapeAutoLinks, avg 18µs/call |
825 | } | ||||
826 | } | ||||
827 | |||||
828 | 18 | 66µ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]] | ||||
832 | 1 | 600ns | 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 | } | ||||
848 | 1 | 6µs | 1 | 8µs | return $this->_externalLink( $link, $text ); # spent 8µs making 1 call to Foswiki::Render::_externalLink |
849 | } | ||||
850 | |||||
851 | # Extract '?params' | ||||
852 | # $link =~ s/(\?.*?)(?>#|$)//; | ||||
853 | 17 | 8µs | my $params = ''; | ||
854 | 17 | 9µs | if ( $link =~ s/(\?.*$)// ) { | ||
855 | $params = $1; | ||||
856 | } | ||||
857 | |||||
858 | 17 | 4µs | $text = _escapeAutoLinks($link) unless defined $text; | ||
859 | 17 | 36µs | $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/_fixedFontText($1,1)/gem; | ||
860 | 17 | 27µs | $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S) | ||
861 | __$ENDWW/<strong><em>$1<\/em><\/strong>/gmx; | ||||
862 | 17 | 25µs | $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/<strong>$1<\/strong>/gm; | ||
863 | 17 | 23µs | $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/<em>$1<\/em>/gm; | ||
864 | 17 | 21µ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\-]*$)//; | ||||
868 | 17 | 4µs | my $anchor = ''; | ||
869 | 17 | 29µs | if ( $link =~ s/($Foswiki::regex{anchorRegex}$)// ) { | ||
870 | $anchor = $1; | ||||
871 | |||||
872 | #$text =~ s/#$anchor//; | ||||
873 | } | ||||
874 | |||||
875 | # filter out &any; entities (legacy) | ||||
876 | 17 | 11µs | $link =~ s/\&[a-z]+\;//gi; | ||
877 | |||||
878 | # filter out { entities (legacy) | ||||
879 | 17 | 7µs | $link =~ s/\&\#[0-9]+\;//g; | ||
880 | |||||
881 | # Filter junk | ||||
882 | 17 | 49µs | $link =~ s/$Foswiki::cfg{NameFilter}+/ /g; | ||
883 | |||||
884 | 17 | 37µs | 17 | 36µ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 | ||||
887 | 17 | 14µs | $link = ucfirst($link); | ||
888 | |||||
889 | # Collapse spaces and capitalise following letter | ||||
890 | 17 | 28µ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 | ||||
893 | 17 | 8µ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) | ||||
897 | 17 | 35µs | 17 | 94µs | $link = Foswiki::Sandbox::untaintUnchecked($link); # spent 94µs making 17 calls to Foswiki::Sandbox::untaintUnchecked, avg 6µs/call |
898 | |||||
899 | 17 | 4µs | $link ||= $topicObject->topic; | ||
900 | |||||
901 | # Topic defaults to the current topic | ||||
902 | 17 | 91µs | 34 | 299µ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 | |||||
905 | 17 | 94µs | 17 | 5.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 | ||||
912 | 18 | 12µs | my ( $this, $url ) = @_; | ||
913 | |||||
914 | 18 | 16µs | return if $url =~ /<nop>/; | ||
915 | 18 | 13µs | $url =~ s/^\s+//; | ||
916 | 18 | 14µs | $url =~ s/\s+$//; | ||
917 | 18 | 8µ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 | } | ||||
922 | 18 | 75µ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 | ||||
928 | 1 | 1µs | my ( $this, $url, $text ) = @_; | ||
929 | |||||
930 | 1 | 200ns | if ( !$text && ( my $img = $this->_isImageLink($url) ) ) { | ||
931 | return $img; | ||||
932 | } | ||||
933 | 1 | 400ns | my $opt = ''; | ||
934 | 1 | 1µ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; # "{" | ||||
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 { | ||||
962 | 1 | 600ns | $opt = ' target="_top"'; | ||
963 | } | ||||
964 | 1 | 200ns | $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. | ||||
968 | 1 | 500ns | $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! | ||||
974 | 1 | 6µs | return '<a href="' . $url . '"' . $opt . '>' . $text . '</a>'; | ||
975 | } | ||||
976 | |||||
977 | # Generate a "mailTo" link | ||||
978 | sub _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 | |||||
1003 | sub _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 | |||||
1016 | Returns the fully rendered expansion of a %FORMFIELD{}% tag. | ||||
1017 | |||||
1018 | =cut | ||||
1019 | |||||
1020 | sub 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 | |||||
1100 | The 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 | ||||
1105 | 5 | 26µs | my ( $this, $text, $topicObject ) = @_; | ||
1106 | 5 | 7µs | 5 | 5µs | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; # spent 5µs making 5 calls to Assert::ASSERTS_OFF, avg 1µs/call |
1107 | |||||
1108 | 5 | 2µs | return '' unless defined $text; # nothing to do | ||
1109 | |||||
1110 | 5 | 4µs | my $session = $this->{session}; | ||
1111 | 5 | 2µs | my $plugins = $session->{plugins}; | ||
1112 | 5 | 2µs | my $prefs = $session->{prefs}; | ||
1113 | |||||
1114 | 5 | 4µs | @{ $this->{LIST} } = (); | ||
1115 | |||||
1116 | # Initial cleanup | ||||
1117 | 5 | 49µs | $text =~ s/\r//g; | ||
1118 | |||||
1119 | # whitespace before <! tag (if it is the first thing) is illegal | ||||
1120 | 5 | 12µs | $text =~ s/^\s+(<![a-z])/$1/i; | ||
1121 | |||||
1122 | # clutch to enforce correct rendering at end of doc | ||||
1123 | 5 | 40µs | $text =~ s/\n?$/\n<nop>\n/s; | ||
1124 | |||||
1125 | # Maps of placeholders to tag parameters and text | ||||
1126 | 5 | 4µs | my $removed = {}; | ||
1127 | |||||
1128 | # verbatim before literal - see Item3431 | ||||
1129 | 5 | 11µs | 5 | 169µs | $text = Foswiki::takeOutBlocks( $text, 'verbatim', $removed ); # spent 169µs making 5 calls to Foswiki::takeOutBlocks, avg 34µs/call |
1130 | 5 | 9µs | 5 | 485µs | $text = Foswiki::takeOutBlocks( $text, 'literal', $removed ); # spent 485µs making 5 calls to Foswiki::takeOutBlocks, avg 97µs/call |
1131 | 5 | 6µs | $text = Foswiki::takeOutBlocks( $text, 'dirtyarea', $removed ) | ||
1132 | if $Foswiki::cfg{Cache}{Enabled}; | ||||
1133 | |||||
1134 | 5 | 40µs | 5 | 142µ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 ); | ||||
1137 | 5 | 21µs | 5 | 207µ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 ); | ||||
1140 | 5 | 20µs | 5 | 179µ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 ); | ||||
1143 | 5 | 18µs | 5 | 157µ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 ); | ||||
1145 | 5 | 17µs | 5 | 265µ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. | ||||
1153 | 5 | 33µs | $text =~ s#</?sticky>##g; | ||
1154 | |||||
1155 | # DEPRECATED startRenderingHandler before PRE removed | ||||
1156 | # SMELL: could parse more efficiently if this wasn't | ||||
1157 | # here. | ||||
1158 | 5 | 28µs | 15 | 9.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 | |||||
1161 | 5 | 11µs | 5 | 145µ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) | ||||
1164 | 5 | 30µs | $text =~ s/\\\n//gs; | ||
1165 | |||||
1166 | 5 | 10µs | 5 | 18.6ms | $plugins->dispatch( 'preRenderingHandler', $text, $removed ); # spent 18.6ms making 5 calls to Foswiki::Plugins::dispatch, avg 3.71ms/call |
1167 | |||||
1168 | 5 | 20µs | 5 | 22µ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 | |||||
1186 | 5 | 9µs | 5 | 10µ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 | ||||
1210 | 5 | 81µs | $text =~ s/<nop>/N$NOPMARK/g; | ||
1211 | 5 | 26µs | 5 | 310µ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 ); | ||||
1213 | 5 | 94µs | $text =~ s/N$NOPMARK/<nop>/g; | ||
1214 | |||||
1215 | # Escape rendering: Change ' !AnyWord' to ' <nop>AnyWord', | ||||
1216 | # for final ' AnyWord' output | ||||
1217 | 5 | 77µ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 '>' | ||||
1221 | 5 | 66µs | $text =~ s/^>(.*?)$/'>'.CGI::cite( {}, $1 ).CGI::br()/gem; | ||
1222 | |||||
1223 | # locate isolated < and > and translate to entities | ||||
1224 | # Protect isolated <!-- and --> | ||||
1225 | 5 | 98µs | $text =~ s/<!--/{$REMARKER!--/g; | ||
1226 | 5 | 92µ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. | ||||
1232 | 5 | 728µs | $text =~ s/<(\/?\w+(:\w+)?)>/{$REMARKER$1}$REMARKER/g; | ||
1233 | 5 | 1.87ms | $text =~ s/<(\w+(:\w+)?(\s+.*?|\/)?)>/{$REMARKER$1}$REMARKER/g; | ||
1234 | |||||
1235 | # XML processing instruction only valid at start of text | ||||
1236 | 5 | 25µ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 > | ||||
1240 | 5 | 34µs | $text =~ s/<(?!\!\[CDATA\[)/<\;/g; | ||
1241 | 5 | 31µs | $text =~ s/(?<!\]\])>/>\;/g; | ||
1242 | 5 | 261µs | $text =~ s/{$REMARKER/</go; | ||
1243 | 5 | 259µs | $text =~ s/}$REMARKER/>/go; | ||
1244 | |||||
1245 | # other entities | ||||
1246 | 5 | 166µs | $text =~ s/&(\w+);/$REMARKER$1;/g; # "&abc;" | ||
1247 | 5 | 92µs | $text =~ s/&(#x?[0-9a-f]+);/$REMARKER$1;/gi; # "{" | ||
1248 | 5 | 38µs | $text =~ s/&/&/g; # escape standalone "&" | ||
1249 | 5 | 114µs | $text =~ s/$REMARKER(#x?[0-9a-f]+;)/&$1/goi; | ||
1250 | 5 | 173µ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() | ||||
1255 | 5 | 23µs | 5 | 109µs | my $anchors = $this->getAnchorNames($topicObject); # spent 109µs making 5 calls to Foswiki::Render::getAnchorNames, avg 22µs/call |
1256 | 5 | 21µs | 5 | 21µ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. | ||||
1260 | 5 | 66µs | $text =~ s/^(\#$Foswiki::regex{wikiWordRegex})/ | ||
1261 | 1 | 5µs | 2 | 86µ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 | ||||
1267 | 5 | 62µs | $text =~ s/$Foswiki::regex{headerPatternHt}/ | ||
1268 | _makeAnchorHeading($this, $2, $1, $anchors)/geo; | ||||
1269 | |||||
1270 | # '----+++++++' rule | ||||
1271 | 5 | 71µs | $text =~ s/$Foswiki::regex{headerPatternDa}/ | ||
1272 | _makeAnchorHeading($this, $2, length($1), $anchors)/geo; | ||||
1273 | |||||
1274 | # Horizontal rule | ||||
1275 | 5 | 20µs | 5 | 292µ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 |
1276 | 5 | 86µs | $text =~ s/^---+/$hr/gm; | ||
1277 | |||||
1278 | # Now we really _do_ need a line loop, to process TML | ||||
1279 | # line-oriented stuff. | ||||
1280 | 5 | 3µs | my $isList = 0; # True when within a list | ||
1281 | 5 | 1µs | my $tableRow = 0; | ||
1282 | 5 | 900ns | my @result; | ||
1283 | 5 | 2µs | my $isFirst = 1; | ||
1284 | |||||
1285 | 5 | 207µs | foreach my $line ( split( /\r?\n/, $text ) ) { | ||
1286 | |||||
1287 | # Table: | cell | cell | | ||||
1288 | # allow trailing white space after the last | | ||||
1289 | 223 | 124µ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 | ||||
1313 | 223 | 425µs | if ( $line =~ m/^\s*$/ ) { | ||
1314 | 52 | 14µs | unless ( $tableRow || $isFirst ) { | ||
1315 | $line = '<p></p>'; | ||||
1316 | } | ||||
1317 | 52 | 10µs | $isList = 0; | ||
1318 | } | ||||
1319 | elsif ( $line =~ m/^\S/ ) { | ||||
1320 | $isList = 0; | ||||
1321 | } | ||||
1322 | elsif ( $line =~ m/^(\t| )+\S/ ) { | ||||
1323 | 62 | 296µ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 | ||||
1340 | 60 | 111µs | 60 | 656µs | _addListItem( $this, \@result, 'ul', 'li', $1 ); # spent 656µs making 60 calls to Foswiki::Render::_addListItem, avg 11µs/call |
1341 | 60 | 25µ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 | ||||
1363 | 2 | 2µs | push( @result, $line ); | ||
1364 | 2 | 1µ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 | ||||
1380 | 221 | 234µs | 156 | 654µs | unless ( $isList || $isFirst ) { # spent 654µs making 156 calls to Foswiki::Render::_addListItem, avg 4µs/call |
1381 | _addListItem( $this, \@result, '', '', '' ); | ||||
1382 | } | ||||
1383 | |||||
1384 | 221 | 123µs | push( @result, $line ); | ||
1385 | 221 | 101µs | $isFirst = 0; | ||
1386 | } | ||||
1387 | |||||
1388 | 5 | 800ns | if ($tableRow) { | ||
1389 | _addTHEADandTFOOT( \@result ); | ||||
1390 | push( @result, '</table>' ); | ||||
1391 | } | ||||
1392 | 5 | 9µs | 5 | 20µs | _addListItem( $this, \@result, '', '', '' ); # spent 20µs making 5 calls to Foswiki::Render::_addListItem, avg 4µs/call |
1393 | |||||
1394 | 5 | 53µ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 | |||||
1400 | 5 | 47µs | $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/_fixedFontText($1,1)/gem; | ||
1401 | 5 | 38µs | $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S) | ||
1402 | __$ENDWW/<strong><em>$1<\/em><\/strong>/gmx; | ||||
1403 | 5 | 112µs | $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/<strong>$1<\/strong>/gm; | ||
1404 | 5 | 54µs | $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/<em>$1<\/em>/gm; | ||
1405 | 5 | 166µ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 | ||||
1409 | 5 | 57µs | $text =~ s/(^|\s)\!\[\[/$1\[<nop>\[/gm; | ||
1410 | |||||
1411 | # Spaced-out Wiki words with alternative link text | ||||
1412 | # i.e. [[$1][$3]] | ||||
1413 | 23 | 231µs | 18 | 7.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 | ||||
1418 | 5 | 5.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) | ||||
1424 | 5 | 5.18ms | $text =~ s/$STARTWW((mailto\:)? | ||
1425 | $Foswiki::regex{emailAddrRegex})$ENDWW/ | ||||
1426 | _mailLink( $this, $1 )/gemx; | ||||
1427 | |||||
1428 | 5 | 21µs | 10 | 317µ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 | ||||
1442 | 5 | 27µs | 5 | 280µs | $this->_putBackProtected( \$text, 'input', $removed ); # spent 280µs making 5 calls to Foswiki::Render::_putBackProtected, avg 56µs/call |
1443 | 5 | 29µs | $text =~ s/N$NOPMARK//g; | ||
1444 | |||||
1445 | 5 | 12µs | 5 | 58µ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 | ||||
1448 | 5 | 10µs | 5 | 16µ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 | ||||
1451 | 5 | 17µs | 5 | 48µs | Foswiki::putBackBlocks( \$text, $removed, 'verbatim', 'pre', # spent 48µs making 5 calls to Foswiki::putBackBlocks, avg 10µs/call |
1452 | \&verbatimCallBack ); | ||||
1453 | 5 | 19µs | $text =~ s|\n?<nop>\n$||o; # clean up clutch | ||
1454 | |||||
1455 | 5 | 15µs | 5 | 270µs | $this->_putBackProtected( \$text, 'script', $removed, \&_filterScript ); # spent 270µs making 5 calls to Foswiki::Render::_putBackProtected, avg 54µs/call |
1456 | 5 | 13µs | 5 | 311µs | Foswiki::putBackBlocks( \$text, $removed, 'literal', '', \&_filterLiteral ); # spent 311µs making 5 calls to Foswiki::putBackBlocks, avg 62µs/call |
1457 | 5 | 10µs | 5 | 34µs | $this->_putBackProtected( \$text, 'literal', $removed ); # spent 34µs making 5 calls to Foswiki::Render::_putBackProtected, avg 7µs/call |
1458 | 5 | 5µs | Foswiki::putBackBlocks( \$text, $removed, 'dirtyarea' ) | ||
1459 | if $Foswiki::cfg{Cache}{Enabled}; | ||||
1460 | 5 | 8µs | 5 | 51µs | $this->_putBackProtected( \$text, 'comment', $removed ); # spent 51µs making 5 calls to Foswiki::Render::_putBackProtected, avg 10µs/call |
1461 | 5 | 8µs | 5 | 48µs | $this->_putBackProtected( \$text, 'head', $removed ); # spent 48µs making 5 calls to Foswiki::Render::_putBackProtected, avg 10µs/call |
1462 | 5 | 8µs | 5 | 26µs | $this->_putBackProtected( \$text, 'textarea', $removed ); # spent 26µs making 5 calls to Foswiki::Render::_putBackProtected, avg 5µs/call |
1463 | |||||
1464 | 5 | 42µs | 10 | 110µ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 | |||||
1466 | 5 | 8µs | 5 | 864µs | $plugins->dispatch( 'postRenderingHandler', $text ); # spent 864µs making 5 calls to Foswiki::Plugins::dispatch, avg 173µs/call |
1467 | 5 | 68µs | return $text; | ||
1468 | } | ||||
1469 | |||||
1470 | =begin TML | ||||
1471 | |||||
1472 | ---++ StaticMethod verbatimCallBack | ||||
1473 | |||||
1474 | Callback for use with putBackBlocks that replaces < and > | ||||
1475 | by their HTML entities &lt; and &gt; | ||||
1476 | |||||
1477 | =cut | ||||
1478 | |||||
1479 | sub 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 | ||||
1491 | 14 | 10µs | my $val = shift; | ||
1492 | 14 | 50µ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 | ||||
1499 | 12 | 5µs | my $val = shift; | ||
1500 | 12 | 41µ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 | |||||
1510 | Strip TML markup from text for display as plain text without | ||||
1511 | pushing it through the full rendering pipeline. Intended for | ||||
1512 | generation of topic and change summaries. Adds nop tags to | ||||
1513 | prevent 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 | |||||
1523 | sub 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 | |||||
1622 | Protect plain text from expansions that would normally be done | ||||
1623 | duing rendering, such as wikiwords. Topic summaries, for example, | ||||
1624 | have to be protected this way. | ||||
1625 | |||||
1626 | =cut | ||||
1627 | |||||
1628 | sub 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 | ||||
1655 | sub 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 <!DOCTYPE blah> | ||||
1671 | # | ||||
1672 | # WARNING: if you want to take out <!-- 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 | ||||
1675 | 30 | 45µs | my ( $this, $intext, $re, $id, $map ) = @_; | ||
1676 | |||||
1677 | 50 | 963µs | 20 | 137µs | $intext =~ s/($re)/_replaceBlock($1, $id, $map)/ge; # spent 137µs making 20 calls to Foswiki::Render::_replaceBlock, avg 7µs/call |
1678 | |||||
1679 | 30 | 143µ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 | ||||
1683 | 20 | 28µs | my ( $scoop, $id, $map ) = @_; | ||
1684 | 20 | 5µs | my $placeholder = $placeholderMarker; | ||
1685 | 20 | 4µs | $placeholderMarker++; | ||
1686 | 20 | 58µs | $map->{ $id . $placeholder }{text} = $scoop; | ||
1687 | |||||
1688 | 20 | 75µ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 | ||||
1700 | 30 | 23µs | my ( $this, $text, $id, $map, $callback ) = @_; | ||
1701 | 30 | 29µs | 30 | 25µs | ASSERT( ref($map) eq 'HASH' ) if DEBUG; # spent 25µs making 30 calls to Assert::ASSERTS_OFF, avg 840ns/call |
1702 | |||||
1703 | 30 | 136µs | foreach my $placeholder ( keys %$map ) { | ||
1704 | 67 | 111µs | next unless $placeholder =~ /^$id\d+$/; | ||
1705 | 20 | 25µs | my $val = $map->{$placeholder}{text}; | ||
1706 | 20 | 23µs | 12 | 27µs | $val = &$callback($val) if ( defined($callback) ); # spent 27µs making 12 calls to Foswiki::Render::_filterScript, avg 2µs/call |
1707 | 20 | 256µs | $$text =~ s/<!--$REMARKER$placeholder$REMARKER-->/$val/; | ||
1708 | 20 | 34µs | delete( $map->{$placeholder} ); | ||
1709 | } | ||||
1710 | } | ||||
1711 | |||||
1712 | =begin TML | ||||
1713 | |||||
1714 | ---++ ObjectMethod renderRevisionInfo($topicObject, $rev, $format) -> $string | ||||
1715 | |||||
1716 | Obtain 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 | ||||
1735 | 3 | 5µs | my ( $this, $topicObject, $rrev, $format ) = @_; | ||
1736 | 3 | 1µs | my $value = $format || 'r$rev - $date - $time - $wikiusername'; | ||
1737 | 3 | 5µs | 3 | 37µ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 | ||||
1740 | 3 | 8µ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 | |||||
1744 | 3 | 3µs | my $users = $this->{session}->{users}; | ||
1745 | 3 | 700ns | 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 | } | ||||
1752 | 3 | 11µs | 3 | 67µs | my $info = $topicObject->getRevisionInfo(); # spent 67µs making 3 calls to Foswiki::Meta::getRevisionInfo, avg 22µs/call |
1753 | |||||
1754 | 3 | 2µs | my $wun = ''; | ||
1755 | 3 | 500ns | my $wn = ''; | ||
1756 | 3 | 1µs | my $un = ''; | ||
1757 | 3 | 2µs | if ( $info->{author} ) { | ||
1758 | 3 | 6µs | 3 | 95µ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 | ||||
1761 | 3 | 2µs | $cUID = $info->{author} | ||
1762 | if ( $info->{author} eq | ||||
1763 | $Foswiki::Users::BaseUserMapping::UNKNOWN_USER_CUID ); | ||||
1764 | 3 | 700ns | if ( !$cUID ) { | ||
1765 | my $ln = $users->getLoginName( $info->{author} ); | ||||
1766 | $cUID = $info->{author} | ||||
1767 | if ( defined($ln) and ( $ln ne 'unknown' ) ); | ||||
1768 | } | ||||
1769 | 3 | 1µs | if ($cUID) { | ||
1770 | 3 | 9µs | 3 | 113µs | $wun = $users->webDotWikiName($cUID); # spent 113µs making 3 calls to Foswiki::Users::webDotWikiName, avg 38µs/call |
1771 | 3 | 4µs | 3 | 7µs | $wn = $users->getWikiName($cUID); # spent 7µs making 3 calls to Foswiki::Users::getWikiName, avg 2µs/call |
1772 | 3 | 7µs | 3 | 105µ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 | ||||
1776 | 3 | 4µ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. | ||||
1795 | 2 | 66µs | 2 | 29µ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 # 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; | ||||
1797 | 2 | 1.98ms | 2 | 24µ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 # 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 | |||||
1805 | 3 | 3µs | $value =~ s/\$web/$topicObject->web() || ''/ge; | ||
1806 | 3 | 2µs | $value =~ s/\$topic\(([^\)]*)\)/ | ||
1807 | Foswiki::Render::breakName( $topicObject->topic(), $1 )/ge; | ||||
1808 | 3 | 2µs | $value =~ s/\$topic/$topicObject->topic() || ''/ge; | ||
1809 | 3 | 10µs | $value =~ s/\$rev/$info->{version}/g; | ||
1810 | 3 | 6µs | 1 | 41µs | $value =~ s/\$time/ # spent 41µs making 1 call to Foswiki::Time::formatTime |
1811 | Foswiki::Time::formatTime($info->{date}, '$hour:$min:$sec')/ge; | ||||
1812 | 3 | 16µs | 3 | 96µ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; | ||||
1815 | 3 | 19µs | $value =~ s/(\$(rcs|longdate|isotz|iso|http|email|))/ | ||
1816 | 8 | 13µs | 8 | 100µs | Foswiki::Time::formatTime($info->{date}, $1 )/ge; # spent 100µs making 8 calls to Foswiki::Time::formatTime, avg 12µs/call |
1817 | |||||
1818 | 3 | 7µ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 | } | ||||
1824 | 3 | 2µs | $value =~ s/\$username/$un/g; | ||
1825 | 3 | 8µs | $value =~ s/\$wikiname/$wn/g; | ||
1826 | 3 | 1µs | $value =~ s/\$wikiusername/$wun/g; | ||
1827 | |||||
1828 | 3 | 15µs | return $value; | ||
1829 | } | ||||
1830 | |||||
1831 | =begin TML | ||||
1832 | |||||
1833 | ---++ ObjectMethod forEachLine( $text, \&fn, \%options ) -> $newText | ||||
1834 | |||||
1835 | Iterate 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 | ||||
1841 | The spec of \&fn is =sub fn( $line, \%options ) -> $newLine=. The %options | ||||
1842 | hash 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 | ||||
1844 | TRUE if the line is from one (or more) of those block types. | ||||
1845 | |||||
1846 | The 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 | ||||
1851 | 5 | 11µs | my ( $this, $text, $fn, $options ) = @_; | ||
1852 | |||||
1853 | 5 | 2µs | return '' unless defined $text; | ||
1854 | |||||
1855 | 5 | 7µs | $options->{in_pre} = 0; | ||
1856 | 5 | 2µs | $options->{in_pre} = 0; | ||
1857 | 5 | 4µs | $options->{in_verbatim} = 0; | ||
1858 | 5 | 4µs | $options->{in_literal} = 0; | ||
1859 | 5 | 3µs | $options->{in_noautolink} = 0; | ||
1860 | 5 | 4µs | my $newText = ''; | ||
1861 | 5 | 363µs | foreach my $line ( split( /([\r\n]+)/, $text ) ) { | ||
1862 | 122 | 354µs | if ( $line =~ /[\r\n]/ ) { | ||
1863 | 59 | 13µs | $newText .= $line; | ||
1864 | 59 | 16µs | next; | ||
1865 | } | ||||
1866 | 63 | 70µs | $options->{in_verbatim}++ if ( $line =~ m|^\s*<verbatim\b[^>]*>\s*$|i ); | ||
1867 | 63 | 31µs | $options->{in_verbatim}-- if ( $line =~ m|^\s*</verbatim>\s*$|i ); | ||
1868 | 63 | 30µs | $options->{in_literal}++ if ( $line =~ m|^\s*<literal\b[^>]*>\s*$|i ); | ||
1869 | 63 | 33µs | $options->{in_literal}-- if ( $line =~ m|^\s*</literal>\s*$|i ); | ||
1870 | 63 | 59µs | unless ( ( $options->{in_verbatim} > 0 ) | ||
1871 | || ( ( $options->{in_literal} > 0 ) ) ) | ||||
1872 | { | ||||
1873 | 63 | 76µs | $options->{in_pre}++ if ( $line =~ m|<pre\b|i ); | ||
1874 | 63 | 76µs | $options->{in_pre}-- if ( $line =~ m|</pre>|i ); | ||
1875 | 63 | 35µs | $options->{in_noautolink}++ | ||
1876 | if ( $line =~ m|^\s*<noautolink\b[^>]*>\s*$|i ); | ||||
1877 | 63 | 31µs | $options->{in_noautolink}-- | ||
1878 | if ( $line =~ m|^\s*</noautolink>\s*|i ); | ||||
1879 | } | ||||
1880 | 63 | 189µs | 63 | 3.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 | } | ||||
1887 | 63 | 77µs | $newText .= $line; | ||
1888 | } | ||||
1889 | 5 | 33µ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. | ||||
1909 | Generate a regular expression that can be used to match references to the | ||||
1910 | specified web/topic. Note that the resultant RE will only match fully | ||||
1911 | qualified (i.e. with web specifier) topic names and topic names that | ||||
1912 | are wikiwords in text. Works for spaced-out wikiwords for topic names. | ||||
1913 | |||||
1914 | The RE returned is designed to be used with =s///= | ||||
1915 | |||||
1916 | =cut | ||||
1917 | |||||
1918 | sub 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*]\.\.\.)?) | ||||
2124 | Hyphenates $text every $1 characters, or if $2 is "..." then shortens to | ||||
2125 | $1 characters and appends "..." (making the final string $1+3 characters | ||||
2126 | long) | ||||
2127 | |||||
2128 | _Moved from Search.pm because it was obviously unhappy there, | ||||
2129 | as it is a rendering function_ | ||||
2130 | |||||
2131 | =cut | ||||
2132 | |||||
2133 | sub 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 | |||||
2162 | Given the value of a form field, and a set of attributes that control how | ||||
2163 | to display that value, protect the value from further processing. | ||||
2164 | |||||
2165 | The protected value is determined from the value of the field after: | ||||
2166 | * newlines are replaced with <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 &#124; or the value of $attrs->{bar} if defined | ||||
2170 | |||||
2171 | =cut | ||||
2172 | |||||
2173 | sub 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 = '|'; | ||||
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 | |||||
2211 | Get the anchor name set generated for the given topic. This is so that the | ||||
2212 | same 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 | ||||
2214 | topic is included. | ||||
2215 | |||||
2216 | Note that anchor names generated this way are unique since the last time the | ||||
2217 | anchor set is cleared, which happens (1) whenever a new session is started | ||||
2218 | and (2) whenever a new %TOC macro is rendered (see Foswiki/Macros/TOC). | ||||
2219 | |||||
2220 | Returns 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 | ||||
2225 | 5 | 6µs | my ( $this, $topicObject ) = @_; | ||
2226 | 5 | 15µs | 5 | 31µs | my $id = $topicObject->getPath(); # spent 31µs making 5 calls to Foswiki::Meta::getPath, avg 6µs/call |
2227 | 5 | 6µs | my $a = $this->{_anchorNames}{$id}; | ||
2228 | 5 | 2µs | unless ($a) { | ||
2229 | 1 | 4µs | 1 | 10µs | $a = new Foswiki::Render::Anchors(); # spent 10µs making 1 call to Foswiki::Render::Anchors::new |
2230 | 1 | 1µs | $this->{_anchorNames}{$id} = $a; | ||
2231 | } | ||||
2232 | 5 | 15µs | return $a; | ||
2233 | } | ||||
2234 | |||||
2235 | =begin TML | ||||
2236 | |||||
2237 | ---++ ObjectMethod renderIconImage($url [, $alt]) -> $html | ||||
2238 | Generate the output for representing an 16x16 icon image. The source of | ||||
2239 | the image is taken from =$url=. The optional =$alt= specifies an alt string. | ||||
2240 | |||||
2241 | re-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% | ||||
2243 | see System.SkinTemplates:base.css for the default of .foswikiIcon img | ||||
2244 | |||||
2245 | TODO: Sven's not sure this code belongs here - its only use appears to be the ICON macro | ||||
2246 | |||||
2247 | =cut | ||||
2248 | |||||
2249 | sub 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 | |||||
2267 | 1 | 10µs | 1; | ||
2268 | __END__ |