Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Templates.pm |
Statements | Executed 15095 statements in 84.2ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
4 | 4 | 4 | 25.5ms | 554ms | readTemplate | Foswiki::Templates::
20 | 2 | 1 | 20.4ms | 487ms | _readTemplateFile | Foswiki::Templates::
156 | 3 | 2 | 13.5ms | 36.0ms | tmplP (recurses: max depth 8, inclusive time 105ms) | Foswiki::Templates::
3611 | 9 | 1 | 8.20ms | 8.20ms | CORE:match (opcode) | Foswiki::Templates::
1474 | 11 | 1 | 5.50ms | 5.50ms | CORE:subst (opcode) | Foswiki::Templates::
1 | 1 | 1 | 5.47ms | 5.99ms | BEGIN@483 | Foswiki::Templates::
142 | 9 | 5 | 3.21ms | 35.3ms | expandTemplate (recurses: max depth 7, inclusive time 88.4ms) | Foswiki::Templates::
257 | 5 | 1 | 1.48ms | 1.48ms | CORE:substcont (opcode) | Foswiki::Templates::
15 | 1 | 1 | 625µs | 1.28ms | _readFile | Foswiki::Templates::
102 | 1 | 1 | 600µs | 600µs | CORE:ftis (opcode) | Foswiki::Templates::
158 | 1 | 1 | 410µs | 410µs | CORE:regcomp (opcode) | Foswiki::Templates::
18 | 2 | 1 | 389µs | 1.48ms | _decomment | Foswiki::Templates::
15 | 1 | 1 | 310µs | 310µs | CORE:open (opcode) | Foswiki::Templates::
15 | 1 | 1 | 269µs | 269µs | CORE:readline (opcode) | Foswiki::Templates::
1 | 1 | 1 | 195µs | 195µs | finish | Foswiki::Templates::
5 | 1 | 1 | 157µs | 32.5ms | _expandTrivialTemplate | Foswiki::Templates::
15 | 1 | 1 | 75µs | 75µs | CORE:close (opcode) | Foswiki::Templates::
1 | 1 | 1 | 28µs | 35µs | BEGIN@33 | Foswiki::Templates::
1 | 1 | 1 | 28µs | 28µs | new | Foswiki::Templates::
1 | 1 | 1 | 27µs | 70µs | BEGIN@35 | Foswiki::Templates::
1 | 1 | 1 | 24µs | 44µs | BEGIN@34 | Foswiki::Templates::
1 | 1 | 1 | 17µs | 120µs | BEGIN@42 | Foswiki::Templates::
1 | 1 | 1 | 9µs | 9µs | BEGIN@37 | Foswiki::Templates::
0 | 0 | 0 | 0s | 0s | getTemplateFromCache | Foswiki::Templates::
0 | 0 | 0 | 0s | 0s | haveTemplate | Foswiki::Templates::
0 | 0 | 0 | 0s | 0s | saveTemplateToCache | Foswiki::Templates::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Templates | ||||
6 | |||||
7 | Support for Skin Template directives | ||||
8 | |||||
9 | =cut | ||||
10 | |||||
11 | =begin TML | ||||
12 | |||||
13 | The following tokens are supported by this language: | ||||
14 | |||||
15 | | %<nop>TMPL:P% | Instantiates a previously defined template | | ||||
16 | | %<nop>TMPL:DEF% | Opens a template definition | | ||||
17 | | %<nop>TMPL:END% | Closes a template definition | | ||||
18 | | %<nop>TMPL:INCLUDE% | Includes another file of templates | | ||||
19 | |||||
20 | Note; the template cache does not get reset during initialisation, so | ||||
21 | the haveTemplate test will return true if a template was loaded during | ||||
22 | a previous run when used with mod_perl or speedycgi. Frustrating for | ||||
23 | the template author, but they just have to switch off | ||||
24 | the accelerators during development. | ||||
25 | |||||
26 | This is to all intents and purposes a singleton object. It could | ||||
27 | easily be coverted into a true singleton (template manager). | ||||
28 | |||||
29 | =cut | ||||
30 | |||||
31 | package Foswiki::Templates; | ||||
32 | |||||
33 | 2 | 53µs | 2 | 43µs | # spent 35µs (28+7) within Foswiki::Templates::BEGIN@33 which was called:
# once (28µs+7µs) by Foswiki::templates at line 33 # spent 35µs making 1 call to Foswiki::Templates::BEGIN@33
# spent 7µs making 1 call to strict::import |
34 | 2 | 48µs | 2 | 64µs | # spent 44µs (24+20) within Foswiki::Templates::BEGIN@34 which was called:
# once (24µs+20µs) by Foswiki::templates at line 34 # spent 44µs making 1 call to Foswiki::Templates::BEGIN@34
# spent 20µs making 1 call to warnings::import |
35 | 2 | 49µs | 2 | 112µs | # spent 70µs (27+42) within Foswiki::Templates::BEGIN@35 which was called:
# once (27µs+42µs) by Foswiki::templates at line 35 # spent 70µs making 1 call to Foswiki::Templates::BEGIN@35
# spent 42µs making 1 call to Assert::import |
36 | |||||
37 | 2 | 47µs | 1 | 9µs | # spent 9µs within Foswiki::Templates::BEGIN@37 which was called:
# once (9µs+0s) by Foswiki::templates at line 37 # spent 9µs making 1 call to Foswiki::Templates::BEGIN@37 |
38 | |||||
39 | # Enable TRACE to get HTML comments in the output showing where templates | ||||
40 | # (both DEFs and files) open and close. Will probably bork the output, so | ||||
41 | # normally you should use it with a bin/view command-line. | ||||
42 | 2 | 3.03ms | 2 | 222µs | # spent 120µs (17+102) within Foswiki::Templates::BEGIN@42 which was called:
# once (17µs+102µs) by Foswiki::templates at line 42 # spent 120µs making 1 call to Foswiki::Templates::BEGIN@42
# spent 102µs making 1 call to constant::import |
43 | |||||
44 | 1 | 2µs | my $MAX_EXPANSION_RECURSIONS = 999; | ||
45 | |||||
46 | =begin TML | ||||
47 | |||||
48 | ---++ ClassMethod new ( $session ) | ||||
49 | |||||
50 | Constructor. Creates a new template database object. | ||||
51 | * $session - session (Foswiki) object | ||||
52 | |||||
53 | =cut | ||||
54 | |||||
55 | # spent 28µs within Foswiki::Templates::new which was called:
# once (28µs+0s) by Foswiki::templates at line 1952 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm | ||||
56 | 5 | 33µs | my ( $class, $session ) = @_; | ||
57 | my $this = bless( { session => $session }, $class ); | ||||
58 | |||||
59 | $this->{VARS} = { sep => ' | ' }; | ||||
60 | $this->{expansionRecursions} = {}; | ||||
61 | return $this; | ||||
62 | } | ||||
63 | |||||
64 | =begin TML | ||||
65 | |||||
66 | ---++ ObjectMethod finish() | ||||
67 | Break circular references. | ||||
68 | |||||
69 | =cut | ||||
70 | |||||
71 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
72 | # whether they are references or not. That way this method is "golden | ||||
73 | # documentation" of the live fields in the object. | ||||
74 | # spent 195µs within Foswiki::Templates::finish which was called:
# once (195µs+0s) by Foswiki::finish at line 2100 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm | ||||
75 | 4 | 194µs | my $this = shift; | ||
76 | undef $this->{VARS}; | ||||
77 | undef $this->{session}; | ||||
78 | undef $this->{expansionRecursions}; | ||||
79 | } | ||||
80 | |||||
81 | =begin TML | ||||
82 | |||||
83 | ---++ ObjectMethod haveTemplate( $name ) -> $boolean | ||||
84 | |||||
85 | Return true if the template exists and is loaded into the cache | ||||
86 | |||||
87 | =cut | ||||
88 | |||||
89 | sub haveTemplate { | ||||
90 | my ( $this, $template ) = @_; | ||||
91 | |||||
92 | return exists( $this->{VARS}->{$template} ); | ||||
93 | } | ||||
94 | |||||
95 | # Expand only simple templates that can be expanded statically. | ||||
96 | # Templates with conditions can only be expanded after the | ||||
97 | # context is fully known. | ||||
98 | # spent 32.5ms (157µs+32.4) within Foswiki::Templates::_expandTrivialTemplate which was called 5 times, avg 6.51ms/call:
# 5 times (157µs+32.4ms) by Foswiki::Templates::readTemplate at line 335, avg 6.51ms/call | ||||
99 | 25 | 173µs | my ( $this, $text ) = @_; | ||
100 | |||||
101 | # SMELL: unchecked implicit untaint? | ||||
102 | 5 | 21µs | $text =~ /%TMPL\:P{(.*)}%/; # spent 21µs making 5 calls to Foswiki::Templates::CORE:match, avg 4µs/call | ||
103 | 5 | 459µs | my $attrs = new Foswiki::Attrs($1); # spent 459µs making 5 calls to Foswiki::Attrs::new, avg 92µs/call | ||
104 | |||||
105 | # Can't expand context-dependant templates | ||||
106 | return $text if ( $attrs->{context} ); | ||||
107 | 5 | 31.9ms | return $this->tmplP($attrs); # spent 31.9ms making 5 calls to Foswiki::Templates::tmplP, avg 6.38ms/call | ||
108 | } | ||||
109 | |||||
110 | =begin TML | ||||
111 | |||||
112 | ---++ ObjectMethod expandTemplate( $params ) -> $string | ||||
113 | |||||
114 | Expand the template specified in the parameter string using =tmplP=. | ||||
115 | |||||
116 | Examples: | ||||
117 | <verbatim> | ||||
118 | $tmpls->expandTemplate("blah"); | ||||
119 | $tmpls->expandTemplate(context="view" then="sigh" else="humph"); | ||||
120 | </verbatim> | ||||
121 | |||||
122 | =cut | ||||
123 | |||||
124 | # spent 35.3ms (3.21+32.1) within Foswiki::Templates::expandTemplate which was called 142 times, avg 249µs/call:
# 131 times (2.91ms+28.1ms) by Foswiki::Templates::tmplP at line 192, avg 237µs/call
# 3 times (80µs+878µs) by Foswiki::Func::expandTemplate at line 2441 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Func.pm, avg 319µs/call
# 2 times (63µs+544µs) by Foswiki::LoginManager::_LOGOUT at line 1086 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/LoginManager.pm, avg 304µs/call
# once (38µs+1.26ms) by Foswiki::Form::renderForDisplay at line 598 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Form.pm
# once (21µs+460µs) by Foswiki::Search::loadTemplates at line 496 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm
# once (37µs+291µs) by Foswiki::Search::loadTemplates at line 495 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm
# once (21µs+210µs) by Foswiki::Form::renderForDisplay at line 618 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Form.pm
# once (20µs+206µs) by Foswiki::Search::loadTemplates at line 507 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm
# once (20µs+200µs) by Foswiki::Form::renderForDisplay at line 600 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Form.pm | ||||
125 | 568 | 3.07ms | my ( $this, $params ) = @_; | ||
126 | |||||
127 | 142 | 13.1ms | my $attrs = new Foswiki::Attrs($params); # spent 13.1ms making 142 calls to Foswiki::Attrs::new, avg 92µs/call | ||
128 | 142 | 2.84ms | my $value = $this->tmplP($attrs); # spent 107ms making 142 calls to Foswiki::Templates::tmplP, avg 757µs/call, recursion: max depth 8, sum of overlapping time 105ms | ||
129 | return $value; | ||||
130 | } | ||||
131 | |||||
132 | =begin TML | ||||
133 | |||||
134 | ---+ ObjectMethod tmplP( $attrs ) -> $string | ||||
135 | |||||
136 | Return value expanded text of the template, as found from looking | ||||
137 | in the register of template definitions. The attrs can contain a template | ||||
138 | name in _DEFAULT, and / or =context=, =then= and =else= values. | ||||
139 | |||||
140 | Recursively expands any contained TMPL:P tags. | ||||
141 | |||||
142 | Note that it would be trivial to add template parameters to this, | ||||
143 | simply by iterating over the other parameters (other than _DEFAULT, context, | ||||
144 | then and else) and doing a s/// in the template for that parameter value. This | ||||
145 | would add considerably to the power of templates. | ||||
146 | |||||
147 | =cut | ||||
148 | |||||
149 | # spent 36.0ms (13.5+22.5) within Foswiki::Templates::tmplP which was called 156 times, avg 230µs/call:
# 142 times (12.1ms+-9.27ms) by Foswiki::Templates::expandTemplate at line 128, avg 20µs/call
# 9 times (800µs+404µs) by Foswiki::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki.pm:281] at line 281 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm, avg 134µs/call
# 5 times (586µs+31.3ms) by Foswiki::Templates::_expandTrivialTemplate at line 107, avg 6.38ms/call | ||||
150 | 2995 | 14.9ms | my ( $this, $params ) = @_; | ||
151 | |||||
152 | 156 | 1.47ms | my $template = $params->remove('_DEFAULT') || ''; # spent 1.47ms making 156 calls to Foswiki::Attrs::remove, avg 9µs/call | ||
153 | 156 | 1.33ms | my $context = $params->remove('context'); # spent 1.33ms making 156 calls to Foswiki::Attrs::remove, avg 9µs/call | ||
154 | 156 | 1.28ms | my $then = $params->remove('then'); # spent 1.28ms making 156 calls to Foswiki::Attrs::remove, avg 8µs/call | ||
155 | 156 | 1.25ms | my $else = $params->remove('else'); # spent 1.25ms making 156 calls to Foswiki::Attrs::remove, avg 8µs/call | ||
156 | if ($context) { | ||||
157 | $template = $then if defined($then); | ||||
158 | foreach my $id ( split( /\,\s*/, $context ) ) { | ||||
159 | unless ( $this->{session}->{context}->{$id} ) { | ||||
160 | $template = ( $else || '' ); | ||||
161 | last; | ||||
162 | } | ||||
163 | } | ||||
164 | } | ||||
165 | |||||
166 | return '' unless $template; | ||||
167 | |||||
168 | $this->{expansionRecursions}->{$template} += 1; | ||||
169 | |||||
170 | if ( $this->{expansionRecursions}->{$template} > $MAX_EXPANSION_RECURSIONS ) | ||||
171 | { | ||||
172 | throw Foswiki::OopsException( | ||||
173 | 'attention', | ||||
174 | def => 'template_recursion', | ||||
175 | params => [$template] | ||||
176 | ); | ||||
177 | } | ||||
178 | |||||
179 | my $val = ''; | ||||
180 | if ( exists( $this->{VARS}->{$template} ) ) { | ||||
181 | $val = $this->{VARS}->{$template}; | ||||
182 | $val = "<!--$template-->\n$val<!--/$template-->\n" if (TRACE); | ||||
183 | foreach my $p ( keys %$params ) { | ||||
184 | if ( $p eq 'then' || $p eq 'else' ) { | ||||
185 | $val =~ s/%$p%/$this->expandTemplate($1)/ge; | ||||
186 | } | ||||
187 | elsif ( defined( $params->{$p} ) ) { | ||||
188 | 336 | 787µs | $val =~ s/%$p%/$params->{$p}/ge; # spent 410µs making 158 calls to Foswiki::Templates::CORE:regcomp, avg 3µs/call
# spent 324µs making 158 calls to Foswiki::Templates::CORE:subst, avg 2µs/call
# spent 52µs making 20 calls to Foswiki::Templates::CORE:substcont, avg 3µs/call | ||
189 | } | ||||
190 | } | ||||
191 | 150 | 263µs | $val =~ s/%TMPL:PREV%/%TMPL:P{"$template:_PREV"}%/g; # spent 263µs making 150 calls to Foswiki::Templates::CORE:subst, avg 2µs/call | ||
192 | 479 | 32.3ms | $val =~ s/%TMPL:P{(.*?)}%/$this->expandTemplate($1)/ge; # spent 119ms making 131 calls to Foswiki::Templates::expandTemplate, avg 911µs/call, recursion: max depth 7, sum of overlapping time 88.4ms
# spent 822µs making 198 calls to Foswiki::Templates::CORE:substcont, avg 4µs/call
# spent 466µs making 150 calls to Foswiki::Templates::CORE:subst, avg 3µs/call | ||
193 | } | ||||
194 | |||||
195 | return $val; | ||||
196 | } | ||||
197 | |||||
198 | =begin TML | ||||
199 | |||||
200 | ---++ ObjectMethod readTemplate ( $name, %options ) -> $text | ||||
201 | |||||
202 | Reads a template, loading the definitions therein. | ||||
203 | |||||
204 | Return value: expanded template text | ||||
205 | |||||
206 | By default throws an OopsException if the template was not found or the | ||||
207 | access controls denied access. | ||||
208 | |||||
209 | %options include: | ||||
210 | * =skin= - skin name, | ||||
211 | * =web= - web to search | ||||
212 | * =no_oops= - if true, will not throw an exception. Instead, returns undef. | ||||
213 | |||||
214 | If template text is found, extracts include statements and fully expands them. | ||||
215 | Also extracts template definitions and adds them to the | ||||
216 | list of loaded templates, overwriting any previous definition. | ||||
217 | |||||
218 | =cut | ||||
219 | |||||
220 | # spent 554ms (25.5+529) within Foswiki::Templates::readTemplate which was called 4 times, avg 139ms/call:
# once (3.10ms+426ms) by Foswiki::Func::loadTemplate at line 2409 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Func.pm
# once (20.5ms+90.6ms) by Foswiki::UI::View::view at line 229 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/UI/View.pm
# once (1.12ms+7.80ms) by Foswiki::Search::loadTemplates at line 480 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm
# once (777µs+4.08ms) by Foswiki::Form::renderForDisplay at line 596 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Form.pm | ||||
221 | 6007 | 33.2ms | my ( $this, $name, %opts ) = @_; | ||
222 | 4 | 16µs | ASSERT($name) if DEBUG; # spent 16µs making 4 calls to Assert::ASSERTS_OFF, avg 4µs/call | ||
223 | 4 | 1.68ms | my $skins = $opts{skins} || $this->{session}->getSkin(); # spent 1.68ms making 4 calls to Foswiki::getSkin, avg 419µs/call | ||
224 | my $web = $opts{web} || $this->{session}->{webName}; | ||||
225 | |||||
226 | $this->{files} = (); | ||||
227 | |||||
228 | # recursively read template file(s) | ||||
229 | 4 | 18.5ms | my $text = _readTemplateFile( $this, $name, $skins, $web ); # spent 18.5ms making 4 calls to Foswiki::Templates::_readTemplateFile, avg 4.62ms/call | ||
230 | |||||
231 | # Check file was found | ||||
232 | unless ( defined $text ) { | ||||
233 | |||||
234 | # if no_oops is given, return undef silently | ||||
235 | if ( $opts{no_oops} ) { | ||||
236 | return undef; | ||||
237 | } | ||||
238 | else { | ||||
239 | throw Foswiki::OopsException( | ||||
240 | 'attention', | ||||
241 | def => 'no_such_template', | ||||
242 | params => [ | ||||
243 | $name, | ||||
244 | |||||
245 | # More info for overridable templates | ||||
246 | ( $name =~ /^(view|edit)$/ ) ? $name . '_TEMPLATE' : '' | ||||
247 | ] | ||||
248 | ); | ||||
249 | } | ||||
250 | } | ||||
251 | |||||
252 | # SMELL: unchecked implicit untaint? | ||||
253 | 4 | 34µs | while ( $text =~ /%TMPL\:INCLUDE{[\s\"]*(.*?)[\"\s]*}%/s ) { # spent 34µs making 4 calls to Foswiki::Templates::CORE:match, avg 8µs/call | ||
254 | 46 | 789µs | $text =~ s/%TMPL\:INCLUDE{[\s\"]*(.*?)[\"\s]*}%/ # spent 485µs making 26 calls to Foswiki::Templates::CORE:substcont, avg 19µs/call
# spent 157µs making 10 calls to Foswiki::Templates::CORE:match, avg 16µs/call
# spent 147µs making 10 calls to Foswiki::Templates::CORE:subst, avg 15µs/call | ||
255 | 16 | 468ms | _readTemplateFile( $this, $1, $skins, $web ) || ''/ge; # spent 468ms making 16 calls to Foswiki::Templates::_readTemplateFile, avg 29.3ms/call | ||
256 | } | ||||
257 | |||||
258 | 4 | 16µs | if ( $text !~ /%TMPL\:/ ) { # spent 16µs making 4 calls to Foswiki::Templates::CORE:match, avg 4µs/call | ||
259 | |||||
260 | # no %TMPL's to process | ||||
261 | |||||
262 | # SMELL: legacy - leading spaces to tabs, should not be required | ||||
263 | $text =~ s|^(( {3})+)|"\t" x (length($1)/3)|geom; | ||||
264 | |||||
265 | return $text; | ||||
266 | } | ||||
267 | |||||
268 | my $result = ''; | ||||
269 | my $key = ''; | ||||
270 | my $val = ''; | ||||
271 | my $delim = ''; | ||||
272 | foreach ( split( /(%TMPL\:)/, $text ) ) { | ||||
273 | 2908 | 6.68ms | if (/^(%TMPL\:)$/) { # spent 6.68ms making 2908 calls to Foswiki::Templates::CORE:match, avg 2µs/call | ||
274 | $delim = $1; | ||||
275 | } | ||||
276 | elsif ( (/^DEF{[\s\"]*(.*?)[\"\s]*}%(.*)/s) && ($1) ) { | ||||
277 | |||||
278 | # handle %TMPL:DEF{key}% | ||||
279 | if ($key) { | ||||
280 | |||||
281 | # if the key is already defined, rename the existing | ||||
282 | # template to key:_PREV | ||||
283 | my $new_value = $val; | ||||
284 | my $prev_key = $key; | ||||
285 | my $prev_value = $this->{VARS}->{$prev_key}; | ||||
286 | $this->{VARS}->{$prev_key} = $new_value; | ||||
287 | while ($prev_value) { | ||||
288 | $new_value = $prev_value; | ||||
289 | $prev_key = "$prev_key:_PREV"; | ||||
290 | $prev_value = $this->{VARS}->{$prev_key}; | ||||
291 | $this->{VARS}->{$prev_key} = $new_value; | ||||
292 | } | ||||
293 | |||||
294 | } | ||||
295 | $key = $1; | ||||
296 | |||||
297 | # SMELL: unchecked implicit untaint? | ||||
298 | $val = $2; | ||||
299 | |||||
300 | } | ||||
301 | elsif (/^END%[\s\n\r]*(.*)/s) { | ||||
302 | |||||
303 | # handle %TMPL:END% | ||||
304 | |||||
305 | # if the key is already defined, rename the existing template to | ||||
306 | # key:_PREV | ||||
307 | my $new_value = $val; | ||||
308 | my $prev_key = $key; | ||||
309 | my $prev_value = $this->{VARS}->{$prev_key}; | ||||
310 | $this->{VARS}->{$prev_key} = $new_value; | ||||
311 | while ($prev_value) { | ||||
312 | $new_value = $prev_value; | ||||
313 | $prev_key = "$prev_key:_PREV"; | ||||
314 | $prev_value = $this->{VARS}->{$prev_key}; | ||||
315 | $this->{VARS}->{$prev_key} = $new_value; | ||||
316 | } | ||||
317 | |||||
318 | $key = ''; | ||||
319 | $val = ''; | ||||
320 | |||||
321 | # SMELL: unchecked implicit untaint? | ||||
322 | $result .= $1; | ||||
323 | |||||
324 | } | ||||
325 | elsif ($key) { | ||||
326 | $val .= "$delim$_"; | ||||
327 | |||||
328 | } | ||||
329 | else { | ||||
330 | $result .= "$delim$_"; | ||||
331 | } | ||||
332 | } | ||||
333 | |||||
334 | # handle %TMPL:P{"..."}% recursively | ||||
335 | 15 | 32.6ms | $result =~ s/(%TMPL\:P{.*?}%)/_expandTrivialTemplate( $this, $1)/geo; # spent 32.5ms making 5 calls to Foswiki::Templates::_expandTrivialTemplate, avg 6.51ms/call
# spent 55µs making 6 calls to Foswiki::Templates::CORE:substcont, avg 9µs/call
# spent 18µs making 4 calls to Foswiki::Templates::CORE:subst, avg 5µs/call | ||
336 | |||||
337 | # SMELL: legacy - leading spaces to tabs, should not be required | ||||
338 | 11 | 151µs | $result =~ s|^(( {3})+)|"\t" x (length($1)/3)|geom; # spent 83µs making 4 calls to Foswiki::Templates::CORE:subst, avg 21µs/call
# spent 68µs making 7 calls to Foswiki::Templates::CORE:substcont, avg 10µs/call | ||
339 | |||||
340 | $this->saveTemplateToCache( '_complete', $name, $skins, $web, $result ) | ||||
341 | if (TRACE); | ||||
342 | return $result; | ||||
343 | } | ||||
344 | |||||
345 | # STATIC: Return value: raw template text, or undef if read fails | ||||
346 | sub _readTemplateFile { | ||||
347 | 5300 | 25.2ms | my ( $this, $name, $skins, $web ) = @_; | ||
348 | my $session = $this->{session}; | ||||
349 | |||||
350 | # SMELL: not i18n-friendly (can't have accented characters in template name) | ||||
351 | # zap anything suspicious | ||||
352 | 20 | 58µs | $name =~ s/[^A-Za-z0-9_,.\/]//go; # spent 58µs making 20 calls to Foswiki::Templates::CORE:subst, avg 3µs/call | ||
353 | |||||
354 | # if the name ends in .tmpl, then this is an explicit include from | ||||
355 | # the templates directory. No further searching required. | ||||
356 | 20 | 44µs | if ( $name =~ /\.tmpl$/ ) { # spent 44µs making 20 calls to Foswiki::Templates::CORE:match, avg 2µs/call | ||
357 | my $text = | ||||
358 | _decomment( | ||||
359 | _readFile( $session, "$Foswiki::cfg{TemplateDir}/$name" ) ); | ||||
360 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, $text ) | ||||
361 | if (TRACE); | ||||
362 | return $text; | ||||
363 | } | ||||
364 | |||||
365 | my $userdirweb = $web; | ||||
366 | my $userdirname = $name; | ||||
367 | 20 | 33µs | if ( $name =~ /^(.+)\.(.+?)$/ ) { # spent 33µs making 20 calls to Foswiki::Templates::CORE:match, avg 2µs/call | ||
368 | |||||
369 | # ucfirst taints if use locale is in force | ||||
370 | $userdirweb = Foswiki::Sandbox::untaintUnchecked( ucfirst($1) ); | ||||
371 | $userdirname = Foswiki::Sandbox::untaintUnchecked( ucfirst($2) ); | ||||
372 | |||||
373 | # if the name can be parsed into $web.$name, then this is an attempt | ||||
374 | # to explicit include that topic. No further searching required. | ||||
375 | if ( $session->topicExists( $userdirweb, $userdirname ) ) { | ||||
376 | my $meta = | ||||
377 | Foswiki::Meta->load( $session, $userdirweb, $userdirname ); | ||||
378 | |||||
379 | # Check we are allowed access | ||||
380 | unless ( $meta->haveAccess( 'VIEW', $session->{user} ) ) { | ||||
381 | return $this->{session}->inlineAlert( 'alerts', 'access_denied', | ||||
382 | "$userdirweb.$userdirname" ); | ||||
383 | } | ||||
384 | my $text = $meta->text(); | ||||
385 | $text = '' unless defined $text; | ||||
386 | |||||
387 | $text = | ||||
388 | "<!--$userdirweb/$userdirname-->\n" | ||||
389 | . $text | ||||
390 | . "<!--/$userdirweb/$userdirname-->\n" | ||||
391 | if (TRACE); | ||||
392 | |||||
393 | $text = _decomment($text); | ||||
394 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, $text ) | ||||
395 | if (TRACE); | ||||
396 | return $text; | ||||
397 | } | ||||
398 | } | ||||
399 | else { | ||||
400 | |||||
401 | # ucfirst taints if use locale is in force | ||||
402 | 20 | 403µs | $userdirweb = # spent 403µs making 20 calls to Foswiki::Sandbox::untaintUnchecked, avg 20µs/call | ||
403 | Foswiki::Sandbox::untaintUnchecked( ucfirst($userdirweb) ); | ||||
404 | 20 | 293µs | $userdirname = # spent 293µs making 20 calls to Foswiki::Sandbox::untaintUnchecked, avg 15µs/call | ||
405 | Foswiki::Sandbox::untaintUnchecked( ucfirst($userdirname) ); | ||||
406 | } | ||||
407 | |||||
408 | my @skinList = split( /\,\s*/, $skins ); | ||||
409 | my $nrskins = $#skinList; | ||||
410 | |||||
411 | my @templatePath = split( /\s*,\s*/, $Foswiki::cfg{TemplatePath} ); | ||||
412 | if ( | ||||
413 | ( $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{Enabled} ) | ||||
414 | && ( lc($name) eq 'foswiki' ) | ||||
415 | && defined( | ||||
416 | $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{TemplatePath} | ||||
417 | ) | ||||
418 | ) | ||||
419 | { | ||||
420 | |||||
421 | # TWikiCompatibility, need to test to see if there is a twiki.skin tmpl | ||||
422 | @templatePath = | ||||
423 | @{ $Foswiki::cfg{Plugins}{TWikiCompatibilityPlugin}{TemplatePath} }; | ||||
424 | } | ||||
425 | |||||
426 | # Search the $Foswiki::cfg{TemplatePath} for the skinned versions | ||||
427 | my @candidates = (); | ||||
428 | |||||
429 | $nrskins = 0 if $nrskins < 0; | ||||
430 | |||||
431 | my $nrtemplates = $#templatePath; | ||||
432 | |||||
433 | for ( my $templateixd = 0 ; $templateixd <= $nrtemplates ; $templateixd++ ) | ||||
434 | { | ||||
435 | for ( my $idx = 0 ; $idx <= $nrskins ; $idx++ ) { | ||||
436 | my $file = $templatePath[$templateixd]; | ||||
437 | my $userdir = 0; | ||||
438 | |||||
439 | # also need to do %PUBURL% etc.? | ||||
440 | # push the first time even if not modified | ||||
441 | my $skin = $skinList[$idx] || ''; | ||||
442 | |||||
443 | # consider skin templates first | ||||
444 | # this is done by giving the template path with 'skin' in it | ||||
445 | # a higher sort priority (so a lower number: 0) | ||||
446 | 320 | 580µs | my $isSkinned = ( $file =~ m/\$skin/ ? 0 : 1 ); # spent 580µs making 320 calls to Foswiki::Templates::CORE:match, avg 2µs/call | ||
447 | |||||
448 | my $webName = $web || ''; | ||||
449 | my $tmplName = $name || ''; | ||||
450 | 320 | 632µs | unless ( $file =~ m/.tmpl$/ ) { # spent 632µs making 320 calls to Foswiki::Templates::CORE:match, avg 2µs/call | ||
451 | |||||
452 | # Could also use $Skin, $Web, $Name to indicate uppercase | ||||
453 | $userdir = 1; | ||||
454 | |||||
455 | # Again untainting when using ucfirst | ||||
456 | 160 | 2.47ms | $skin = Foswiki::Sandbox::untaintUnchecked( ucfirst($skin) ); # spent 2.47ms making 160 calls to Foswiki::Sandbox::untaintUnchecked, avg 15µs/call | ||
457 | $webName = $userdirweb; | ||||
458 | $tmplName = $userdirname; | ||||
459 | } | ||||
460 | 320 | 958µs | $file =~ s/\$skin/$skin/go; # spent 958µs making 320 calls to Foswiki::Templates::CORE:subst, avg 3µs/call | ||
461 | 320 | 940µs | $file =~ s/\$web/$webName/go; # spent 940µs making 320 calls to Foswiki::Templates::CORE:subst, avg 3µs/call | ||
462 | 320 | 1.15ms | $file =~ s/\$name/$tmplName/go; # spent 1.15ms making 320 calls to Foswiki::Templates::CORE:subst, avg 4µs/call | ||
463 | |||||
464 | # sort priority is: | ||||
465 | # primary: if template path has 'skin' in it; so that skin templates are considered first | ||||
466 | # secondary: the skin order number | ||||
467 | # tertiary: the template path order number | ||||
468 | |||||
469 | push( | ||||
470 | @candidates, | ||||
471 | { | ||||
472 | primary => $isSkinned, | ||||
473 | secondary => $idx, | ||||
474 | tertiary => $templateixd, | ||||
475 | file => $file, | ||||
476 | userdir => $userdir, | ||||
477 | skin => $skin | ||||
478 | } | ||||
479 | ); | ||||
480 | } | ||||
481 | } | ||||
482 | |||||
483 | 2 | 1.47ms | 2 | 6.06ms | # spent 5.99ms (5.47+518µs) within Foswiki::Templates::BEGIN@483 which was called:
# once (5.47ms+518µs) by Foswiki::templates at line 483 # spent 5.99ms making 1 call to Foswiki::Templates::BEGIN@483
# spent 73µs making 1 call to Exporter::import |
484 | 20 | 17.6ms | my $sorter = make_sorter( # spent 17.6ms making 20 calls to Sort::Maker::make_sorter, avg 878µs/call | ||
485 | qw( ST ), | ||||
486 | number => '$_->{primary}', | ||||
487 | number => '$_->{secondary}', | ||||
488 | number => '$_->{tertiary}' | ||||
489 | ); | ||||
490 | |||||
491 | # sort | ||||
492 | 20 | 5.95ms | @candidates = $sorter->(@candidates); # spent 5.95ms making 20 calls to Sort::Maker::__ANON__[(eval 136)[/usr/local/src/github.com/foswiki/core/lib/CPAN/lib/Sort/Maker.pm:140]:18], avg 297µs/call | ||
493 | |||||
494 | foreach my $candidate (@candidates) { | ||||
495 | my $file = $candidate->{file}; | ||||
496 | |||||
497 | 102 | 600µs | if ( $candidate->{userdir} ) { # spent 600µs making 102 calls to Foswiki::Templates::CORE:ftis, avg 6µs/call | ||
498 | |||||
499 | 70 | 4.25ms | my ( $web1, $name1 ) = # spent 4.25ms making 70 calls to Foswiki::normalizeWebTopicName, avg 61µs/call | ||
500 | $session->normalizeWebTopicName( $web, $file ); | ||||
501 | |||||
502 | 70 | 11.0ms | if ( $session->topicExists( $web1, $name1 ) ) { # spent 11.0ms making 70 calls to Foswiki::topicExists, avg 157µs/call | ||
503 | |||||
504 | # recursion prevention. | ||||
505 | next | ||||
506 | if ( | ||||
507 | defined( | ||||
508 | $this->{files} | ||||
509 | ->{ 'topic' . $session->{user}, $name1, $web1 } | ||||
510 | ) | ||||
511 | ); | ||||
512 | $this->{files}->{ 'topic' . $session->{user}, $name1, $web1 } = | ||||
513 | 1; | ||||
514 | |||||
515 | # access control | ||||
516 | 3 | 409ms | my $meta = Foswiki::Meta->load( $session, $web1, $name1 ); # spent 409ms making 3 calls to Foswiki::Meta::load, avg 136ms/call | ||
517 | 3 | 7.78ms | next unless $meta->haveAccess( 'VIEW', $session->{user} ); # spent 7.78ms making 3 calls to Foswiki::Meta::haveAccess, avg 2.59ms/call | ||
518 | |||||
519 | 3 | 104µs | my $text = $meta->text(); # spent 104µs making 3 calls to Foswiki::Meta::text, avg 35µs/call | ||
520 | $text = '' unless defined $text; | ||||
521 | |||||
522 | $text = "<!--$web1.$name1-->\n$text<!--/$web1.$name1-->\n" | ||||
523 | if (TRACE); | ||||
524 | |||||
525 | 3 | 130µs | $text = _decomment($text); # spent 130µs making 3 calls to Foswiki::Templates::_decomment, avg 43µs/call | ||
526 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, | ||||
527 | $text ) | ||||
528 | if (TRACE); | ||||
529 | return $text; | ||||
530 | } | ||||
531 | } | ||||
532 | elsif ( -e $file ) { | ||||
533 | next if ( defined( $this->{files}->{$file} ) ); | ||||
534 | |||||
535 | # recursion prevention. | ||||
536 | $this->{files}->{$file} = 1; | ||||
537 | |||||
538 | 30 | 2.63ms | my $text = _decomment( _readFile( $session, $file ) ); # spent 1.35ms making 15 calls to Foswiki::Templates::_decomment, avg 90µs/call
# spent 1.28ms making 15 calls to Foswiki::Templates::_readFile, avg 85µs/call | ||
539 | $this->saveTemplateToCache( '_cache', $name, $skins, $web, $text ) | ||||
540 | if (TRACE); | ||||
541 | return $text; | ||||
542 | } | ||||
543 | } | ||||
544 | |||||
545 | # File was not found | ||||
546 | return undef; | ||||
547 | } | ||||
548 | |||||
549 | # spent 1.28ms (625µs+653µs) within Foswiki::Templates::_readFile which was called 15 times, avg 85µs/call:
# 15 times (625µs+653µs) by Foswiki::Templates::_readTemplateFile at line 538, avg 85µs/call | ||||
550 | 105 | 1.28ms | my ( $session, $fn ) = @_; | ||
551 | my $F; | ||||
552 | |||||
553 | 15 | 310µs | if ( open( $F, '<', $fn ) ) { # spent 310µs making 15 calls to Foswiki::Templates::CORE:open, avg 21µs/call | ||
554 | local $/; | ||||
555 | 15 | 269µs | my $text = <$F>; # spent 269µs making 15 calls to Foswiki::Templates::CORE:readline, avg 18µs/call | ||
556 | 15 | 75µs | close($F); # spent 75µs making 15 calls to Foswiki::Templates::CORE:close, avg 5µs/call | ||
557 | |||||
558 | $text = "<!--$fn-->\n$text<!--/$fn-->\n" if (TRACE); | ||||
559 | |||||
560 | return $text; | ||||
561 | } | ||||
562 | else { | ||||
563 | $session->logger->log( 'warning', "$fn: $!" ); | ||||
564 | return undef; | ||||
565 | } | ||||
566 | } | ||||
567 | |||||
568 | sub _decomment { | ||||
569 | 72 | 1.49ms | my $text = shift; | ||
570 | |||||
571 | return $text unless $text; | ||||
572 | |||||
573 | # Kill comments, marked by %{ ... }% | ||||
574 | # (and remove whitespace either side of the comment) | ||||
575 | 18 | 1.09ms | $text =~ s/\s*%{.*?}%\s*//sg; # spent 1.09ms making 18 calls to Foswiki::Templates::CORE:subst, avg 61µs/call | ||
576 | return $text; | ||||
577 | } | ||||
578 | |||||
579 | #See http://wikiring.com/Blog/BlogEntry8?cat=WikiRing | ||||
580 | #used for debugging templates, and later maybe for speed. | ||||
581 | sub saveTemplateToCache { | ||||
582 | my ( $this, $cacheName, $name, $skins, $web, $tmplText ) = @_; | ||||
583 | $skins = '' unless ( defined($skins) ); | ||||
584 | $web = '' unless ( defined($web) ); | ||||
585 | |||||
586 | my $tmpl_cachedir = $Foswiki::cfg{TemplateDir} . $cacheName; | ||||
587 | mkdir($tmpl_cachedir) unless ( -e $tmpl_cachedir ); | ||||
588 | my $filename = Foswiki::Sandbox::untaintUnchecked( | ||||
589 | $tmpl_cachedir . '/' . $name . '__' . $skins . '__' . $web . '.tmpl' ); | ||||
590 | |||||
591 | unless ( open( FILE, ">$filename" ) ) { | ||||
592 | die "Can't create file $filename - $!\n" if DEBUG; | ||||
593 | print STDERR "Can't create file $filename - $!\n"; | ||||
594 | |||||
595 | return; | ||||
596 | } | ||||
597 | print FILE $tmplText; | ||||
598 | close(FILE); | ||||
599 | } | ||||
600 | |||||
601 | #unused, but can be used for a speedup by caching the expanded Template | ||||
602 | sub getTemplateFromCache { | ||||
603 | my ( $this, $name, $skins, $web ) = @_; | ||||
604 | $skins = '' unless ( defined($skins) ); | ||||
605 | $web = '' unless ( defined($web) ); | ||||
606 | |||||
607 | my $tmpl_cachedir = $Foswiki::cfg{TemplateDir} . '_cache'; | ||||
608 | mkdir($tmpl_cachedir) unless ( -e $tmpl_cachedir ); | ||||
609 | my $filename = Foswiki::Sandbox::untaintUnchecked( | ||||
610 | $tmpl_cachedir . '/' . $name . '__' . $skins . '__' . $web . '.tmpl' ); | ||||
611 | |||||
612 | if ( -e $filename ) { | ||||
613 | open( IN_FILE, "<$filename" ) || return; | ||||
614 | local $/ = undef; # set to read to EOF | ||||
615 | my $data = <IN_FILE>; | ||||
616 | close(IN_FILE); | ||||
617 | return $data; | ||||
618 | } | ||||
619 | } | ||||
620 | |||||
621 | 1 | 5µs | 1; | ||
622 | __END__ | ||||
# spent 75µs within Foswiki::Templates::CORE:close which was called 15 times, avg 5µs/call:
# 15 times (75µs+0s) by Foswiki::Templates::_readFile at line 556, avg 5µs/call | |||||
# spent 600µs within Foswiki::Templates::CORE:ftis which was called 102 times, avg 6µs/call:
# 102 times (600µs+0s) by Foswiki::Templates::_readTemplateFile at line 497, avg 6µs/call | |||||
# spent 8.20ms within Foswiki::Templates::CORE:match which was called 3611 times, avg 2µs/call:
# 2908 times (6.68ms+0s) by Foswiki::Templates::readTemplate at line 273, avg 2µs/call
# 320 times (632µs+0s) by Foswiki::Templates::_readTemplateFile at line 450, avg 2µs/call
# 320 times (580µs+0s) by Foswiki::Templates::_readTemplateFile at line 446, avg 2µs/call
# 20 times (44µs+0s) by Foswiki::Templates::_readTemplateFile at line 356, avg 2µs/call
# 20 times (33µs+0s) by Foswiki::Templates::_readTemplateFile at line 367, avg 2µs/call
# 10 times (157µs+0s) by Foswiki::Templates::readTemplate at line 254, avg 16µs/call
# 5 times (21µs+0s) by Foswiki::Templates::_expandTrivialTemplate at line 102, avg 4µs/call
# 4 times (34µs+0s) by Foswiki::Templates::readTemplate at line 253, avg 8µs/call
# 4 times (16µs+0s) by Foswiki::Templates::readTemplate at line 258, avg 4µs/call | |||||
# spent 310µs within Foswiki::Templates::CORE:open which was called 15 times, avg 21µs/call:
# 15 times (310µs+0s) by Foswiki::Templates::_readFile at line 553, avg 21µs/call | |||||
# spent 269µs within Foswiki::Templates::CORE:readline which was called 15 times, avg 18µs/call:
# 15 times (269µs+0s) by Foswiki::Templates::_readFile at line 555, avg 18µs/call | |||||
# spent 410µs within Foswiki::Templates::CORE:regcomp which was called 158 times, avg 3µs/call:
# 158 times (410µs+0s) by Foswiki::Templates::tmplP at line 188, avg 3µs/call | |||||
# spent 5.50ms within Foswiki::Templates::CORE:subst which was called 1474 times, avg 4µs/call:
# 320 times (1.15ms+0s) by Foswiki::Templates::_readTemplateFile at line 462, avg 4µs/call
# 320 times (958µs+0s) by Foswiki::Templates::_readTemplateFile at line 460, avg 3µs/call
# 320 times (940µs+0s) by Foswiki::Templates::_readTemplateFile at line 461, avg 3µs/call
# 158 times (324µs+0s) by Foswiki::Templates::tmplP at line 188, avg 2µs/call
# 150 times (466µs+0s) by Foswiki::Templates::tmplP at line 192, avg 3µs/call
# 150 times (263µs+0s) by Foswiki::Templates::tmplP at line 191, avg 2µs/call
# 20 times (58µs+0s) by Foswiki::Templates::_readTemplateFile at line 352, avg 3µs/call
# 18 times (1.09ms+0s) by Foswiki::Templates::_decomment at line 575, avg 61µs/call
# 10 times (147µs+0s) by Foswiki::Templates::readTemplate at line 254, avg 15µs/call
# 4 times (83µs+0s) by Foswiki::Templates::readTemplate at line 338, avg 21µs/call
# 4 times (18µs+0s) by Foswiki::Templates::readTemplate at line 335, avg 5µs/call | |||||
# spent 1.48ms within Foswiki::Templates::CORE:substcont which was called 257 times, avg 6µs/call:
# 198 times (822µs+0s) by Foswiki::Templates::tmplP at line 192, avg 4µs/call
# 26 times (485µs+0s) by Foswiki::Templates::readTemplate at line 254, avg 19µs/call
# 20 times (52µs+0s) by Foswiki::Templates::tmplP at line 188, avg 3µs/call
# 7 times (68µs+0s) by Foswiki::Templates::readTemplate at line 338, avg 10µs/call
# 6 times (55µs+0s) by Foswiki::Templates::readTemplate at line 335, avg 9µs/call |