Filename | /var/www/foswikidev/core/lib/Foswiki.pm |
Statements | Executed 1001070 statements in 1.30s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
99088 | 13 | 11 | 658ms | 658ms | normalizeWebTopicName | Foswiki::
52603 | 6 | 5 | 94.3ms | 101ms | search | Foswiki::
271 | 2 | 1 | 19.6ms | 137s | _processMacros (recurses: max depth 5, inclusive time 186ms) | Foswiki::
537 | 2 | 1 | 16.9ms | 137s | _expandMacroOnTopicRendering (recurses: max depth 3, inclusive time 151ms) | Foswiki::
1 | 1 | 1 | 11.5ms | 13.4ms | BEGIN@642 | Foswiki::
1 | 1 | 1 | 10.2ms | 15.1ms | BEGIN@51 | Foswiki::
100 | 1 | 1 | 8.76ms | 137s | expandMacros (recurses: max depth 2, inclusive time 62.2ms) | Foswiki::
14 | 3 | 3 | 7.85ms | 9.03ms | renderer | Foswiki::
310 | 7 | 3 | 6.55ms | 6.55ms | takeOutBlocks | Foswiki::
1479 | 39 | 9 | 5.93ms | 5.93ms | isTrue | Foswiki::
238 | 6 | 3 | 5.22ms | 137s | innerExpandMacros (recurses: max depth 3, inclusive time 171ms) | Foswiki::
1 | 1 | 1 | 4.94ms | 23.4ms | writeCompletePage | Foswiki::
1 | 1 | 1 | 4.49ms | 11.4ms | BEGIN@48 | Foswiki::
1 | 1 | 1 | 3.97ms | 55.0ms | BEGIN@53 | Foswiki::
175 | 10 | 4 | 3.22ms | 3.45ms | templates | Foswiki::
1 | 1 | 1 | 2.79ms | 3.13ms | BEGIN@50 | Foswiki::
1 | 1 | 1 | 2.75ms | 13.1ms | BEGIN@647 | Foswiki::
1 | 1 | 1 | 2.68ms | 2.78ms | attach | Foswiki::
1 | 1 | 1 | 2.34ms | 12.2ms | BEGIN@643 | Foswiki::
1 | 1 | 1 | 2.17ms | 6.33ms | BEGIN@646 | Foswiki::
1 | 1 | 1 | 2.07ms | 3.57ms | BEGIN@644 | Foswiki::
1 | 1 | 1 | 1.83ms | 4.47ms | logger | Foswiki::
1 | 1 | 1 | 1.79ms | 4.30ms | BEGIN@645 | Foswiki::
50 | 3 | 2 | 1.61ms | 6.54ms | getSkin | Foswiki::
60 | 3 | 3 | 1.59ms | 2.34ms | i18n | Foswiki::
1 | 1 | 1 | 1.52ms | 2.46ms | BEGIN@208 | Foswiki::
1 | 1 | 1 | 1.43ms | 11.5ms | finish | Foswiki::
210 | 6 | 3 | 1.18ms | 1.18ms | putBackBlocks | Foswiki::
504 | 2 | 1 | 1.12ms | 8.48ms | access | Foswiki::
1 | 1 | 1 | 1.00ms | 1.35ms | BEGIN@52 | Foswiki::
100 | 7 | 6 | 964µs | 964µs | expandStandardEscapes | Foswiki::
1 | 1 | 1 | 947µs | 1.03ms | BEGIN@641 | Foswiki::
106 | 1 | 1 | 895µs | 958µs | isValidWebName | Foswiki::
41 | 2 | 2 | 788µs | 788µs | addToZone | Foswiki::
54 | 6 | 5 | 757µs | 1.43ms | getScriptUrl | Foswiki::
4 | 3 | 1 | 756µs | 18.0ms | _renderZone | Foswiki::
156 | 10 | 10 | 754µs | 11.1ms | topicExists | Foswiki::
1 | 1 | 1 | 651µs | 3.01ms | BEGIN@46 | Foswiki::
1 | 1 | 1 | 500µs | 940µs | spaceOutWikiWord | Foswiki::
59 | 3 | 3 | 476µs | 940µs | getPubURL | Foswiki::
101 | 3 | 3 | 442µs | 7.35ms | webExists | Foswiki::
17 | 2 | 2 | 422µs | 1.00ms | isValidTopicName | Foswiki::
89 | 2 | 1 | 383µs | 383µs | _visitZoneID (recurses: max depth 3, inclusive time 243µs) | Foswiki::
1 | 1 | 1 | 360µs | 36.6ms | BEGIN@176 | Foswiki::
72 | 7 | 3 | 273µs | 544µs | urlEncode | Foswiki::
135 | 13 | 5 | 257µs | 257µs | inContext | Foswiki::
240 | 3 | 1 | 244µs | 244µs | SINGLE_SINGLETONS | Foswiki::
4 | 1 | 1 | 240µs | 34.1ms | inlineAlert | Foswiki::
1 | 1 | 1 | 231µs | 251ms | new | Foswiki::
2 | 1 | 1 | 230µs | 37.7ms | deepWebList | Foswiki::
1 | 1 | 1 | 225µs | 17.6ms | load_package | Foswiki::
54 | 1 | 1 | 201µs | 282µs | make_params | Foswiki::
62 | 13 | 4 | 154µs | 154µs | registerTagHandler | Foswiki::
49 | 8 | 5 | 140µs | 140µs | enterContext | Foswiki::
1 | 1 | 1 | 128µs | 18.2ms | _renderZones | Foswiki::
8 | 1 | 1 | 103µs | 103µs | parseSections | Foswiki::
63 | 1 | 1 | 96µs | 96µs | __ANON__[:401] | Foswiki::
1 | 1 | 1 | 63µs | 390µs | _parsePath | Foswiki::
10 | 6 | 4 | 56µs | 83µs | getLoginManager | Foswiki::
6 | 1 | 1 | 44µs | 185µs | __ANON__[:247] | Foswiki::
1 | 1 | 1 | 37µs | 141µs | generateHTTPHeaders | Foswiki::
4 | 4 | 2 | 28µs | 28µs | leaveContext | Foswiki::
1 | 1 | 1 | 24µs | 49µs | BEGIN@44 | Foswiki::
2 | 1 | 1 | 22µs | 18.0ms | _renderZoneById | Foswiki::
10 | 1 | 1 | 21µs | 21µs | __ANON__[:405] | Foswiki::
2 | 1 | 1 | 18µs | 145µs | __ANON__[:323] | Foswiki::
1 | 1 | 1 | 16µs | 24µs | BEGIN@45 | Foswiki::
1 | 1 | 1 | 15µs | 39µs | BEGIN@47 | Foswiki::
1 | 1 | 1 | 15µs | 15µs | BEGIN@55 | Foswiki::
5 | 1 | 1 | 14µs | 14µs | __ANON__[:394] | Foswiki::
1 | 1 | 1 | 13µs | 13µs | _getLibDir | Foswiki::
1 | 1 | 1 | 12µs | 106µs | __ANON__[:289] | Foswiki::
2 | 1 | 1 | 11µs | 37µs | getCGISession | Foswiki::
1 | 1 | 1 | 11µs | 61µs | getWorkArea | Foswiki::
1 | 1 | 1 | 11µs | 11µs | setCacheControl | Foswiki::
1 | 1 | 1 | 10µs | 24µs | BEGIN@2114 | Foswiki::
1 | 1 | 1 | 9µs | 19µs | BEGIN@2116 | Foswiki::
1 | 1 | 1 | 8µs | 13µs | __ANON__[:312] | Foswiki::
2 | 1 | 1 | 8µs | 8µs | __ANON__[:344] | Foswiki::
2 | 1 | 1 | 8µs | 8µs | __ANON__[:347] | Foswiki::
1 | 1 | 1 | 7µs | 69µs | urlDecode | Foswiki::
1 | 1 | 1 | 7µs | 7µs | BEGIN@49 | Foswiki::
2 | 2 | 1 | 5µs | 5µs | SINGLE_SINGLETONS_TRACE | Foswiki::
1 | 1 | 1 | 5µs | 5µs | __ANON__[:409] | Foswiki::
1 | 1 | 1 | 5µs | 5µs | satisfiedByCache | Foswiki::
1 | 1 | 1 | 4µs | 4µs | BEGIN@639 | Foswiki::
1 | 1 | 1 | 4µs | 4µs | __ANON__[:243] | Foswiki::
1 | 1 | 1 | 4µs | 4µs | __ANON__[:450] | Foswiki::
1 | 1 | 1 | 4µs | 4µs | __ANON__[:399] | Foswiki::
1 | 1 | 1 | 3µs | 3µs | BEGIN@640 | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:191] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:221] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:230] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:237] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:253] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:262] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:274] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:277] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:295] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:303] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:307] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:319] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:329] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:3416] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:343] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:345] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:346] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:348] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:349] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:350] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:391] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:392] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:393] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:395] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:397] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:398] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:400] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:402] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:403] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:404] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:406] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:407] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:408] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:410] | Foswiki::
0 | 0 | 0 | 0s | 0s | __ANON__[:437] | Foswiki::
0 | 0 | 0 | 0s | 0s | _expandMacroOnTopicCreation | Foswiki::
0 | 0 | 0 | 0s | 0s | _gzipAccepted | Foswiki::
0 | 0 | 0 | 0s | 0s | _isRedirectSafe | Foswiki::
0 | 0 | 0 | 0s | 0s | cacheQuery | Foswiki::
0 | 0 | 0 | 0s | 0s | entityDecode | Foswiki::
0 | 0 | 0 | 0s | 0s | entityEncode | Foswiki::
0 | 0 | 0 | 0s | 0s | expandMacrosOnTopicCreation | Foswiki::
0 | 0 | 0 | 0s | 0s | getApproxRevTime | Foswiki::
0 | 0 | 0 | 0s | 0s | isValidEmailAddress | Foswiki::
0 | 0 | 0 | 0s | 0s | isValidWikiWord | Foswiki::
0 | 0 | 0 | 0s | 0s | logEvent | Foswiki::
0 | 0 | 0 | 0s | 0s | net | Foswiki::
0 | 0 | 0 | 0s | 0s | readFile | Foswiki::
0 | 0 | 0 | 0s | 0s | redirect | Foswiki::
0 | 0 | 0 | 0s | 0s | redirectto | Foswiki::
0 | 0 | 0 | 0s | 0s | reset_i18n | Foswiki::
0 | 0 | 0 | 0s | 0s | setETags | Foswiki::
0 | 0 | 0 | 0s | 0s | splitAnchorFromUrl | Foswiki::
0 | 0 | 0 | 0s | 0s | validatePattern | Foswiki::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | |||||
2 | package Foswiki; | ||||
3 | |||||
4 | =begin TML | ||||
5 | |||||
6 | ---+ package Foswiki | ||||
7 | |||||
8 | Foswiki operates by creating a singleton object (known as the Session | ||||
9 | object) that acts as a point of reference for all the different | ||||
10 | modules in the system. This package is the class for this singleton, | ||||
11 | and also contains the vast bulk of the basic constants and the per- | ||||
12 | site configuration mechanisms. | ||||
13 | |||||
14 | Global variables are avoided wherever possible to avoid problems | ||||
15 | with CGI accelerators such as mod_perl. | ||||
16 | |||||
17 | ---++ Public Data members | ||||
18 | * =request= Pointer to the Foswiki::Request | ||||
19 | * =response= Pointer to the Foswiki::Response | ||||
20 | * =context= Hash of context ids | ||||
21 | * =plugins= Foswiki::Plugins singleton | ||||
22 | * =prefs= Foswiki::Prefs singleton | ||||
23 | * =remoteUser= Login ID when using ApacheLogin. Maintained for | ||||
24 | compatibility only, do not use. | ||||
25 | * =requestedWebName= Name of web found in URL path or =web= URL parameter | ||||
26 | * =scriptUrlPath= URL path to the current script. May be dynamically | ||||
27 | extracted from the URL path if {GetScriptUrlFromCgi}. | ||||
28 | Only required to support {GetScriptUrlFromCgi} and | ||||
29 | not consistently used. Avoid. | ||||
30 | * =access= Foswiki::Access singleton | ||||
31 | * =store= Foswiki::Store singleton | ||||
32 | * =topicName= Name of topic found in URL path or =topic= URL | ||||
33 | parameter | ||||
34 | * =urlHost= Host part of the URL (including the protocol) | ||||
35 | determined during intialisation and defaulting to | ||||
36 | {DefaultUrlHost} | ||||
37 | * =user= Unique user ID of logged-in user | ||||
38 | * =users= Foswiki::Users singleton | ||||
39 | * =webName= Name of web found in URL path, or =web= URL parameter, | ||||
40 | or {UsersWebName} | ||||
41 | |||||
42 | =cut | ||||
43 | |||||
44 | 2 | 51µs | 2 | 75µs | # spent 49µs (24+25) within Foswiki::BEGIN@44 which was called:
# once (24µs+25µs) by main::BEGIN@27 at line 44 # spent 49µs making 1 call to Foswiki::BEGIN@44
# spent 25µs making 1 call to strict::import |
45 | 2 | 42µs | 2 | 32µs | # spent 24µs (16+8) within Foswiki::BEGIN@45 which was called:
# once (16µs+8µs) by main::BEGIN@27 at line 45 # spent 24µs making 1 call to Foswiki::BEGIN@45
# spent 8µs making 1 call to warnings::import |
46 | 2 | 171µs | 2 | 3.07ms | # spent 3.01ms (651µs+2.36) within Foswiki::BEGIN@46 which was called:
# once (651µs+2.36ms) by main::BEGIN@27 at line 46 # spent 3.01ms making 1 call to Foswiki::BEGIN@46
# spent 58µs making 1 call to Exporter::import |
47 | 2 | 104µs | 2 | 62µs | # spent 39µs (15+24) within Foswiki::BEGIN@47 which was called:
# once (15µs+24µs) by main::BEGIN@27 at line 47 # spent 39µs making 1 call to Foswiki::BEGIN@47
# spent 24µs making 1 call to Exporter::import |
48 | 2 | 173µs | 2 | 14.0ms | # spent 11.4ms (4.49+6.89) within Foswiki::BEGIN@48 which was called:
# once (4.49ms+6.89ms) by main::BEGIN@27 at line 48 # spent 11.4ms making 1 call to Foswiki::BEGIN@48
# spent 2.60ms making 1 call to Error::import |
49 | 2 | 31µs | 1 | 7µs | # spent 7µs within Foswiki::BEGIN@49 which was called:
# once (7µs+0s) by main::BEGIN@27 at line 49 # spent 7µs making 1 call to Foswiki::BEGIN@49 |
50 | 2 | 170µs | 1 | 3.13ms | # spent 3.13ms (2.79+342µs) within Foswiki::BEGIN@50 which was called:
# once (2.79ms+342µs) by main::BEGIN@27 at line 50 # spent 3.13ms making 1 call to Foswiki::BEGIN@50 |
51 | 2 | 159µs | 1 | 15.1ms | # spent 15.1ms (10.2+4.99) within Foswiki::BEGIN@51 which was called:
# once (10.2ms+4.99ms) by main::BEGIN@27 at line 51 # spent 15.1ms making 1 call to Foswiki::BEGIN@51 |
52 | 2 | 155µs | 1 | 1.35ms | # spent 1.35ms (1.00+350µs) within Foswiki::BEGIN@52 which was called:
# once (1.00ms+350µs) by main::BEGIN@27 at line 52 # spent 1.35ms making 1 call to Foswiki::BEGIN@52 |
53 | 2 | 142µs | 1 | 55.0ms | # spent 55.0ms (3.97+51.0) within Foswiki::BEGIN@53 which was called:
# once (3.97ms+51.0ms) by main::BEGIN@27 at line 53 # spent 55.0ms making 1 call to Foswiki::BEGIN@53 |
54 | |||||
55 | 2 | 617µs | 1 | 15µs | # spent 15µs within Foswiki::BEGIN@55 which was called:
# once (15µs+0s) by main::BEGIN@27 at line 55 # spent 15µs making 1 call to Foswiki::BEGIN@55 |
56 | |||||
57 | # Item13331 - use CGI::ENCODE_ENTITIES introduced in CGI>=4.14 to restrict encoding | ||||
58 | # in CGI's html rendering code to only these; note that CGI's default values | ||||
59 | # still breaks some unicode byte strings | ||||
60 | 1 | 1µs | $CGI::ENCODE_ENTITIES = q{&<>"'}; | ||
61 | |||||
62 | #SMELL: Perl 5.10.0 on Mac OSX Snow Leopard warns "v-string in use/require non-portable" | ||||
63 | 1 | 12µs | require 5.008_008; # see http://foswiki.org/Development/RequirePerl588 | ||
64 | |||||
65 | # Site configuration constants | ||||
66 | 1 | 500ns | our %cfg; | ||
67 | |||||
68 | # Other computed constants | ||||
69 | 1 | 100ns | our $foswikiLibDir; | ||
70 | 1 | 100ns | our %regex; | ||
71 | 1 | 100ns | our %macros; | ||
72 | 1 | 0s | our %contextFreeSyntax; | ||
73 | 1 | 100ns | our $VERSION; | ||
74 | 1 | 100ns | our $RELEASE; | ||
75 | 1 | 300ns | our $UNICODE = 1; # flag that extensions can use to test if the core is unicode | ||
76 | 1 | 100ns | our $TRUE = 1; | ||
77 | 1 | 100ns | our $FALSE = 0; | ||
78 | 1 | 100ns | our $engine; | ||
79 | 1 | 300ns | our $TranslationToken = "\0"; # Do not deprecate - used in many plugins | ||
80 | 1 | 100ns | our $system_message; # Important broadcast message from the system | ||
81 | 1 | 500ns | my $bootstrap_message = ''; # Bootstrap message. | ||
82 | |||||
83 | # Note: the following marker is used in text to mark RENDERZONE | ||||
84 | # macros that have been hoisted from the source text of a page. It is | ||||
85 | # carefully chosen so that it is (1) not normally present in written | ||||
86 | # text (2) does not combine with other characters to form valid | ||||
87 | # wide-byte characters and (3) does not conflict with other markers used | ||||
88 | # by Foswiki/Render.pm | ||||
89 | 1 | 200ns | our $RENDERZONE_MARKER = "\3"; | ||
90 | |||||
91 | # Used by takeOut/putBack blocks | ||||
92 | 1 | 100ns | our $BLOCKID = 0; | ||
93 | 1 | 300ns | our $OC = "<!--\0"; | ||
94 | 1 | 300ns | our $CC = "\0-->"; | ||
95 | |||||
96 | # This variable is set if Foswiki is running in unit test mode. | ||||
97 | # It is provided so that modules can detect unit test mode to avoid | ||||
98 | # corrupting data spaces. | ||||
99 | 1 | 300ns | our $inUnitTestMode = 0; | ||
100 | |||||
101 | 240 | 443µs | sub SINGLE_SINGLETONS { 0 } | ||
102 | 2 | 8µs | sub SINGLE_SINGLETONS_TRACE { 0 } | ||
103 | |||||
104 | # Returns the full path of the directory containing Foswiki.pm | ||||
105 | # spent 13µs within Foswiki::_getLibDir which was called:
# once (13µs+0s) by Foswiki::BEGIN@176 at line 623 | ||||
106 | 1 | 300ns | return $foswikiLibDir if $foswikiLibDir; | ||
107 | |||||
108 | 1 | 900ns | $foswikiLibDir = $INC{'Foswiki.pm'}; | ||
109 | |||||
110 | # fix path relative to location of called script | ||||
111 | 1 | 1µs | if ( $foswikiLibDir =~ m/^\./ ) { | ||
112 | print STDERR | ||||
113 | "WARNING: Foswiki lib path $foswikiLibDir is relative; you should make it absolute, otherwise some scripts may not run from the command line."; | ||||
114 | my $bin; | ||||
115 | |||||
116 | # SMELL : Should not assume environment variables; get data from request | ||||
117 | if ( $ENV{SCRIPT_FILENAME} | ||||
118 | && $ENV{SCRIPT_FILENAME} =~ m#^(.+)/.+?$# ) | ||||
119 | { | ||||
120 | |||||
121 | # CGI script name | ||||
122 | # implicit untaint OK, because of use of $SCRIPT_FILENAME | ||||
123 | $bin = $1; | ||||
124 | } | ||||
125 | elsif ( $0 =~ m#^(.*)/.*?$# ) { | ||||
126 | |||||
127 | # program name | ||||
128 | # implicit untaint OK, because of use of $PROGRAM_NAME ($0) | ||||
129 | $bin = $1; | ||||
130 | } | ||||
131 | else { | ||||
132 | |||||
133 | # last ditch; relative to current directory. | ||||
134 | require Cwd; | ||||
135 | $bin = Cwd::cwd(); | ||||
136 | } | ||||
137 | $foswikiLibDir = "$bin/$foswikiLibDir/"; | ||||
138 | |||||
139 | # normalize "/../" and "/./" | ||||
140 | while ( $foswikiLibDir =~ s|([\\/])[^\\/]+[\\/]\.\.[\\/]|$1| ) { | ||||
141 | } | ||||
142 | $foswikiLibDir =~ s|([\\/])\.[\\/]|$1|g; | ||||
143 | } | ||||
144 | 1 | 8µs | $foswikiLibDir =~ s|([\\/])[\\/]*|$1|g; # reduce "//" to "/" | ||
145 | 1 | 900ns | $foswikiLibDir =~ s|[\\/]$||; # cut trailing "/" | ||
146 | |||||
147 | 1 | 5µs | return $foswikiLibDir; | ||
148 | } | ||||
149 | |||||
150 | # Character encoding/decoding stubs. Done so we can ovveride | ||||
151 | # if necessary (e.g. on OSX we may want to monkey-patch in a | ||||
152 | # NFC/NFD module) | ||||
153 | |||||
154 | =begin TML | ||||
155 | |||||
156 | ---++ StaticMethod decode_utf8($octets) -> $unicode | ||||
157 | |||||
158 | Decode a binary string of octets known to be encoded using UTF-8 into | ||||
159 | perl characters (unicode). | ||||
160 | |||||
161 | =cut | ||||
162 | |||||
163 | 1 | 2µs | *decode_utf8 = \&Encode::decode_utf8; | ||
164 | |||||
165 | =begin TML | ||||
166 | |||||
167 | ---++ StaticMethod encode_utf8($unicode) -> $octets | ||||
168 | |||||
169 | Encode a perl character string into a binary string of octets | ||||
170 | encoded using UTF-8. | ||||
171 | |||||
172 | =cut | ||||
173 | |||||
174 | 1 | 600ns | *encode_utf8 = \&Encode::encode_utf8; | ||
175 | |||||
176 | # spent 36.6ms (360µs+36.2) within Foswiki::BEGIN@176 which was called:
# once (360µs+36.2ms) by main::BEGIN@27 at line 636 | ||||
177 | |||||
178 | # First thing we do; make sure we print unicode errors | ||||
179 | 1 | 10µs | binmode( STDERR, ":utf8" ); | ||
180 | |||||
181 | #Monitor::MARK("Start of BEGIN block in Foswiki.pm"); | ||||
182 | 1 | 300ns | if (DEBUG) { | ||
183 | if ( not $Assert::soft ) { | ||||
184 | |||||
185 | # If ASSERTs are on (and not soft), then warnings are errors. | ||||
186 | # Paranoid, but the only way to be sure we eliminate them all. | ||||
187 | # ASSERTS are turned on by defining the environment variable | ||||
188 | # FOSWIKI_ASSERTS. If ASSERTs are off, this is assumed to be a | ||||
189 | # production environment, and no stack traces or paths are | ||||
190 | # output to the browser. | ||||
191 | $SIG{'__WARN__'} = sub { die @_ }; | ||||
192 | $Error::Debug = 1; # verbose stack traces, please | ||||
193 | } | ||||
194 | else { | ||||
195 | |||||
196 | # ASSERTs are soft, so warnings are not errors | ||||
197 | # but ASSERTs are enabled. This is useful for tracking down | ||||
198 | # problems that only manifest on production servers. | ||||
199 | $Error::Debug = 0; # no verbose stack traces | ||||
200 | } | ||||
201 | } | ||||
202 | else { | ||||
203 | 1 | 700ns | $Error::Debug = 0; # no verbose stack traces | ||
204 | } | ||||
205 | |||||
206 | # DO NOT CHANGE THE FORMAT OF $VERSION. | ||||
207 | # Use $RELEASE for a descriptive version. | ||||
208 | 4 | 2.38ms | 4 | 2.58ms | # spent 2.46ms (1.52+932µs) within Foswiki::BEGIN@208 which was called:
# once (1.52ms+932µs) by main::BEGIN@27 at line 208 # spent 2.46ms making 1 call to Foswiki::BEGIN@208
# spent 52µs making 1 call to version::vxs::declare
# spent 48µs making 1 call to UNIVERSAL::VERSION
# spent 21µs making 1 call to version::import |
209 | 1 | 300ns | $RELEASE = 'Foswiki-2.0.1-RC2'; | ||
210 | |||||
211 | # Default handlers for different %TAGS% | ||||
212 | # Where an entry is set as 'undef', the tag will be demand-loaded | ||||
213 | # from Foswiki::Macros, if it is used. This tactic is used to reduce | ||||
214 | # the load time of this module, especially when it is used from | ||||
215 | # REST handlers. | ||||
216 | %macros = ( | ||||
217 | ADDTOHEAD => undef, | ||||
218 | |||||
219 | # deprecated, use ADDTOZONE instead | ||||
220 | ADDTOZONE => undef, | ||||
221 | ALLVARIABLES => sub { $_[0]->{prefs}->stringify() }, | ||||
222 | ATTACHURL => undef, | ||||
223 | ATTACHURLPATH => undef, | ||||
224 | DATE => sub { | ||||
225 | Foswiki::Time::formatTime( | ||||
226 | time(), | ||||
227 | $Foswiki::cfg{DefaultDateFormat}, | ||||
228 | $Foswiki::cfg{DisplayTimeValues} | ||||
229 | ); | ||||
230 | }, | ||||
231 | DISPLAYTIME => sub { | ||||
232 | Foswiki::Time::formatTime( | ||||
233 | time(), | ||||
234 | $_[1]->{_DEFAULT} || '', | ||||
235 | $Foswiki::cfg{DisplayTimeValues} | ||||
236 | ); | ||||
237 | }, | ||||
238 | ENCODE => undef, | ||||
239 | ENV => undef, | ||||
240 | EXPAND => undef, | ||||
241 | FORMAT => undef, | ||||
242 | FORMFIELD => undef, | ||||
243 | 1 | 5µs | # spent 4µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:243] which was called:
# once (4µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435 | ||
244 | # spent 185µs (44+141) within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:247] which was called 6 times, avg 31µs/call:
# 6 times (44µs+141µs) by Foswiki::_expandMacroOnTopicRendering at line 3435, avg 31µs/call | ||||
245 | 6 | 33µs | 6 | 141µs | Foswiki::Time::formatTime( time(), $_[1]->{_DEFAULT} || '', # spent 141µs making 6 calls to Foswiki::Time::formatTime, avg 24µs/call |
246 | 'gmtime' ); | ||||
247 | }, | ||||
248 | GROUPINFO => undef, | ||||
249 | GROUPS => undef, | ||||
250 | HTTP_HOST => | ||||
251 | |||||
252 | #deprecated functionality, now implemented using %ENV% | ||||
253 | sub { $_[0]->{request}->header('Host') || '' }, | ||||
254 | HTTP => undef, | ||||
255 | HTTPS => undef, | ||||
256 | ICON => undef, | ||||
257 | ICONURL => undef, | ||||
258 | ICONURLPATH => undef, | ||||
259 | IF => undef, | ||||
260 | INCLUDE => undef, | ||||
261 | INTURLENCODE => undef, | ||||
262 | LANGUAGE => sub { $_[0]->i18n->language(); }, | ||||
263 | LANGUAGES => undef, | ||||
264 | MAKETEXT => undef, | ||||
265 | META => undef, # deprecated | ||||
266 | METASEARCH => undef, # deprecated | ||||
267 | NONCE => undef, | ||||
268 | PERLDEPENDENCYREPORT => undef, | ||||
269 | NOP => | ||||
270 | |||||
271 | # Remove NOP tag in template topics but show content. | ||||
272 | # Used in template _topics_ (not templates, per se, but | ||||
273 | # topics used as templates for new topics) | ||||
274 | sub { $_[1]->{_RAW} ? $_[1]->{_RAW} : '<nop>' }, | ||||
275 | PLUGINVERSION => sub { | ||||
276 | $_[0]->{plugins}->getPluginVersion( $_[1]->{_DEFAULT} ); | ||||
277 | }, | ||||
278 | PUBURL => undef, | ||||
279 | PUBURLPATH => undef, | ||||
280 | QUERY => undef, | ||||
281 | QUERYPARAMS => undef, | ||||
282 | # spent 106µs (12+94) within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:289] which was called:
# once (12µs+94µs) by Foswiki::_expandMacroOnTopicRendering at line 3435 | ||||
283 | 1 | 3µs | 1 | 94µs | my $s = $_[0]->{request}->queryString(); # spent 94µs making 1 call to Foswiki::Request::queryString |
284 | |||||
285 | # Aggressively encode QUERYSTRING (even more than the | ||||
286 | # default) because it might be leveraged for XSS | ||||
287 | 1 | 3µs | $s =~ s/(['\/])/'%'.sprintf('%02x', ord($1))/ge; | ||
288 | 1 | 5µs | return $s; | ||
289 | }, | ||||
290 | RELATIVETOPICPATH => undef, | ||||
291 | REMOTE_ADDR => | ||||
292 | |||||
293 | # DEPRECATED, now implemented using %ENV% | ||||
294 | #move to compatibility plugin in Foswiki 2.0 | ||||
295 | sub { $_[0]->{request}->remoteAddress() || ''; }, | ||||
296 | REMOTE_PORT => | ||||
297 | |||||
298 | # DEPRECATED | ||||
299 | # CGI/1.1 (RFC 3875) doesn't specify REMOTE_PORT, | ||||
300 | # but some webservers implement it. However, since | ||||
301 | # it's not RFC compliant, Foswiki should not rely on | ||||
302 | # it. So we get more portability. | ||||
303 | sub { '' }, | ||||
304 | REMOTE_USER => | ||||
305 | |||||
306 | # DEPRECATED | ||||
307 | sub { $_[0]->{request}->remoteUser() || '' }, | ||||
308 | RENDERZONE => undef, | ||||
309 | REVINFO => undef, | ||||
310 | REVTITLE => undef, | ||||
311 | REVARG => undef, | ||||
312 | 1 | 7µs | 1 | 5µs | # spent 13µs (8+5) within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:312] which was called:
# once (8µs+5µs) by Foswiki::_expandMacroOnTopicRendering at line 3435 # spent 5µs making 1 call to Foswiki::Request::action |
313 | SCRIPTURL => undef, | ||||
314 | SCRIPTURLPATH => undef, | ||||
315 | SEARCH => undef, | ||||
316 | SEP => | ||||
317 | |||||
318 | # Shortcut to %TMPL:P{"sep"}% | ||||
319 | sub { $_[0]->templates->expandTemplate('sep') }, | ||||
320 | # spent 145µs (18+127) within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:323] which was called 2 times, avg 72µs/call:
# 2 times (18µs+127µs) by Foswiki::_expandMacroOnTopicRendering at line 3435, avg 72µs/call | ||||
321 | 2 | 15µs | 2 | 127µs | Foswiki::Time::formatTime( time(), $_[1]->{_DEFAULT} || '', # spent 127µs making 2 calls to Foswiki::Time::formatTime, avg 64µs/call |
322 | 'servertime' ); | ||||
323 | }, | ||||
324 | SERVERINFORMATION => undef, | ||||
325 | SET => undef, | ||||
326 | SHOWPREFERENCE => undef, | ||||
327 | SPACEDTOPIC => undef, | ||||
328 | SPACEOUT => undef, | ||||
329 | 'TMPL:P' => sub { $_[0]->templates->tmplP( $_[1] ) }, | ||||
330 | TOPICLIST => undef, | ||||
331 | URLENCODE => undef, | ||||
332 | URLPARAM => undef, | ||||
333 | USERINFO => undef, | ||||
334 | USERNAME => undef, | ||||
335 | VAR => undef, | ||||
336 | WEBLIST => undef, | ||||
337 | WIKINAME => undef, | ||||
338 | WIKIUSERNAME => undef, | ||||
339 | DISPLAYDEPENDENCIES => undef, | ||||
340 | |||||
341 | # Constant tag strings _not_ dependent on config. These get nicely | ||||
342 | # optimised by the compiler. | ||||
343 | STOPSECTION => sub { '' }, | ||||
344 | 2 | 8µs | # spent 8µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:344] which was called 2 times, avg 4µs/call:
# 2 times (8µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435, avg 4µs/call | ||
345 | WIKIVERSION => sub { $VERSION }, | ||||
346 | WIKIRELEASE => sub { $RELEASE }, | ||||
347 | 2 | 8µs | # spent 8µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:347] which was called 2 times, avg 4µs/call:
# 2 times (8µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435, avg 4µs/call | ||
348 | STARTINCLUDE => sub { '' }, | ||||
349 | STOPINCLUDE => sub { '' }, | ||||
350 | ENDINCLUDE => sub { '' }, | ||||
351 | 1 | 57µs | ); | ||
352 | 1 | 900ns | $contextFreeSyntax{IF} = 1; | ||
353 | |||||
354 | # Load LocalSite.cfg | ||||
355 | 1 | 3µs | 1 | 20.7ms | if ( Foswiki::Configure::Load::readConfig( 0, 0, 0 ) ) { # spent 20.7ms making 1 call to Foswiki::Configure::Load::readConfig |
356 | $Foswiki::cfg{isVALID} = 1; | ||||
357 | } | ||||
358 | else { | ||||
359 | $bootstrap_message = Foswiki::Configure::Load::bootstrapConfig(); | ||||
360 | eval 'require Foswiki::Plugins::ConfigurePlugin'; | ||||
361 | die | ||||
362 | "LocalSite.cfg load failed, and ConfigurePlugin could not be loaded: $@" | ||||
363 | if $@; | ||||
364 | } | ||||
365 | |||||
366 | 1 | 400ns | if ( $Foswiki::cfg{UseLocale} ) { | ||
367 | require locale; | ||||
368 | import locale(); | ||||
369 | } | ||||
370 | elsif (DEBUG) { | ||||
371 | eval { require Taint::Runtime; }; | ||||
372 | if ($@) { | ||||
373 | print STDERR | ||||
374 | "DEVELOPER WARNING: taint mode could not be enabled. Is Taint::Runtime installed?\n"; | ||||
375 | } | ||||
376 | else { | ||||
377 | # Enable taint checking | ||||
378 | Taint::Runtime::_taint_start(); | ||||
379 | } | ||||
380 | } | ||||
381 | |||||
382 | # If not set, default to strikeone validation | ||||
383 | 1 | 500ns | $Foswiki::cfg{Validation}{Method} ||= 'strikeone'; | ||
384 | 1 | 300ns | $Foswiki::cfg{Validation}{ValidForTime} = $Foswiki::cfg{LeaseLength} | ||
385 | unless defined $Foswiki::cfg{Validation}{ValidForTime}; | ||||
386 | 1 | 800ns | $Foswiki::cfg{Validation}{MaxKeys} = 1000 | ||
387 | unless defined $Foswiki::cfg{Validation}{MaxKeys}; | ||||
388 | |||||
389 | # Constant tags dependent on the config | ||||
390 | $macros{ALLOWLOGINNAME} = | ||||
391 | 1 | 2µs | sub { $Foswiki::cfg{Register}{AllowLoginName} || 0 }; | ||
392 | 1 | 1µs | $macros{AUTHREALM} = sub { $Foswiki::cfg{AuthRealm} }; | ||
393 | 1 | 1µs | $macros{DEFAULTURLHOST} = sub { $Foswiki::cfg{DefaultUrlHost} }; | ||
394 | 6 | 16µs | # spent 14µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:394] which was called 5 times, avg 3µs/call:
# 5 times (14µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435, avg 3µs/call | ||
395 | 1 | 1µs | $macros{LOCALSITEPREFS} = sub { $Foswiki::cfg{LocalSitePreferences} }; | ||
396 | $macros{NOFOLLOW} = | ||||
397 | 1 | 2µs | sub { $Foswiki::cfg{NoFollow} ? 'rel=' . $Foswiki::cfg{NoFollow} : '' }; | ||
398 | 1 | 2µs | $macros{NOTIFYTOPIC} = sub { $Foswiki::cfg{NotifyTopicName} }; | ||
399 | 2 | 8µs | # spent 4µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:399] which was called:
# once (4µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435 | ||
400 | 1 | 2µs | $macros{STATISTICSTOPIC} = sub { $Foswiki::cfg{Stats}{TopicName} }; | ||
401 | 64 | 151µs | # spent 96µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:401] which was called 63 times, avg 2µs/call:
# 63 times (96µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435, avg 2µs/call | ||
402 | 1 | 1µs | $macros{TRASHWEB} = sub { $Foswiki::cfg{TrashWebName} }; | ||
403 | 1 | 1µs | $macros{SANDBOXWEB} = sub { $Foswiki::cfg{SandboxWebName} }; | ||
404 | 1 | 900ns | $macros{WIKIADMINLOGIN} = sub { $Foswiki::cfg{AdminUserLogin} }; | ||
405 | 11 | 28µs | # spent 21µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:405] which was called 10 times, avg 2µs/call:
# 10 times (21µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435, avg 2µs/call | ||
406 | 1 | 1µs | $macros{WEBPREFSTOPIC} = sub { $Foswiki::cfg{WebPrefsTopicName} }; | ||
407 | 1 | 900ns | $macros{WIKIPREFSTOPIC} = sub { $Foswiki::cfg{SitePrefsTopicName} }; | ||
408 | 1 | 900ns | $macros{WIKIUSERSTOPIC} = sub { $Foswiki::cfg{UsersTopicName} }; | ||
409 | 2 | 7µs | # spent 5µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:409] which was called:
# once (5µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435 | ||
410 | 1 | 1µs | $macros{WIKIWEBMASTERNAME} = sub { $Foswiki::cfg{WebMasterName} }; | ||
411 | |||||
412 | # locale setup | ||||
413 | # | ||||
414 | # | ||||
415 | # Note that 'use locale' must be done in BEGIN block for regexes and | ||||
416 | # sorting to work properly, although regexes can still work without | ||||
417 | # this in 'non-locale regexes' mode. | ||||
418 | |||||
419 | 1 | 200ns | if ( $Foswiki::cfg{UseLocale} ) { | ||
420 | |||||
421 | # Set environment variables for grep | ||||
422 | $ENV{LC_CTYPE} = $Foswiki::cfg{Site}{Locale}; | ||||
423 | |||||
424 | # Load POSIX for I18N support. | ||||
425 | require POSIX; | ||||
426 | import POSIX qw( locale_h LC_CTYPE LC_COLLATE ); | ||||
427 | |||||
428 | # SMELL: mod_perl compatibility note: If Foswiki is running under Apache, | ||||
429 | # won't this play with the Apache process's locale settings too? | ||||
430 | # What effects would this have? | ||||
431 | setlocale( &LC_CTYPE, $Foswiki::cfg{Site}{Locale} ); | ||||
432 | setlocale( &LC_COLLATE, $Foswiki::cfg{Site}{Locale} ); | ||||
433 | } | ||||
434 | |||||
435 | $macros{CHARSET} = sub { | ||||
436 | 'utf-8'; | ||||
437 | 1 | 1µs | }; | ||
438 | |||||
439 | # spent 4µs within Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:450] which was called:
# once (4µs+0s) by Foswiki::_expandMacroOnTopicRendering at line 3435 | ||||
440 | 1 | 800ns | my $lang = 'en'; # the default | ||
441 | 1 | 1µs | if ( $Foswiki::cfg{UseLocale} | ||
442 | && $Foswiki::cfg{Site}{Locale} =~ m/^([a-z]+)(?:_([a-z]+))?/i ) | ||||
443 | { | ||||
444 | |||||
445 | # Locale identifiers use _ as the separator in the language, but a minus sign is required | ||||
446 | # for HTML (see http://www.ietf.org/rfc/rfc1766.txt) | ||||
447 | $lang = $1 . ( $2 ? "-$2" : '' ); | ||||
448 | } | ||||
449 | 1 | 4µs | return $lang; | ||
450 | 1 | 2µs | }; | ||
451 | |||||
452 | # Set up pre-compiled regexes for use in rendering. | ||||
453 | # In the regex hash, all precompiled REs have "Regex" at the | ||||
454 | # end of the name. Anything else is a string, either intended | ||||
455 | # for use as a character class, or as a sub-expression in | ||||
456 | # another compiled RE. | ||||
457 | |||||
458 | # Character class components for use in regexes. | ||||
459 | # (Pre-UTF-8 compatibility; not used in core) | ||||
460 | 1 | 700ns | $regex{upperAlpha} = '[:upper:]'; | ||
461 | 1 | 300ns | $regex{lowerAlpha} = '[:lower:]'; | ||
462 | 1 | 400ns | $regex{numeric} = '[:digit:]'; | ||
463 | 1 | 400ns | $regex{mixedAlpha} = '[:alpha:]'; | ||
464 | 1 | 400ns | $regex{mixedAlphaNum} = '[:alnum:]'; | ||
465 | 1 | 300ns | $regex{lowerAlphaNum} = '[:lower:][:digit:]'; | ||
466 | 1 | 400ns | $regex{upperAlphaNum} = '[:upper:][:digit:]'; | ||
467 | |||||
468 | # Compile regexes for efficiency and ease of use | ||||
469 | # Note: qr// locks in regex modes (i.e. '-xism' here) - see Friedl | ||||
470 | # book at http://regex.info/. | ||||
471 | |||||
472 | 1 | 1µs | $regex{linkProtocolPattern} = $Foswiki::cfg{LinkProtocolPattern} | ||
473 | || '(file|ftp|gopher|https|http|irc|mailto|news|nntp|telnet)'; | ||||
474 | |||||
475 | # Header patterns based on '+++'. The '###' are reserved for numbered | ||||
476 | # headers | ||||
477 | # '---++ Header', '---## Header' | ||||
478 | 1 | 2µs | $regex{headerPatternDa} = qr/^---+(\++|\#+)(.*)$/m; | ||
479 | |||||
480 | # '<h6>Header</h6> | ||||
481 | 1 | 900ns | $regex{headerPatternHt} = qr/^<h([1-6])>(.+?)<\/h\1>/mi; | ||
482 | |||||
483 | # '---++!! Header' or '---++ Header %NOTOC% ^top' | ||||
484 | 1 | 500ns | $regex{headerPatternNoTOC} = '(\!\!+|%NOTOC%)'; | ||
485 | |||||
486 | # Foswiki concept regexes | ||||
487 | 1 | 1µs | $regex{wikiWordRegex} = qr( | ||
488 | [[:upper:]]+ | ||||
489 | [[:lower:][:digit:]]+ | ||||
490 | [[:upper:]]+ | ||||
491 | [[:alnum:]]* | ||||
492 | )xo; | ||||
493 | 1 | 900ns | $regex{webNameBaseRegex} = qr/[[:upper:]]+[[:alnum:]_]*/; | ||
494 | 1 | 500ns | if ( $Foswiki::cfg{EnableHierarchicalWebs} ) { | ||
495 | 1 | 23µs | $regex{webNameRegex} = qr( | ||
496 | $regex{webNameBaseRegex} | ||||
497 | (?:(?:[\.\/]$regex{webNameBaseRegex})+)* | ||||
498 | )xo; | ||||
499 | } | ||||
500 | else { | ||||
501 | $regex{webNameRegex} = $regex{webNameBaseRegex}; | ||||
502 | } | ||||
503 | 1 | 1µs | $regex{defaultWebNameRegex} = qr/_[[:alnum:]_]+/; | ||
504 | 1 | 2µs | $regex{anchorRegex} = qr/\#[[:alnum:]:._]+/; | ||
505 | 1 | 600ns | my $abbrevLength = $Foswiki::cfg{AcronymLength} || 3; | ||
506 | 1 | 9µs | $regex{abbrevRegex} = qr/[[:upper:]]{$abbrevLength,}s?\b/; | ||
507 | |||||
508 | 1 | 21µs | $regex{topicNameRegex} = | ||
509 | qr/(?:(?:$regex{wikiWordRegex})|(?:$regex{abbrevRegex}))/; | ||||
510 | |||||
511 | # Email regex, e.g. for WebNotify processing and email matching | ||||
512 | # during rendering. | ||||
513 | |||||
514 | 1 | 7µs | my $emailAtom = qr([A-Z0-9\Q!#\$%&'*+-/=?^_`{|}~\E])i; # Per RFC 5322 ] | ||
515 | |||||
516 | # Valid TLD's at http://data.iana.org/TLD/tlds-alpha-by-domain.txt | ||||
517 | # Version 2012022300, Last Updated Thu Feb 23 15:07:02 2012 UTC | ||||
518 | 1 | 700ns | my $validTLD = $Foswiki::cfg{Email}{ValidTLD}; | ||
519 | |||||
520 | 2 | 14µs | unless ( eval { qr/$validTLD/ } ) { | ||
521 | $validTLD = | ||||
522 | qr(AERO|ARPA|ASIA|BIZ|CAT|COM|COOP|EDU|GOV|INFO|INT|JOBS|MIL|MOBI|MUSEUM|NAME|NET|ORG|PRO|TEL|TRAVEL|XXX)i; | ||||
523 | |||||
524 | # Too early to log, should do something here other than die (which prevents fixing) | ||||
525 | # warn is trapped and turned into a die... | ||||
526 | #warn( "{Email}{ValidTLD} does not compile, using default" ); | ||||
527 | } | ||||
528 | |||||
529 | 1 | 47µs | $regex{emailAddrRegex} = qr( | ||
530 | (?: # LEFT Side of Email address | ||||
531 | (?:$emailAtom+ # Valid characters left side of email address | ||||
532 | (?:\.$emailAtom+)* # And 0 or more dotted atoms | ||||
533 | ) | ||||
534 | | | ||||
535 | (?:"[\x21\x23-\x5B\x5D-\x7E\s]+?") # or a quoted string per RFC 5322 | ||||
536 | ) | ||||
537 | @ | ||||
538 | (?: # RIGHT side of Email address | ||||
539 | (?: # FQDN | ||||
540 | [a-z0-9-]+ # hostname part | ||||
541 | (?:\.[a-z0-9-]+)* # 0 or more alphanumeric domains following a dot. | ||||
542 | \.(?: # TLD | ||||
543 | (?:[a-z]{2,2}) # 2 character TLD | ||||
544 | | | ||||
545 | $validTLD # TLD's longer than 2 characters | ||||
546 | ) | ||||
547 | ) | ||||
548 | | | ||||
549 | (?:\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\]) # dotted triplets IP Address | ||||
550 | ) | ||||
551 | )oxi; | ||||
552 | |||||
553 | # Item11185: This is how things were before we began Operation Unicode: | ||||
554 | # | ||||
555 | # $regex{filenameInvalidCharRegex} = qr/[^[:alnum:]\. _-]/; | ||||
556 | # | ||||
557 | # It was only used in Foswiki::Sandbox::sanitizeAttachmentName(), which now | ||||
558 | # uses $Foswiki::cfg{NameFilter} instead. | ||||
559 | # See RobustnessTests::test_sanitizeAttachmentName | ||||
560 | # | ||||
561 | # Actually, this is used in GenPDFPrincePlugin; let's copy NameFilter | ||||
562 | 1 | 6µs | $regex{filenameInvalidCharRegex} = qr/$Foswiki::cfg{NameFilter}/; | ||
563 | |||||
564 | # Multi-character alpha-based regexes | ||||
565 | 1 | 2µs | $regex{mixedAlphaNumRegex} = qr/[[:alnum:]]*/; | ||
566 | |||||
567 | # %TAG% name | ||||
568 | 1 | 400ns | $regex{tagNameRegex} = '[A-Za-z][A-Za-z0-9_:]*'; | ||
569 | |||||
570 | # Set statement in a topic | ||||
571 | 1 | 400ns | $regex{bulletRegex} = '^(?:\t| )+\*'; | ||
572 | 1 | 1µs | $regex{setRegex} = $regex{bulletRegex} . '\s+(Set|Local)\s+'; | ||
573 | 1 | 1µs | $regex{setVarRegex} = | ||
574 | $regex{setRegex} . '(' . $regex{tagNameRegex} . ')\s*=\s*(.*)$'; | ||||
575 | |||||
576 | # Character encoding regexes | ||||
577 | |||||
578 | # Regex to match only a valid UTF-8 character, taking care to avoid | ||||
579 | # security holes due to overlong encodings by excluding the relevant | ||||
580 | # gaps in UTF-8 encoding space - see 'perldoc perlunicode', Unicode | ||||
581 | # Encodings section. Tested against Markus Kuhn's UTF-8 test file | ||||
582 | # at http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt. | ||||
583 | 1 | 1µs | $regex{validUtf8CharRegex} = qr{ | ||
584 | # Single byte - ASCII | ||||
585 | [\x00-\x7F] | ||||
586 | | | ||||
587 | |||||
588 | # 2 bytes | ||||
589 | [\xC2-\xDF][\x80-\xBF] | ||||
590 | | | ||||
591 | |||||
592 | # 3 bytes | ||||
593 | |||||
594 | # Avoid illegal codepoints - negative lookahead | ||||
595 | (?!\xEF\xBF[\xBE\xBF]) | ||||
596 | |||||
597 | # Match valid codepoints | ||||
598 | (?: | ||||
599 | ([\xE0][\xA0-\xBF])| | ||||
600 | ([\xE1-\xEC\xEE-\xEF][\x80-\xBF])| | ||||
601 | ([\xED][\x80-\x9F]) | ||||
602 | ) | ||||
603 | [\x80-\xBF] | ||||
604 | | | ||||
605 | |||||
606 | # 4 bytes | ||||
607 | (?: | ||||
608 | ([\xF0][\x90-\xBF])| | ||||
609 | ([\xF1-\xF3][\x80-\xBF])| | ||||
610 | ([\xF4][\x80-\x8F]) | ||||
611 | ) | ||||
612 | [\x80-\xBF][\x80-\xBF] | ||||
613 | }xo; | ||||
614 | |||||
615 | 1 | 30µs | $regex{validUtf8StringRegex} = qr/^(?:$regex{validUtf8CharRegex})+$/; | ||
616 | |||||
617 | # Check for unsafe search regex mode (affects filtering in) - default | ||||
618 | # to safe mode | ||||
619 | 1 | 300ns | $Foswiki::cfg{ForceUnsafeRegexes} = 0 | ||
620 | unless defined $Foswiki::cfg{ForceUnsafeRegexes}; | ||||
621 | |||||
622 | # initialize lib directory early because of later 'cd's | ||||
623 | 1 | 2µs | 1 | 13µs | _getLibDir(); # spent 13µs making 1 call to Foswiki::_getLibDir |
624 | |||||
625 | # initialize the runtime engine | ||||
626 | 1 | 400ns | if ( !defined $Foswiki::cfg{Engine} ) { | ||
627 | |||||
628 | # Caller did not define an engine; try and work it out (mainly for | ||||
629 | # the benefit of pre-1.0 CGI scripts) | ||||
630 | $Foswiki::cfg{Engine} = 'Foswiki::Engine::Legacy'; | ||||
631 | } | ||||
632 | 1 | 35µs | $engine = eval qq(use $Foswiki::cfg{Engine}; $Foswiki::cfg{Engine}->new); # spent 104µs executing statements in string eval # includes 736µs spent executing 1 call to 1 sub defined therein. | ||
633 | 1 | 4µs | die $@ if $@; | ||
634 | |||||
635 | #Monitor::MARK('End of BEGIN block in Foswiki.pm'); | ||||
636 | 1 | 23µs | 1 | 36.6ms | } # spent 36.6ms making 1 call to Foswiki::BEGIN@176 |
637 | |||||
638 | # Components that all requests need | ||||
639 | 2 | 20µs | 1 | 4µs | # spent 4µs within Foswiki::BEGIN@639 which was called:
# once (4µs+0s) by main::BEGIN@27 at line 639 # spent 4µs making 1 call to Foswiki::BEGIN@639 |
640 | 2 | 18µs | 1 | 3µs | # spent 3µs within Foswiki::BEGIN@640 which was called:
# once (3µs+0s) by main::BEGIN@27 at line 640 # spent 3µs making 1 call to Foswiki::BEGIN@640 |
641 | 2 | 86µs | 1 | 1.03ms | # spent 1.03ms (947µs+79µs) within Foswiki::BEGIN@641 which was called:
# once (947µs+79µs) by main::BEGIN@27 at line 641 # spent 1.03ms making 1 call to Foswiki::BEGIN@641 |
642 | 2 | 144µs | 1 | 13.4ms | # spent 13.4ms (11.5+1.95) within Foswiki::BEGIN@642 which was called:
# once (11.5ms+1.95ms) by main::BEGIN@27 at line 642 # spent 13.4ms making 1 call to Foswiki::BEGIN@642 |
643 | 2 | 117µs | 1 | 12.2ms | # spent 12.2ms (2.34+9.82) within Foswiki::BEGIN@643 which was called:
# once (2.34ms+9.82ms) by main::BEGIN@27 at line 643 # spent 12.2ms making 1 call to Foswiki::BEGIN@643 |
644 | 2 | 103µs | 1 | 3.57ms | # spent 3.57ms (2.07+1.50) within Foswiki::BEGIN@644 which was called:
# once (2.07ms+1.50ms) by main::BEGIN@27 at line 644 # spent 3.57ms making 1 call to Foswiki::BEGIN@644 |
645 | 2 | 82µs | 1 | 4.30ms | # spent 4.30ms (1.79+2.51) within Foswiki::BEGIN@645 which was called:
# once (1.79ms+2.51ms) by main::BEGIN@27 at line 645 # spent 4.30ms making 1 call to Foswiki::BEGIN@645 |
646 | 2 | 90µs | 1 | 6.33ms | # spent 6.33ms (2.17+4.16) within Foswiki::BEGIN@646 which was called:
# once (2.17ms+4.16ms) by main::BEGIN@27 at line 646 # spent 6.33ms making 1 call to Foswiki::BEGIN@646 |
647 | 2 | 4.46ms | 1 | 13.1ms | # spent 13.1ms (2.75+10.4) within Foswiki::BEGIN@647 which was called:
# once (2.75ms+10.4ms) by main::BEGIN@27 at line 647 # spent 13.1ms making 1 call to Foswiki::BEGIN@647 |
648 | |||||
649 | =begin TML | ||||
650 | |||||
651 | ---++ ObjectMethod writeCompletePage( $text, $pageType, $contentType ) | ||||
652 | |||||
653 | Write a complete HTML page with basic header to the browser. | ||||
654 | * =$text= is the text of the page script (<html> to </html> if it's HTML) | ||||
655 | * =$pageType= - May be "edit", which will cause headers to be generated that force | ||||
656 | caching for 24 hours, to prevent Codev.BackFromPreviewLosesText bug, which caused | ||||
657 | data loss with IE5 and IE6. | ||||
658 | * =$contentType= - page content type | text/html | ||||
659 | |||||
660 | This method removes noautolink and nop tags before outputting the page unless | ||||
661 | $contentType is text/plain. | ||||
662 | |||||
663 | =cut | ||||
664 | |||||
665 | # spent 23.4ms (4.94+18.5) within Foswiki::writeCompletePage which was called:
# once (4.94ms+18.5ms) by Foswiki::UI::View::view at line 472 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm | ||||
666 | 1 | 5µs | my ( $this, $text, $pageType, $contentType ) = @_; | ||
667 | |||||
668 | # true if the body is to be output without encoding to utf8 | ||||
669 | # first. This is the case if the body has been gzipped and/or | ||||
670 | # rendered from the cache | ||||
671 | 1 | 1µs | my $binary_body = 0; | ||
672 | |||||
673 | 1 | 400ns | $contentType ||= 'text/html'; | ||
674 | |||||
675 | 1 | 3µs | 1 | 9µs | my $cgis = $this->{users}->getCGISession(); # spent 9µs making 1 call to Foswiki::Users::getCGISession |
676 | 1 | 600ns | if ( $cgis | ||
677 | && $contentType =~ m!^text/html! | ||||
678 | && $Foswiki::cfg{Validation}{Method} ne 'none' ) | ||||
679 | { | ||||
680 | |||||
681 | # Don't expire the validation key through login, or when | ||||
682 | # endpoint is an error. | ||||
683 | Foswiki::Validation::expireValidationKeys($cgis) | ||||
684 | unless ( $this->{request}->action() eq 'login' | ||||
685 | or ( $ENV{REDIRECT_STATUS} || 0 ) >= 400 ); | ||||
686 | |||||
687 | my $usingStrikeOne = $Foswiki::cfg{Validation}{Method} eq 'strikeone'; | ||||
688 | if ($usingStrikeOne) { | ||||
689 | |||||
690 | # add the validation cookie | ||||
691 | my $valCookie = Foswiki::Validation::getCookie($cgis); | ||||
692 | $valCookie->secure( $this->{request}->secure ); | ||||
693 | $this->{response} | ||||
694 | ->cookies( [ $this->{response}->cookies, $valCookie ] ); | ||||
695 | |||||
696 | # Add the strikeone JS module to the page. | ||||
697 | my $src = (DEBUG) ? '.uncompressed' : ''; | ||||
698 | $this->addToZone( | ||||
699 | 'script', | ||||
700 | 'JavascriptFiles/strikeone', | ||||
701 | '<script type="text/javascript" src="' | ||||
702 | . $this->getPubURL( | ||||
703 | $Foswiki::cfg{SystemWebName}, 'JavascriptFiles', | ||||
704 | "strikeone$src.js" | ||||
705 | ) | ||||
706 | . '"></script>', | ||||
707 | 'JQUERYPLUGIN' | ||||
708 | ); | ||||
709 | |||||
710 | # Add the onsubmit handler to the form | ||||
711 | $text =~ s/(<form[^>]*method=['"]POST['"][^>]*>)/ | ||||
712 | Foswiki::Validation::addOnSubmit($1)/gei; | ||||
713 | } | ||||
714 | |||||
715 | my $context = | ||||
716 | $this->{request}->url( -full => 1, -path => 1, -query => 1 ) . time(); | ||||
717 | |||||
718 | # Inject validation key in HTML forms | ||||
719 | $text =~ s/(<form[^>]*method=['"]POST['"][^>]*>)/ | ||||
720 | $1 . Foswiki::Validation::addValidationKey( | ||||
721 | $cgis, $context, $usingStrikeOne )/gei; | ||||
722 | |||||
723 | #add validation key to HTTP header so we can update it for ajax use | ||||
724 | $this->{response}->pushHeader( | ||||
725 | 'X-Foswiki-Validation', | ||||
726 | Foswiki::Validation::generateValidationKey( | ||||
727 | $cgis, $context, $usingStrikeOne | ||||
728 | ) | ||||
729 | ) if ($cgis); | ||||
730 | } | ||||
731 | |||||
732 | 1 | 4µs | 1 | 18.2ms | if ( $contentType !~ m!^text/plain! ) { # spent 18.2ms making 1 call to Foswiki::_renderZones |
733 | |||||
734 | $text = $this->_renderZones($text); | ||||
735 | } | ||||
736 | |||||
737 | # Validate format of content-type (defined in rfc2616) | ||||
738 | 1 | 4µs | my $tch = qr/[^\[\]()<>@,;:\\"\/?={}\s]/; | ||
739 | 1 | 34µs | if ( $contentType =~ m/($tch+\/$tch+(\s*;\s*$tch+=($tch+|"[^"]*"))*)$/i ) { | ||
740 | $contentType = $1; | ||||
741 | } | ||||
742 | else { | ||||
743 | # SMELL: can't compute; faking content-type for backwards compatibility; | ||||
744 | # any other information might become bogus later anyway | ||||
745 | $contentType = "text/plain;contenttype=invalid"; | ||||
746 | } | ||||
747 | 1 | 2µs | my $hdr = "Content-type: " . $1 . "\r\n"; | ||
748 | |||||
749 | # Call final handler | ||||
750 | 1 | 2µs | 1 | 4µs | $this->{plugins}->dispatch( 'completePageHandler', $text, $hdr ); # spent 4µs making 1 call to Foswiki::Plugins::dispatch |
751 | |||||
752 | # cache final page, but only view and rest | ||||
753 | 1 | 200ns | my $cachedPage; | ||
754 | 1 | 800ns | if ( $contentType ne 'text/plain' ) { | ||
755 | |||||
756 | # Remove <nop> and <noautolink> tags | ||||
757 | 1 | 4.79ms | $text =~ s/([\t ]?)[ \t]*<\/?(nop|noautolink)\/?>/$1/gis; | ||
758 | 1 | 1µs | if ( $Foswiki::cfg{Cache}{Enabled} | ||
759 | && ( $this->inContext('view') || $this->inContext('rest') ) ) | ||||
760 | { | ||||
761 | $cachedPage = $this->{cache}->cachePage( $contentType, $text ); | ||||
762 | $this->{cache}->renderDirtyAreas( \$text ) | ||||
763 | if $cachedPage && $cachedPage->{isdirty}; | ||||
764 | } | ||||
765 | |||||
766 | # remove <dirtyarea> tags | ||||
767 | 1 | 37µs | $text =~ s/<\/?dirtyarea[^>]*>//g; | ||
768 | |||||
769 | # Check that the templates specified clean HTML | ||||
770 | 1 | 400ns | if (DEBUG) { | ||
771 | |||||
772 | # When tracing is enabled in Foswiki::Templates, then there will | ||||
773 | # always be a <!--bodyend--> after </html>. So we need to disable | ||||
774 | # this check. | ||||
775 | require Foswiki::Templates; | ||||
776 | if ( !Foswiki::Templates->TRACE | ||||
777 | && $contentType =~ m#text/html# | ||||
778 | && $text =~ m#</html>(.*?\S.*)$#s ) | ||||
779 | { | ||||
780 | ASSERT( 0, <<BOGUS ); | ||||
781 | Junk after </html>: $1. Templates may be bogus | ||||
782 | - Check for excess blank lines at ends of .tmpl files | ||||
783 | - or newlines after %TMPL:INCLUDE | ||||
784 | - You can enable TRACE in Foswiki::Templates to help debug | ||||
785 | BOGUS | ||||
786 | } | ||||
787 | } | ||||
788 | } | ||||
789 | |||||
790 | 1 | 13µs | 2 | 54µs | $this->{response}->pushHeader( 'X-Foswiki-Monitor-renderTime', # spent 32µs making 1 call to Foswiki::Request::getTime
# spent 22µs making 1 call to Foswiki::Response::pushHeader |
791 | $this->{request}->getTime() ); | ||||
792 | |||||
793 | 1 | 2µs | my $hopts = { 'Content-Type' => $contentType }; | ||
794 | |||||
795 | 1 | 6µs | 1 | 11µs | $this->setCacheControl( $pageType, $hopts ); # spent 11µs making 1 call to Foswiki::setCacheControl |
796 | |||||
797 | 1 | 400ns | if ($cachedPage) { | ||
798 | $text = '' unless $this->setETags( $cachedPage, $hopts ); | ||||
799 | } | ||||
800 | |||||
801 | 1 | 600ns | if ( $Foswiki::cfg{HttpCompress} && length($text) ) { | ||
802 | |||||
803 | # Generate a zipped page, if the client accepts them | ||||
804 | |||||
805 | # SMELL: $ENV{SPDY} is a non-standard way to detect spdy protocol | ||||
806 | if ( my $encoding = _gzipAccepted() ) { | ||||
807 | $hopts->{'Content-Encoding'} = $encoding; | ||||
808 | $hopts->{'Vary'} = 'Accept-Encoding'; | ||||
809 | |||||
810 | # check if we take the version from the cache. NOTE: we don't | ||||
811 | # set X-Foswiki-Pagecache because this is *not* coming from | ||||
812 | # the cache (well it is, but it was only just put there) | ||||
813 | if ( $cachedPage && !$cachedPage->{isdirty} ) { | ||||
814 | $text = $cachedPage->{data}; | ||||
815 | } | ||||
816 | else { | ||||
817 | # Not available from the cache, or it has dirty areas | ||||
818 | require Compress::Zlib; | ||||
819 | $text = Compress::Zlib::memGzip( encode_utf8($text) ); | ||||
820 | } | ||||
821 | $binary_body = 1; | ||||
822 | } | ||||
823 | } # Otherwise fall through and generate plain text | ||||
824 | |||||
825 | # Generate (and print) HTTP headers. | ||||
826 | 1 | 4µs | 1 | 141µs | $this->generateHTTPHeaders($hopts); # spent 141µs making 1 call to Foswiki::generateHTTPHeaders |
827 | |||||
828 | 1 | 7µs | if ($binary_body) { | ||
829 | $this->{response}->body($text); | ||||
830 | } | ||||
831 | else { | ||||
832 | 1 | 4µs | 1 | 89µs | $this->{response}->print($text); # spent 89µs making 1 call to Foswiki::Response::print |
833 | } | ||||
834 | } | ||||
835 | |||||
836 | # PRIVATE | ||||
837 | sub _gzipAccepted { | ||||
838 | my $encoding; | ||||
839 | if ( ( $ENV{'HTTP_ACCEPT_ENCODING'} || '' ) =~ | ||||
840 | /(?:^|\b)((?:x-)?gzip)(?:$|\b)/ ) | ||||
841 | { | ||||
842 | $encoding = $1; | ||||
843 | } | ||||
844 | elsif ( $ENV{'SPDY'} ) { | ||||
845 | $encoding = 'gzip'; | ||||
846 | } | ||||
847 | return $encoding; | ||||
848 | } | ||||
849 | |||||
850 | =begin TML | ||||
851 | |||||
852 | ---++ ObjectMethod satisfiedByCache( $action, $web, $topic ) -> $boolean | ||||
853 | |||||
854 | Try and satisfy the current request for the given web.topic from the cache, given | ||||
855 | the current action (view, edit, rest etc). | ||||
856 | |||||
857 | If the action is satisfied, the cache content is written to the output and | ||||
858 | true is returned. Otherwise ntohing is written, and false is returned. | ||||
859 | |||||
860 | Designed for calling from Foswiki::UI::* | ||||
861 | |||||
862 | =cut | ||||
863 | |||||
864 | # spent 5µs within Foswiki::satisfiedByCache which was called:
# once (5µs+0s) by Foswiki::UI::View::view at line 86 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm | ||||
865 | 1 | 1µs | my ( $this, $action, $web, $topic ) = @_; | ||
866 | |||||
867 | 1 | 800ns | my $cache = $this->{cache}; | ||
868 | 1 | 4µs | return 0 unless $cache; | ||
869 | |||||
870 | my $cachedPage = $cache->getPage( $web, $topic ) if $cache; | ||||
871 | return 0 unless $cachedPage; | ||||
872 | |||||
873 | Foswiki::Func::writeDebug("found $web.$topic for $action in cache") | ||||
874 | if Foswiki::PageCache::TRACE(); | ||||
875 | if ( int( $this->{response}->status() || 200 ) >= 500 ) { | ||||
876 | Foswiki::Func::writeDebug( | ||||
877 | "Cache retrieval skipped due to non-200 status code " | ||||
878 | . $this->{response}->status() ) | ||||
879 | if DEBUG; | ||||
880 | return 0; | ||||
881 | } | ||||
882 | Monitor::MARK("found page in cache"); | ||||
883 | |||||
884 | my $hdrs = { 'Content-Type' => $cachedPage->{contenttype} }; | ||||
885 | |||||
886 | # render uncacheable areas | ||||
887 | my $text = $cachedPage->{data}; | ||||
888 | |||||
889 | if ( $cachedPage->{isdirty} ) { | ||||
890 | $cache->renderDirtyAreas( \$text ); | ||||
891 | |||||
892 | # dirty pages are cached in unicode | ||||
893 | $text = encode_utf8($text); | ||||
894 | } | ||||
895 | elsif ( $Foswiki::cfg{HttpCompress} ) { | ||||
896 | |||||
897 | # Does the client accept gzip? | ||||
898 | if ( my $encoding = _gzipAccepted() ) { | ||||
899 | |||||
900 | # Cache has compressed data, just whack it out | ||||
901 | $hdrs->{'Content-Encoding'} = $encoding; | ||||
902 | $hdrs->{'Vary'} = 'Accept-Encoding'; | ||||
903 | |||||
904 | # Mark the response so we know it was satisfied from the cache | ||||
905 | $hdrs->{'X-Foswiki-PageCache'} = 1; | ||||
906 | } | ||||
907 | else { | ||||
908 | # e.g. CLI request satisfied from the cache, or old browser that doesn't | ||||
909 | # support gzip. Non-isdirty pages are cached already utf8-encoded, so | ||||
910 | # all we have to do is unzip. | ||||
911 | require Compress::Zlib; | ||||
912 | $text = Compress::Zlib::memGunzip( $cachedPage->{data} ); | ||||
913 | } | ||||
914 | } # else { Non-isdirty pages are stored already utf8-encoded } | ||||
915 | |||||
916 | # set status | ||||
917 | my $response = $this->{response}; | ||||
918 | if ( $cachedPage->{status} == 302 ) { | ||||
919 | $response->redirect( $cachedPage->{location} ); | ||||
920 | } | ||||
921 | else { | ||||
922 | |||||
923 | # See Item9941 | ||||
924 | # Don't allow a 200 status to overwrite a status (possibly an error status) | ||||
925 | # coming from elsewhere in the code. Note that 401's are not cached (they | ||||
926 | # fail Foswiki::PageCache::isCacheable) but all other statuses are. | ||||
927 | # SMELL: Cdot doesn't think any other status can get this far. | ||||
928 | $response->status( $cachedPage->{status} ) | ||||
929 | unless int( $cachedPage->{status} ) == 200; | ||||
930 | } | ||||
931 | |||||
932 | # set remaining headers | ||||
933 | $text = undef unless $this->setETags( $cachedPage, $hdrs ); | ||||
934 | $this->generateHTTPHeaders($hdrs); | ||||
935 | |||||
936 | # send it out | ||||
937 | $response->body($text) if defined $text; | ||||
938 | |||||
939 | Monitor::MARK('Wrote HTML'); | ||||
940 | $this->logger->log( | ||||
941 | { | ||||
942 | level => 'info', | ||||
943 | action => $action, | ||||
944 | webTopic => $web . '.' . $topic, | ||||
945 | extra => '(cached)', | ||||
946 | } | ||||
947 | ); | ||||
948 | |||||
949 | return 1; | ||||
950 | } | ||||
951 | |||||
952 | =begin TML | ||||
953 | |||||
954 | ---++ ObjectMethod setCacheControl( $pageType, \%hopts ) | ||||
955 | |||||
956 | Set the cache control headers in a response | ||||
957 | |||||
958 | * =$pageType= - page type - 'view', ;edit' etc | ||||
959 | * =\%hopts - ref to partially filled in hash of headers | ||||
960 | |||||
961 | =cut | ||||
962 | |||||
963 | # spent 11µs within Foswiki::setCacheControl which was called:
# once (11µs+0s) by Foswiki::writeCompletePage at line 795 | ||||
964 | 1 | 1µs | my ( $this, $pageType, $hopts ) = @_; | ||
965 | |||||
966 | 1 | 4µs | if ( $pageType && $pageType eq 'edit' ) { | ||
967 | |||||
968 | # Edit pages - future versions will extend to | ||||
969 | # of other types of page, with expiry time driven by page type. | ||||
970 | |||||
971 | # Get time now in HTTP header format | ||||
972 | my $lastModifiedString = | ||||
973 | Foswiki::Time::formatTime( time, '$http', 'gmtime' ); | ||||
974 | |||||
975 | # Expiry time is set high to avoid any data loss. Each instance of | ||||
976 | # Edit page has a unique URL with time-string suffix (fix for | ||||
977 | # RefreshEditPage), so this long expiry time simply means that the | ||||
978 | # browser Back button always works. The next Edit on this page | ||||
979 | # will use another URL and therefore won't use any cached | ||||
980 | # version of this Edit page. | ||||
981 | my $expireHours = 24; | ||||
982 | my $expireSeconds = $expireHours * 60 * 60; | ||||
983 | |||||
984 | # and cache control headers, to ensure edit page | ||||
985 | # is cached until required expiry time. | ||||
986 | $hopts->{'last-modified'} = $lastModifiedString; | ||||
987 | $hopts->{expires} = "+${expireHours}h"; | ||||
988 | $hopts->{'Cache-Control'} = "max-age=$expireSeconds"; | ||||
989 | } | ||||
990 | else { | ||||
991 | |||||
992 | # we need to force the browser into a check on every | ||||
993 | # request; let the server decide on an 304 as below | ||||
994 | 1 | 1µs | my $cacheControl = 'max-age=0'; | ||
995 | |||||
996 | # allow the admin to disable us from setting the max-age, as then | ||||
997 | # it can't be set by apache | ||||
998 | 1 | 2µs | $cacheControl = $Foswiki::cfg{BrowserCacheControl}->{ $this->{webName} } | ||
999 | if ( $Foswiki::cfg{BrowserCacheControl} | ||||
1000 | && defined( | ||||
1001 | $Foswiki::cfg{BrowserCacheControl}->{ $this->{webName} } ) ); | ||||
1002 | |||||
1003 | # don't remove the 'if'; we need the header to not be there at | ||||
1004 | # all for the browser to use the cached version | ||||
1005 | 1 | 2µs | $hopts->{'Cache-Control'} = $cacheControl if ( $cacheControl ne '' ); | ||
1006 | } | ||||
1007 | } | ||||
1008 | |||||
1009 | =begin TML | ||||
1010 | |||||
1011 | ---++ ObjectMethod setETags( $cachedPage, \%hopts ) -> $boolean | ||||
1012 | |||||
1013 | Set etags (and modify status) depending on what the cached page specifies. | ||||
1014 | Return 1 if the page has been modified since it was last retrieved, 0 otherwise. | ||||
1015 | |||||
1016 | * =$cachedPage= - page cache to use | ||||
1017 | * =\%hopts - ref to partially filled in hash of headers | ||||
1018 | |||||
1019 | =cut | ||||
1020 | |||||
1021 | sub setETags { | ||||
1022 | my ( $this, $cachedPage, $hopts ) = @_; | ||||
1023 | |||||
1024 | # check etag and last modification time | ||||
1025 | my $etag = $cachedPage->{etag}; | ||||
1026 | my $lastModified = $cachedPage->{lastmodified}; | ||||
1027 | |||||
1028 | $hopts->{'ETag'} = $etag if $etag; | ||||
1029 | $hopts->{'Last-Modified'} = $lastModified if $lastModified; | ||||
1030 | |||||
1031 | # only send a 304 if both criteria are true | ||||
1032 | return 1 | ||||
1033 | unless ( | ||||
1034 | $etag | ||||
1035 | && $lastModified | ||||
1036 | |||||
1037 | && $ENV{'HTTP_IF_NONE_MATCH'} | ||||
1038 | && $etag eq $ENV{'HTTP_IF_NONE_MATCH'} | ||||
1039 | |||||
1040 | && $ENV{'HTTP_IF_MODIFIED_SINCE'} | ||||
1041 | && $lastModified eq $ENV{'HTTP_IF_MODIFIED_SINCE'} | ||||
1042 | ); | ||||
1043 | |||||
1044 | # finally decide on a 304 reply | ||||
1045 | $hopts->{'Status'} = '304 Not Modified'; | ||||
1046 | |||||
1047 | #print STDERR "NOT modified\n"; | ||||
1048 | return 0; | ||||
1049 | } | ||||
1050 | |||||
1051 | =begin TML | ||||
1052 | |||||
1053 | ---++ ObjectMethod generateHTTPHeaders( \%hopts ) | ||||
1054 | |||||
1055 | All parameters are optional. | ||||
1056 | * =\%hopts - optional ref to partially filled in hash of headers (will be written to) | ||||
1057 | |||||
1058 | =cut | ||||
1059 | |||||
1060 | # spent 141µs (37+104) within Foswiki::generateHTTPHeaders which was called:
# once (37µs+104µs) by Foswiki::writeCompletePage at line 826 | ||||
1061 | 1 | 800ns | my ( $this, $hopts ) = @_; | ||
1062 | |||||
1063 | 1 | 400ns | $hopts ||= {}; | ||
1064 | |||||
1065 | # DEPRECATED plugins header handler. Plugins should use | ||||
1066 | # modifyHeaderHandler instead. | ||||
1067 | 1 | 3µs | 1 | 3µs | my $pluginHeaders = # spent 3µs making 1 call to Foswiki::Plugins::dispatch |
1068 | $this->{plugins}->dispatch( 'writeHeaderHandler', $this->{request} ) | ||||
1069 | || ''; | ||||
1070 | 1 | 400ns | if ($pluginHeaders) { | ||
1071 | foreach ( split /\r?\n/, $pluginHeaders ) { | ||||
1072 | |||||
1073 | # Implicit untaint OK; data from plugin handler | ||||
1074 | if (m/^([\-a-z]+): (.*)$/i) { | ||||
1075 | $hopts->{$1} = $2; | ||||
1076 | } | ||||
1077 | } | ||||
1078 | } | ||||
1079 | |||||
1080 | 1 | 900ns | my $contentType = $hopts->{'Content-Type'}; | ||
1081 | 1 | 200ns | $contentType = 'text/html' unless $contentType; | ||
1082 | 1 | 5µs | $contentType .= '; charset=utf-8' | ||
1083 | if $contentType =~ m!^text/! | ||||
1084 | && $contentType !~ /\bcharset\b/; | ||||
1085 | |||||
1086 | # use our version of the content type | ||||
1087 | 1 | 700ns | $hopts->{'Content-Type'} = $contentType; | ||
1088 | |||||
1089 | 1 | 6µs | 1 | 6µs | $hopts->{'X-FoswikiAction'} = $this->{request}->action; # spent 6µs making 1 call to Foswiki::Request::action |
1090 | 1 | 3µs | 1 | 3µs | $hopts->{'X-FoswikiURI'} = $this->{request}->uri; # spent 3µs making 1 call to Foswiki::Request::uri |
1091 | |||||
1092 | # Turn off XSS protection in DEBUG so it doesn't mask problems | ||||
1093 | $hopts->{'X-XSS-Protection'} = 0 if DEBUG; | ||||
1094 | |||||
1095 | 1 | 2µs | 1 | 50µs | $this->{plugins} # spent 50µs making 1 call to Foswiki::Plugins::dispatch |
1096 | ->dispatch( 'modifyHeaderHandler', $hopts, $this->{request} ); | ||||
1097 | |||||
1098 | # The headers method resets all headers to what we pass | ||||
1099 | # what we want is simply ensure our headers are there | ||||
1100 | 1 | 8µs | 1 | 42µs | $this->{response}->setDefaultHeaders($hopts); # spent 42µs making 1 call to Foswiki::Response::setDefaultHeaders |
1101 | } | ||||
1102 | |||||
1103 | # Tests if the $redirect is an external URL, returning false if | ||||
1104 | # AllowRedirectUrl is denied | ||||
1105 | sub _isRedirectSafe { | ||||
1106 | my $redirect = shift; | ||||
1107 | |||||
1108 | return 1 if ( $Foswiki::cfg{AllowRedirectUrl} ); | ||||
1109 | return 1 if $redirect =~ m#^/#; # relative URL - OK | ||||
1110 | |||||
1111 | #TODO: this should really use URI | ||||
1112 | # Compare protocol, host name and port number | ||||
1113 | if ( $redirect =~ m!^(.*?://[^/?#]*)! ) { | ||||
1114 | |||||
1115 | # implicit untaints OK because result not used. uc retaints | ||||
1116 | # if use locale anyway. | ||||
1117 | my $target = uc($1); | ||||
1118 | |||||
1119 | $Foswiki::cfg{DefaultUrlHost} =~ m!^(.*?://[^/]*)!; | ||||
1120 | return 1 if ( $target eq uc($1) ); | ||||
1121 | |||||
1122 | if ( $Foswiki::cfg{PermittedRedirectHostUrls} ) { | ||||
1123 | foreach my $red ( | ||||
1124 | split( /\s*,\s*/, $Foswiki::cfg{PermittedRedirectHostUrls} ) ) | ||||
1125 | { | ||||
1126 | $red =~ m!^(.*?://[^/]*)!; | ||||
1127 | return 1 if ( $target eq uc($1) ); | ||||
1128 | } | ||||
1129 | } | ||||
1130 | } | ||||
1131 | return 0; | ||||
1132 | } | ||||
1133 | |||||
1134 | =begin TML | ||||
1135 | |||||
1136 | ---++ ObjectMethod redirectto($url) -> $url | ||||
1137 | |||||
1138 | If the CGI parameter 'redirectto' is present on the query, then will validate | ||||
1139 | that it is a legal redirection target (url or topic name). If 'redirectto' | ||||
1140 | is not present on the query, performs the same steps on $url. | ||||
1141 | |||||
1142 | Returns undef if the target is not valid, and the target URL otherwise. | ||||
1143 | |||||
1144 | =cut | ||||
1145 | |||||
1146 | sub redirectto { | ||||
1147 | my ( $this, $url ) = @_; | ||||
1148 | |||||
1149 | my $redirecturl = $this->{request}->param('redirectto'); | ||||
1150 | $redirecturl = $url unless $redirecturl; | ||||
1151 | |||||
1152 | return unless $redirecturl; | ||||
1153 | |||||
1154 | if ( $redirecturl =~ m#^$regex{linkProtocolPattern}://# ) { | ||||
1155 | |||||
1156 | # assuming URL | ||||
1157 | return $redirecturl if _isRedirectSafe($redirecturl); | ||||
1158 | return; | ||||
1159 | } | ||||
1160 | |||||
1161 | my @attrs = (); | ||||
1162 | |||||
1163 | # capture anchor | ||||
1164 | if ( $redirecturl =~ s/#(.*)// ) { | ||||
1165 | push( @attrs, '#' => $1 ); | ||||
1166 | } | ||||
1167 | |||||
1168 | # capture params | ||||
1169 | if ( $redirecturl =~ s/\?(.*)// ) { | ||||
1170 | push( @attrs, map { split( '=', $_, 2 ) } split( /[;&]/, $1 ) ); | ||||
1171 | } | ||||
1172 | |||||
1173 | # assuming 'web.topic' or 'topic' | ||||
1174 | my ( $w, $t ) = | ||||
1175 | $this->normalizeWebTopicName( $this->{webName}, $redirecturl ); | ||||
1176 | |||||
1177 | return $this->getScriptUrl( 0, 'view', $w, $t, @attrs ); | ||||
1178 | } | ||||
1179 | |||||
1180 | =begin TML | ||||
1181 | |||||
1182 | ---++ StaticMethod splitAnchorFromUrl( $url ) -> ( $url, $anchor ) | ||||
1183 | |||||
1184 | Takes a full url (including possible query string) and splits off the anchor. | ||||
1185 | The anchor includes the # sign. Returns an empty string if not found in the url. | ||||
1186 | |||||
1187 | =cut | ||||
1188 | |||||
1189 | sub splitAnchorFromUrl { | ||||
1190 | my ($url) = @_; | ||||
1191 | |||||
1192 | ( $url, my $anchor ) = $url =~ m/^(.*?)(#(.*?))*$/; | ||||
1193 | return ( $url, $anchor ); | ||||
1194 | } | ||||
1195 | |||||
1196 | =begin TML | ||||
1197 | |||||
1198 | ---++ ObjectMethod redirect( $url, $passthrough, $status ) | ||||
1199 | |||||
1200 | * $url - url or topic to redirect to | ||||
1201 | * $passthrough - (optional) parameter to pass through current query | ||||
1202 | parameters (see below) | ||||
1203 | * $status - HTTP status code (30x) to redirect with. Defaults to 302. | ||||
1204 | |||||
1205 | Redirects the request to =$url=, *unless* | ||||
1206 | 1 It is overridden by a plugin declaring a =redirectCgiQueryHandler= | ||||
1207 | (a dangerous, deprecated handler!) | ||||
1208 | 1 =$session->{request}= is =undef= | ||||
1209 | Thus a redirect is only generated when in a CGI context. | ||||
1210 | |||||
1211 | Normally this method will ignore parameters to the current query. Sometimes, | ||||
1212 | for example when redirecting to a login page during authentication (and then | ||||
1213 | again from the login page to the original requested URL), you want to make | ||||
1214 | sure all parameters are passed on, and for this $passthrough should be set to | ||||
1215 | true. In this case it will pass all parameters that were passed to the | ||||
1216 | current query on to the redirect target. If the request_method for the | ||||
1217 | current query was GET, then all parameters will be passed by encoding them | ||||
1218 | in the URL (after ?). If the request_method was POST, then there is a risk the | ||||
1219 | URL would be too big for the receiver, so it caches the form data and passes | ||||
1220 | over a cache reference in the redirect GET. | ||||
1221 | |||||
1222 | NOTE: Passthrough is only meaningful if the redirect target is on the same | ||||
1223 | server. | ||||
1224 | |||||
1225 | =cut | ||||
1226 | |||||
1227 | sub redirect { | ||||
1228 | my ( $this, $url, $passthru, $status ) = @_; | ||||
1229 | ASSERT( defined $url ) if DEBUG; | ||||
1230 | |||||
1231 | return unless $this->{request}; | ||||
1232 | |||||
1233 | ( $url, my $anchor ) = splitAnchorFromUrl($url); | ||||
1234 | |||||
1235 | if ( $passthru && defined $this->{request}->method() ) { | ||||
1236 | my $existing = ''; | ||||
1237 | if ( $url =~ s/\?(.*)$// ) { | ||||
1238 | $existing = $1; # implicit untaint OK; recombined later | ||||
1239 | } | ||||
1240 | if ( uc( $this->{request}->method() ) eq 'POST' ) { | ||||
1241 | |||||
1242 | # Redirecting from a post to a get | ||||
1243 | my $cache = $this->cacheQuery(); | ||||
1244 | if ($cache) { | ||||
1245 | if ( $url eq '/' ) { | ||||
1246 | $url = $this->getScriptUrl( 1, 'view' ); | ||||
1247 | } | ||||
1248 | $url .= $cache; | ||||
1249 | } | ||||
1250 | } | ||||
1251 | else { | ||||
1252 | |||||
1253 | # Redirecting a get to a get; no need to use passthru | ||||
1254 | if ( $this->{request}->query_string() ) { | ||||
1255 | $url .= '?' . $this->{request}->query_string(); | ||||
1256 | } | ||||
1257 | if ($existing) { | ||||
1258 | if ( $url =~ m/\?/ ) { | ||||
1259 | $url .= ';'; | ||||
1260 | } | ||||
1261 | else { | ||||
1262 | $url .= '?'; | ||||
1263 | } | ||||
1264 | $url .= $existing; | ||||
1265 | } | ||||
1266 | } | ||||
1267 | } | ||||
1268 | |||||
1269 | # prevent phishing by only allowing redirect to configured host | ||||
1270 | # do this check as late as possible to catch _any_ last minute hacks | ||||
1271 | # TODO: this should really use URI | ||||
1272 | if ( !_isRedirectSafe($url) ) { | ||||
1273 | |||||
1274 | # goto oops if URL is trying to take us somewhere dangerous | ||||
1275 | $url = $this->getScriptUrl( | ||||
1276 | 1, 'oops', | ||||
1277 | $this->{webName} || $Foswiki::cfg{UsersWebName}, | ||||
1278 | $this->{topicName} || $Foswiki::cfg{HomeTopicName}, | ||||
1279 | template => 'oopsredirectdenied', | ||||
1280 | def => 'redirect_denied', | ||||
1281 | param1 => "$url", | ||||
1282 | param2 => "$Foswiki::cfg{DefaultUrlHost}", | ||||
1283 | ); | ||||
1284 | } | ||||
1285 | |||||
1286 | $url .= $anchor if $anchor; | ||||
1287 | |||||
1288 | # Dangerous, deprecated handler! Might work, probably won't. | ||||
1289 | return | ||||
1290 | if ( $this->{plugins} | ||||
1291 | ->dispatch( 'redirectCgiQueryHandler', $this->{response}, $url ) ); | ||||
1292 | |||||
1293 | $url = $this->getLoginManager()->rewriteRedirectUrl($url); | ||||
1294 | |||||
1295 | # Foswiki::Response::redirect doesn't automatically pass on the cookies | ||||
1296 | # for us, so we have to do it explicitly; otherwise the session cookie | ||||
1297 | # won't get passed on. | ||||
1298 | $this->{response}->redirect( | ||||
1299 | -url => $url, | ||||
1300 | -cookies => $this->{response}->cookies(), | ||||
1301 | -status => $status, | ||||
1302 | ); | ||||
1303 | } | ||||
1304 | |||||
1305 | =begin TML | ||||
1306 | |||||
1307 | ---++ ObjectMethod cacheQuery() -> $queryString | ||||
1308 | |||||
1309 | Caches the current query in the params cache, and returns a rewritten | ||||
1310 | query string for the cache to be picked up again on the other side of a | ||||
1311 | redirect. | ||||
1312 | |||||
1313 | We can't encode post params into a redirect, because they may exceed the | ||||
1314 | size of the GET request. So we cache the params, and reload them when the | ||||
1315 | redirect target is reached. | ||||
1316 | |||||
1317 | =cut | ||||
1318 | |||||
1319 | sub cacheQuery { | ||||
1320 | my $this = shift; | ||||
1321 | my $query = $this->{request}; | ||||
1322 | |||||
1323 | return '' unless ( $query->param() ); | ||||
1324 | |||||
1325 | # Don't double-cache | ||||
1326 | return '' if ( $query->param('foswiki_redirect_cache') ); | ||||
1327 | |||||
1328 | require Foswiki::Request::Cache; | ||||
1329 | my $uid = Foswiki::Request::Cache->new()->save($query); | ||||
1330 | if ( $Foswiki::cfg{UsePathForRedirectCache} ) { | ||||
1331 | return '/foswiki_redirect_cache/' . $uid; | ||||
1332 | } | ||||
1333 | else { | ||||
1334 | return '?foswiki_redirect_cache=' . $uid; | ||||
1335 | } | ||||
1336 | } | ||||
1337 | |||||
1338 | =begin TML | ||||
1339 | |||||
1340 | ---++ ObjectMethod getCGISession() -> $cgisession | ||||
1341 | |||||
1342 | Get the CGI::Session object associated with this session, if there is | ||||
1343 | one. May return undef. | ||||
1344 | |||||
1345 | =cut | ||||
1346 | |||||
1347 | # spent 37µs (11+26) within Foswiki::getCGISession which was called 2 times, avg 18µs/call:
# 2 times (11µs+26µs) by Foswiki::Plugins::SubscribePlugin::_getNonce at line 180 of /var/www/foswikidev/core/lib/Foswiki/Plugins/SubscribePlugin.pm, avg 18µs/call | ||||
1348 | 2 | 11µs | 2 | 26µs | $_[0]->{users}->getCGISession(); # spent 26µs making 2 calls to Foswiki::Users::getCGISession, avg 13µs/call |
1349 | } | ||||
1350 | |||||
1351 | =begin TML | ||||
1352 | |||||
1353 | ---++ ObjectMethod getLoginManager() -> $loginManager | ||||
1354 | |||||
1355 | Get the Foswiki::LoginManager object associated with this session, if there is | ||||
1356 | one. May return undef. | ||||
1357 | |||||
1358 | =cut | ||||
1359 | |||||
1360 | # spent 83µs (56+27) within Foswiki::getLoginManager which was called 10 times, avg 8µs/call:
# 5 times (27µs+17µs) by Foswiki::Render::getRenderedVersion at line 567 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 9µs/call
# once (13µs+3µs) by Foswiki::Func::setSessionValue at line 428 of /var/www/foswikidev/core/lib/Foswiki/Func.pm
# once (5µs+2µs) by Foswiki::LoginManager::_LOGIN at line 1366 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (5µs+2µs) by Foswiki::UI::__ANON__[/var/www/foswikidev/core/lib/Foswiki/UI.pm:376] at line 373 of /var/www/foswikidev/core/lib/Foswiki/UI.pm
# once (3µs+1µs) by Foswiki::LoginManager::_LOGOUTURL at line 1387 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (3µs+1µs) by Foswiki::Func::clearSessionValue at line 445 of /var/www/foswikidev/core/lib/Foswiki/Func.pm | ||||
1361 | 10 | 51µs | 10 | 27µs | $_[0]->{users}->getLoginManager(); # spent 27µs making 10 calls to Foswiki::Users::getLoginManager, avg 3µs/call |
1362 | } | ||||
1363 | |||||
1364 | =begin TML | ||||
1365 | |||||
1366 | ---++ StaticMethod isValidWikiWord( $name ) -> $boolean | ||||
1367 | |||||
1368 | Check for a valid WikiWord or WikiName | ||||
1369 | |||||
1370 | =cut | ||||
1371 | |||||
1372 | sub isValidWikiWord { | ||||
1373 | my $name = shift || ''; | ||||
1374 | return ( $name =~ m/^$regex{wikiWordRegex}$/ ); | ||||
1375 | } | ||||
1376 | |||||
1377 | =begin TML | ||||
1378 | |||||
1379 | ---++ StaticMethod isValidTopicName( $name [, $nonww] ) -> $boolean | ||||
1380 | |||||
1381 | Check for a valid topic =$name=. If =$nonww=, then accept non wiki-words | ||||
1382 | (though they must still be composed of only valid, unfiltered characters) | ||||
1383 | |||||
1384 | =cut | ||||
1385 | |||||
1386 | # Note: must work on tainted names. | ||||
1387 | # spent 1.00ms (422µs+579µs) within Foswiki::isValidTopicName which was called 17 times, avg 59µs/call:
# 12 times (355µs+579µs) by Foswiki::_includeTopic at line 147 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 78µs/call
# 5 times (68µs+0s) by Foswiki::Sandbox::validateTopicName at line 167 of /var/www/foswikidev/core/lib/Foswiki/Sandbox.pm, avg 14µs/call | ||||
1388 | 17 | 10µs | my ( $name, $nonww ) = @_; | ||
1389 | |||||
1390 | 17 | 8µs | return 0 unless defined $name && $name ne ''; | ||
1391 | 17 | 397µs | 3 | 579µs | return 1 if ( $name =~ m/^$regex{topicNameRegex}$/ ); # spent 579µs making 3 calls to utf8::SWASHNEW, avg 193µs/call |
1392 | 3 | 600ns | return 0 unless $nonww; | ||
1393 | 3 | 12µs | return 0 if $name =~ m/$cfg{NameFilter}/; | ||
1394 | 3 | 11µs | return 1; | ||
1395 | } | ||||
1396 | |||||
1397 | =begin TML | ||||
1398 | |||||
1399 | ---++ StaticMethod isValidWebName( $name, $system ) -> $boolean | ||||
1400 | |||||
1401 | STATIC Check for a valid web name. If $system is true, then | ||||
1402 | system web names are considered valid (names starting with _) | ||||
1403 | otherwise only user web names are valid | ||||
1404 | |||||
1405 | If $Foswiki::cfg{EnableHierarchicalWebs} is off, it will also return false | ||||
1406 | when a nested web name is passed to it. | ||||
1407 | |||||
1408 | =cut | ||||
1409 | |||||
1410 | # Note: must work on tainted names. | ||||
1411 | # spent 958µs (895+63) within Foswiki::isValidWebName which was called 106 times, avg 9µs/call:
# 106 times (895µs+63µs) by Foswiki::Sandbox::validateWebName at line 152 of /var/www/foswikidev/core/lib/Foswiki/Sandbox.pm, avg 9µs/call | ||||
1412 | 106 | 40µs | my $name = shift || ''; | ||
1413 | 106 | 32µs | my $sys = shift; | ||
1414 | 106 | 246µs | return 1 if ( $sys && $name =~ m/^$regex{defaultWebNameRegex}$/ ); | ||
1415 | 104 | 720µs | 1 | 63µs | return ( $name =~ m/^$regex{webNameRegex}$/ ); # spent 63µs making 1 call to utf8::SWASHNEW |
1416 | } | ||||
1417 | |||||
1418 | =begin TML | ||||
1419 | |||||
1420 | ---++ StaticMethod isValidEmailAddress( $name ) -> $boolean | ||||
1421 | |||||
1422 | STATIC Check for a valid email address name. | ||||
1423 | |||||
1424 | =cut | ||||
1425 | |||||
1426 | # Note: must work on tainted names. | ||||
1427 | sub isValidEmailAddress { | ||||
1428 | my $name = shift || ''; | ||||
1429 | return $name =~ m/^$regex{emailAddrRegex}$/; | ||||
1430 | } | ||||
1431 | |||||
1432 | =begin TML | ||||
1433 | |||||
1434 | ---++ ObjectMethod getSkin () -> $string | ||||
1435 | |||||
1436 | Get the currently requested skin path | ||||
1437 | |||||
1438 | =cut | ||||
1439 | |||||
1440 | # spent 6.54ms (1.61+4.93) within Foswiki::getSkin which was called 50 times, avg 131µs/call:
# 47 times (1.57ms+4.80ms) by Foswiki::Templates::readTemplate at line 248 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 135µs/call
# 2 times (28µs+89µs) by Foswiki::UI::View::view at line 390 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm, avg 59µs/call
# once (15µs+44µs) by Foswiki::UI::View::view at line 413 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm | ||||
1441 | 50 | 49µs | my $this = shift; | ||
1442 | |||||
1443 | 50 | 17µs | my @skinpath; | ||
1444 | 50 | 33µs | my $skins; | ||
1445 | |||||
1446 | 50 | 63µs | if ( $this->{request} ) { | ||
1447 | 50 | 305µs | 50 | 1.97ms | $skins = $this->{request}->param('cover'); # spent 1.97ms making 50 calls to Foswiki::Request::param, avg 39µs/call |
1448 | 50 | 16µs | if ( defined $skins | ||
1449 | && $skins =~ m/([[:alnum:].,\s]+)/ ) | ||||
1450 | { | ||||
1451 | |||||
1452 | # Implicit untaint ok - validated | ||||
1453 | $skins = $1; | ||||
1454 | push( @skinpath, split( /,\s]+/, $skins ) ); | ||||
1455 | } | ||||
1456 | } | ||||
1457 | |||||
1458 | 50 | 333µs | 50 | 2.19ms | $skins = $this->{prefs}->getPreference('COVER'); # spent 2.19ms making 50 calls to Foswiki::Prefs::getPreference, avg 44µs/call |
1459 | 50 | 14µs | if ( defined $skins | ||
1460 | && $skins =~ m/([[:alnum:].,\s]+)/ ) | ||||
1461 | { | ||||
1462 | |||||
1463 | # Implicit untaint ok - validated | ||||
1464 | $skins = $1; | ||||
1465 | push( @skinpath, split( /[,\s]+/, $skins ) ); | ||||
1466 | } | ||||
1467 | |||||
1468 | 50 | 155µs | 50 | 761µs | $skins = $this->{request} ? $this->{request}->param('skin') : undef; # spent 761µs making 50 calls to Foswiki::Request::param, avg 15µs/call |
1469 | 50 | 15µs | $skins = $this->{prefs}->getPreference('SKIN') unless $skins; | ||
1470 | |||||
1471 | 50 | 184µs | if ( defined $skins && $skins =~ m/([[:alnum:].,\s]+)/ ) { | ||
1472 | |||||
1473 | # Implicit untaint ok - validated | ||||
1474 | 50 | 62µs | $skins = $1; | ||
1475 | 50 | 158µs | push( @skinpath, split( /[,\s]+/, $skins ) ); | ||
1476 | } | ||||
1477 | |||||
1478 | 50 | 215µs | return join( ',', @skinpath ); | ||
1479 | } | ||||
1480 | |||||
1481 | =begin TML | ||||
1482 | |||||
1483 | ---++ ObjectMethod getScriptUrl( $absolute, $script, $web, $topic, ... ) -> $scriptURL | ||||
1484 | |||||
1485 | Returns the URL to a Foswiki script, providing the web and topic as | ||||
1486 | "path info" parameters. The result looks something like this: | ||||
1487 | "http://host/foswiki/bin/$script/$web/$topic". | ||||
1488 | * =...= - an arbitrary number of name,value parameter pairs that will | ||||
1489 | be url-encoded and added to the url. The special parameter name '#' is | ||||
1490 | reserved for specifying an anchor. e.g. | ||||
1491 | =getScriptUrl('x','y','view','#'=>'XXX',a=>1,b=>2)= will give | ||||
1492 | =.../view/x/y?a=1&b=2#XXX= | ||||
1493 | |||||
1494 | If $absolute is set, generates an absolute URL. $absolute is advisory only; | ||||
1495 | Foswiki can decide to generate absolute URLs (for example when run from the | ||||
1496 | command-line) even when relative URLs have been requested. | ||||
1497 | |||||
1498 | The default script url is taken from {ScriptUrlPath}, unless there is | ||||
1499 | an exception defined for the given script in {ScriptUrlPaths}. Both | ||||
1500 | {ScriptUrlPath} and {ScriptUrlPaths} may be absolute or relative URIs. If | ||||
1501 | they are absolute, then they will always generate absolute URLs. if they | ||||
1502 | are relative, then they will be converted to absolute when required (e.g. | ||||
1503 | when running from the command line, or when generating rss). If | ||||
1504 | $script is not given, absolute URLs will always be generated. | ||||
1505 | |||||
1506 | If either the web or the topic is defined, will generate a full url (including web and topic). Otherwise will generate only up to the script name. An undefined web will default to the main web name. | ||||
1507 | |||||
1508 | As required by RFC3986, the returned URL will only contain the | ||||
1509 | allowed characters -A-Za-z0-9_.~!*\'();:@&=+$,/?%#[] | ||||
1510 | |||||
1511 | =cut | ||||
1512 | |||||
1513 | # spent 1.43ms (757µs+668µs) within Foswiki::getScriptUrl which was called 54 times, avg 26µs/call:
# 30 times (356µs+120µs) by Foswiki::SCRIPTURL at line 30 of /var/www/foswikidev/core/lib/Foswiki/Macros/SCRIPTURL.pm, avg 16µs/call
# 18 times (289µs+317µs) by Foswiki::Render::_renderExistingWikiWord at line 1336 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 34µs/call
# 2 times (44µs+112µs) by Foswiki::UI::View::revisionsAround at line 559 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm, avg 78µs/call
# 2 times (31µs+70µs) by Foswiki::UI::View::revisionsAround at line 547 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm, avg 51µs/call
# once (19µs+44µs) by Foswiki::LoginManager::_LOGOUTURL at line 1389 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (19µs+5µs) by Foswiki::Func::getScriptUrl at line 182 of /var/www/foswikidev/core/lib/Foswiki/Func.pm | ||||
1514 | 54 | 56µs | my ( $this, $absolute, $script, $web, $topic, @params ) = @_; | ||
1515 | |||||
1516 | 54 | 53µs | 40 | 72µs | $absolute ||= # spent 72µs making 40 calls to Foswiki::inContext, avg 2µs/call |
1517 | ( $this->inContext('command_line') || $this->inContext('absolute_urls') ); | ||||
1518 | |||||
1519 | # SMELL: topics and webs that contain spaces? | ||||
1520 | |||||
1521 | 54 | 6µs | my $url; | ||
1522 | 54 | 50µs | if ( defined $Foswiki::cfg{ScriptUrlPaths} && $script ) { | ||
1523 | $url = $Foswiki::cfg{ScriptUrlPaths}{$script}; | ||||
1524 | } | ||||
1525 | 54 | 12µs | unless ( defined($url) ) { | ||
1526 | 16 | 10µs | $url = $Foswiki::cfg{ScriptUrlPath}; | ||
1527 | 16 | 6µs | if ($script) { | ||
1528 | 14 | 11µs | $url .= '/' unless $url =~ m/\/$/; | ||
1529 | 14 | 5µs | $url .= $script; | ||
1530 | 14 | 36µs | if ( | ||
1531 | rindex( $url, $Foswiki::cfg{ScriptSuffix} ) != | ||||
1532 | ( length($url) - length( $Foswiki::cfg{ScriptSuffix} ) ) ) | ||||
1533 | { | ||||
1534 | $url .= $Foswiki::cfg{ScriptSuffix} if $script; | ||||
1535 | } | ||||
1536 | } | ||||
1537 | } | ||||
1538 | |||||
1539 | 54 | 85µs | if ( $absolute && $url !~ /^[a-z]+:/ ) { | ||
1540 | |||||
1541 | # See http://www.ietf.org/rfc/rfc2396.txt for the definition of | ||||
1542 | # "absolute URI". Foswiki bastardises this definition by assuming | ||||
1543 | # that all relative URLs lack the <authority> component as well. | ||||
1544 | $url = $this->{urlHost} . $url; | ||||
1545 | } | ||||
1546 | |||||
1547 | 54 | 16µs | if ($topic) { | ||
1548 | 23 | 50µs | 23 | 120µs | ( $web, $topic ) = $this->normalizeWebTopicName( $web, $topic ); # spent 120µs making 23 calls to Foswiki::normalizeWebTopicName, avg 5µs/call |
1549 | |||||
1550 | 23 | 47µs | 23 | 194µs | $url .= urlEncode( '/' . $web . '/' . $topic ); # spent 194µs making 23 calls to Foswiki::urlEncode, avg 8µs/call |
1551 | |||||
1552 | } | ||||
1553 | elsif ($web) { | ||||
1554 | $url .= urlEncode( '/' . $web ); | ||||
1555 | } | ||||
1556 | 54 | 73µs | 54 | 282µs | $url .= make_params(@params); # spent 282µs making 54 calls to Foswiki::make_params, avg 5µs/call |
1557 | |||||
1558 | 54 | 124µs | return $url; | ||
1559 | } | ||||
1560 | |||||
1561 | =begin TML | ||||
1562 | |||||
1563 | ---++ StaticMethod make_params(...) | ||||
1564 | Generate a URL parameters string from parameters given. A parameter | ||||
1565 | named '#' will generate a fragment identifier. | ||||
1566 | |||||
1567 | =cut | ||||
1568 | |||||
1569 | # spent 282µs (201+81) within Foswiki::make_params which was called 54 times, avg 5µs/call:
# 54 times (201µs+81µs) by Foswiki::getScriptUrl at line 1556, avg 5µs/call | ||||
1570 | 54 | 16µs | my $url = ''; | ||
1571 | 54 | 5µs | my @ps; | ||
1572 | 54 | 7µs | my $anchor = ''; | ||
1573 | 54 | 43µs | while ( my $p = shift @_ ) { | ||
1574 | 7 | 3µs | if ( $p eq '#' ) { | ||
1575 | $anchor = '#' . urlEncode( shift(@_) ); | ||||
1576 | } | ||||
1577 | else { | ||||
1578 | 7 | 2µs | my $v = shift(@_); | ||
1579 | 7 | 1µs | $v = '' unless defined $v; | ||
1580 | 7 | 16µs | 14 | 81µs | push( @ps, urlEncode($p) . '=' . urlEncode($v) ); # spent 81µs making 14 calls to Foswiki::urlEncode, avg 6µs/call |
1581 | } | ||||
1582 | } | ||||
1583 | 54 | 17µs | if ( scalar(@ps) ) { | ||
1584 | @ps = sort(@ps) if (DEBUG); | ||||
1585 | 5 | 4µs | $url .= '?' . join( ';', @ps ); | ||
1586 | } | ||||
1587 | 54 | 131µs | return $url . $anchor; | ||
1588 | } | ||||
1589 | |||||
1590 | =begin TML | ||||
1591 | |||||
1592 | ---++ ObjectMethod getPubURL($web, $topic, $attachment, %options) -> $url | ||||
1593 | |||||
1594 | Composes a pub url. | ||||
1595 | * =$web= - name of the web for the URL, defaults to $session->{webName} | ||||
1596 | * =$topic= - name of the topic, defaults to $session->{topicName} | ||||
1597 | * =$attachment= - name of the attachment, defaults to no attachment | ||||
1598 | Supported %options are: | ||||
1599 | * =topic_version= - version of topic to retrieve attachment from | ||||
1600 | * =attachment_version= - version of attachment to retrieve | ||||
1601 | * =absolute= - requests an absolute URL (rather than a relative path) | ||||
1602 | |||||
1603 | If =$web= is not given, =$topic= and =$attachment= are ignored. | ||||
1604 | If =$topic= is not given, =$attachment= is ignored. | ||||
1605 | |||||
1606 | If =topic_version= is not given, the most recent revision of the topic | ||||
1607 | will be linked. Similarly if attachment_version= is not given, the most recent | ||||
1608 | revision of the attachment will be assumed. If =topic_version= is specified | ||||
1609 | but =attachment_version= is not (or the specified =attachment_version= is not | ||||
1610 | present), then the most recent version of the attachment in that topic version | ||||
1611 | will be linked. | ||||
1612 | |||||
1613 | If Foswiki is running in an absolute URL context (e.g. the skin requires | ||||
1614 | absolute URLs, such as print or rss, or Foswiki is running from the | ||||
1615 | command-line) then =absolute= will automatically be set. | ||||
1616 | |||||
1617 | Note: for compatibility with older plugins, which use %PUBURL*% with | ||||
1618 | a constructed URL path, do not use =*= unless =web=, =topic=, and | ||||
1619 | =attachment= are all specified. | ||||
1620 | |||||
1621 | As required by RFC3986, the returned URL will only contain the | ||||
1622 | allowed characters -A-Za-z0-9_.~!*\'();:@&=+$,/?%#[] | ||||
1623 | |||||
1624 | =cut | ||||
1625 | |||||
1626 | # spent 940µs (476+464) within Foswiki::getPubURL which was called 59 times, avg 16µs/call:
# 47 times (354µs+314µs) by Foswiki::PUBURL at line 25 of /var/www/foswikidev/core/lib/Foswiki/Macros/PUBURL.pm, avg 14µs/call
# 11 times (111µs+88µs) by Foswiki::Func::getPubUrlPath at line 281 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 18µs/call
# once (11µs+62µs) by Foswiki::_getIconURL at line 128 of /var/www/foswikidev/core/lib/Foswiki/Macros/ICON.pm | ||||
1627 | 59 | 68µs | my ( $this, $web, $topic, $attachment, %options ) = @_; | ||
1628 | |||||
1629 | 59 | 85µs | 56 | 99µs | $options{absolute} ||= # spent 99µs making 56 calls to Foswiki::inContext, avg 2µs/call |
1630 | ( $this->inContext('command_line') || $this->inContext('absolute_urls') ); | ||||
1631 | |||||
1632 | 59 | 244µs | 59 | 365µs | return $this->{store} # spent 365µs making 59 calls to Foswiki::Store::getAttachmentURL, avg 6µs/call |
1633 | ->getAttachmentURL( $this, $web, $topic, $attachment, %options ); | ||||
1634 | } | ||||
1635 | |||||
1636 | =begin TML | ||||
1637 | |||||
1638 | ---++ ObjectMethod deepWebList($filter, $web) -> @list | ||||
1639 | |||||
1640 | Deep list subwebs of the named web. $filter is a Foswiki::WebFilter | ||||
1641 | object that is used to filter the list. The listing of subwebs is | ||||
1642 | dependent on $Foswiki::cfg{EnableHierarchicalWebs} being true. | ||||
1643 | |||||
1644 | Webs are returned as absolute web pathnames. | ||||
1645 | |||||
1646 | =cut | ||||
1647 | |||||
1648 | # spent 37.7ms (230µs+37.5) within Foswiki::deepWebList which was called 2 times, avg 18.9ms/call:
# 2 times (230µs+37.5ms) by Foswiki::Func::getListOfWebs at line 1543 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 18.9ms/call | ||||
1649 | 2 | 2µs | my ( $this, $filter, $rootWeb ) = @_; | ||
1650 | 2 | 300ns | my @list; | ||
1651 | 2 | 5µs | 2 | 21µs | my $webObject = new Foswiki::Meta( $this, $rootWeb ); # spent 21µs making 2 calls to Foswiki::Meta::new, avg 11µs/call |
1652 | 2 | 6µs | 2 | 17.9ms | my $it = $webObject->eachWeb( $Foswiki::cfg{EnableHierarchicalWebs} ); # spent 17.9ms making 2 calls to Foswiki::Meta::eachWeb, avg 8.96ms/call |
1653 | 2 | 500ns | return $it->all() unless $filter; | ||
1654 | 2 | 5µs | 2 | 12µs | while ( $it->hasNext() ) { # spent 12µs making 2 calls to Foswiki::ListIterator::hasNext, avg 6µs/call |
1655 | 24 | 8µs | my $w = $rootWeb || ''; | ||
1656 | 24 | 5µs | $w .= '/' if $w; | ||
1657 | 24 | 33µs | 24 | 119µs | $w .= $it->next(); # spent 119µs making 24 calls to Foswiki::ListIterator::next, avg 5µs/call |
1658 | 24 | 77µs | 48 | 19.4ms | if ( $filter->ok( $this, $w ) ) { # spent 19.3ms making 24 calls to Foswiki::WebFilter::ok, avg 806µs/call
# spent 88µs making 24 calls to Foswiki::ListIterator::hasNext, avg 4µs/call |
1659 | push( @list, $w ); | ||||
1660 | } | ||||
1661 | } | ||||
1662 | 2 | 16µs | return @list; | ||
1663 | } | ||||
1664 | |||||
1665 | =begin TML | ||||
1666 | |||||
1667 | ---++ ObjectMethod normalizeWebTopicName( $web, $topic ) -> ( $web, $topic ) | ||||
1668 | |||||
1669 | Normalize a Web<nop>.<nop>TopicName | ||||
1670 | |||||
1671 | See =Foswiki::Func= for a full specification of the expansion (not duplicated | ||||
1672 | here) | ||||
1673 | |||||
1674 | *WARNING* if there is no web specification (in the web or topic parameters) | ||||
1675 | the web defaults to $Foswiki::cfg{UsersWebName}. If there is no topic | ||||
1676 | specification, or the topic is '0', the topic defaults to the web home topic | ||||
1677 | name. | ||||
1678 | |||||
1679 | *WARNING* if the input topic name is tainted, then the output web and | ||||
1680 | topic names will be tainted. | ||||
1681 | |||||
1682 | =cut | ||||
1683 | |||||
1684 | # spent 658ms (658+40µs) within Foswiki::normalizeWebTopicName which was called 99088 times, avg 7µs/call:
# 98793 times (656ms+0s) by Foswiki::Func::normalizeWebTopicName at line 2970 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 7µs/call
# 116 times (1.10ms+0s) by Foswiki::Templates::_readTemplateFile at line 535 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 9µs/call
# 103 times (491µs+0s) by Foswiki::Func::_checkWTA at line 80 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 5µs/call
# 23 times (120µs+0s) by Foswiki::getScriptUrl at line 1548, avg 5µs/call
# 18 times (99µs+0s) by Foswiki::Render::_handleSquareBracketedLink at line 1562 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 5µs/call
# 12 times (93µs+0s) by Foswiki::_includeTopic at line 143 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 8µs/call
# 12 times (58µs+0s) by Foswiki::VAR at line 22 of /var/www/foswikidev/core/lib/Foswiki/Macros/VAR.pm, avg 5µs/call
# 4 times (32µs+0s) by Foswiki::If::OP_istopic::evaluate at line 46 of /var/www/foswikidev/core/lib/Foswiki/If/OP_istopic.pm, avg 8µs/call
# 3 times (18µs+0s) by Foswiki::REVINFO at line 27 of /var/www/foswikidev/core/lib/Foswiki/Macros/REVINFO.pm, avg 6µs/call
# once (22µs+40µs) by Foswiki::_lookupIcon at line 34 of /var/www/foswikidev/core/lib/Foswiki/Macros/ICON.pm
# once (13µs+0s) by Foswiki::Prefs::loadSitePreferences at line 381 of /var/www/foswikidev/core/lib/Foswiki/Prefs.pm
# once (8µs+0s) by Foswiki::If::OP_allows::evaluate at line 48 of /var/www/foswikidev/core/lib/Foswiki/If/OP_allows.pm
# once (7µs+0s) by Foswiki::_getIconURL at line 126 of /var/www/foswikidev/core/lib/Foswiki/Macros/ICON.pm | ||||
1685 | 99088 | 59.6ms | my ( $this, $web, $topic ) = @_; | ||
1686 | |||||
1687 | ASSERT( defined $topic ) if DEBUG; | ||||
1688 | |||||
1689 | #SMELL: Item12567: Writing the separator as a character class for some reason | ||||
1690 | # taints all the results including the data ouside the character class.. | ||||
1691 | 99088 | 255ms | if ( defined $topic && $topic =~ m{^(.*)(?:\.|/)(.*?)$} ) { | ||
1692 | 35182 | 34.0ms | $web = $1; | ||
1693 | 35182 | 10.5ms | $topic = $2; | ||
1694 | |||||
1695 | 35182 | 6.53ms | if ( DEBUG && !UNTAINTED( $_[2] ) ) { | ||
1696 | |||||
1697 | # retaint data untainted by RE above | ||||
1698 | $web = TAINT($web); | ||||
1699 | $topic = TAINT($topic); | ||||
1700 | } | ||||
1701 | } | ||||
1702 | 99088 | 14.1ms | $web ||= $cfg{UsersWebName}; | ||
1703 | 99088 | 9.95ms | $topic ||= $cfg{HomeTopicName}; | ||
1704 | |||||
1705 | # MAINWEB and TWIKIWEB expanded for compatibility reasons | ||||
1706 | 99088 | 98.2ms | while ( | ||
1707 | $web =~ s/%((MAIN|TWIKI|USERS|SYSTEM|DOC)WEB)%/ | ||||
1708 | 1 | 5µs | 1 | 0s | $this->_expandMacroOnTopicRendering( $1 ) || ''/e # spent 40µs making 1 call to Foswiki::_expandMacroOnTopicRendering, recursion: max depth 3, sum of overlapping time 40µs |
1709 | ) | ||||
1710 | { | ||||
1711 | } | ||||
1712 | |||||
1713 | # Normalize web name to use / and not . as a subweb separator | ||||
1714 | 99088 | 28.9ms | $web =~ s#\.#/#g; | ||
1715 | |||||
1716 | 99088 | 493ms | return ( $web, $topic ); | ||
1717 | } | ||||
1718 | |||||
1719 | =begin TML | ||||
1720 | |||||
1721 | ---++ StaticMethod load_package( $full_package_name ) | ||||
1722 | |||||
1723 | Will cleanly load the package or fail. This is better than 'eval "require $package"'. | ||||
1724 | |||||
1725 | It is not perfect for Perl < 5.10. For Perl 5.8, if somewhere else 'eval "require $package"' | ||||
1726 | was used *earlier* for a module that fails to load, then not only is the failure not detected | ||||
1727 | then. Neither will it be detected here. | ||||
1728 | |||||
1729 | The recommendation is to replace all dynamic require calls in Foswiki to be replaced with this call. | ||||
1730 | |||||
1731 | This functionality used to be done via module Class::Load, but that had painful dependencies. | ||||
1732 | |||||
1733 | See http://blog.fox.geek.nz/2010/11/searching-design-spec-for-ultimate.html for the gory details. | ||||
1734 | |||||
1735 | =cut | ||||
1736 | |||||
1737 | # spent 17.6ms (225µs+17.3) within Foswiki::load_package which was called:
# once (225µs+17.3ms) by Foswiki::new at line 2105 | ||||
1738 | 1 | 700ns | my $fullname = shift; | ||
1739 | |||||
1740 | # Is it already loaded? If so, it might be an internal class an missing | ||||
1741 | # from @INC, so skip it. See perldoc UNIVERSAL for what this does. | ||||
1742 | 2 | 9µs | 1 | 2µs | return if eval { $fullname->isa($fullname) }; # spent 2µs making 1 call to UNIVERSAL::isa |
1743 | |||||
1744 | 1 | 4µs | $fullname =~ s{::}{/}g; | ||
1745 | 1 | 500ns | $fullname .= '.pm'; | ||
1746 | 1 | 80µs | require $fullname; | ||
1747 | } | ||||
1748 | |||||
1749 | =begin TML | ||||
1750 | |||||
1751 | ---++ Private _parsePath( $this, $webtopic, $defaultweb, $topicOverride ) | ||||
1752 | |||||
1753 | Parses the Web/Topic path parameters to safely establish a valid web and topic, | ||||
1754 | or assign safe defaults. | ||||
1755 | |||||
1756 | * $webtopic - A "web/topic" path. It might have originated from the query path_info, | ||||
1757 | or from the topic= URL parameter. (only when the topic param contained a web component) | ||||
1758 | * $defaultweb - The default web to use if the web part of webtopic is missing or invalid. | ||||
1759 | This can be from the default UsersWebName, or from the url parameter defaultweb. | ||||
1760 | * $topicOverride - A topic name to use instead of any topic provided in the pathinfo. | ||||
1761 | |||||
1762 | Note if $webtopic ends with a trailing slash, it provides a hint that the last component should be | ||||
1763 | considered web. This allows disambiguation between a topic and subweb with the same name. | ||||
1764 | Trailing slash forces it to be recognized as a webname, otherwise the topic is shown. | ||||
1765 | Note. If the web doesn't exist, the force will be ignored. It's not possible to create a missing web | ||||
1766 | by referencing it in a URL. | ||||
1767 | |||||
1768 | This routine sets two variables when encountering invalid input: | ||||
1769 | * $this->{invalidWeb} contains original invalid web / pathinfo content when validation fails. | ||||
1770 | * $this->{invalidTopic} Same function but for topic name | ||||
1771 | When invalid / illegal characters are encountered, the session {webName} and {topicName} will be | ||||
1772 | defaulted to safe defaults. Scripts using those fields should also test if the corresponding | ||||
1773 | invalid* versions are defined, and should throw an oops exception rathern than allowing execution | ||||
1774 | to proceed with defaulted values. | ||||
1775 | |||||
1776 | The topic name will always have the first character converted to upper case, to prevent creation of | ||||
1777 | or access to invalid topics. | ||||
1778 | |||||
1779 | =cut | ||||
1780 | |||||
1781 | # spent 390µs (63+328) within Foswiki::_parsePath which was called:
# once (63µs+328µs) by Foswiki::new at line 2210 | ||||
1782 | 1 | 500ns | my $this = shift; | ||
1783 | 1 | 300ns | my $webtopic = shift; | ||
1784 | 1 | 300ns | my $defaultweb = shift; | ||
1785 | 1 | 700ns | my $topicOverride = shift; | ||
1786 | |||||
1787 | #print STDERR "_parsePath called WT ($webtopic) DEF ($defaultweb)\n"; | ||||
1788 | |||||
1789 | 1 | 1µs | my $trailingSlash = ( $webtopic =~ s/\/$// ); | ||
1790 | |||||
1791 | #print STDERR "TRAILING = $trailingSlash\n"; | ||||
1792 | |||||
1793 | # Remove any leading slashes or dots. | ||||
1794 | 1 | 1µs | $webtopic =~ s/^[\/.]+//; | ||
1795 | |||||
1796 | 1 | 3µs | my @parts = split /[\/.]+/, $webtopic; | ||
1797 | 1 | 200ns | my $cur = 0; | ||
1798 | 1 | 200ns | my @webs; # Collect valid webs from path | ||
1799 | 1 | 600ns | my @badpath; # Collect all webs, including illegal | ||
1800 | 1 | 200ns | my $temptopic; # Candidate topicname extracted from path, defaults. | ||
1801 | |||||
1802 | 1 | 1µs | foreach (@parts) { | ||
1803 | |||||
1804 | # Lax check on name to eliminate evil characters. | ||||
1805 | 3 | 6µs | 3 | 94µs | my $p = Foswiki::Sandbox::untaint( $_, # spent 94µs making 3 calls to Foswiki::Sandbox::untaint, avg 31µs/call |
1806 | \&Foswiki::Sandbox::validateTopicName ); | ||||
1807 | 3 | 500ns | unless ($p) { | ||
1808 | push @badpath, $_; | ||||
1809 | next; | ||||
1810 | } | ||||
1811 | |||||
1812 | 3 | 9µs | if ( \$_ == \$parts[-1] ) { # This is the last part of path | ||
1813 | |||||
1814 | 1 | 6µs | 1 | 140µs | if ( $this->topicExists( join( '/', @webs ) || $defaultweb, $p ) # spent 140µs making 1 call to Foswiki::topicExists |
1815 | && !$trailingSlash ) | ||||
1816 | { | ||||
1817 | |||||
1818 | #print STDERR "Exists and no trailing slash\n"; | ||||
1819 | |||||
1820 | # It exists in Store as a topic and there is no trailing slash | ||||
1821 | $temptopic = $p || ''; | ||||
1822 | } | ||||
1823 | elsif ( $this->webExists( join( '/', @webs, $p ) ) ) { | ||||
1824 | |||||
1825 | #print STDERR "Web Exists " . join( '/', @webs, $p ) . "\n"; | ||||
1826 | |||||
1827 | # It exists in Store as a web | ||||
1828 | push @badpath, $p; | ||||
1829 | push @webs, $p; | ||||
1830 | } | ||||
1831 | elsif ($trailingSlash) { | ||||
1832 | |||||
1833 | #print STDERR "Web forced ...\n"; | ||||
1834 | if ( !$this->webExists( join( '/', @webs, $p ) ) | ||||
1835 | && $this->topicExists( join( '/', @webs ) || $defaultweb, | ||||
1836 | $p ) ) | ||||
1837 | { | ||||
1838 | |||||
1839 | #print STDERR "Forced, but no such web, and topic exists"; | ||||
1840 | $temptopic = $p; | ||||
1841 | } | ||||
1842 | else { | ||||
1843 | |||||
1844 | #print STDERR "Append it to the webs\n"; | ||||
1845 | $p = Foswiki::Sandbox::untaint( $p, | ||||
1846 | \&Foswiki::Sandbox::validateWebName ); | ||||
1847 | |||||
1848 | unless ($p) { | ||||
1849 | push @badpath, $_; | ||||
1850 | next; | ||||
1851 | } | ||||
1852 | else { | ||||
1853 | push @badpath, $p; | ||||
1854 | push @webs, $p; | ||||
1855 | } | ||||
1856 | } | ||||
1857 | } | ||||
1858 | else { | ||||
1859 | #print STDERR "Just a topic. " . scalar @webs . "\n"; | ||||
1860 | $temptopic = $p; | ||||
1861 | } | ||||
1862 | } | ||||
1863 | else { | ||||
1864 | 2 | 5µs | 2 | 64µs | $p = Foswiki::Sandbox::untaint( $p, # spent 64µs making 2 calls to Foswiki::Sandbox::untaint, avg 32µs/call |
1865 | \&Foswiki::Sandbox::validateWebName ); | ||||
1866 | 2 | 1µs | unless ($p) { | ||
1867 | push @badpath, $_; | ||||
1868 | next; | ||||
1869 | } | ||||
1870 | else { | ||||
1871 | 2 | 1µs | push @badpath, $p; | ||
1872 | 2 | 800ns | push @webs, $p; | ||
1873 | } | ||||
1874 | } | ||||
1875 | } | ||||
1876 | |||||
1877 | 1 | 1µs | my $web = join( '/', @webs ); | ||
1878 | 1 | 800ns | my $badweb = join( '/', @badpath ); | ||
1879 | |||||
1880 | # Set the requestedWebName before applying defaults - used by statistics | ||||
1881 | # generation. Note: This is validated using Topic name rules to permit | ||||
1882 | # names beginning with lower case. | ||||
1883 | 1 | 3µs | 1 | 16µs | $this->{requestedWebName} = # spent 16µs making 1 call to Foswiki::Sandbox::untaint |
1884 | Foswiki::Sandbox::untaint( $badweb, | ||||
1885 | \&Foswiki::Sandbox::validateTopicName ); | ||||
1886 | |||||
1887 | #print STDERR "Set requestedWebName to $this->{requestedWebName} \n" | ||||
1888 | # if $this->{requestedWebName}; | ||||
1889 | |||||
1890 | 1 | 2µs | if ( length($web) != length($badweb) ) { | ||
1891 | |||||
1892 | #print STDERR "RESULTS:\nPATH: $web\nBAD: $badweb\n"; | ||||
1893 | $this->{invalidWeb} = $badweb; | ||||
1894 | } | ||||
1895 | |||||
1896 | 1 | 300ns | unless ($web) { | ||
1897 | $web = Foswiki::Sandbox::untaint( $defaultweb, | ||||
1898 | \&Foswiki::Sandbox::validateWebName ); | ||||
1899 | unless ($web) { | ||||
1900 | $this->{invalidWeb} = $defaultweb; | ||||
1901 | $web = $Foswiki::cfg{UsersWebName}; | ||||
1902 | } | ||||
1903 | } | ||||
1904 | |||||
1905 | # Override topicname if urlparam $topic is provided. | ||||
1906 | 1 | 300ns | $temptopic = $topicOverride if ($topicOverride); | ||
1907 | |||||
1908 | # Provide a default topic if none specified | ||||
1909 | 1 | 400ns | $temptopic = $Foswiki::cfg{HomeTopicName} unless defined($temptopic); | ||
1910 | |||||
1911 | # Item3270 - here's the appropriate place to enforce spec | ||||
1912 | # http://develop.twiki.org/~twiki4/cgi-bin/view/Bugs/Item3270 | ||||
1913 | 1 | 4µs | 1 | 13µs | my $topic = # spent 13µs making 1 call to Foswiki::Sandbox::untaint |
1914 | Foswiki::Sandbox::untaint( ucfirst($temptopic), | ||||
1915 | \&Foswiki::Sandbox::validateTopicName ); | ||||
1916 | |||||
1917 | 1 | 200ns | unless ($topic) { | ||
1918 | $this->{invalidTopic} = $temptopic; | ||||
1919 | $topic = $Foswiki::cfg{HomeTopicName}; | ||||
1920 | |||||
1921 | #print STDERR "RESULTS:\nTOPIC $topic\nBAD: $temptopic\n"; | ||||
1922 | $this->{invalidTopic} = $temptopic; | ||||
1923 | } | ||||
1924 | |||||
1925 | #print STDERR "PARSE returns web $web topic $topic\n"; | ||||
1926 | |||||
1927 | 1 | 4µs | return ( $web, $topic ); | ||
1928 | } | ||||
1929 | |||||
1930 | =begin TML | ||||
1931 | |||||
1932 | ---++ ClassMethod new( $defaultUser, $query, \%initialContext ) | ||||
1933 | |||||
1934 | Constructs a new Foswiki session object. A unique session object exists for | ||||
1935 | every transaction with Foswiki, for example every browser request, or every | ||||
1936 | script run. Session objects do not persist between mod_perl runs. | ||||
1937 | |||||
1938 | * =$defaultUser= is the username (*not* the wikiname) of the default | ||||
1939 | user you want to be logged-in, if none is available from a session | ||||
1940 | or browser. Used mainly for unit tests and debugging, it is typically | ||||
1941 | undef, in which case the default user is taken from | ||||
1942 | $Foswiki::cfg{DefaultUserName}. | ||||
1943 | * =$query= the Foswiki::Request query (may be undef, in which case an | ||||
1944 | empty query is used) | ||||
1945 | * =\%initialContext= - reference to a hash containing context | ||||
1946 | name=value pairs to be pre-installed in the context hash. May be undef. | ||||
1947 | |||||
1948 | =cut | ||||
1949 | |||||
1950 | # spent 251ms (231µs+251) within Foswiki::new which was called:
# once (231µs+251ms) by Foswiki::UI::__ANON__[/var/www/foswikidev/core/lib/Foswiki/UI.pm:376] at line 363 of /var/www/foswikidev/core/lib/Foswiki/UI.pm | ||||
1951 | 1 | 1µs | my ( $class, $defaultUser, $query, $initialContext ) = @_; | ||
1952 | |||||
1953 | 1 | 2µs | 1 | 2µs | Monitor::MARK("Static init over; make Foswiki object"); # spent 2µs making 1 call to Monitor::__ANON__[/var/www/foswikidev/core/lib/Monitor.pm:119] |
1954 | ASSERT( !$query || UNIVERSAL::isa( $query, 'Foswiki::Request' ) ) | ||||
1955 | if DEBUG; | ||||
1956 | |||||
1957 | # Override user to be admin if no configuration exists. | ||||
1958 | # Do this really early, so that later changes in isBOOTSTRAPPING can't change | ||||
1959 | # Foswiki's behavior. | ||||
1960 | 1 | 600ns | $defaultUser = 'admin' if ( $Foswiki::cfg{isBOOTSTRAPPING} ); | ||
1961 | |||||
1962 | 1 | 3µs | unless ( $Foswiki::cfg{TempfileDir} ) { | ||
1963 | |||||
1964 | # Give it a sane default. | ||||
1965 | if ( $^O eq 'MSWin32' ) { | ||||
1966 | |||||
1967 | # Windows default tmpdir is the C: root use something sane. | ||||
1968 | # Configure does a better job, it should be run. | ||||
1969 | $Foswiki::cfg{TempfileDir} = $Foswiki::cfg{WorkingDir}; | ||||
1970 | } | ||||
1971 | else { | ||||
1972 | 1 | 6µs | 1 | 45µs | $Foswiki::cfg{TempfileDir} = File::Spec->tmpdir(); # spent 45µs making 1 call to File::Spec::Unix::tmpdir |
1973 | } | ||||
1974 | } | ||||
1975 | |||||
1976 | # Cover all the possibilities | ||||
1977 | 1 | 3µs | $ENV{TMPDIR} = $Foswiki::cfg{TempfileDir}; | ||
1978 | 1 | 2µs | $ENV{TEMP} = $Foswiki::cfg{TempfileDir}; | ||
1979 | 1 | 2µs | $ENV{TMP} = $Foswiki::cfg{TempfileDir}; | ||
1980 | |||||
1981 | # Make sure CGI is also using the appropriate tempfile location | ||||
1982 | 1 | 300ns | $CGI::TMPDIRECTORY = $Foswiki::cfg{TempfileDir}; | ||
1983 | |||||
1984 | # Make %ENV safer, preventing hijack of the search path. The | ||||
1985 | # environment is set per-query, so this can't be done in a BEGIN. | ||||
1986 | # This MUST be done before any external programs are run via Sandbox. | ||||
1987 | # or it will fail with taint errors. See Item13237 | ||||
1988 | 1 | 800ns | if ( defined $Foswiki::cfg{SafeEnvPath} ) { | ||
1989 | $ENV{PATH} = $Foswiki::cfg{SafeEnvPath}; | ||||
1990 | } | ||||
1991 | else { | ||||
1992 | # Default $ENV{PATH} must be untainted because | ||||
1993 | # Foswiki may be run with the -T flag. | ||||
1994 | # SMELL: how can we validate the PATH? | ||||
1995 | 1 | 4µs | 1 | 8µs | $ENV{PATH} = Foswiki::Sandbox::untaintUnchecked( $ENV{PATH} ); # spent 8µs making 1 call to Foswiki::Sandbox::untaintUnchecked |
1996 | } | ||||
1997 | 1 | 6µs | delete @ENV{qw( IFS CDPATH ENV BASH_ENV )}; | ||
1998 | |||||
1999 | 1 | 1µs | if ( $Foswiki::cfg{WarningFileName} | ||
2000 | && $Foswiki::cfg{Log}{Implementation} eq 'Foswiki::Logger::PlainFile' ) | ||||
2001 | { | ||||
2002 | |||||
2003 | # Admin has already expressed a preference for where they want their | ||||
2004 | # logfiles to go, and has obviously not re-run configure yet. | ||||
2005 | $Foswiki::cfg{Log}{Implementation} = 'Foswiki::Logger::Compatibility'; | ||||
2006 | |||||
2007 | #print STDERR "WARNING: Foswiki is using the compatibility logger. Please re-run configure and check your logfiles settings\n"; | ||||
2008 | } | ||||
2009 | |||||
2010 | # Make sure LogFielname is defined for use in old plugins, | ||||
2011 | # but don't overwrite the setting from configure, if there is one. | ||||
2012 | # This is especially important when the admin has *chosen* | ||||
2013 | # to use the compatibility logger. (Some old TWiki heritage | ||||
2014 | # plugins write directly to the configured LogFileName | ||||
2015 | 1 | 400ns | if ( not $Foswiki::cfg{LogFileName} ) { | ||
2016 | 1 | 2µs | if ( $Foswiki::cfg{Log}{Implementation} eq | ||
2017 | 'Foswiki::Logger::Compatibility' ) | ||||
2018 | { | ||||
2019 | my $stamp = | ||||
2020 | Foswiki::Time::formatTime( time(), '$year$mo', 'servertime' ); | ||||
2021 | my $defaultLogDir = "$Foswiki::cfg{DataDir}"; | ||||
2022 | $Foswiki::cfg{LogFileName} = $defaultLogDir . "/log$stamp.txt"; | ||||
2023 | |||||
2024 | #print STDERR "Overrode LogFileName to $Foswiki::cfg{LogFileName} for CompatibilityLogger\n" | ||||
2025 | } | ||||
2026 | else { | ||||
2027 | 1 | 2µs | $Foswiki::cfg{LogFileName} = "$Foswiki::cfg{Log}{Dir}/events.log"; | ||
2028 | |||||
2029 | #print STDERR "Overrode LogFileName to $Foswiki::cfg{LogFileName} for PlainFileLogger\n" | ||||
2030 | } | ||||
2031 | } | ||||
2032 | |||||
2033 | # Set command_line context if there is no query | ||||
2034 | 1 | 300ns | $initialContext ||= defined($query) ? {} : { command_line => 1 }; | ||
2035 | |||||
2036 | # This foswiki supports: | ||||
2037 | 1 | 600ns | $initialContext->{SUPPORTS_PARA_INDENT} = 1; # paragraph indent | ||
2038 | 1 | 300ns | $initialContext->{SUPPORTS_PREF_SET_URLS} = 1; # ?Set+, ?Local+ etc URLs | ||
2039 | 1 | 1µs | if ( $Foswiki::cfg{Password} ) { | ||
2040 | $initialContext->{admin_available} = 1; # True if sudo supported. | ||||
2041 | } | ||||
2042 | |||||
2043 | 1 | 200ns | $query ||= new Foswiki::Request(); | ||
2044 | |||||
2045 | # Phase 2 of Bootstrap. Web settings require that the Foswiki request | ||||
2046 | # has been parsed. | ||||
2047 | 1 | 300ns | if ( $Foswiki::cfg{isBOOTSTRAPPING} ) { | ||
2048 | my $phase2_message = | ||||
2049 | Foswiki::Configure::Load::bootstrapWebSettings( $query->action() ); | ||||
2050 | unless ($system_message) { # Don't do this more than once. | ||||
2051 | $system_message = | ||||
2052 | ( $Foswiki::cfg{Engine} && $Foswiki::cfg{Engine} !~ /CLI/i ) | ||||
2053 | ? ( '<div class="foswikiHelp"> ' | ||||
2054 | . $bootstrap_message | ||||
2055 | . $phase2_message | ||||
2056 | . '</div>' ) | ||||
2057 | : $bootstrap_message . $phase2_message; | ||||
2058 | } | ||||
2059 | } | ||||
2060 | |||||
2061 | 1 | 7µs | my $this = bless( { sandbox => 'Foswiki::Sandbox' }, $class ); | ||
2062 | |||||
2063 | 1 | 2µs | 1 | 2µs | if (SINGLE_SINGLETONS_TRACE) { # spent 2µs making 1 call to Foswiki::SINGLE_SINGLETONS_TRACE |
2064 | require Data::Dumper; | ||||
2065 | print STDERR "new $this: " | ||||
2066 | . Data::Dumper->Dump( [ [caller], [ caller(1) ] ] ); | ||||
2067 | } | ||||
2068 | |||||
2069 | 1 | 700ns | $this->{request} = $query; | ||
2070 | 1 | 300ns | $this->{cgiQuery} = $query; # for backwards compatibility in contribs | ||
2071 | 1 | 4µs | 1 | 10µs | $this->{response} = new Foswiki::Response(); # spent 10µs making 1 call to Foswiki::Response::new |
2072 | |||||
2073 | # This is required in case we get an exception during | ||||
2074 | # initialisation, so that we have a session to handle it with. | ||||
2075 | 1 | 1µs | 1 | 1µs | ASSERT( !$Foswiki::Plugins::SESSION ) if SINGLE_SINGLETONS; # spent 1µs making 1 call to Foswiki::SINGLE_SINGLETONS |
2076 | 1 | 300ns | $Foswiki::Plugins::SESSION = $this; | ||
2077 | ASSERT( $Foswiki::Plugins::SESSION->isa('Foswiki') ) if DEBUG; | ||||
2078 | |||||
2079 | # hash of zone records | ||||
2080 | 1 | 500ns | $this->{_zones} = (); | ||
2081 | |||||
2082 | # hash of occurences of RENDERZONE | ||||
2083 | 1 | 200ns | $this->{_renderZonePlaceholder} = (); | ||
2084 | |||||
2085 | 1 | 600ns | $this->{context} = $initialContext; | ||
2086 | |||||
2087 | # Construct the plugins objects and load the code for the plugins, | ||||
2088 | # calling any preload handlers. | ||||
2089 | 1 | 5µs | 1 | 118ms | $this->{plugins} = new Foswiki::Plugins($this); # spent 118ms making 1 call to Foswiki::Plugins::new |
2090 | |||||
2091 | 1 | 2µs | if ( $Foswiki::cfg{Cache}{Enabled} && $Foswiki::cfg{Cache}{Implementation} ) | ||
2092 | { | ||||
2093 | eval "require $Foswiki::cfg{Cache}{Implementation}"; | ||||
2094 | ASSERT( !$@, $@ ) if DEBUG; | ||||
2095 | $this->{cache} = $Foswiki::cfg{Cache}{Implementation}->new(); | ||||
2096 | } | ||||
2097 | |||||
2098 | 1 | 11µs | 1 | 1.16ms | my $prefs = new Foswiki::Prefs($this); # spent 1.16ms making 1 call to Foswiki::Prefs::new |
2099 | 1 | 1µs | $this->{prefs} = $prefs; | ||
2100 | |||||
2101 | # construct the store object | ||||
2102 | 1 | 2µs | my $base = $Foswiki::cfg{Store}{Implementation} | ||
2103 | || 'Foswiki::Store::PlainFile'; | ||||
2104 | |||||
2105 | 1 | 3µs | 1 | 17.6ms | load_package($base); # spent 17.6ms making 1 call to Foswiki::load_package |
2106 | |||||
2107 | 1 | 2µs | foreach my $class ( @{ $Foswiki::cfg{Store}{ImplementationClasses} } ) { | ||
2108 | |||||
2109 | # this allows us to add an arbitary set of mixins for things | ||||
2110 | # like recordChanges | ||||
2111 | |||||
2112 | # Rejig the store impl's ISA to use each Class in order.' | ||||
2113 | load_package($class); | ||||
2114 | 2 | 45µs | 2 | 38µs | # spent 24µs (10+14) within Foswiki::BEGIN@2114 which was called:
# once (10µs+14µs) by main::BEGIN@27 at line 2114 # spent 24µs making 1 call to Foswiki::BEGIN@2114
# spent 14µs making 1 call to strict::unimport |
2115 | push( @{ $class . '::ISA' }, $base ); | ||||
2116 | 2 | 6.58ms | 2 | 29µs | # spent 19µs (9+10) within Foswiki::BEGIN@2116 which was called:
# once (9µs+10µs) by main::BEGIN@27 at line 2116 # spent 19µs making 1 call to Foswiki::BEGIN@2116
# spent 10µs making 1 call to strict::import |
2117 | $base = $class; | ||||
2118 | } | ||||
2119 | |||||
2120 | 1 | 8µs | 1 | 12µs | $this->{store} = $base->new(); # spent 12µs making 1 call to Foswiki::Store::new |
2121 | ASSERT( $this->{store}, "no $base object created" ) if DEBUG; | ||||
2122 | |||||
2123 | #Monitor::MARK("Created store"); | ||||
2124 | |||||
2125 | 1 | 20µs | 1 | 13µs | $this->{digester} = new Digest::MD5(); # spent 13µs making 1 call to Digest::MD5::new |
2126 | 1 | 6µs | 1 | 8.55ms | $this->{users} = new Foswiki::Users($this); # spent 8.55ms making 1 call to Foswiki::Users::new |
2127 | |||||
2128 | #Monitor::MARK("Created users object"); | ||||
2129 | |||||
2130 | #{urlHost} is needed by loadSession.. | ||||
2131 | 1 | 4µs | 1 | 200µs | my $url = $query->url(); # spent 200µs making 1 call to Foswiki::Request::url |
2132 | 1 | 5µs | if ( $url | ||
2133 | && !$Foswiki::cfg{ForceDefaultUrlHost} | ||||
2134 | && $url =~ m{^([^:]*://[^/]*).*$} ) | ||||
2135 | { | ||||
2136 | 1 | 3µs | $this->{urlHost} = $1; | ||
2137 | |||||
2138 | 1 | 800ns | if ( $Foswiki::cfg{RemovePortNumber} ) { | ||
2139 | $this->{urlHost} =~ s/\:[0-9]+$//; | ||||
2140 | } | ||||
2141 | |||||
2142 | # If the urlHost in the url is localhost, this is a lot less | ||||
2143 | # useful than the default url host. This is because new CGI("") | ||||
2144 | # assigns this host by default - it's a default setting, used | ||||
2145 | # when there is nothing better available. | ||||
2146 | 1 | 3µs | if ( $this->{urlHost} =~ m/^(https?:\/\/)localhost$/i ) { | ||
2147 | my $protocol = $1; | ||||
2148 | |||||
2149 | #only replace localhost _if_ the protocol matches the one specified in the DefaultUrlHost | ||||
2150 | if ( $Foswiki::cfg{DefaultUrlHost} =~ m/^$protocol/i ) { | ||||
2151 | $this->{urlHost} = $Foswiki::cfg{DefaultUrlHost}; | ||||
2152 | } | ||||
2153 | } | ||||
2154 | } | ||||
2155 | else { | ||||
2156 | $this->{urlHost} = $Foswiki::cfg{DefaultUrlHost}; | ||||
2157 | } | ||||
2158 | ASSERT( $this->{urlHost} ) if DEBUG; | ||||
2159 | |||||
2160 | # Load (or create) the CGI session | ||||
2161 | 1 | 4µs | 1 | 168µs | $this->{remoteUser} = $this->{users}->loadSession($defaultUser); # spent 168µs making 1 call to Foswiki::Users::loadSession |
2162 | |||||
2163 | 1 | 2µs | $this->{scriptUrlPath} = $Foswiki::cfg{ScriptUrlPath}; | ||
2164 | 1 | 500ns | if ( $Foswiki::cfg{GetScriptUrlFromCgi} | ||
2165 | && $url | ||||
2166 | && $url =~ m{^[^:]*://[^/]*(.*)/.*$} | ||||
2167 | && $1 ) | ||||
2168 | { | ||||
2169 | |||||
2170 | # SMELL: this is a really dangerous hack. It will fail | ||||
2171 | # spectacularly with mod_perl. | ||||
2172 | # SMELL: why not just use $query->script_name? | ||||
2173 | # SMELL: unchecked implicit untaint? | ||||
2174 | $this->{scriptUrlPath} = $1; | ||||
2175 | } | ||||
2176 | |||||
2177 | # The web/topic can be provided by either the query path_info, | ||||
2178 | # or by URL Parameters: | ||||
2179 | # topic: Specifies web.topic or topic. | ||||
2180 | # Overrides the path given in the URL | ||||
2181 | # defaultweb: Overrides the default web, for use when topic= | ||||
2182 | # does not provide a web. | ||||
2183 | # path_info Defaults to the Users web Home topic | ||||
2184 | |||||
2185 | # Set the default for web | ||||
2186 | # Development.AddWebParamToAllCgiScripts: enables | ||||
2187 | # bin/script?topic=WebPreferences;defaultweb=Sandbox | ||||
2188 | 1 | 3µs | 1 | 16µs | my $defaultweb = $query->param('defaultweb') || $Foswiki::cfg{UsersWebName}; # spent 16µs making 1 call to Foswiki::Request::param |
2189 | |||||
2190 | 1 | 5µs | 2 | 71µs | my $webtopic = urlDecode( $query->path_info() || '' ); # spent 69µs making 1 call to Foswiki::urlDecode
# spent 2µs making 1 call to Foswiki::Request::pathInfo |
2191 | 1 | 400ns | my $topicOverride = ''; | ||
2192 | 1 | 2µs | 1 | 16µs | my $topic = $query->param('topic'); # spent 16µs making 1 call to Foswiki::Request::param |
2193 | 1 | 500ns | if ( defined $topic ) { | ||
2194 | 1 | 3µs | if ( $topic =~ m/[\/.]+/ ) { | ||
2195 | $webtopic = $topic; | ||||
2196 | |||||
2197 | #print STDERR "candidate webtopic set to $webtopic by query param\n"; | ||||
2198 | } | ||||
2199 | else { | ||||
2200 | $topicOverride = $topic; | ||||
2201 | |||||
2202 | #print STDERR | ||||
2203 | # "candidate topic set to $topicOverride by query param\n"; | ||||
2204 | } | ||||
2205 | } | ||||
2206 | |||||
2207 | # SMELL Scripts like rest, jsonrpc, don't use web/topic path. | ||||
2208 | # So this ends up all bogus, but doesn't do any harm. | ||||
2209 | |||||
2210 | 1 | 5µs | 1 | 390µs | ( my $web, $topic ) = # spent 390µs making 1 call to Foswiki::_parsePath |
2211 | $this->_parsePath( $webtopic, $defaultweb, $topicOverride ); | ||||
2212 | |||||
2213 | 1 | 700ns | $this->{topicName} = $topic; | ||
2214 | 1 | 500ns | $this->{webName} = $web; | ||
2215 | |||||
2216 | # Form definition cache | ||||
2217 | 1 | 1µs | $this->{forms} = {}; | ||
2218 | |||||
2219 | # Push global preferences from %SYSTEMWEB%.DefaultPreferences | ||||
2220 | 1 | 4µs | 1 | 18.7ms | $prefs->loadDefaultPreferences(); # spent 18.7ms making 1 call to Foswiki::Prefs::loadDefaultPreferences |
2221 | |||||
2222 | #Monitor::MARK("Loaded default prefs"); | ||||
2223 | |||||
2224 | # SMELL: what happens if we move this into the Foswiki::Users::new? | ||||
2225 | # Note: The initializeUserHandler() can override settings like | ||||
2226 | # topicName and webName. For example, HomePagePlugin. | ||||
2227 | 1 | 5µs | 1 | 3.11ms | $this->{user} = $this->{users}->initialiseUser( $this->{remoteUser} ); # spent 3.11ms making 1 call to Foswiki::Users::initialiseUser |
2228 | |||||
2229 | #Monitor::MARK("Initialised user"); | ||||
2230 | |||||
2231 | # Static session variables that can be expanded in topics when they | ||||
2232 | # are enclosed in % signs | ||||
2233 | # SMELL: should collapse these into one. The duplication is pretty | ||||
2234 | # pointless. | ||||
2235 | 1 | 4µs | 1 | 14µs | $prefs->setInternalPreferences( # spent 14µs making 1 call to Foswiki::Prefs::setInternalPreferences |
2236 | BASEWEB => $this->{webName}, | ||||
2237 | BASETOPIC => $this->{topicName}, | ||||
2238 | INCLUDINGTOPIC => $this->{topicName}, | ||||
2239 | INCLUDINGWEB => $this->{webName} | ||||
2240 | ); | ||||
2241 | |||||
2242 | # Push plugin settings | ||||
2243 | 1 | 2µs | 1 | 12.0ms | $this->{plugins}->settings(); # spent 12.0ms making 1 call to Foswiki::Plugins::settings |
2244 | |||||
2245 | # Now the rest of the preferences | ||||
2246 | 1 | 3µs | 1 | 817µs | $prefs->loadSitePreferences(); # spent 817µs making 1 call to Foswiki::Prefs::loadSitePreferences |
2247 | |||||
2248 | # User preferences only available if we can get to a valid wikiname, | ||||
2249 | # which depends on the user mapper. | ||||
2250 | 1 | 4µs | 1 | 48µs | my $wn = $this->{users}->getWikiName( $this->{user} ); # spent 48µs making 1 call to Foswiki::Users::getWikiName |
2251 | 1 | 3µs | 1 | 734µs | if ($wn) { # spent 734µs making 1 call to Foswiki::Prefs::setUserPreferences |
2252 | $prefs->setUserPreferences($wn); | ||||
2253 | } | ||||
2254 | |||||
2255 | 1 | 3µs | 1 | 3.32ms | $prefs->pushTopicContext( $this->{webName}, $this->{topicName} ); # spent 3.32ms making 1 call to Foswiki::Prefs::pushTopicContext |
2256 | |||||
2257 | #Monitor::MARK("Preferences all set up"); | ||||
2258 | |||||
2259 | # Set both isadmin and authenticated contexts. If the current user | ||||
2260 | # is admin, then they either authenticated, or we are in bootstrap. | ||||
2261 | 1 | 3µs | 1 | 110µs | if ( $this->{users}->isAdmin( $this->{user} ) ) { # spent 110µs making 1 call to Foswiki::Users::isAdmin |
2262 | 1 | 800ns | $this->{context}{authenticated} = 1; | ||
2263 | 1 | 900ns | $this->{context}{isadmin} = 1; | ||
2264 | } | ||||
2265 | |||||
2266 | # Finish plugin initialization - register handlers | ||||
2267 | 1 | 3µs | 1 | 65.8ms | $this->{plugins}->enable(); # spent 65.8ms making 1 call to Foswiki::Plugins::enable |
2268 | |||||
2269 | 1 | 3µs | 1 | 2µs | Monitor::MARK("Foswiki object created"); # spent 2µs making 1 call to Monitor::__ANON__[/var/www/foswikidev/core/lib/Monitor.pm:119] |
2270 | |||||
2271 | 1 | 7µs | return $this; | ||
2272 | } | ||||
2273 | |||||
2274 | =begin TML | ||||
2275 | |||||
2276 | ---++ ObjectMethod renderer() | ||||
2277 | Get a reference to the renderer object. Done lazily because not everyone | ||||
2278 | needs the renderer. | ||||
2279 | |||||
2280 | =cut | ||||
2281 | |||||
2282 | # spent 9.03ms (7.85+1.19) within Foswiki::renderer which was called 14 times, avg 645µs/call:
# 6 times (7.83ms+1.19ms) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 324 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 1.50ms/call
# 5 times (12µs+0s) by Foswiki::Meta::renderTML at line 3367 of /var/www/foswikidev/core/lib/Foswiki/Meta.pm, avg 2µs/call
# 3 times (6µs+0s) by Foswiki::REVINFO at line 45 of /var/www/foswikidev/core/lib/Foswiki/Macros/REVINFO.pm, avg 2µs/call | ||||
2283 | 14 | 7µs | my ($this) = @_; | ||
2284 | |||||
2285 | 14 | 7µs | unless ( $this->{renderer} ) { | ||
2286 | 1 | 81µs | require Foswiki::Render; | ||
2287 | 1 | 5µs | 1 | 10µs | $this->{renderer} = new Foswiki::Render($this); # spent 10µs making 1 call to Foswiki::Render::new |
2288 | } | ||||
2289 | 14 | 42µs | return $this->{renderer}; | ||
2290 | } | ||||
2291 | |||||
2292 | =begin TML | ||||
2293 | |||||
2294 | ---++ ObjectMethod attach() | ||||
2295 | Get a reference to the attach object. Done lazily because not everyone | ||||
2296 | needs the attach. | ||||
2297 | |||||
2298 | =cut | ||||
2299 | |||||
2300 | # spent 2.78ms (2.68+97µs) within Foswiki::attach which was called:
# once (2.68ms+97µs) by Foswiki::META at line 40 of /var/www/foswikidev/core/lib/Foswiki/Macros/META.pm | ||||
2301 | 1 | 1µs | my ($this) = @_; | ||
2302 | |||||
2303 | 1 | 900ns | unless ( $this->{attach} ) { | ||
2304 | 1 | 76µs | require Foswiki::Attach; | ||
2305 | 1 | 6µs | 1 | 12µs | $this->{attach} = new Foswiki::Attach($this); # spent 12µs making 1 call to Foswiki::Attach::new |
2306 | } | ||||
2307 | 1 | 4µs | return $this->{attach}; | ||
2308 | } | ||||
2309 | |||||
2310 | =begin TML | ||||
2311 | |||||
2312 | ---++ ObjectMethod templates() | ||||
2313 | Get a reference to the templates object. Done lazily because not everyone | ||||
2314 | needs the templates. | ||||
2315 | |||||
2316 | =cut | ||||
2317 | |||||
2318 | # spent 3.45ms (3.22+237µs) within Foswiki::templates which was called 175 times, avg 20µs/call:
# 40 times (230µs+0s) by Foswiki::Search::loadTemplates at line 495 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 6µs/call
# 40 times (79µs+0s) by Foswiki::Search::loadTemplates at line 510 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (72µs+0s) by Foswiki::Search::loadTemplates at line 522 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (69µs+0s) by Foswiki::Search::loadTemplates at line 511 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 4 times (12µs+0s) by Foswiki::inlineAlert at line 2619, avg 3µs/call
# 4 times (8µs+0s) by Foswiki::inlineAlert at line 2621, avg 2µs/call
# 4 times (6µs+0s) by Foswiki::Func::expandTemplate at line 2600 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 2µs/call
# once (2.74ms+237µs) by Foswiki::Func::readTemplate at line 2535 of /var/www/foswikidev/core/lib/Foswiki/Func.pm
# once (2µs+0s) by Foswiki::Func::loadTemplate at line 2568 of /var/www/foswikidev/core/lib/Foswiki/Func.pm
# once (2µs+0s) by Foswiki::UI::View::view at line 292 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm | ||||
2319 | 175 | 73µs | my ($this) = @_; | ||
2320 | |||||
2321 | 175 | 84µs | unless ( $this->{templates} ) { | ||
2322 | 1 | 86µs | require Foswiki::Templates; | ||
2323 | 1 | 6µs | 1 | 20µs | $this->{templates} = new Foswiki::Templates($this); # spent 20µs making 1 call to Foswiki::Templates::new |
2324 | } | ||||
2325 | 175 | 513µs | return $this->{templates}; | ||
2326 | } | ||||
2327 | |||||
2328 | =begin TML | ||||
2329 | |||||
2330 | ---++ ObjectMethod i18n() | ||||
2331 | Get a reference to the i18n object. Done lazily because not everyone | ||||
2332 | needs the i18ner. | ||||
2333 | |||||
2334 | =cut | ||||
2335 | |||||
2336 | # spent 2.34ms (1.59+755µs) within Foswiki::i18n which was called 60 times, avg 39µs/call:
# 57 times (1.58ms+755µs) by Foswiki::MAKETEXT at line 57 of /var/www/foswikidev/core/lib/Foswiki/Macros/MAKETEXT.pm, avg 41µs/call
# 2 times (4µs+0s) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 95 of /var/www/foswikidev/core/lib/Foswiki/Plugins/SubscribePlugin.pm, avg 2µs/call
# once (2µs+0s) by Foswiki::Plugins::JQueryPlugin::UI::init at line 63 of /var/www/foswikidev/core/lib/Foswiki/Plugins/JQueryPlugin/UI.pm | ||||
2337 | 60 | 21µs | my ($this) = @_; | ||
2338 | |||||
2339 | 60 | 21µs | unless ( $this->{i18n} ) { | ||
2340 | 1 | 71µs | require Foswiki::I18N; | ||
2341 | |||||
2342 | # language information; must be loaded after | ||||
2343 | # *all possible preferences sources* are available | ||||
2344 | 1 | 5µs | 1 | 540µs | $this->{i18n} = new Foswiki::I18N($this); # spent 540µs making 1 call to Foswiki::I18N::new |
2345 | } | ||||
2346 | 60 | 135µs | return $this->{i18n}; | ||
2347 | } | ||||
2348 | |||||
2349 | =begin TML | ||||
2350 | |||||
2351 | ---++ ObjectMethod reset_i18n() | ||||
2352 | Kill the i18n object, if there is one, to force language re-initialisation. | ||||
2353 | Essential for changing language dynamically. | ||||
2354 | |||||
2355 | =cut | ||||
2356 | |||||
2357 | sub reset_i18n { | ||||
2358 | my $this = shift; | ||||
2359 | |||||
2360 | return unless $this->{i18n}; | ||||
2361 | $this->{i18n}->finish(); | ||||
2362 | undef $this->{i18n}; | ||||
2363 | } | ||||
2364 | |||||
2365 | =begin TML | ||||
2366 | |||||
2367 | ---++ ObjectMethod logger() | ||||
2368 | |||||
2369 | =cut | ||||
2370 | |||||
2371 | # spent 4.47ms (1.83+2.64) within Foswiki::logger which was called:
# once (1.83ms+2.64ms) by Foswiki::UI::View::view at line 256 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm | ||||
2372 | 1 | 700ns | my $this = shift; | ||
2373 | |||||
2374 | 1 | 1µs | unless ( $this->{logger} ) { | ||
2375 | 1 | 2µs | if ( $Foswiki::cfg{Log}{Implementation} eq 'none' ) { | ||
2376 | $this->{logger} = Foswiki::Logger->new(); | ||||
2377 | } | ||||
2378 | else { | ||||
2379 | 1 | 26µs | eval "require $Foswiki::cfg{Log}{Implementation}"; # spent 62µs executing statements in string eval | ||
2380 | 1 | 700ns | if ($@) { | ||
2381 | print STDERR "Logger load failed: $@"; | ||||
2382 | $this->{logger} = Foswiki::Logger->new(); | ||||
2383 | } | ||||
2384 | else { | ||||
2385 | 1 | 7µs | 1 | 9µs | $this->{logger} = $Foswiki::cfg{Log}{Implementation}->new(); # spent 9µs making 1 call to Foswiki::Logger::PlainFile::new |
2386 | } | ||||
2387 | } | ||||
2388 | } | ||||
2389 | |||||
2390 | 1 | 4µs | return $this->{logger}; | ||
2391 | } | ||||
2392 | |||||
2393 | =begin TML | ||||
2394 | |||||
2395 | ---++ ObjectMethod search() | ||||
2396 | Get a reference to the search object. Done lazily because not everyone | ||||
2397 | needs the searcher. | ||||
2398 | |||||
2399 | =cut | ||||
2400 | |||||
2401 | # spent 101ms (94.3+7.13) within Foswiki::search which was called 52603 times, avg 2µs/call:
# 17520 times (29.7ms+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:194] at line 176 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 2µs/call
# 17520 times (29.4ms+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:194] at line 186 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 2µs/call
# 8760 times (14.7ms+0s) by Foswiki::Search::InfoCache::addTopic at line 105 of /var/www/foswikidev/core/lib/Foswiki/Search/InfoCache.pm, avg 2µs/call
# 8760 times (14.7ms+0s) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 203 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 2µs/call
# 40 times (65µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/SEARCH.pm:40] at line 39 of /var/www/foswikidev/core/lib/Foswiki/Macros/SEARCH.pm, avg 2µs/call
# 3 times (5.74ms+7.13ms) by Foswiki::Meta::unload at line 504 of /var/www/foswikidev/core/lib/Foswiki/Meta.pm, avg 4.29ms/call | ||||
2402 | 52603 | 17.2ms | my ($this) = @_; | ||
2403 | |||||
2404 | 52603 | 17.4ms | unless ( $this->{search} ) { | ||
2405 | 1 | 73µs | require Foswiki::Search; | ||
2406 | 1 | 6µs | 1 | 15µs | $this->{search} = new Foswiki::Search($this); # spent 15µs making 1 call to Foswiki::Search::new |
2407 | } | ||||
2408 | 52603 | 161ms | return $this->{search}; | ||
2409 | } | ||||
2410 | |||||
2411 | =begin TML | ||||
2412 | |||||
2413 | ---++ ObjectMethod net() | ||||
2414 | Get a reference to the net object. Done lazily because not everyone | ||||
2415 | needs the net. | ||||
2416 | |||||
2417 | =cut | ||||
2418 | |||||
2419 | sub net { | ||||
2420 | my ($this) = @_; | ||||
2421 | |||||
2422 | unless ( $this->{net} ) { | ||||
2423 | require Foswiki::Net; | ||||
2424 | $this->{net} = new Foswiki::Net($this); | ||||
2425 | } | ||||
2426 | return $this->{net}; | ||||
2427 | } | ||||
2428 | |||||
2429 | =begin TML | ||||
2430 | |||||
2431 | ---++ ObjectMethod access() | ||||
2432 | Get a reference to the ACL object. | ||||
2433 | |||||
2434 | =cut | ||||
2435 | |||||
2436 | # spent 8.48ms (1.12+7.36) within Foswiki::access which was called 504 times, avg 17µs/call:
# 252 times (779µs+7.36ms) by Foswiki::Meta::haveAccess at line 1925 of /var/www/foswikidev/core/lib/Foswiki/Meta.pm, avg 32µs/call
# 252 times (342µs+0s) by Foswiki::Meta::haveAccess at line 1926 of /var/www/foswikidev/core/lib/Foswiki/Meta.pm, avg 1µs/call | ||||
2437 | 504 | 138µs | my ($this) = @_; | ||
2438 | |||||
2439 | 504 | 142µs | unless ( $this->{access} ) { | ||
2440 | 1 | 70µs | require Foswiki::Access; | ||
2441 | 1 | 4µs | 1 | 7.25ms | $this->{access} = Foswiki::Access->new($this); # spent 7.25ms making 1 call to Foswiki::Access::new |
2442 | } | ||||
2443 | ASSERT( $this->{access} ) if DEBUG; | ||||
2444 | 504 | 973µs | return $this->{access}; | ||
2445 | } | ||||
2446 | |||||
2447 | =begin TML | ||||
2448 | |||||
2449 | ---++ ObjectMethod DESTROY() | ||||
2450 | |||||
2451 | called by Perl when the Foswiki object goes out of scope | ||||
2452 | (maybe should be used kist to ASSERT that finish() was called.. | ||||
2453 | |||||
2454 | =cut | ||||
2455 | |||||
2456 | #sub DESTROY { | ||||
2457 | # my $this = shift; | ||||
2458 | # $this->finish(); | ||||
2459 | #} | ||||
2460 | |||||
2461 | =begin TML | ||||
2462 | |||||
2463 | ---++ ObjectMethod finish() | ||||
2464 | Break circular references. | ||||
2465 | |||||
2466 | =cut | ||||
2467 | |||||
2468 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
2469 | # whether they are references or not. That way this method is "golden | ||||
2470 | # documentation" of the live fields in the object. | ||||
2471 | # spent 11.5ms (1.43+10.1) within Foswiki::finish which was called:
# once (1.43ms+10.1ms) by Foswiki::UI::_execute at line 501 of /var/www/foswikidev/core/lib/Foswiki/UI.pm | ||||
2472 | 1 | 600ns | my $this = shift; | ||
2473 | |||||
2474 | # Print any macros that are never loaded | ||||
2475 | #print STDERR "NEVER USED\n"; | ||||
2476 | #for my $i (keys %macros) { | ||||
2477 | # print STDERR "\t$i\n" unless defined $macros{$i}; | ||||
2478 | #} | ||||
2479 | 1 | 3µs | $_->finish() foreach values %{ $this->{forms} }; | ||
2480 | 1 | 1µs | undef $this->{forms}; | ||
2481 | 1 | 2µs | foreach my $key ( | ||
2482 | qw(plugins users prefs templates renderer net | ||||
2483 | store search attach access i18n cache logger) | ||||
2484 | ) | ||||
2485 | { | ||||
2486 | next | ||||
2487 | 13 | 15µs | unless ref( $this->{$key} ); | ||
2488 | 11 | 90µs | 11 | 10.1ms | $this->{$key}->finish(); # spent 6.10ms making 1 call to Foswiki::Search::finish
# spent 1.35ms making 1 call to Foswiki::Users::finish
# spent 988µs making 1 call to Foswiki::Templates::finish
# spent 861µs making 1 call to Foswiki::Prefs::finish
# spent 703µs making 1 call to Foswiki::Plugins::finish
# spent 37µs making 1 call to Foswiki::I18N::Fallback::finish
# spent 30µs making 1 call to Foswiki::Store::Rcs::Store::finish
# spent 5µs making 1 call to Foswiki::Render::finish
# spent 4µs making 1 call to Foswiki::Attach::finish
# spent 3µs making 1 call to Foswiki::Access::finish
# spent 3µs making 1 call to Foswiki::Logger::finish |
2489 | 11 | 1.10ms | undef $this->{$key}; | ||
2490 | } | ||||
2491 | |||||
2492 | 1 | 3µs | undef $this->{_zones}; | ||
2493 | 1 | 10µs | undef $this->{_renderZonePlaceholder}; | ||
2494 | |||||
2495 | 1 | 700ns | undef $this->{request}; | ||
2496 | 1 | 300ns | undef $this->{cgiQuery}; | ||
2497 | |||||
2498 | 1 | 162µs | undef $this->{digester}; | ||
2499 | 1 | 2µs | 1 | 11µs | undef $this->{urlHost}; # spent 11µs making 1 call to Digest::MD5::DESTROY |
2500 | 1 | 1µs | undef $this->{web}; | ||
2501 | 1 | 1µs | undef $this->{topic}; | ||
2502 | 1 | 500ns | undef $this->{webName}; | ||
2503 | 1 | 300ns | undef $this->{topicName}; | ||
2504 | 1 | 2µs | undef $this->{invalidWeb}; | ||
2505 | 1 | 600ns | undef $this->{invalidTopic}; | ||
2506 | 1 | 2µs | undef $this->{_ICONSPACE}; | ||
2507 | 1 | 700ns | undef $this->{_EXT2ICON}; | ||
2508 | 1 | 900ns | undef $this->{_KNOWNICON}; | ||
2509 | 1 | 1µs | undef $this->{_ICONSTEMPLATE}; | ||
2510 | 1 | 600ns | undef $this->{context}; | ||
2511 | 1 | 2µs | undef $this->{remoteUser}; | ||
2512 | 1 | 600ns | undef $this->{requestedWebName}; # Web name before renaming | ||
2513 | 1 | 800ns | undef $this->{scriptUrlPath}; | ||
2514 | 1 | 700ns | undef $this->{user}; | ||
2515 | 1 | 800ns | undef $this->{_INCLUDES}; | ||
2516 | 1 | 300ns | undef $this->{response}; | ||
2517 | 1 | 1µs | undef $this->{evaluating_if}; | ||
2518 | 1 | 600ns | undef $this->{_addedToHEAD}; | ||
2519 | 1 | 2µs | undef $this->{sandbox}; | ||
2520 | 1 | 500ns | undef $this->{evaluatingEval}; | ||
2521 | 1 | 400ns | undef $this->{_ffCache}; | ||
2522 | |||||
2523 | 1 | 600ns | undef $this->{DebugVerificationCode}; # from Foswiki::UI::Register | ||
2524 | 1 | 4µs | 1 | 4µs | if (SINGLE_SINGLETONS_TRACE) { # spent 4µs making 1 call to Foswiki::SINGLE_SINGLETONS_TRACE |
2525 | require Data::Dumper; | ||||
2526 | print STDERR "finish $this: " | ||||
2527 | . Data::Dumper->Dump( [ [caller], [ caller(1) ] ] ); | ||||
2528 | } | ||||
2529 | 1 | 2µs | 1 | 2µs | if (SINGLE_SINGLETONS) { # spent 2µs making 1 call to Foswiki::SINGLE_SINGLETONS |
2530 | ASSERT( defined $Foswiki::Plugins::SESSION ); | ||||
2531 | ASSERT( $Foswiki::Plugins::SESSION == $this ); | ||||
2532 | ASSERT( $Foswiki::Plugins::SESSION->isa('Foswiki') ); | ||||
2533 | } | ||||
2534 | 1 | 600ns | undef $Foswiki::Plugins::SESSION; | ||
2535 | |||||
2536 | 1 | 5µs | if (DEBUG) { | ||
2537 | my $remaining = join ',', grep { defined $this->{$_} } keys %$this; | ||||
2538 | ASSERT( 0, | ||||
2539 | "Fields with defined values in " | ||||
2540 | . ref($this) | ||||
2541 | . "->finish(): " | ||||
2542 | . $remaining ) | ||||
2543 | if $remaining; | ||||
2544 | } | ||||
2545 | } | ||||
2546 | |||||
2547 | =begin TML | ||||
2548 | |||||
2549 | ---++ DEPRECATED ObjectMethod logEvent( $action, $webTopic, $extra, $user ) | ||||
2550 | * =$action= - what happened, e.g. view, save, rename | ||||
2551 | * =$webTopic= - what it happened to | ||||
2552 | * =$extra= - extra info, such as minor flag | ||||
2553 | * =$user= - login name of user - default current user, | ||||
2554 | or failing that the user agent | ||||
2555 | |||||
2556 | Write the log for an event to the logfile | ||||
2557 | |||||
2558 | The method is deprecated, Call logger->log directly. | ||||
2559 | |||||
2560 | =cut | ||||
2561 | |||||
2562 | sub logEvent { | ||||
2563 | my $this = shift; | ||||
2564 | |||||
2565 | my $action = shift || ''; | ||||
2566 | my $webTopic = shift || ''; | ||||
2567 | my $extra = shift || ''; | ||||
2568 | my $user = shift; | ||||
2569 | |||||
2570 | $this->logger->log( | ||||
2571 | { | ||||
2572 | level => 'info', | ||||
2573 | action => $action || '', | ||||
2574 | webTopic => $webTopic || '', | ||||
2575 | extra => $extra || '', | ||||
2576 | user => $user, | ||||
2577 | } | ||||
2578 | ); | ||||
2579 | } | ||||
2580 | |||||
2581 | =begin TML | ||||
2582 | |||||
2583 | ---++ StaticMethod validatePattern( $pattern ) -> $pattern | ||||
2584 | |||||
2585 | Validate a pattern provided in a parameter to $pattern so that | ||||
2586 | dangerous chars (interpolation and execution) are disabled. | ||||
2587 | |||||
2588 | =cut | ||||
2589 | |||||
2590 | sub validatePattern { | ||||
2591 | my $pattern = shift; | ||||
2592 | |||||
2593 | # Escape unescaped $ and @ characters that might interpolate | ||||
2594 | # an internal variable. | ||||
2595 | # There is no need to defuse (??{ and (?{ as perl won't allow | ||||
2596 | # it anyway, unless one uses re 'eval' which we won't do | ||||
2597 | $pattern =~ s/(^|[^\\])([\$\@])/$1\\$2/g; | ||||
2598 | return $pattern; | ||||
2599 | } | ||||
2600 | |||||
2601 | =begin TML | ||||
2602 | |||||
2603 | ---++ ObjectMethod inlineAlert($template, $def, ... ) -> $string | ||||
2604 | |||||
2605 | Format an error for inline inclusion in rendered output. The message string | ||||
2606 | is obtained from the template 'oops'.$template, and the DEF $def is | ||||
2607 | selected. The parameters (...) are used to populate %PARAM1%..%PARAMn% | ||||
2608 | |||||
2609 | =cut | ||||
2610 | |||||
2611 | # spent 34.1ms (240µs+33.8) within Foswiki::inlineAlert which was called 4 times, avg 8.52ms/call:
# 4 times (240µs+33.8ms) by Foswiki::_includeWarning at line 102 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 8.52ms/call | ||||
2612 | 4 | 2µs | my $this = shift; | ||
2613 | 4 | 2µs | my $template = shift; | ||
2614 | 4 | 2µs | my $def = shift; | ||
2615 | |||||
2616 | # web and topic can be anything; they are not used | ||||
2617 | 4 | 14µs | 4 | 44µs | my $topicObject = # spent 44µs making 4 calls to Foswiki::Meta::new, avg 11µs/call |
2618 | Foswiki::Meta->new( $this, $this->{webName}, $this->{topicName} ); | ||||
2619 | 4 | 23µs | 8 | 30.0ms | my $text = $this->templates->readTemplate( 'oops' . $template ); # spent 30.0ms making 4 calls to Foswiki::Templates::readTemplate, avg 7.50ms/call
# spent 12µs making 4 calls to Foswiki::templates, avg 3µs/call |
2620 | 4 | 3µs | if ($text) { | ||
2621 | 4 | 16µs | 8 | 274µs | my $blah = $this->templates->expandTemplate($def); # spent 266µs making 4 calls to Foswiki::Templates::expandTemplate, avg 67µs/call
# spent 8µs making 4 calls to Foswiki::templates, avg 2µs/call |
2622 | 4 | 20µs | $text =~ s/%INSTANTIATE%/$blah/; | ||
2623 | |||||
2624 | 4 | 14µs | 4 | 0s | $text = $topicObject->expandMacros($text); # spent 3.52ms making 4 calls to Foswiki::Meta::expandMacros, avg 880µs/call, recursion: max depth 2, sum of overlapping time 3.52ms |
2625 | 4 | 2µs | my $n = 1; | ||
2626 | 4 | 11µs | while ( defined( my $param = shift ) ) { | ||
2627 | 8 | 74µs | $text =~ s/%PARAM$n%/$param/g; | ||
2628 | 8 | 2µs | $n++; | ||
2629 | } | ||||
2630 | |||||
2631 | # Suppress missing params | ||||
2632 | 4 | 7µs | $text =~ s/%PARAM\d+%//g; | ||
2633 | |||||
2634 | # Suppress missing params | ||||
2635 | 4 | 6µs | $text =~ s/%PARAM\d+%//g; | ||
2636 | } | ||||
2637 | else { | ||||
2638 | |||||
2639 | # Error in the template system. | ||||
2640 | $text = $topicObject->renderTML(<<MESSAGE); | ||||
2641 | ---+ Foswiki Installation Error | ||||
2642 | Template 'oops$template' not found or returned no text, expanding $def. | ||||
2643 | |||||
2644 | Check your configuration settings for {TemplateDir} and {TemplatePath} | ||||
2645 | or check for syntax errors in templates, or a missing TMPL:END. | ||||
2646 | MESSAGE | ||||
2647 | } | ||||
2648 | |||||
2649 | 4 | 20µs | return $text; | ||
2650 | } | ||||
2651 | |||||
2652 | =begin TML | ||||
2653 | |||||
2654 | ---++ StaticMethod parseSections($text) -> ($string,$sectionlistref) | ||||
2655 | |||||
2656 | Generic parser for sections within a topic. Sections are delimited | ||||
2657 | by STARTSECTION and ENDSECTION, which may be nested, overlapped or | ||||
2658 | otherwise abused. The parser builds an array of sections, which is | ||||
2659 | ordered by the order of the STARTSECTION within the topic. It also | ||||
2660 | removes all the SECTION tags from the text, and returns the text | ||||
2661 | and the array of sections. | ||||
2662 | |||||
2663 | Each section is a =Foswiki::Attrs= object, which contains the attributes | ||||
2664 | {type, name, start, end} | ||||
2665 | where start and end are character offsets in the | ||||
2666 | string *after all section tags have been removed*. All sections | ||||
2667 | are required to be uniquely named; if a section is unnamed, it | ||||
2668 | will be given a generated name. Sections may overlap or nest. | ||||
2669 | |||||
2670 | See test/unit/Fn_SECTION.pm for detailed testcases that | ||||
2671 | round out the spec. | ||||
2672 | |||||
2673 | =cut | ||||
2674 | |||||
2675 | # spent 103µs within Foswiki::parseSections which was called 8 times, avg 13µs/call:
# 8 times (103µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 230 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 13µs/call | ||||
2676 | |||||
2677 | 8 | 6µs | my $text = shift; | ||
2678 | |||||
2679 | 8 | 2µs | return ( '', [] ) unless defined $text; | ||
2680 | |||||
2681 | 8 | 1µs | my %sections; | ||
2682 | 8 | 4µs | my @list = (); | ||
2683 | |||||
2684 | 8 | 2µs | my $seq = 0; | ||
2685 | 8 | 2µs | my $ntext = ''; | ||
2686 | 8 | 1µs | my $offset = 0; | ||
2687 | 8 | 26µs | foreach | ||
2688 | my $bit ( split( /(%(?:START|STOP|END)SECTION(?:{.*?})?%)/, $text ) ) | ||||
2689 | { | ||||
2690 | 8 | 19µs | if ( $bit =~ m/^%STARTSECTION(?:{(.*)})?%$/ ) { | ||
2691 | require Foswiki::Attrs; | ||||
2692 | |||||
2693 | # SMELL: unchecked implicit untaint? | ||||
2694 | my $attrs = new Foswiki::Attrs($1); | ||||
2695 | $attrs->{type} ||= 'section'; | ||||
2696 | $attrs->{name} = | ||||
2697 | $attrs->{_DEFAULT} | ||||
2698 | || $attrs->{name} | ||||
2699 | || '_SECTION' . $seq++; | ||||
2700 | delete $attrs->{_DEFAULT}; | ||||
2701 | my $id = $attrs->{type} . ':' . $attrs->{name}; | ||||
2702 | if ( $sections{$id} ) { | ||||
2703 | |||||
2704 | # error, this named section already defined, ignore | ||||
2705 | next; | ||||
2706 | } | ||||
2707 | |||||
2708 | # close open unnamed sections of the same type | ||||
2709 | foreach my $s (@list) { | ||||
2710 | if ( $s->{end} < 0 | ||||
2711 | && $s->{type} eq $attrs->{type} | ||||
2712 | && $s->{name} =~ m/^_SECTION\d+$/ ) | ||||
2713 | { | ||||
2714 | $s->{end} = $offset; | ||||
2715 | } | ||||
2716 | } | ||||
2717 | $attrs->{start} = $offset; | ||||
2718 | $attrs->{end} = -1; # open section | ||||
2719 | $sections{$id} = $attrs; | ||||
2720 | push( @list, $attrs ); | ||||
2721 | } | ||||
2722 | elsif ( $bit =~ m/^%(?:END|STOP)SECTION(?:{(.*)})?%$/ ) { | ||||
2723 | require Foswiki::Attrs; | ||||
2724 | |||||
2725 | # SMELL: unchecked implicit untaint? | ||||
2726 | my $attrs = new Foswiki::Attrs($1); | ||||
2727 | $attrs->{type} ||= 'section'; | ||||
2728 | $attrs->{name} = $attrs->{_DEFAULT} || $attrs->{name} || ''; | ||||
2729 | delete $attrs->{_DEFAULT}; | ||||
2730 | unless ( $attrs->{name} ) { | ||||
2731 | |||||
2732 | # find the last open unnamed section of this type | ||||
2733 | foreach my $s ( reverse @list ) { | ||||
2734 | if ( $s->{end} == -1 | ||||
2735 | && $s->{type} eq $attrs->{type} | ||||
2736 | && $s->{name} =~ m/^_SECTION\d+$/ ) | ||||
2737 | { | ||||
2738 | $attrs->{name} = $s->{name}; | ||||
2739 | last; | ||||
2740 | } | ||||
2741 | } | ||||
2742 | |||||
2743 | # ignore it if no matching START found | ||||
2744 | next unless $attrs->{name}; | ||||
2745 | } | ||||
2746 | my $id = $attrs->{type} . ':' . $attrs->{name}; | ||||
2747 | if ( !$sections{$id} || $sections{$id}->{end} >= 0 ) { | ||||
2748 | |||||
2749 | # error, no such open section, ignore | ||||
2750 | next; | ||||
2751 | } | ||||
2752 | $sections{$id}->{end} = $offset; | ||||
2753 | } | ||||
2754 | else { | ||||
2755 | 8 | 5µs | $ntext .= $bit; | ||
2756 | 8 | 8µs | $offset = length($ntext); | ||
2757 | } | ||||
2758 | } | ||||
2759 | |||||
2760 | # close open sections | ||||
2761 | 8 | 6µs | foreach my $s (@list) { | ||
2762 | $s->{end} = $offset if $s->{end} < 0; | ||||
2763 | } | ||||
2764 | |||||
2765 | 8 | 33µs | return ( $ntext, \@list ); | ||
2766 | } | ||||
2767 | |||||
2768 | =begin TML | ||||
2769 | |||||
2770 | ---++ ObjectMethod expandMacrosOnTopicCreation ( $topicObject ) | ||||
2771 | |||||
2772 | * =$topicObject= - the topic | ||||
2773 | |||||
2774 | Expand only that subset of Foswiki variables that are | ||||
2775 | expanded during topic creation, in the body text and | ||||
2776 | PREFERENCE meta only. The expansion is in-place inside | ||||
2777 | the topic object. | ||||
2778 | |||||
2779 | # SMELL: no plugin handler | ||||
2780 | |||||
2781 | =cut | ||||
2782 | |||||
2783 | sub expandMacrosOnTopicCreation { | ||||
2784 | my ( $this, $topicObject ) = @_; | ||||
2785 | |||||
2786 | # Make sure func works, for registered tag handlers | ||||
2787 | if (SINGLE_SINGLETONS) { | ||||
2788 | ASSERT( defined $Foswiki::Plugins::SESSION ); | ||||
2789 | ASSERT( $Foswiki::Plugins::SESSION == $this ); | ||||
2790 | } | ||||
2791 | local $Foswiki::Plugins::SESSION = $this; | ||||
2792 | ASSERT( $Foswiki::Plugins::SESSION->isa('Foswiki') ) if DEBUG; | ||||
2793 | |||||
2794 | my $text = $topicObject->text(); | ||||
2795 | if ($text) { | ||||
2796 | |||||
2797 | # Chop out templateonly sections | ||||
2798 | my ( $ntext, $sections ) = parseSections($text); | ||||
2799 | if ( scalar(@$sections) ) { | ||||
2800 | |||||
2801 | # Note that if named templateonly sections overlap, | ||||
2802 | # the behaviour is undefined. | ||||
2803 | |||||
2804 | # First excise all templateonly sections by replacing | ||||
2805 | # with nulls of the same length. This keeps the string | ||||
2806 | # length the same so offsets remain current. | ||||
2807 | foreach my $s ( reverse @$sections ) { | ||||
2808 | next unless ( $s->{type} eq 'templateonly' ); | ||||
2809 | my $r = "\0" x ( $s->{end} - $s->{start} ); | ||||
2810 | substr( $ntext, $s->{start}, $s->{end} - $s->{start}, $r ); | ||||
2811 | } | ||||
2812 | |||||
2813 | # Now restore the macros for other sections. | ||||
2814 | foreach my $s ( reverse @$sections ) { | ||||
2815 | next if ( $s->{type} eq 'templateonly' ); | ||||
2816 | |||||
2817 | my $start = $s->remove('start'); | ||||
2818 | my $end = $s->remove('end'); | ||||
2819 | $ntext = | ||||
2820 | substr( $ntext, 0, $start ) | ||||
2821 | . '%STARTSECTION{' | ||||
2822 | . $s->{_RAW} . '}%' | ||||
2823 | . substr( $ntext, $start, $end - $start ) | ||||
2824 | . '%ENDSECTION{' | ||||
2825 | . $s->{_RAW} . '}%' | ||||
2826 | . substr( $ntext, $end, length($ntext) ); | ||||
2827 | } | ||||
2828 | |||||
2829 | # Chop the nulls | ||||
2830 | $ntext =~ s/\0*//g; | ||||
2831 | $text = $ntext; | ||||
2832 | } | ||||
2833 | |||||
2834 | $text = _processMacros( $this, $text, \&_expandMacroOnTopicCreation, | ||||
2835 | $topicObject, 16 ); | ||||
2836 | |||||
2837 | # expand all variables for type="expandvariables" sections | ||||
2838 | ( $ntext, $sections ) = parseSections($text); | ||||
2839 | if ( scalar(@$sections) ) { | ||||
2840 | foreach my $s ( reverse @$sections ) { | ||||
2841 | if ( $s->{type} eq 'expandvariables' ) { | ||||
2842 | my $etext = | ||||
2843 | substr( $ntext, $s->{start}, $s->{end} - $s->{start} ); | ||||
2844 | $this->innerExpandMacros( \$etext, $topicObject ); | ||||
2845 | $ntext = | ||||
2846 | substr( $ntext, 0, $s->{start} ) | ||||
2847 | . $etext | ||||
2848 | . substr( $ntext, $s->{end}, length($ntext) ); | ||||
2849 | } | ||||
2850 | else { | ||||
2851 | |||||
2852 | # put back non-expandvariables sections | ||||
2853 | my $start = $s->remove('start'); | ||||
2854 | my $end = $s->remove('end'); | ||||
2855 | $ntext = | ||||
2856 | substr( $ntext, 0, $start ) | ||||
2857 | . '%STARTSECTION{' | ||||
2858 | . $s->{_RAW} . '}%' | ||||
2859 | . substr( $ntext, $start, $end - $start ) | ||||
2860 | . '%ENDSECTION{' | ||||
2861 | . $s->{_RAW} . '}%' | ||||
2862 | . substr( $ntext, $end, length($ntext) ); | ||||
2863 | } | ||||
2864 | } | ||||
2865 | $text = $ntext; | ||||
2866 | } | ||||
2867 | |||||
2868 | # kill markers used to prevent variable expansion | ||||
2869 | $text =~ s/%NOP%//g; | ||||
2870 | $topicObject->text($text); | ||||
2871 | } | ||||
2872 | |||||
2873 | # Expand preferences | ||||
2874 | my @prefs = $topicObject->find('PREFERENCE'); | ||||
2875 | foreach my $p (@prefs) { | ||||
2876 | $p->{value} = | ||||
2877 | _processMacros( $this, $p->{value}, \&_expandMacroOnTopicCreation, | ||||
2878 | $topicObject, 16 ); | ||||
2879 | |||||
2880 | # kill markers used to prevent variable expansion | ||||
2881 | $p->{value} =~ s/%NOP%//g; | ||||
2882 | } | ||||
2883 | } | ||||
2884 | |||||
2885 | =begin TML | ||||
2886 | |||||
2887 | ---++ StaticMethod entityEncode( $text [, $extras] ) -> $encodedText | ||||
2888 | |||||
2889 | Escape special characters to HTML numeric entities. This is *not* a generic | ||||
2890 | encoding, it is tuned specifically for use in Foswiki. | ||||
2891 | |||||
2892 | HTML4.0 spec: | ||||
2893 | "Certain characters in HTML are reserved for use as markup and must be | ||||
2894 | escaped to appear literally. The "<" character may be represented with | ||||
2895 | an <em>entity</em>, <strong class=html>&lt;</strong>. Similarly, ">" | ||||
2896 | is escaped as <strong class=html>&gt;</strong>, and "&" is escaped | ||||
2897 | as <strong class=html>&amp;</strong>. If an attribute value contains a | ||||
2898 | double quotation mark and is delimited by double quotation marks, then the | ||||
2899 | quote should be escaped as <strong class=html>&quot;</strong>. | ||||
2900 | |||||
2901 | Other entities exist for special characters that cannot easily be entered | ||||
2902 | with some keyboards..." | ||||
2903 | |||||
2904 | This method encodes: | ||||
2905 | * all non-printable 7-bit chars (< \x1f), except \n (\xa) and \r (\xd) | ||||
2906 | * HTML special characters '>', '<', '&', ''' and '"'. | ||||
2907 | * TML special characters '%', '|', '[', ']', '@', '_', | ||||
2908 | |||||
2909 | $extras is an optional param that may be used to include *additional* | ||||
2910 | characters in the set of encoded characters. It should be a string | ||||
2911 | containing the additional chars. | ||||
2912 | |||||
2913 | =cut | ||||
2914 | |||||
2915 | sub entityEncode { | ||||
2916 | my ( $text, $extra ) = @_; | ||||
2917 | $extra = '' unless defined $extra; | ||||
2918 | |||||
2919 | # Safe on utf8 binary strings, as none of the characters has bit 7 set | ||||
2920 | $text =~ | ||||
2921 | s/([[\x01-\x09\x0b\x0c\x0e-\x1f"%&'*<=>@[_\|$extra])/'&#'.ord($1).';'/ge; | ||||
2922 | return $text; | ||||
2923 | } | ||||
2924 | |||||
2925 | =begin TML | ||||
2926 | |||||
2927 | ---++ StaticMethod entityDecode ( $encodedText ) -> $text | ||||
2928 | |||||
2929 | Decodes all numeric entities (e.g. &#123;). _Does not_ decode | ||||
2930 | named entities such as &amp; (use HTML::Entities for that) | ||||
2931 | |||||
2932 | =cut | ||||
2933 | |||||
2934 | sub entityDecode { | ||||
2935 | my $text = shift; | ||||
2936 | |||||
2937 | $text =~ s/&#(\d+);/chr($1)/ge; | ||||
2938 | return $text; | ||||
2939 | } | ||||
2940 | |||||
2941 | =begin TML | ||||
2942 | |||||
2943 | ---++ StaticMethod urlEncode( $perlstring ) -> $bytestring | ||||
2944 | |||||
2945 | Encode by converting characters that are reserved in URLs to | ||||
2946 | their %NN equivalents. This method is used for encoding | ||||
2947 | strings that must be embedded _verbatim_ in URLs; it cannot | ||||
2948 | be applied to URLs themselves, as it escapes reserved | ||||
2949 | characters such as = and ?. | ||||
2950 | |||||
2951 | RFC 1738, Dec. '94: | ||||
2952 | <verbatim> | ||||
2953 | ...Only alphanumerics [0-9a-zA-Z], the special | ||||
2954 | characters $-_.+!*'(), and reserved characters used for their | ||||
2955 | reserved purposes may be used unencoded within a URL. | ||||
2956 | </verbatim> | ||||
2957 | |||||
2958 | However this function is tuned for use with Foswiki. As such, it | ||||
2959 | encodes *all* characters except 0-9a-zA-Z-_.:~!*#/ | ||||
2960 | |||||
2961 | =cut | ||||
2962 | |||||
2963 | # spent 544µs (273+271) within Foswiki::urlEncode which was called 72 times, avg 8µs/call:
# 23 times (99µs+95µs) by Foswiki::getScriptUrl at line 1550, avg 8µs/call
# 16 times (68µs+76µs) by Foswiki::Request::queryString at line 233 of /var/www/foswikidev/core/lib/Foswiki/Request.pm, avg 9µs/call
# 16 times (56µs+48µs) by Foswiki::Request::queryString at line 234 of /var/www/foswikidev/core/lib/Foswiki/Request.pm, avg 7µs/call
# 14 times (40µs+41µs) by Foswiki::make_params at line 1580, avg 6µs/call
# once (4µs+5µs) by Foswiki::Store::getAttachmentURL at line 212 of /var/www/foswikidev/core/lib/Foswiki/Store.pm
# once (3µs+3µs) by Foswiki::Store::getAttachmentURL at line 218 of /var/www/foswikidev/core/lib/Foswiki/Store.pm
# once (3µs+3µs) by Foswiki::Store::getAttachmentURL at line 228 of /var/www/foswikidev/core/lib/Foswiki/Store.pm | ||||
2964 | 72 | 24µs | my $text = shift; | ||
2965 | |||||
2966 | 72 | 74µs | 72 | 271µs | $text = encode_utf8($text); # spent 271µs making 72 calls to Encode::encode_utf8, avg 4µs/call |
2967 | 72 | 51µs | $text =~ s{([^0-9a-zA-Z-_.:~!*#/])}{sprintf('%%%02x',ord($1))}ge; | ||
2968 | |||||
2969 | 72 | 132µs | return $text; | ||
2970 | } | ||||
2971 | |||||
2972 | =begin TML | ||||
2973 | |||||
2974 | ---++ StaticMethod urlDecode( $bytestring ) -> $perlstring | ||||
2975 | |||||
2976 | Reverses the encoding done in urlEncode. | ||||
2977 | |||||
2978 | =cut | ||||
2979 | |||||
2980 | # spent 69µs (7+62) within Foswiki::urlDecode which was called:
# once (7µs+62µs) by Foswiki::new at line 2190 | ||||
2981 | 1 | 700ns | my $text = shift; | ||
2982 | |||||
2983 | 1 | 800ns | $text =~ s/%([\da-f]{2})/chr(hex($1))/gei; | ||
2984 | 1 | 2µs | 1 | 62µs | $text = decode_utf8($text); # spent 62µs making 1 call to Encode::decode_utf8 |
2985 | |||||
2986 | 1 | 5µs | return $text; | ||
2987 | } | ||||
2988 | |||||
2989 | =begin TML | ||||
2990 | |||||
2991 | ---++ StaticMethod isTrue( $value, $default ) -> $boolean | ||||
2992 | |||||
2993 | Returns 1 if =$value= is true, and 0 otherwise. "true" means set to | ||||
2994 | something with a Perl true value, with the special cases that "off", | ||||
2995 | "false" and "no" (case insensitive) are forced to false. Leading and | ||||
2996 | trailing spaces in =$value= are ignored. | ||||
2997 | |||||
2998 | If the value is undef, then =$default= is returned. If =$default= is | ||||
2999 | not specified it is taken as 0. | ||||
3000 | |||||
3001 | =cut | ||||
3002 | |||||
3003 | # spent 5.93ms within Foswiki::isTrue which was called 1479 times, avg 4µs/call:
# 80 times (721µs+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:235] at line 224 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 9µs/call
# 80 times (681µs+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::query at line 132 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 9µs/call
# 80 times (228µs+0s) by Foswiki::Search::InfoCache::sortResults at line 145 of /var/www/foswikidev/core/lib/Foswiki/Search/InfoCache.pm, avg 3µs/call
# 63 times (105µs+0s) by Foswiki::Func::isTrue at line 3102 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 2µs/call
# 40 times (423µs+0s) by Foswiki::Search::searchWeb at line 423 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 11µs/call
# 40 times (350µs+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::getListOfWebs at line 455 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 9µs/call
# 40 times (319µs+0s) by Foswiki::Search::searchWeb at line 275 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 8µs/call
# 40 times (283µs+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::getListOfWebs at line 500 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 7µs/call
# 40 times (266µs+0s) by Foswiki::Search::searchWeb at line 277 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 7µs/call
# 40 times (205µs+0s) by Foswiki::Search::formatResults at line 587 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 5µs/call
# 40 times (193µs+0s) by Foswiki::Search::searchWeb at line 286 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 5µs/call
# 40 times (192µs+0s) by Foswiki::Search::formatResults at line 716 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 5µs/call
# 40 times (184µs+0s) by Foswiki::Search::formatResults at line 717 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 5µs/call
# 40 times (171µs+0s) by Foswiki::Search::formatResults at line 589 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 4µs/call
# 40 times (122µs+0s) by Foswiki::Search::searchWeb at line 447 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 3µs/call
# 40 times (85µs+0s) by Foswiki::Search::searchWeb at line 267 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (82µs+0s) by Foswiki::Search::searchWeb at line 353 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (76µs+0s) by Foswiki::Search::searchWeb at line 424 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (69µs+0s) by Foswiki::Search::searchWeb at line 278 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (67µs+0s) by Foswiki::Search::formatResults at line 586 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (65µs+0s) by Foswiki::Search::formatResults at line 588 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (65µs+0s) by Foswiki::Search::formatResults at line 729 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (62µs+0s) by Foswiki::Search::searchWeb at line 290 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (61µs+0s) by Foswiki::Search::searchWeb at line 272 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (60µs+0s) by Foswiki::Search::formatResults at line 590 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 2µs/call
# 40 times (58µs+0s) by Foswiki::Search::searchWeb at line 284 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 1µs/call
# 40 times (58µs+0s) by Foswiki::Search::searchWeb at line 306 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 1µs/call
# 40 times (57µs+0s) by Foswiki::Search::searchWeb at line 308 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 1µs/call
# 40 times (56µs+0s) by Foswiki::Search::searchWeb at line 300 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 1µs/call
# 40 times (54µs+0s) by Foswiki::Search::formatResults at line 730 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 1µs/call
# 40 times (54µs+0s) by Foswiki::Search::searchWeb at line 301 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 1µs/call
# 40 times (53µs+0s) by Foswiki::Search::formatResults at line 732 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 1µs/call
# 19 times (145µs+0s) by Foswiki::WebFilter::ok at line 51 of /var/www/foswikidev/core/lib/Foswiki/WebFilter.pm, avg 8µs/call
# 13 times (57µs+0s) by Foswiki::Func::getPreferencesFlag at line 884 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 4µs/call
# 8 times (70µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 294 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 9µs/call
# 6 times (56µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 318 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 9µs/call
# 5 times (49µs+0s) by Foswiki::Render::getRenderedVersion at line 528 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 10µs/call
# 3 times (7µs+0s) by Foswiki::URLPARAM at line 27 of /var/www/foswikidev/core/lib/Foswiki/Macros/URLPARAM.pm, avg 2µs/call
# 2 times (19µs+0s) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 90 of /var/www/foswikidev/core/lib/Foswiki/Plugins/SubscribePlugin.pm, avg 10µs/call | ||||
3004 | 1479 | 973µs | my ( $value, $default ) = @_; | ||
3005 | |||||
3006 | 1479 | 372µs | $default ||= 0; | ||
3007 | |||||
3008 | 1479 | 8.03ms | return $default unless defined($value); | ||
3009 | |||||
3010 | 521 | 2.30ms | $value =~ s/^\s*(.*?)\s*$/$1/gi; | ||
3011 | 521 | 131µs | $value =~ s/off//gi; | ||
3012 | 521 | 136µs | $value =~ s/no//gi; | ||
3013 | 521 | 84µs | $value =~ s/false//gi; | ||
3014 | 521 | 1.57ms | return ($value) ? 1 : 0; | ||
3015 | } | ||||
3016 | |||||
3017 | =begin TML | ||||
3018 | |||||
3019 | ---++ StaticMethod spaceOutWikiWord( $word, $sep ) -> $string | ||||
3020 | |||||
3021 | Spaces out a wiki word by inserting a string (default: one space) between each word component. | ||||
3022 | With parameter $sep any string may be used as separator between the word components; if $sep is undefined it defaults to a space. | ||||
3023 | |||||
3024 | =cut | ||||
3025 | |||||
3026 | # spent 940µs (500+441) within Foswiki::spaceOutWikiWord which was called:
# once (500µs+441µs) by Foswiki::SPACEOUT at line 18 of /var/www/foswikidev/core/lib/Foswiki/Macros/SPACEOUT.pm | ||||
3027 | 1 | 1µs | my ( $word, $sep ) = @_; | ||
3028 | |||||
3029 | # Both could have the value 0 so we cannot use simple = || '' | ||||
3030 | 1 | 600ns | $word = defined($word) ? $word : ''; | ||
3031 | 1 | 700ns | $sep = defined($sep) ? $sep : ' '; | ||
3032 | 1 | 400ns | my $mark = "\001"; | ||
3033 | 1 | 107µs | 2 | 132µs | $word =~ s/([[:upper:]])([[:digit:]])/$1$mark$2/g; # spent 132µs making 2 calls to utf8::SWASHNEW, avg 66µs/call |
3034 | 1 | 21µs | 1 | 55µs | $word =~ s/([[:digit:]])([[:upper:]])/$1$mark$2/g; # spent 55µs making 1 call to utf8::SWASHNEW |
3035 | 1 | 181µs | 2 | 142µs | $word =~ s/([[:lower:]])([[:upper:][:digit:]]+)/$1$mark$2/g; # spent 142µs making 2 calls to utf8::SWASHNEW, avg 71µs/call |
3036 | 1 | 161µs | 2 | 112µs | $word =~ s/([[:upper:]])([[:upper:]])(?=[[:lower:]])/$1$mark$2/g; # spent 112µs making 2 calls to utf8::SWASHNEW, avg 56µs/call |
3037 | 1 | 10µs | $word =~ s/$mark/$sep/g; | ||
3038 | 1 | 5µs | return $word; | ||
3039 | } | ||||
3040 | |||||
3041 | =begin TML | ||||
3042 | |||||
3043 | ---++ ObjectMethod innerExpandMacros(\$text, $topicObject) | ||||
3044 | Expands variables by replacing the variables with their | ||||
3045 | values. Some example variables: %<nop>TOPIC%, %<nop>SCRIPTURL%, | ||||
3046 | %<nop>WIKINAME%, etc. | ||||
3047 | $web and $incs are passed in for recursive include expansion. They can | ||||
3048 | safely be undef. | ||||
3049 | The rules for tag expansion are: | ||||
3050 | 1 Tags are expanded left to right, in the order they are encountered. | ||||
3051 | 1 Tags are recursively expanded as soon as they are encountered - | ||||
3052 | the algorithm is inherently single-pass | ||||
3053 | 1 A tag is not "encountered" until the matching }% has been seen, by | ||||
3054 | which time all tags in parameters will have been expanded | ||||
3055 | 1 Tag expansions that create new tags recursively are limited to a | ||||
3056 | set number of hierarchical levels of expansion | ||||
3057 | |||||
3058 | =cut | ||||
3059 | |||||
3060 | # spent 137s (5.22ms+137) within Foswiki::innerExpandMacros which was called 238 times, avg 576ms/call:
# 100 times (2.14ms+137s) by Foswiki::expandMacros at line 3620, avg 1.37s/call
# 100 times (2.16ms+-977µs) by Foswiki::expandMacros at line 3630, avg 12µs/call
# 16 times (379µs+-379µs) by Foswiki::If::OP_dollar::evaluate at line 47 of /var/www/foswikidev/core/lib/Foswiki/If/OP_dollar.pm, avg 0s/call
# 8 times (194µs+-194µs) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 313 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 0s/call
# 8 times (192µs+-192µs) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 300 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 0s/call
# 6 times (147µs+-147µs) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 336 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 0s/call | ||||
3061 | 238 | 106µs | my ( $this, $text, $topicObject ) = @_; | ||
3062 | |||||
3063 | # push current context | ||||
3064 | 238 | 306µs | 238 | 572µs | my $memTopic = $this->{prefs}->getPreference('TOPIC'); # spent 572µs making 238 calls to Foswiki::Prefs::getPreference, avg 2µs/call |
3065 | 238 | 272µs | 238 | 498µs | my $memWeb = $this->{prefs}->getPreference('WEB'); # spent 498µs making 238 calls to Foswiki::Prefs::getPreference, avg 2µs/call |
3066 | |||||
3067 | # Historically this couldn't be called on web objects. | ||||
3068 | 238 | 296µs | 238 | 346µs | my $webContext = $topicObject->web || $this->{webName}; # spent 346µs making 238 calls to Foswiki::Meta::web, avg 1µs/call |
3069 | 238 | 264µs | 238 | 312µs | my $topicContext = $topicObject->topic || $this->{topicName}; # spent 312µs making 238 calls to Foswiki::Meta::topic, avg 1µs/call |
3070 | |||||
3071 | 238 | 282µs | 238 | 1.14ms | $this->{prefs}->setInternalPreferences( # spent 1.14ms making 238 calls to Foswiki::Prefs::setInternalPreferences, avg 5µs/call |
3072 | TOPIC => $topicContext, | ||||
3073 | WEB => $webContext | ||||
3074 | ); | ||||
3075 | |||||
3076 | # Escape ' !%VARIABLE%' | ||||
3077 | 238 | 491µs | $$text =~ s/(?<=[\s\(\.])!%($regex{tagNameRegex})/%$1/g; | ||
3078 | |||||
3079 | # Make sure func works, for registered tag handlers | ||||
3080 | 238 | 262µs | 238 | 241µs | if (SINGLE_SINGLETONS) { # spent 241µs making 238 calls to Foswiki::SINGLE_SINGLETONS, avg 1µs/call |
3081 | ASSERT( defined $Foswiki::Plugins::SESSION ); | ||||
3082 | ASSERT( $Foswiki::Plugins::SESSION == $this ); | ||||
3083 | } | ||||
3084 | 238 | 79µs | local $Foswiki::Plugins::SESSION = $this; | ||
3085 | ASSERT( $Foswiki::Plugins::SESSION->isa('Foswiki') ) if DEBUG; | ||||
3086 | |||||
3087 | # NOTE TO DEBUGGERS | ||||
3088 | # The depth parameter in the following call controls the maximum number | ||||
3089 | # of levels of expansion. If it is set to 1 then only macros in the | ||||
3090 | # topic will be expanded; macros that they in turn generate will be | ||||
3091 | # left unexpanded. If it is set to 2 then the expansion will stop after | ||||
3092 | # the first recursive inclusion, and so on. This is incredible useful | ||||
3093 | # when debugging. The default, 16, was selected empirically. | ||||
3094 | 238 | 410µs | 238 | 137s | $$text = _processMacros( $this, $$text, \&_expandMacroOnTopicRendering, # spent 137s making 238 calls to Foswiki::_processMacros, avg 577ms/call, recursion: max depth 5, sum of overlapping time 163ms |
3095 | $topicObject, 16 ); | ||||
3096 | |||||
3097 | # restore previous context | ||||
3098 | 238 | 800µs | 238 | 1.09ms | $this->{prefs}->setInternalPreferences( # spent 1.09ms making 238 calls to Foswiki::Prefs::setInternalPreferences, avg 5µs/call |
3099 | TOPIC => $memTopic, | ||||
3100 | WEB => $memWeb | ||||
3101 | ); | ||||
3102 | } | ||||
3103 | |||||
3104 | =begin TML | ||||
3105 | |||||
3106 | ---++ StaticMethod takeOutBlocks( \$text, $tag, \%map ) -> $text | ||||
3107 | * =$text= - Text to process | ||||
3108 | * =$tag= - XML-style tag. | ||||
3109 | * =\%map= - Reference to a hash to contain the removed blocks | ||||
3110 | |||||
3111 | Return value: $text with blocks removed | ||||
3112 | |||||
3113 | Searches through $text and extracts blocks delimited by an XML-style tag, | ||||
3114 | storing the extracted block, and replacing with a token string which is | ||||
3115 | not affected by TML rendering. The text after these substitutions is | ||||
3116 | returned. | ||||
3117 | |||||
3118 | =cut | ||||
3119 | |||||
3120 | # spent 6.55ms within Foswiki::takeOutBlocks which was called 310 times, avg 21µs/call:
# 100 times (1.07ms+0s) by Foswiki::expandMacros at line 3602, avg 11µs/call
# 100 times (988µs+0s) by Foswiki::expandMacros at line 3622, avg 10µs/call
# 87 times (1.76ms+0s) by Foswiki::_processMacros at line 3249, avg 20µs/call
# 8 times (325µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 303 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 41µs/call
# 5 times (1.42ms+0s) by Foswiki::Render::getRenderedVersion at line 230 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 284µs/call
# 5 times (603µs+0s) by Foswiki::Render::getRenderedVersion at line 229 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 121µs/call
# 5 times (385µs+0s) by Foswiki::Render::getRenderedVersion at line 264 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 77µs/call | ||||
3121 | 310 | 188µs | my ( $intext, $tag, $map ) = @_; | ||
3122 | |||||
3123 | 310 | 9.93ms | return $intext unless ( $intext =~ m/<$tag\b/i ); | ||
3124 | |||||
3125 | 2 | 800ns | my $out = ''; | ||
3126 | 2 | 800ns | my $depth = 0; | ||
3127 | 2 | 400ns | my $scoop; | ||
3128 | 2 | 400ns | my $tagParams; | ||
3129 | |||||
3130 | 2 | 270µs | foreach my $token ( split( /(<\/?$tag[^>]*>)/i, $intext ) ) { | ||
3131 | 118 | 485µs | if ( $token =~ m/<$tag\b([^>]*)?>/i ) { | ||
3132 | 29 | 5µs | $depth++; | ||
3133 | 29 | 9µs | if ( $depth eq 1 ) { | ||
3134 | 29 | 15µs | $tagParams = $1; | ||
3135 | 29 | 5µs | next; | ||
3136 | } | ||||
3137 | } | ||||
3138 | elsif ( $token =~ m/<\/$tag>/i ) { | ||||
3139 | 29 | 5µs | if ( $depth > 0 ) { | ||
3140 | 29 | 11µs | $depth--; | ||
3141 | 29 | 6µs | if ( $depth eq 0 ) { | ||
3142 | 29 | 10µs | my $placeholder = "$tag$BLOCKID"; | ||
3143 | 29 | 4µs | $BLOCKID++; | ||
3144 | 29 | 43µs | $map->{$placeholder}{text} = $scoop; | ||
3145 | 29 | 14µs | $map->{$placeholder}{params} = $tagParams; | ||
3146 | 29 | 21µs | $out .= "$OC$placeholder$CC"; | ||
3147 | 29 | 4µs | $scoop = ''; | ||
3148 | 29 | 7µs | next; | ||
3149 | } | ||||
3150 | } | ||||
3151 | } | ||||
3152 | 60 | 28µs | if ( $depth > 0 ) { | ||
3153 | $scoop .= $token; | ||||
3154 | } | ||||
3155 | else { | ||||
3156 | 31 | 13µs | $out .= $token; | ||
3157 | } | ||||
3158 | } | ||||
3159 | |||||
3160 | # unmatched tags | ||||
3161 | 2 | 2µs | if ( defined($scoop) && ( $scoop ne '' ) ) { | ||
3162 | my $placeholder = "$tag$BLOCKID"; | ||||
3163 | $BLOCKID++; | ||||
3164 | $map->{$placeholder}{text} = $scoop; | ||||
3165 | $map->{$placeholder}{params} = $tagParams; | ||||
3166 | $out .= "$OC$placeholder$CC"; | ||||
3167 | } | ||||
3168 | |||||
3169 | 2 | 9µs | return $out; | ||
3170 | } | ||||
3171 | |||||
3172 | =begin TML | ||||
3173 | |||||
3174 | ---++ StaticMethod putBackBlocks( \$text, \%map, $tag, $newtag, $callBack ) -> $text | ||||
3175 | |||||
3176 | Return value: $text with blocks added back | ||||
3177 | * =\$text= - reference to text to process | ||||
3178 | * =\%map= - map placeholders to blocks removed by takeOutBlocks | ||||
3179 | * =$tag= - Tag name processed by takeOutBlocks | ||||
3180 | * =$newtag= - Tag name to use in output, in place of $tag. | ||||
3181 | If undefined, uses $tag. | ||||
3182 | * =$callback= - Reference to function to call on each block | ||||
3183 | being inserted (optional) | ||||
3184 | |||||
3185 | Reverses the actions of takeOutBlocks. | ||||
3186 | |||||
3187 | Each replaced block is processed by the callback (if there is one) before | ||||
3188 | re-insertion. | ||||
3189 | |||||
3190 | Parameters to the outermost cut block are replaced into the open tag, | ||||
3191 | even if that tag is changed. This allows things like =<verbatim class=''>= | ||||
3192 | to be changed to =<pre class=''>= | ||||
3193 | |||||
3194 | If you set $newtag to '', replaces the taken-out block with the contents | ||||
3195 | of the block, not including the open/close. This is used for <literal>, | ||||
3196 | for example. | ||||
3197 | |||||
3198 | =cut | ||||
3199 | |||||
3200 | # spent 1.18ms within Foswiki::putBackBlocks which was called 210 times, avg 6µs/call:
# 100 times (307µs+0s) by Foswiki::expandMacros at line 3655, avg 3µs/call
# 87 times (290µs+0s) by Foswiki::_processMacros at line 3372, avg 3µs/call
# 8 times (26µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 309 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 3µs/call
# 5 times (440µs+0s) by Foswiki::Render::getRenderedVersion at line 557 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 88µs/call
# 5 times (60µs+0s) by Foswiki::Render::getRenderedVersion at line 551 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 12µs/call
# 5 times (59µs+0s) by Foswiki::Render::getRenderedVersion at line 545 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 12µs/call | ||||
3201 | 210 | 129µs | my ( $text, $map, $tag, $newtag, $callback ) = @_; | ||
3202 | |||||
3203 | 210 | 63µs | $newtag = $tag if ( !defined($newtag) ); | ||
3204 | |||||
3205 | 210 | 691µs | foreach my $placeholder ( keys %$map ) { | ||
3206 | 141 | 143µs | if ( $placeholder =~ m/^$tag\d+$/ ) { | ||
3207 | 29 | 15µs | my $params = $map->{$placeholder}{params} || ''; | ||
3208 | 29 | 9µs | my $val = $map->{$placeholder}{text}; | ||
3209 | 29 | 3µs | $val = &$callback($val) if ( defined($callback) ); | ||
3210 | 29 | 14µs | if ( $newtag eq '' ) { | ||
3211 | 29 | 299µs | $$text =~ s($OC$placeholder$CC)($val); | ||
3212 | } | ||||
3213 | else { | ||||
3214 | $$text =~ s($OC$placeholder$CC) | ||||
3215 | (<$newtag$params>$val</$newtag>); | ||||
3216 | } | ||||
3217 | 29 | 24µs | delete( $map->{$placeholder} ); | ||
3218 | } | ||||
3219 | } | ||||
3220 | } | ||||
3221 | |||||
3222 | # Process Foswiki %TAGS{}% by parsing the input tokenised into | ||||
3223 | # % separated sections. The parser is a simple stack-based parse, | ||||
3224 | # sufficient to ensure nesting of tags is correct, but no more | ||||
3225 | # than that. | ||||
3226 | # $depth limits the number of recursive expansion steps that | ||||
3227 | # can be performed on expanded tags. | ||||
3228 | sub _processMacros { | ||||
3229 | 271 | 218µs | my ( $this, $text, $tagf, $topicObject, $depth ) = @_; | ||
3230 | 271 | 47µs | my $tell = 0; | ||
3231 | |||||
3232 | 271 | 154µs | return '' if ( ( !defined($text) ) | ||
3233 | || ( $text eq '' ) ); | ||||
3234 | |||||
3235 | #no tags to process | ||||
3236 | 246 | 600µs | return $text unless ( $text =~ m/%/ ); | ||
3237 | |||||
3238 | 87 | 12µs | unless ($depth) { | ||
3239 | my $mess = "Max recursive depth reached: $text"; | ||||
3240 | $this->logger->log( 'warning', $mess ); | ||||
3241 | |||||
3242 | # prevent recursive expansion that just has been detected | ||||
3243 | # from happening in the error message | ||||
3244 | $text =~ s/%(.*?)%/$1/g; | ||||
3245 | return $text; | ||||
3246 | } | ||||
3247 | |||||
3248 | 87 | 42µs | my $verbatim = {}; | ||
3249 | 87 | 112µs | 87 | 1.76ms | $text = takeOutBlocks( $text, 'verbatim', $verbatim ); # spent 1.76ms making 87 calls to Foswiki::takeOutBlocks, avg 20µs/call |
3250 | |||||
3251 | 87 | 28µs | my $dirtyAreas = {}; | ||
3252 | 87 | 116µs | 87 | 145µs | $text = takeOutBlocks( $text, 'dirtyarea', $dirtyAreas ) # spent 145µs making 87 calls to Foswiki::Meta::isCacheable, avg 2µs/call |
3253 | if $topicObject->isCacheable(); | ||||
3254 | |||||
3255 | 87 | 993µs | my @queue = split( /(%)/, $text ); | ||
3256 | 87 | 12µs | my @stack; | ||
3257 | 87 | 21µs | my $stackTop = ''; # the top stack entry. Done this way instead of | ||
3258 | # referring to the top of the stack for efficiency. This var | ||||
3259 | # should be considered to be $stack[$#stack] | ||||
3260 | |||||
3261 | 87 | 222µs | while ( scalar(@queue) ) { | ||
3262 | |||||
3263 | #print STDERR "QUEUE:".join("\n ", map { "'$_'" } @queue)."\n"; | ||||
3264 | 2248 | 623µs | my $token = shift(@queue); | ||
3265 | |||||
3266 | #print STDERR ' ' x $tell,"PROCESSING $token \n"; | ||||
3267 | |||||
3268 | # each % sign either closes an existing stacked context, or | ||||
3269 | # opens a new context. | ||||
3270 | 2248 | 1.09ms | if ( $token eq '%' ) { | ||
3271 | |||||
3272 | #print STDERR ' ' x $tell,"CONSIDER $stackTop\n"; | ||||
3273 | # If this is a closing }%, try to rejoin the previous | ||||
3274 | # tokens until we get to a valid tag construct. This is | ||||
3275 | # a bit of a hack, but it's hard to think of a better | ||||
3276 | # way to do this without a full parse that takes % signs | ||||
3277 | # in tag parameters into account. | ||||
3278 | 1101 | 711µs | if ( $stackTop =~ m/}$/s ) { | ||
3279 | 251 | 713µs | while ( scalar(@stack) | ||
3280 | && $stackTop !~ /^%$regex{tagNameRegex}\{.*}$/s ) | ||||
3281 | { | ||||
3282 | 35 | 10µs | my $top = $stackTop; | ||
3283 | |||||
3284 | #print STDERR ' ' x $tell,"COLLAPSE $top \n"; | ||||
3285 | 35 | 67µs | $stackTop = pop(@stack) . $top; | ||
3286 | } | ||||
3287 | } | ||||
3288 | |||||
3289 | # /s so you can have newlines in parameters | ||||
3290 | 1101 | 2.49ms | if ( $stackTop =~ m/^%(($regex{tagNameRegex})(?:{(.*)})?)$/s ) { | ||
3291 | |||||
3292 | # SMELL: unchecked implicit untaint? | ||||
3293 | 536 | 800µs | my ( $expr, $tag, $args ) = ( $1, $2, $3 ); | ||
3294 | |||||
3295 | #Foswiki::Func::writeDebug("POP $tag") if $tracing; | ||||
3296 | #Monitor::MARK("Before $tag"); | ||||
3297 | 536 | 898µs | 536 | 137s | my $e = &$tagf( $this, $tag, $args, $topicObject ); # spent 137s making 536 calls to Foswiki::_expandMacroOnTopicRendering, avg 256ms/call, recursion: max depth 3, sum of overlapping time 151ms |
3298 | |||||
3299 | #Monitor::MARK("After $tag"); | ||||
3300 | |||||
3301 | 536 | 152µs | if ( defined($e) ) { | ||
3302 | |||||
3303 | #Foswiki::Func::writeDebug("EXPANDED $tag -> $e") if $tracing; | ||||
3304 | 503 | 299µs | $stackTop = pop(@stack); | ||
3305 | |||||
3306 | # Don't bother recursively expanding unless there are | ||||
3307 | # unexpanded tags in the result. | ||||
3308 | 503 | 1.00ms | unless ( $e =~ m/%$regex{tagNameRegex}(?:{.*})?%/s ) { | ||
3309 | 470 | 471µs | $stackTop .= $e; | ||
3310 | 470 | 290µs | next; | ||
3311 | } | ||||
3312 | |||||
3313 | # Recursively expand tags in the expansion of $tag | ||||
3314 | $stackTop .= | ||||
3315 | 33 | 107µs | 33 | 0s | $this->_processMacros( $e, $tagf, $topicObject, # spent 22.8ms making 33 calls to Foswiki::_processMacros, avg 691µs/call, recursion: max depth 4, sum of overlapping time 22.8ms |
3316 | $depth - 1 ); | ||||
3317 | } | ||||
3318 | else { | ||||
3319 | |||||
3320 | #Foswiki::Func::writeDebug("EXPAND $tag FAILED") if $tracing; | ||||
3321 | # To handle %NOP | ||||
3322 | # correctly, we have to handle the %VAR% case differently | ||||
3323 | # to the %VAR{}% case when a variable expansion fails. | ||||
3324 | # This is so that recursively define variables e.g. | ||||
3325 | # %A%B%D% expand correctly, but at the same time we ensure | ||||
3326 | # that a mismatched }% can't accidentally close a context | ||||
3327 | # that was left open when a tag expansion failed. | ||||
3328 | # However TWiki didn't do this, so for compatibility | ||||
3329 | # we have to accept that %NOP can never be fixed. if it | ||||
3330 | # could, then we could uncomment the following: | ||||
3331 | |||||
3332 | #if( $stackTop =~ m/}$/ ) { | ||||
3333 | # # %VAR{...}% case | ||||
3334 | # # We need to push the unexpanded expression back | ||||
3335 | # # onto the stack, but we don't want it to match the | ||||
3336 | # # tag expression again. So we protect the %'s | ||||
3337 | # $stackTop = "%$expr%"; | ||||
3338 | #} else | ||||
3339 | #{ | ||||
3340 | |||||
3341 | # %VAR% case. | ||||
3342 | # In this case we *do* want to match the tag expression | ||||
3343 | # again, as an embedded %VAR% may have expanded to | ||||
3344 | # create a valid outer expression. This is directly | ||||
3345 | # at odds with the %VAR{...}% case. | ||||
3346 | 33 | 16µs | push( @stack, $stackTop ); | ||
3347 | 33 | 9µs | $stackTop = '%'; # open new context | ||
3348 | #} | ||||
3349 | } | ||||
3350 | } | ||||
3351 | else { | ||||
3352 | 565 | 459µs | push( @stack, $stackTop ); | ||
3353 | 565 | 152µs | $stackTop = '%'; # push a new context | ||
3354 | #$tell++; | ||||
3355 | } | ||||
3356 | } | ||||
3357 | else { | ||||
3358 | 1147 | 555µs | $stackTop .= $token; | ||
3359 | } | ||||
3360 | } | ||||
3361 | |||||
3362 | # Run out of input. Gather up everything in the stack. | ||||
3363 | 87 | 30µs | while ( scalar(@stack) ) { | ||
3364 | 60 | 25µs | my $expr = $stackTop; | ||
3365 | 60 | 15µs | $stackTop = pop(@stack); | ||
3366 | 60 | 50µs | $stackTop .= $expr; | ||
3367 | } | ||||
3368 | |||||
3369 | 87 | 115µs | 87 | 141µs | putBackBlocks( \$stackTop, $dirtyAreas, 'dirtyarea' ) # spent 141µs making 87 calls to Foswiki::Meta::isCacheable, avg 2µs/call |
3370 | if $topicObject->isCacheable(); | ||||
3371 | |||||
3372 | 87 | 115µs | 87 | 290µs | putBackBlocks( \$stackTop, $verbatim, 'verbatim' ); # spent 290µs making 87 calls to Foswiki::putBackBlocks, avg 3µs/call |
3373 | |||||
3374 | #print STDERR "FINAL $stackTop\n"; | ||||
3375 | |||||
3376 | 87 | 279µs | return $stackTop; | ||
3377 | } | ||||
3378 | |||||
3379 | # Handle expansion of a tag during topic rendering | ||||
3380 | # $tag is the tag name | ||||
3381 | # $args is the bit in the {} (if there are any) | ||||
3382 | # $topicObject should be passed for dynamic tags (not needed for | ||||
3383 | # session or constant tags | ||||
3384 | sub _expandMacroOnTopicRendering { | ||||
3385 | 537 | 346µs | my ( $this, $tag, $args, $topicObject ) = @_; | ||
3386 | |||||
3387 | 537 | 225µs | require Foswiki::Attrs; | ||
3388 | |||||
3389 | 537 | 851µs | 537 | 22.4ms | my $e = $this->{prefs}->getPreference($tag); # spent 22.4ms making 537 calls to Foswiki::Prefs::getPreference, avg 42µs/call |
3390 | 537 | 470µs | if ( defined $e ) { | ||
3391 | 107 | 20µs | if ( $args && $args =~ m/\S/ ) { | ||
3392 | my $attrs = new Foswiki::Attrs( $args, 0 ); | ||||
3393 | |||||
3394 | $e = $this->_processMacros( | ||||
3395 | $e, | ||||
3396 | sub { | ||||
3397 | # Expand %DEFAULT and any parameter tags | ||||
3398 | my ( $this, $tag, $args, $topicObject ) = @_; | ||||
3399 | my $tattrs = new Foswiki::Attrs($args); | ||||
3400 | |||||
3401 | if ( $tag eq 'DEFAULT' ) { | ||||
3402 | |||||
3403 | # Define the %DEFAULT macro to return the value | ||||
3404 | # passed (if any) or the default= parameter (if | ||||
3405 | # present) otherwise. | ||||
3406 | return $attrs->{_DEFAULT} if defined $attrs->{_DEFAULT}; | ||||
3407 | return $tattrs->{default} if defined $tattrs->{default}; | ||||
3408 | |||||
3409 | # No default and no value - kill it. | ||||
3410 | return ''; | ||||
3411 | } | ||||
3412 | my $val = $attrs->{$tag}; | ||||
3413 | $val = $tattrs->{default} unless defined $val; | ||||
3414 | return expandStandardEscapes($val) if defined $val; | ||||
3415 | return undef; | ||||
3416 | }, | ||||
3417 | $topicObject, | ||||
3418 | 1 | ||||
3419 | ); | ||||
3420 | } | ||||
3421 | } | ||||
3422 | elsif ( exists( $macros{$tag} ) ) { | ||||
3423 | 397 | 164µs | unless ( defined( $macros{$tag} ) ) { | ||
3424 | |||||
3425 | # Demand-load the macro module | ||||
3426 | 21 | 39µs | die $tag unless $tag =~ m/([A-Z_:]+)/i; | ||
3427 | 21 | 19µs | $tag = $1; | ||
3428 | 21 | 565µs | eval "require Foswiki::Macros::$tag"; # spent 123µs executing statements in 2 string evals (merged)
# spent 72µs executing statements in string eval
# spent 70µs executing statements in string eval
# spent 68µs executing statements in string eval
# spent 68µs executing statements in string eval
# spent 67µs executing statements in string eval
# spent 66µs executing statements in string eval
# spent 64µs executing statements in string eval
# spent 64µs executing statements in string eval
# spent 63µs executing statements in string eval
# spent 63µs executing statements in string eval
# spent 63µs executing statements in string eval
# spent 63µs executing statements in string eval
# spent 60µs executing statements in string eval
# spent 60µs executing statements in string eval
# spent 58µs executing statements in string eval
# spent 56µs executing statements in string eval
# spent 56µs executing statements in string eval
# spent 3µs executing statements in string eval
# spent 2µs executing statements in string eval | ||
3429 | 21 | 5µs | die $@ if $@; | ||
3430 | 21 | 434µs | $macros{$tag} = eval "\\&$tag"; # spent 5µs executing statements in 2 string evals (merged)
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval
# spent 2µs executing statements in string eval | ||
3431 | 21 | 8µs | die $@ if $@; | ||
3432 | } | ||||
3433 | |||||
3434 | 397 | 1.49ms | 397 | 7.72ms | my $attrs = new Foswiki::Attrs( $args, $contextFreeSyntax{$tag} ); # spent 7.72ms making 397 calls to Foswiki::Attrs::new, avg 19µs/call |
3435 | 397 | 1.71ms | 397 | 137s | $e = &{ $macros{$tag} }( $this, $attrs, $topicObject ); # spent 137s making 40 calls to Foswiki::SEARCH, avg 3.42s/call
# spent 132ms making 14 calls to Foswiki::Func::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Func.pm:662], avg 9.43ms/call
# spent 199ms making 10 calls to Foswiki::INCLUDE, avg 19.9ms/call, recursion: max depth 2, sum of overlapping time 73.2ms
# spent 48.4ms making 3 calls to Foswiki::REVINFO, avg 16.1ms/call
# spent 39.8ms making 43 calls to Foswiki::IF, avg 925µs/call
# spent 9.33ms making 3 calls to Foswiki::META, avg 3.11ms/call
# spent 3.90ms making 57 calls to Foswiki::MAKETEXT, avg 68µs/call
# spent 1.71ms making 1 call to Foswiki::QUERY
# spent 997µs making 45 calls to Foswiki::PUBURLPATH, avg 22µs/call
# spent 948µs making 1 call to Foswiki::SPACEOUT
# spent 877µs making 12 calls to Foswiki::VAR, avg 73µs/call
# spent 503µs making 17 calls to Foswiki::SCRIPTURLPATH, avg 30µs/call
# spent 472µs making 1 call to Foswiki::ICONURL
# spent 356µs making 7 calls to Foswiki::WIKINAME, avg 51µs/call
# spent 355µs making 13 calls to Foswiki::SCRIPTURL, avg 27µs/call
# spent 198µs making 5 calls to Foswiki::ADDTOZONE, avg 40µs/call
# spent 185µs making 6 calls to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:247], avg 31µs/call
# spent 160µs making 2 calls to Foswiki::WIKIUSERNAME, avg 80µs/call
# spent 145µs making 2 calls to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:323], avg 72µs/call
# spent 123µs making 2 calls to Foswiki::USERNAME, avg 61µs/call
# spent 120µs making 3 calls to Foswiki::URLPARAM, avg 40µs/call
# spent 106µs making 1 call to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:289]
# spent 99µs making 15 calls to Foswiki::ENCODE, avg 7µs/call
# spent 96µs making 63 calls to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:401], avg 2µs/call
# spent 92µs making 1 call to Foswiki::LoginManager::_LOGOUTURL
# spent 33µs making 2 calls to Foswiki::PUBURL, avg 16µs/call
# spent 24µs making 1 call to Foswiki::LoginManager::_LOGIN
# spent 21µs making 10 calls to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:405], avg 2µs/call
# spent 14µs making 5 calls to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:394], avg 3µs/call
# spent 13µs making 1 call to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:312]
# spent 11µs making 2 calls to Foswiki::RENDERZONE, avg 6µs/call
# spent 8µs making 2 calls to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:344], avg 4µs/call
# spent 8µs making 2 calls to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:347], avg 4µs/call
# spent 5µs making 1 call to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:409]
# spent 4µs making 1 call to Foswiki::LoginManager::ApacheLogin::__ANON__[/var/www/foswikidev/core/lib/Foswiki/LoginManager/ApacheLogin.pm:53]
# spent 4µs making 1 call to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:243]
# spent 4µs making 1 call to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:450]
# spent 4µs making 1 call to Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki.pm:399] |
3436 | } | ||||
3437 | elsif ( $args && $args =~ m/\S/ ) { | ||||
3438 | |||||
3439 | # Arbitrary %SOMESTRING{default="xxx"}% will expand to xxx | ||||
3440 | # in the absence of any definition. | ||||
3441 | 1 | 4µs | 1 | 60µs | my $attrs = new Foswiki::Attrs($args); # spent 60µs making 1 call to Foswiki::Attrs::new |
3442 | 1 | 2µs | if ( defined $attrs->{default} ) { | ||
3443 | $e = expandStandardEscapes( $attrs->{default} ); | ||||
3444 | } | ||||
3445 | } | ||||
3446 | 537 | 6.11ms | return $e; | ||
3447 | } | ||||
3448 | |||||
3449 | # Handle expansion of a tag during new topic creation. When creating a | ||||
3450 | # new topic from a template we only expand a subset of the available legal | ||||
3451 | # tags, and we expand %NOP% differently. | ||||
3452 | sub _expandMacroOnTopicCreation { | ||||
3453 | my $this = shift; | ||||
3454 | |||||
3455 | # my( $tag, $args, $topicObject ) = @_; | ||||
3456 | |||||
3457 | # Required for Cairo compatibility. Ignore %NOP{...}% | ||||
3458 | # %NOP% is *not* ignored until all variable expansion is complete, | ||||
3459 | # otherwise them inside-out rule would remove it too early e.g. | ||||
3460 | # %GM%NOP%TIME -> %GMTIME -> 12:00. So we ignore it here and scrape it | ||||
3461 | # out later. We *have* to remove %NOP{...}% because it can foul up | ||||
3462 | # brace-matching. | ||||
3463 | return '' if $_[0] eq 'NOP' && defined $_[1]; | ||||
3464 | |||||
3465 | # Only expand a subset of legal tags. Warning: $this->{user} may be | ||||
3466 | # overridden during this call, when a new user topic is being created. | ||||
3467 | # This is what we want to make sure new user templates are populated | ||||
3468 | # correctly, but you need to think about this if you extend the set of | ||||
3469 | # tags expanded here. | ||||
3470 | return | ||||
3471 | unless $_[0] =~ | ||||
3472 | m/^(URLPARAM|DATE|(SERVER|GM)TIME|(USER|WIKI)NAME|WIKIUSERNAME|USERINFO)$/; | ||||
3473 | |||||
3474 | return $this->_expandMacroOnTopicRendering(@_); | ||||
3475 | } | ||||
3476 | |||||
3477 | =begin TML | ||||
3478 | |||||
3479 | ---++ ObjectMethod enterContext( $id, $val ) | ||||
3480 | |||||
3481 | Add the context id $id into the set of active contexts. The $val | ||||
3482 | can be anything you like, but should always evaluate to boolean | ||||
3483 | TRUE. | ||||
3484 | |||||
3485 | An example of the use of contexts is in the use of tag | ||||
3486 | expansion. The commonTagsHandler in plugins is called every | ||||
3487 | time tags need to be expanded, and the context of that expansion | ||||
3488 | is signalled by the expanding module using a context id. So the | ||||
3489 | forms module adds the context id "form" before invoking common | ||||
3490 | tags expansion. | ||||
3491 | |||||
3492 | Contexts are not just useful for tag expansion; they are also | ||||
3493 | relevant when rendering. | ||||
3494 | |||||
3495 | Contexts are intended for use mainly by plugins. Core modules can | ||||
3496 | use $session->inContext( $id ) to determine if a context is active. | ||||
3497 | |||||
3498 | =cut | ||||
3499 | |||||
3500 | # spent 140µs within Foswiki::enterContext which was called 49 times, avg 3µs/call:
# 42 times (117µs+0s) by Foswiki::Plugin::registerHandlers at line 301 of /var/www/foswikidev/core/lib/Foswiki/Plugin.pm, avg 3µs/call
# once (6µs+0s) by Foswiki::UI::View::view at line 464 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
# once (4µs+0s) by Foswiki::UI::View::view at line 458 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
# once (4µs+0s) by Foswiki::LoginManager::ApacheLogin::new at line 50 of /var/www/foswikidev/core/lib/Foswiki/LoginManager/ApacheLogin.pm
# once (3µs+0s) by Foswiki::Users::new at line 121 of /var/www/foswikidev/core/lib/Foswiki/Users.pm
# once (3µs+0s) by Foswiki::UI::View::view at line 438 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
# once (2µs+0s) by Foswiki::Users::new at line 122 of /var/www/foswikidev/core/lib/Foswiki/Users.pm
# once (2µs+0s) by Foswiki::LoginManager::userLoggedIn at line 812 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm | ||||
3501 | 49 | 31µs | my ( $this, $id, $val ) = @_; | ||
3502 | 49 | 14µs | $val ||= 1; | ||
3503 | 49 | 149µs | $this->{context}->{$id} = $val; | ||
3504 | } | ||||
3505 | |||||
3506 | =begin TML | ||||
3507 | |||||
3508 | ---++ ObjectMethod leaveContext( $id ) | ||||
3509 | |||||
3510 | Remove the context id $id from the set of active contexts. | ||||
3511 | (see =enterContext= for more information on contexts) | ||||
3512 | |||||
3513 | =cut | ||||
3514 | |||||
3515 | # spent 28µs within Foswiki::leaveContext which was called 4 times, avg 7µs/call:
# once (10µs+0s) by Foswiki::UI::View::view at line 460 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
# once (6µs+0s) by Foswiki::UI::View::view at line 466 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
# once (6µs+0s) by Foswiki::UI::View::view at line 440 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
# once (5µs+0s) by Foswiki::LoginManager::new at line 181 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm | ||||
3516 | 4 | 6µs | my ( $this, $id ) = @_; | ||
3517 | 4 | 4µs | my $res = $this->{context}->{$id}; | ||
3518 | 4 | 8µs | delete $this->{context}->{$id}; | ||
3519 | 4 | 19µs | return $res; | ||
3520 | } | ||||
3521 | |||||
3522 | =begin TML | ||||
3523 | |||||
3524 | ---++ ObjectMethod inContext( $id ) | ||||
3525 | |||||
3526 | Return the value for the given context id | ||||
3527 | (see =enterContext= for more information on contexts) | ||||
3528 | |||||
3529 | =cut | ||||
3530 | |||||
3531 | # spent 257µs within Foswiki::inContext which was called 135 times, avg 2µs/call:
# 56 times (99µs+0s) by Foswiki::getPubURL at line 1629, avg 2µs/call
# 40 times (72µs+0s) by Foswiki::getScriptUrl at line 1516, avg 2µs/call
# 14 times (28µs+0s) by Foswiki::Func::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Func.pm:662] at line 649 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 2µs/call
# 12 times (28µs+0s) by Foswiki::If::OP_context::evaluate at line 43 of /var/www/foswikidev/core/lib/Foswiki/If/OP_context.pm, avg 2µs/call
# 5 times (14µs+0s) by Foswiki::LoginManager::endRenderingHandler at line 1043 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm, avg 3µs/call
# once (4µs+0s) by Foswiki::LoginManager::makeLoginManager at line 107 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::Plugins::HomePagePlugin::initializeUserHandler at line 31 of /var/www/foswikidev/core/lib/Foswiki/Plugins/HomePagePlugin.pm
# once (2µs+0s) by Foswiki::LoginManager::loadSession at line 324 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::_LOGIN at line 1368 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::checkAccess at line 691 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::userLoggedIn at line 808 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::makeLoginManager at line 146 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::Plugins::HomePagePlugin::initializeUserHandler at line 35 of /var/www/foswikidev/core/lib/Foswiki/Plugins/HomePagePlugin.pm | ||||
3532 | 135 | 53µs | my ( $this, $id ) = @_; | ||
3533 | 135 | 402µs | return $this->{context}->{$id}; | ||
3534 | } | ||||
3535 | |||||
3536 | =begin TML | ||||
3537 | |||||
3538 | ---++ StaticMethod registerTagHandler( $tag, $fnref, $syntax ) | ||||
3539 | |||||
3540 | STATIC Add a tag handler to the function tag handlers. | ||||
3541 | * =$tag= name of the tag e.g. MYTAG | ||||
3542 | * =$fnref= Function to execute. Will be passed ($session, \%params, $web, $topic ) | ||||
3543 | * =$syntax= somewhat legacy - 'classic' or 'context-free' (context-free may be removed in future) | ||||
3544 | |||||
3545 | |||||
3546 | $syntax parameter: | ||||
3547 | Way back in prehistory, back when the dinosaur still roamed the earth, | ||||
3548 | Crawford tried to extend the tag syntax of macros such that they could be processed | ||||
3549 | by a context-free parser (hence the "context-free") | ||||
3550 | and bring them into line with HTML. | ||||
3551 | This work was banjaxed by one particular tyrranosaur, | ||||
3552 | who felt that the existing syntax was perfect. | ||||
3553 | However by that time Crawford had used it in a couple of places - most notable in the action tracker. | ||||
3554 | |||||
3555 | The syntax isn't vastly different from what's there; the differences are: | ||||
3556 | 1 Use either type of quote for parameters | ||||
3557 | 2 Optional quotes on parameter values e.g. recurse=on | ||||
3558 | 3 Standardised use of \ for escapes | ||||
3559 | 4 Boolean (valueless) options (i.e. recurse instead of recurse="on" | ||||
3560 | |||||
3561 | |||||
3562 | =cut | ||||
3563 | |||||
3564 | # spent 154µs within Foswiki::registerTagHandler which was called 62 times, avg 2µs/call:
# 50 times (126µs+0s) by Foswiki::Func::registerTagHandler at line 663 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 3µs/call
# once (6µs+0s) by Foswiki::Plugins::new at line 84 of /var/www/foswikidev/core/lib/Foswiki/Plugins.pm
# once (3µs+0s) by Foswiki::LoginManager::new at line 186 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::new at line 188 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::new at line 190 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::Plugins::new at line 88 of /var/www/foswikidev/core/lib/Foswiki/Plugins.pm
# once (2µs+0s) by Foswiki::Plugins::new at line 89 of /var/www/foswikidev/core/lib/Foswiki/Plugins.pm
# once (2µs+0s) by Foswiki::Plugins::new at line 86 of /var/www/foswikidev/core/lib/Foswiki/Plugins.pm
# once (2µs+0s) by Foswiki::LoginManager::new at line 192 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::new at line 187 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::new at line 191 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
# once (2µs+0s) by Foswiki::LoginManager::ApacheLogin::new at line 53 of /var/www/foswikidev/core/lib/Foswiki/LoginManager/ApacheLogin.pm
# once (2µs+0s) by Foswiki::LoginManager::new at line 189 of /var/www/foswikidev/core/lib/Foswiki/LoginManager.pm | ||||
3565 | 62 | 34µs | my ( $tag, $fnref, $syntax ) = @_; | ||
3566 | 62 | 57µs | $macros{$tag} = $fnref; | ||
3567 | 62 | 148µs | if ( $syntax && $syntax eq 'context-free' ) { | ||
3568 | $contextFreeSyntax{$tag} = 1; | ||||
3569 | } | ||||
3570 | } | ||||
3571 | |||||
3572 | =begin TML | ||||
3573 | |||||
3574 | ---++ ObjectMethod expandMacros( $text, $topicObject ) -> $text | ||||
3575 | |||||
3576 | Processes %<nop>VARIABLE%, and %<nop>TOC% syntax; also includes | ||||
3577 | 'commonTagsHandler' plugin hook. | ||||
3578 | |||||
3579 | Returns the text of the topic, after file inclusion, variable substitution, | ||||
3580 | table-of-contents generation, and any plugin changes from commonTagsHandler. | ||||
3581 | |||||
3582 | $topicObject may be undef when, for example, expanding templates, or one-off strings | ||||
3583 | at a time when meta isn't available. | ||||
3584 | |||||
3585 | DO NOT CALL THIS DIRECTLY; use $topicObject->expandMacros instead. | ||||
3586 | |||||
3587 | =cut | ||||
3588 | |||||
3589 | # spent 137s (8.76ms+137) within Foswiki::expandMacros which was called 100 times, avg 1.37s/call:
# 100 times (8.76ms+137s) by Foswiki::Meta::expandMacros at line 3353 of /var/www/foswikidev/core/lib/Foswiki/Meta.pm, avg 1.37s/call | ||||
3590 | 100 | 60µs | my ( $this, $text, $topicObject ) = @_; | ||
3591 | |||||
3592 | 100 | 16µs | return '' unless defined $text; | ||
3593 | |||||
3594 | # Plugin Hook | ||||
3595 | 100 | 332µs | 300 | 4.75ms | $this->{plugins} # spent 4.51ms making 100 calls to Foswiki::Plugins::dispatch, avg 45µs/call, recursion: max depth 1, sum of overlapping time 52µs
# spent 145µs making 100 calls to Foswiki::Meta::web, avg 1µs/call
# spent 142µs making 100 calls to Foswiki::Meta::topic, avg 1µs/call |
3596 | ->dispatch( 'beforeCommonTagsHandler', $text, $topicObject->topic, | ||||
3597 | $topicObject->web, $topicObject ); | ||||
3598 | |||||
3599 | #use a "global var", so included topics can extract and putback | ||||
3600 | #their verbatim blocks safetly. | ||||
3601 | 100 | 45µs | my $verbatim = {}; | ||
3602 | 100 | 136µs | 100 | 1.07ms | $text = takeOutBlocks( $text, 'verbatim', $verbatim ); # spent 1.07ms making 100 calls to Foswiki::takeOutBlocks, avg 11µs/call |
3603 | |||||
3604 | # take out dirty areas | ||||
3605 | 100 | 34µs | my $dirtyAreas = {}; | ||
3606 | 100 | 130µs | 100 | 172µs | $text = takeOutBlocks( $text, 'dirtyarea', $dirtyAreas ) # spent 172µs making 100 calls to Foswiki::Meta::isCacheable, avg 2µs/call |
3607 | if $topicObject->isCacheable(); | ||||
3608 | |||||
3609 | # Require defaults for plugin handlers :-( | ||||
3610 | 100 | 123µs | 100 | 144µs | my $webContext = $topicObject->web || $this->{webName}; # spent 144µs making 100 calls to Foswiki::Meta::web, avg 1µs/call |
3611 | 100 | 114µs | 100 | 134µs | my $topicContext = $topicObject->topic || $this->{topicName}; # spent 134µs making 100 calls to Foswiki::Meta::topic, avg 1µs/call |
3612 | |||||
3613 | 100 | 159µs | 100 | 211µs | my $memW = $this->{prefs}->getPreference('INCLUDINGWEB'); # spent 211µs making 100 calls to Foswiki::Prefs::getPreference, avg 2µs/call |
3614 | 100 | 117µs | 100 | 155µs | my $memT = $this->{prefs}->getPreference('INCLUDINGTOPIC'); # spent 155µs making 100 calls to Foswiki::Prefs::getPreference, avg 2µs/call |
3615 | 100 | 129µs | 100 | 553µs | $this->{prefs}->setInternalPreferences( # spent 553µs making 100 calls to Foswiki::Prefs::setInternalPreferences, avg 6µs/call |
3616 | INCLUDINGWEB => $webContext, | ||||
3617 | INCLUDINGTOPIC => $topicContext | ||||
3618 | ); | ||||
3619 | |||||
3620 | 100 | 156µs | 100 | 137s | $this->innerExpandMacros( \$text, $topicObject ); # spent 137s making 100 calls to Foswiki::innerExpandMacros, avg 1.37s/call, recursion: max depth 2, sum of overlapping time 26.3ms |
3621 | |||||
3622 | 100 | 127µs | 100 | 988µs | $text = takeOutBlocks( $text, 'verbatim', $verbatim ); # spent 988µs making 100 calls to Foswiki::takeOutBlocks, avg 10µs/call |
3623 | |||||
3624 | # Plugin Hook | ||||
3625 | 100 | 157µs | 100 | 16.5ms | $this->{plugins} # spent 16.7ms making 100 calls to Foswiki::Plugins::dispatch, avg 167µs/call, recursion: max depth 1, sum of overlapping time 122µs |
3626 | ->dispatch( 'commonTagsHandler', $text, $topicContext, $webContext, 0, | ||||
3627 | $topicObject ); | ||||
3628 | |||||
3629 | # process tags again because plugin hook may have added more in | ||||
3630 | 100 | 151µs | 100 | 1.18ms | $this->innerExpandMacros( \$text, $topicObject ); # spent 5.40ms making 100 calls to Foswiki::innerExpandMacros, avg 54µs/call, recursion: max depth 2, sum of overlapping time 4.21ms |
3631 | |||||
3632 | 100 | 125µs | 100 | 420µs | $this->{prefs}->setInternalPreferences( # spent 420µs making 100 calls to Foswiki::Prefs::setInternalPreferences, avg 4µs/call |
3633 | INCLUDINGWEB => $memW, | ||||
3634 | INCLUDINGTOPIC => $memT | ||||
3635 | ); | ||||
3636 | |||||
3637 | # 'Special plugin tag' TOC hack, must be done after all other expansions | ||||
3638 | # are complete, and has to reprocess the entire topic. | ||||
3639 | |||||
3640 | 100 | 89µs | if ( $text =~ m/%TOC(?:{.*})?%/ ) { | ||
3641 | require Foswiki::Macros::TOC; | ||||
3642 | $text =~ s/%TOC(?:{(.*?)})?%/$this->TOC($text, $topicObject, $1)/ge; | ||||
3643 | } | ||||
3644 | |||||
3645 | # Codev.FormattedSearchWithConditionalOutput: remove <nop> lines, | ||||
3646 | # possibly introduced by SEARCHes with conditional CALC. This needs | ||||
3647 | # to be done after CALC and before table rendering in order to join | ||||
3648 | # table rows properly | ||||
3649 | 100 | 153µs | $text =~ s/^<nop>\r?\n//gm; | ||
3650 | |||||
3651 | # restore dirty areas | ||||
3652 | 100 | 124µs | 100 | 166µs | putBackBlocks( \$text, $dirtyAreas, 'dirtyarea' ) # spent 166µs making 100 calls to Foswiki::Meta::isCacheable, avg 2µs/call |
3653 | if $topicObject->isCacheable(); | ||||
3654 | |||||
3655 | 100 | 119µs | 100 | 307µs | putBackBlocks( \$text, $verbatim, 'verbatim' ); # spent 307µs making 100 calls to Foswiki::putBackBlocks, avg 3µs/call |
3656 | |||||
3657 | # Foswiki Plugin Hook (for cache Plugins only) | ||||
3658 | 100 | 151µs | 100 | 1.41ms | $this->{plugins} # spent 1.43ms making 100 calls to Foswiki::Plugins::dispatch, avg 14µs/call, recursion: max depth 1, sum of overlapping time 14µs |
3659 | ->dispatch( 'afterCommonTagsHandler', $text, $topicContext, $webContext, | ||||
3660 | $topicObject ); | ||||
3661 | |||||
3662 | 100 | 322µs | return $text; | ||
3663 | } | ||||
3664 | |||||
3665 | =begin TML | ||||
3666 | |||||
3667 | ---++ ObjectMethod addToZone($zone, $id, $data, $requires) | ||||
3668 | |||||
3669 | Add =$data= identified as =$id= to =$zone=, which will later be expanded (with | ||||
3670 | renderZone() - implements =%<nop>RENDERZONE%=). =$ids= are unique within | ||||
3671 | the zone that they are added - dependencies between =$ids= in different zones | ||||
3672 | will not be resolved, except for the special case of =head= and =script= zones | ||||
3673 | when ={MergeHeadAndScriptZones}= is enabled. | ||||
3674 | |||||
3675 | In this case, they are treated as separate zones when adding to them, but as | ||||
3676 | one merged zone when rendering, i.e. a call to render either =head= or =script= | ||||
3677 | zones will actually render both zones in this one call. Both zones are undef'd | ||||
3678 | afterward to avoid double rendering of content from either zone, to support | ||||
3679 | proper behaviour when =head= and =script= are rendered with separate calls even | ||||
3680 | when ={MergeHeadAndScriptZones}= is set. See ZoneTests/explicit_RENDERZONE*. | ||||
3681 | |||||
3682 | This behaviour allows an addToZone('head') call to require an id that has been | ||||
3683 | added to =script= only. | ||||
3684 | |||||
3685 | * =$zone= - name of the zone | ||||
3686 | * =$id= - unique identifier | ||||
3687 | * =$data= - content | ||||
3688 | * =$requires= - optional, comma-separated string of =$id= identifiers | ||||
3689 | that should precede the content | ||||
3690 | |||||
3691 | <blockquote class="foswikiHelp">%X% | ||||
3692 | *Note:* Read the developer supplement at Foswiki:Development.AddToZoneFromPluginHandlers if you | ||||
3693 | are calling =addToZone()= from a rendering or macro/tag-related plugin handler | ||||
3694 | </blockquote> | ||||
3695 | |||||
3696 | Implements =%<nop>ADDTOZONE%=. | ||||
3697 | |||||
3698 | =cut | ||||
3699 | |||||
3700 | # spent 788µs within Foswiki::addToZone which was called 41 times, avg 19µs/call:
# 36 times (648µs+0s) by Foswiki::Func::addToZone at line 2782 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 18µs/call
# 5 times (140µs+0s) by Foswiki::ADDTOZONE at line 43 of /var/www/foswikidev/core/lib/Foswiki/Macros/ADDTOZONE.pm, avg 28µs/call | ||||
3701 | 41 | 34µs | my ( $this, $zone, $id, $data, $requires ) = @_; | ||
3702 | |||||
3703 | 41 | 8µs | $requires ||= ''; | ||
3704 | |||||
3705 | # get a random one | ||||
3706 | 41 | 7µs | unless ($id) { | ||
3707 | $id = int( rand(10000) ) + 1; | ||||
3708 | } | ||||
3709 | |||||
3710 | # get zone, or create record | ||||
3711 | 41 | 26µs | my $thisZone = $this->{_zones}{$zone}; | ||
3712 | 41 | 10µs | unless ( defined $thisZone ) { | ||
3713 | $this->{_zones}{$zone} = $thisZone = {}; | ||||
3714 | } | ||||
3715 | |||||
3716 | 41 | 5µs | my @requires; | ||
3717 | 41 | 88µs | foreach my $req ( split( /\s*,\s*/, $requires ) ) { | ||
3718 | 53 | 33µs | unless ( $thisZone->{$req} ) { | ||
3719 | $thisZone->{$req} = { | ||||
3720 | id => $req, | ||||
3721 | zone => $zone, | ||||
3722 | requires => [], | ||||
3723 | missingrequires => [], | ||||
3724 | text => '', | ||||
3725 | populated => 0 | ||||
3726 | }; | ||||
3727 | } | ||||
3728 | 53 | 51µs | push( @requires, $thisZone->{$req} ); | ||
3729 | } | ||||
3730 | |||||
3731 | # store record within zone | ||||
3732 | 41 | 17µs | my $zoneID = $thisZone->{$id}; | ||
3733 | 41 | 12µs | unless ($zoneID) { | ||
3734 | 33 | 32µs | $zoneID = { id => $id }; | ||
3735 | 33 | 29µs | $thisZone->{$id} = $zoneID; | ||
3736 | } | ||||
3737 | |||||
3738 | # add class to script and link data | ||||
3739 | 41 | 192µs | $data =~ s/<script\s+((?![^>]*class=))/<script class='\$zone \$id' $1/g; | ||
3740 | 41 | 34µs | $data =~ s/<link\s+((?![^>]class=))/<link class='\$zone \$id' $1/g; | ||
3741 | 41 | 46µs | $data =~ s/<style\s+((?![^>]*class=))/<style class='\$zone \$id' $1/g; | ||
3742 | |||||
3743 | # override previous properties | ||||
3744 | 41 | 25µs | $zoneID->{zone} = $zone; | ||
3745 | 41 | 23µs | $zoneID->{requires} = \@requires; | ||
3746 | 41 | 21µs | $zoneID->{missingrequires} = []; | ||
3747 | 41 | 29µs | $zoneID->{text} = $data; | ||
3748 | 41 | 12µs | $zoneID->{populated} = 1; | ||
3749 | |||||
3750 | 41 | 113µs | return; | ||
3751 | } | ||||
3752 | |||||
3753 | # spent 18.0ms (22µs+18.0) within Foswiki::_renderZoneById which was called 2 times, avg 9.02ms/call:
# 2 times (22µs+18.0ms) by Foswiki::_renderZones at line 3935, avg 9.02ms/call | ||||
3754 | 2 | 900ns | my $this = shift; | ||
3755 | 2 | 2µs | my $id = shift; | ||
3756 | |||||
3757 | 2 | 600ns | return '' unless defined $id; | ||
3758 | |||||
3759 | 2 | 4µs | my $renderZone = $this->{_renderZonePlaceholder}{$id}; | ||
3760 | |||||
3761 | 2 | 200ns | return '' unless defined $renderZone; | ||
3762 | |||||
3763 | 2 | 2µs | my $params = $renderZone->{params}; | ||
3764 | 2 | 500ns | my $topicObject = $renderZone->{topicObject}; | ||
3765 | 2 | 1µs | my $zone = $params->{_DEFAULT} || $params->{zone}; | ||
3766 | |||||
3767 | 2 | 10µs | 2 | 18.0ms | return _renderZone( $this, $zone, $params, $topicObject ); # spent 18.0ms making 2 calls to Foswiki::_renderZone, avg 9.01ms/call |
3768 | } | ||||
3769 | |||||
3770 | # This private function is used in ZoneTests | ||||
3771 | sub _renderZone { | ||||
3772 | 4 | 3µs | my ( $this, $zone, $params, $topicObject ) = @_; | ||
3773 | |||||
3774 | # Check the zone is defined and has not already been rendered | ||||
3775 | 4 | 12µs | return '' unless $zone && $this->{_zones}{$zone}; | ||
3776 | |||||
3777 | 2 | 4µs | $params->{header} ||= ''; | ||
3778 | 2 | 1µs | $params->{footer} ||= ''; | ||
3779 | 2 | 1µs | $params->{chomp} ||= 'off'; | ||
3780 | 2 | 1µs | $params->{missingformat} = '$id: requires= missing ids: $missingids'; | ||
3781 | 2 | 2µs | $params->{format} = '$item<!--<literal>$missing</literal>-->' | ||
3782 | unless defined $params->{format}; | ||||
3783 | 2 | 2µs | $params->{separator} = '$n()' unless defined $params->{separator}; | ||
3784 | |||||
3785 | 2 | 300ns | unless ( defined $topicObject ) { | ||
3786 | $topicObject = | ||||
3787 | Foswiki::Meta->new( $this, $this->{webName}, $this->{topicName} ); | ||||
3788 | } | ||||
3789 | |||||
3790 | # Loop through the vertices of the graph, in any order, initiating | ||||
3791 | # a depth-first search for any vertex that has not already been | ||||
3792 | # visited by a previous search. The desired topological sorting is | ||||
3793 | # the reverse postorder of these searches. That is, we can construct | ||||
3794 | # the ordering as a list of vertices, by adding each vertex to the | ||||
3795 | # start of the list at the time when the depth-first search is | ||||
3796 | # processing that vertex and has returned from processing all children | ||||
3797 | # of that vertex. Since each edge and vertex is visited once, the | ||||
3798 | # algorithm runs in linear time. | ||||
3799 | 2 | 1µs | my %visited; | ||
3800 | 2 | 500ns | my @total; | ||
3801 | |||||
3802 | # When {MergeHeadAndScriptZones} is set, try to treat head and script | ||||
3803 | # zones as merged for compatibility with ADDTOHEAD usage where requirements | ||||
3804 | # have been moved to the script zone. See ZoneTests/Item9317 | ||||
3805 | 2 | 21µs | if ( $Foswiki::cfg{MergeHeadAndScriptZones} | ||
3806 | and ( ( $zone eq 'head' ) or ( $zone eq 'script' ) ) ) | ||||
3807 | { | ||||
3808 | my @zoneIDs = ( | ||||
3809 | values %{ $this->{_zones}{head} }, | ||||
3810 | values %{ $this->{_zones}{script} } | ||||
3811 | ); | ||||
3812 | |||||
3813 | foreach my $zoneID (@zoneIDs) { | ||||
3814 | $this->_visitZoneID( $zoneID, \%visited, \@total ); | ||||
3815 | } | ||||
3816 | undef $this->{_zones}{head}; | ||||
3817 | undef $this->{_zones}{script}; | ||||
3818 | } | ||||
3819 | else { | ||||
3820 | 2 | 18µs | my @zoneIDs = values %{ $this->{_zones}{$zone} }; | ||
3821 | |||||
3822 | 2 | 2µs | foreach my $zoneID (@zoneIDs) { | ||
3823 | 38 | 53µs | 38 | 383µs | $this->_visitZoneID( $zoneID, \%visited, \@total ); # spent 383µs making 38 calls to Foswiki::_visitZoneID, avg 10µs/call |
3824 | } | ||||
3825 | |||||
3826 | # kill a zone once it has been rendered, to prevent it being | ||||
3827 | # added twice (e.g. by duplicate %RENDERZONEs or by automatic | ||||
3828 | # zone expansion in the head or script) | ||||
3829 | 2 | 3µs | undef $this->{_zones}{$zone}; | ||
3830 | } | ||||
3831 | |||||
3832 | # nothing rendered for a zone with no ADDTOZONE calls | ||||
3833 | 2 | 1µs | return '' unless scalar(@total) > 0; | ||
3834 | |||||
3835 | 2 | 1µs | my @result = (); | ||
3836 | 2 | 1µs | my $missingformat = $params->{missingformat}; | ||
3837 | 2 | 2µs | foreach my $item (@total) { | ||
3838 | 38 | 21µs | my $text = $item->{text}; | ||
3839 | 38 | 19µs | my @missingids = @{ $item->{missingrequires} }; | ||
3840 | 38 | 11µs | my $missingformat = | ||
3841 | ( scalar(@missingids) ) ? $params->{missingformat} : ''; | ||||
3842 | |||||
3843 | 38 | 13µs | if ( $params->{'chomp'} ) { | ||
3844 | 38 | 20µs | $text =~ s/^\s+//g; | ||
3845 | 38 | 121µs | $text =~ s/\s+$//g; | ||
3846 | } | ||||
3847 | |||||
3848 | # ASSERT($text, "No content for zone id $item->{id} in zone $zone") | ||||
3849 | # if DEBUG; | ||||
3850 | |||||
3851 | 38 | 5µs | next unless $text; | ||
3852 | 27 | 15µs | my $id = $item->{id} || ''; | ||
3853 | 27 | 6µs | my $line = $params->{format}; | ||
3854 | 27 | 11µs | if ( scalar(@missingids) ) { | ||
3855 | $line =~ s/\$missing\b/$missingformat/g; | ||||
3856 | $line =~ s/\$missingids\b/join(', ', @missingids)/ge; | ||||
3857 | } | ||||
3858 | else { | ||||
3859 | 27 | 42µs | $line =~ s/\$missing\b/\$id/g; | ||
3860 | } | ||||
3861 | 27 | 44µs | $line =~ s/\$item\b/$text/g; | ||
3862 | 27 | 72µs | $line =~ s/\$id\b/$id/g; | ||
3863 | 27 | 72µs | $line =~ s/\$zone\b/$item->{zone}/g; | ||
3864 | 27 | 29µs | push @result, $line if $line; | ||
3865 | } | ||||
3866 | 2 | 18µs | 2 | 158µs | my $result = # spent 158µs making 2 calls to Foswiki::expandStandardEscapes, avg 79µs/call |
3867 | expandStandardEscapes( $params->{header} | ||||
3868 | . join( $params->{separator}, @result ) | ||||
3869 | . $params->{footer} ); | ||||
3870 | |||||
3871 | # delay rendering the zone until now | ||||
3872 | 2 | 4µs | 2 | 10.8ms | $result = $topicObject->expandMacros($result); # spent 10.8ms making 2 calls to Foswiki::Meta::expandMacros, avg 5.40ms/call |
3873 | 2 | 3µs | 2 | 5.92ms | $result = $topicObject->renderTML($result); # spent 5.92ms making 2 calls to Foswiki::Meta::renderTML, avg 2.96ms/call |
3874 | |||||
3875 | 2 | 68µs | return $result; | ||
3876 | } | ||||
3877 | |||||
3878 | sub _visitZoneID { | ||||
3879 | 89 | 31µs | my ( $this, $zoneID, $visited, $list ) = @_; | ||
3880 | |||||
3881 | 89 | 116µs | return if $visited->{$zoneID}; | ||
3882 | |||||
3883 | 38 | 27µs | $visited->{$zoneID} = 1; | ||
3884 | |||||
3885 | 38 | 48µs | foreach my $requiredZoneID ( @{ $zoneID->{requires} } ) { | ||
3886 | 51 | 5µs | my $zoneIDToVisit; | ||
3887 | |||||
3888 | 51 | 16µs | if ( $Foswiki::cfg{MergeHeadAndScriptZones} | ||
3889 | and not $requiredZoneID->{populated} ) | ||||
3890 | { | ||||
3891 | |||||
3892 | # Compatibility mode, where we are trying to treat head and script | ||||
3893 | # zones as merged, and a required ZoneID isn't populated. Try | ||||
3894 | # opposite zone to see if it exists there instead. Item9317 | ||||
3895 | if ( $requiredZoneID->{zone} eq 'head' ) { | ||||
3896 | $zoneIDToVisit = | ||||
3897 | $this->{_zones}{script}{ $requiredZoneID->{id} }; | ||||
3898 | } | ||||
3899 | else { | ||||
3900 | $zoneIDToVisit = $this->{_zones}{head}{ $requiredZoneID->{id} }; | ||||
3901 | } | ||||
3902 | if ( not $zoneIDToVisit->{populated} ) { | ||||
3903 | |||||
3904 | # Oops, the required ZoneID doesn't exist there either; reset | ||||
3905 | $zoneIDToVisit = $requiredZoneID; | ||||
3906 | } | ||||
3907 | } | ||||
3908 | else { | ||||
3909 | 51 | 10µs | $zoneIDToVisit = $requiredZoneID; | ||
3910 | } | ||||
3911 | 51 | 51µs | 51 | 0s | $this->_visitZoneID( $zoneIDToVisit, $visited, $list ); # spent 243µs making 51 calls to Foswiki::_visitZoneID, avg 5µs/call, recursion: max depth 3, sum of overlapping time 243µs |
3912 | |||||
3913 | 51 | 37µs | if ( not $zoneIDToVisit->{populated} ) { | ||
3914 | |||||
3915 | # Finally, we got to here and the required ZoneID just cannot be | ||||
3916 | # found in either head or script (or other) zones, so record it for | ||||
3917 | # diagnostic purposes ($missingids format token) | ||||
3918 | push( @{ $zoneID->{missingrequires} }, $zoneIDToVisit->{id} ); | ||||
3919 | } | ||||
3920 | } | ||||
3921 | 38 | 11µs | push( @{$list}, $zoneID ); | ||
3922 | |||||
3923 | 38 | 58µs | return; | ||
3924 | } | ||||
3925 | |||||
3926 | # This private function is used in ZoneTests | ||||
3927 | # spent 18.2ms (128µs+18.0) within Foswiki::_renderZones which was called:
# once (128µs+18.0ms) by Foswiki::writeCompletePage at line 732 | ||||
3928 | 1 | 7µs | my ( $this, $text ) = @_; | ||
3929 | |||||
3930 | # Render zones that were pulled out by Foswiki/Macros/RENDERZONE.pm | ||||
3931 | # NOTE: once a zone has been rendered it is cleared, so cannot | ||||
3932 | # be rendered again. | ||||
3933 | |||||
3934 | 1 | 89µs | $text =~ s/${RENDERZONE_MARKER}RENDERZONE\{(.*?)\}${RENDERZONE_MARKER}/ | ||
3935 | 2 | 4µs | 2 | 18.0ms | _renderZoneById($this, $1)/geo; # spent 18.0ms making 2 calls to Foswiki::_renderZoneById, avg 9.02ms/call |
3936 | |||||
3937 | # get the head zone and insert it at the end of the </head> | ||||
3938 | # *if it has not already been rendered* | ||||
3939 | 1 | 4µs | 1 | 4µs | my $headZone = _renderZone( $this, 'head', { chomp => "on" } ); # spent 4µs making 1 call to Foswiki::_renderZone |
3940 | 1 | 500ns | $text =~ s!(</head>)!$headZone\n$1!i if $headZone; | ||
3941 | |||||
3942 | # SMELL: Item9480 - can't trust that _renderzone(head) above has truly | ||||
3943 | # flushed both script and head zones empty when {MergeHeadAndScriptZones} = 1. | ||||
3944 | 1 | 2µs | 1 | 2µs | my $scriptZone = _renderZone( $this, 'script', { chomp => "on" } ); # spent 2µs making 1 call to Foswiki::_renderZone |
3945 | 1 | 400ns | $text =~ s!(</head>)!$scriptZone\n$1!i if $scriptZone; | ||
3946 | |||||
3947 | 1 | 1µs | chomp($text); | ||
3948 | |||||
3949 | 1 | 14µs | return $text; | ||
3950 | } | ||||
3951 | |||||
3952 | =begin TML | ||||
3953 | |||||
3954 | ---++ StaticMethod readFile( $filename ) -> $text | ||||
3955 | |||||
3956 | Returns the entire contents of the given file, which can be specified in any | ||||
3957 | format acceptable to the Perl open() function. Fast, but inherently unsafe. | ||||
3958 | |||||
3959 | WARNING: Never, ever use this for accessing topics or attachments! Use the | ||||
3960 | Store API for that. This is for global control files only, and should be | ||||
3961 | used *only* if there is *absolutely no alternative*. | ||||
3962 | |||||
3963 | =cut | ||||
3964 | |||||
3965 | sub readFile { | ||||
3966 | my $name = shift; | ||||
3967 | ASSERT(0) if DEBUG; | ||||
3968 | my $IN_FILE; | ||||
3969 | open( $IN_FILE, "<$name" ) || return ''; | ||||
3970 | local $/ = undef; | ||||
3971 | my $data = <$IN_FILE>; | ||||
3972 | close($IN_FILE); | ||||
3973 | $data = '' unless ( defined($data) ); | ||||
3974 | return $data; | ||||
3975 | } | ||||
3976 | |||||
3977 | =begin TML | ||||
3978 | |||||
3979 | ---++ StaticMethod expandStandardEscapes($str) -> $unescapedStr | ||||
3980 | |||||
3981 | Expands standard escapes used in parameter values to block evaluation. See | ||||
3982 | System.FormatTokens for a full list of supported tokens. | ||||
3983 | |||||
3984 | =cut | ||||
3985 | |||||
3986 | # spent 964µs within Foswiki::expandStandardEscapes which was called 100 times, avg 10µs/call:
# 40 times (460µs+0s) by Foswiki::Search::searchWeb at line 444 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 12µs/call
# 23 times (92µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm:50] at line 48 of /var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm, avg 4µs/call
# 20 times (164µs+0s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm:50] at line 44 of /var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm, avg 8µs/call
# 11 times (45µs+0s) by Foswiki::USERINFO at line 132 of /var/www/foswikidev/core/lib/Foswiki/Macros/USERINFO.pm, avg 4µs/call
# 3 times (41µs+0s) by Foswiki::Render::renderRevisionInfo at line 787 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 14µs/call
# 2 times (158µs+0s) by Foswiki::_renderZone at line 3866, avg 79µs/call
# once (3µs+0s) by Foswiki::META at line 69 of /var/www/foswikidev/core/lib/Foswiki/Macros/META.pm | ||||
3987 | 100 | 77µs | my $text = shift; | ||
3988 | |||||
3989 | # expand '$n()' and $n! to new line | ||||
3990 | 100 | 118µs | $text =~ s/\$n\(\)/\n/gs; | ||
3991 | 100 | 67µs | $text =~ s/\$n(?=[^[:alpha:]]|$)/\n/gs; | ||
3992 | |||||
3993 | # filler, useful for nested search | ||||
3994 | 100 | 58µs | $text =~ s/\$nop(\(\))?//gs; | ||
3995 | |||||
3996 | # $quot -> " | ||||
3997 | 100 | 70µs | $text =~ s/\$quot(\(\))?/\"/gs; | ||
3998 | |||||
3999 | # $comma -> , | ||||
4000 | 100 | 40µs | $text =~ s/\$comma(\(\))?/,/gs; | ||
4001 | |||||
4002 | # $percent -> % | ||||
4003 | 100 | 63µs | $text =~ s/\$perce?nt(\(\))?/\%/gs; | ||
4004 | |||||
4005 | # $lt -> < | ||||
4006 | 100 | 68µs | $text =~ s/\$lt(\(\))?/\</gs; | ||
4007 | |||||
4008 | # $gt -> > | ||||
4009 | 100 | 59µs | $text =~ s/\$gt(\(\))?/\>/gs; | ||
4010 | |||||
4011 | # $amp -> & | ||||
4012 | 100 | 41µs | $text =~ s/\$amp(\(\))?/\&/gs; | ||
4013 | |||||
4014 | # $dollar -> $, done last to avoid creating the above tokens | ||||
4015 | 100 | 44µs | $text =~ s/\$dollar(\(\))?/\$/gs; | ||
4016 | |||||
4017 | 100 | 264µs | return $text; | ||
4018 | } | ||||
4019 | |||||
4020 | =begin TML | ||||
4021 | |||||
4022 | ---++ ObjectMethod webExists( $web ) -> $boolean | ||||
4023 | |||||
4024 | Test if web exists | ||||
4025 | * =$web= - Web name, required, e.g. ='Sandbox'= | ||||
4026 | |||||
4027 | A web _has_ to have a preferences topic to be a web. | ||||
4028 | |||||
4029 | =cut | ||||
4030 | |||||
4031 | # spent 7.35ms (442µs+6.90) within Foswiki::webExists which was called 101 times, avg 73µs/call:
# 80 times (367µs+5.51ms) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:235] at line 221 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 74µs/call
# 20 times (65µs+1.32ms) by Foswiki::WebFilter::ok at line 48 of /var/www/foswikidev/core/lib/Foswiki/WebFilter.pm, avg 69µs/call
# once (10µs+72µs) by Foswiki::UI::checkWebExists at line 572 of /var/www/foswikidev/core/lib/Foswiki/UI.pm | ||||
4032 | 101 | 61µs | my ( $this, $web ) = @_; | ||
4033 | |||||
4034 | ASSERT( UNTAINTED($web), 'web is tainted' ) if DEBUG; | ||||
4035 | 101 | 481µs | 101 | 6.90ms | return $this->{store}->webExists($web); # spent 6.90ms making 101 calls to Foswiki::Store::Rcs::Store::webExists, avg 68µs/call |
4036 | } | ||||
4037 | |||||
4038 | =begin TML | ||||
4039 | |||||
4040 | ---++ ObjectMethod topicExists( $web, $topic ) -> $boolean | ||||
4041 | |||||
4042 | Test if topic exists | ||||
4043 | * =$web= - Web name, optional, e.g. ='Main'= | ||||
4044 | * =$topic= - Topic name, required, e.g. ='TokyoOffice'=, or ="Main.TokyoOffice"= | ||||
4045 | |||||
4046 | =cut | ||||
4047 | |||||
4048 | # spent 11.1ms (754µs+10.4) within Foswiki::topicExists which was called 156 times, avg 71µs/call:
# 116 times (601µs+7.47ms) by Foswiki::Templates::_readTemplateFile at line 538 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 70µs/call
# 18 times (58µs+1.26ms) by Foswiki::Render::_renderWikiWord at line 1272 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 73µs/call
# 7 times (30µs+517µs) by Foswiki::Plugin::topicWeb at line 391 of /var/www/foswikidev/core/lib/Foswiki/Plugin.pm, avg 78µs/call
# 6 times (23µs+422µs) by Foswiki::Func::topicExists at line 1631 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 74µs/call
# 4 times (14µs+283µs) by Foswiki::If::OP_istopic::evaluate at line 48 of /var/www/foswikidev/core/lib/Foswiki/If/OP_istopic.pm, avg 74µs/call
# once (10µs+130µs) by Foswiki::_parsePath at line 1814
# once (4µs+78µs) by Foswiki::Users::TopicUserMapping::_loadMapping at line 1676 of /var/www/foswikidev/core/lib/Foswiki/Users/TopicUserMapping.pm
# once (4µs+76µs) by Foswiki::_lookupIcon at line 36 of /var/www/foswikidev/core/lib/Foswiki/Macros/ICON.pm
# once (4µs+64µs) by Foswiki::If::OP_allows::evaluate at line 54 of /var/www/foswikidev/core/lib/Foswiki/If/OP_allows.pm
# once (4µs+56µs) by Foswiki::UI::View::view at line 125 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm | ||||
4049 | 156 | 104µs | my ( $this, $web, $topic ) = @_; | ||
4050 | ASSERT( UNTAINTED($web), 'web is tainted' ) if DEBUG; | ||||
4051 | 156 | 13µs | ASSERT( UNTAINTED($topic), 'topic is tainted' ) if DEBUG; | ||
4052 | 156 | 686µs | 156 | 10.4ms | return $this->{store}->topicExists( $web, $topic ); # spent 10.4ms making 156 calls to Foswiki::Store::Rcs::Store::topicExists, avg 66µs/call |
4053 | } | ||||
4054 | |||||
4055 | =begin TML | ||||
4056 | |||||
4057 | ---+++ ObjectMethod getWorkArea( $key ) -> $directorypath | ||||
4058 | |||||
4059 | Gets a private directory uniquely identified by $key. The directory is | ||||
4060 | intended as a work area for plugins etc. The directory will exist. | ||||
4061 | |||||
4062 | =cut | ||||
4063 | |||||
4064 | # spent 61µs (11+50) within Foswiki::getWorkArea which was called:
# once (11µs+50µs) by Foswiki::Func::getWorkArea at line 2876 of /var/www/foswikidev/core/lib/Foswiki/Func.pm | ||||
4065 | 1 | 1µs | my ( $this, $key ) = @_; | ||
4066 | 1 | 10µs | 1 | 50µs | return $this->{store}->getWorkArea($key); # spent 50µs making 1 call to Foswiki::Store::getWorkArea |
4067 | } | ||||
4068 | |||||
4069 | =begin TML | ||||
4070 | |||||
4071 | ---++ ObjectMethod getApproxRevTime ( $web, $topic ) -> $epochSecs | ||||
4072 | |||||
4073 | Get an approximate rev time for the latest rev of the topic. This method | ||||
4074 | is used to optimise searching. Needs to be as fast as possible. | ||||
4075 | |||||
4076 | SMELL: is there a reason this is in Foswiki.pm, and not in Search? | ||||
4077 | |||||
4078 | =cut | ||||
4079 | |||||
4080 | sub getApproxRevTime { | ||||
4081 | my ( $this, $web, $topic ) = @_; | ||||
4082 | |||||
4083 | my $metacache = $this->search->metacache; | ||||
4084 | if ( $metacache->hasCached( $web, $topic ) ) { | ||||
4085 | |||||
4086 | #don't kill me - this should become a property on Meta | ||||
4087 | return $metacache->get( $web, $topic )->{modified}; | ||||
4088 | } | ||||
4089 | |||||
4090 | return $this->{store}->getApproxRevTime( $web, $topic ); | ||||
4091 | } | ||||
4092 | |||||
4093 | 1 | 11µs | 1; | ||
4094 | __END__ |