Filename | /var/www/foswiki11/lib/Foswiki/Plugins/FindElsewherePlugin/Core.pm |
Statements | Executed 58 statements in 1.85ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
5 | 1 | 1 | 74µs | 7.55ms | _lazyInit | Foswiki::Plugins::FindElsewherePlugin::Core::
5 | 1 | 1 | 58µs | 7.63ms | handle | Foswiki::Plugins::FindElsewherePlugin::Core::
5 | 1 | 1 | 20µs | 20µs | _debug | Foswiki::Plugins::FindElsewherePlugin::Core::
1 | 1 | 1 | 16µs | 32µs | BEGIN@23 | Foswiki::Plugins::FindElsewherePlugin::Core::
1 | 1 | 1 | 15µs | 20µs | BEGIN@25 | Foswiki::Plugins::FindElsewherePlugin::Core::
1 | 1 | 1 | 13µs | 112µs | BEGIN@33 | Foswiki::Plugins::FindElsewherePlugin::Core::
0 | 0 | 0 | 0s | 0s | findTopicElsewhere | Foswiki::Plugins::FindElsewherePlugin::Core::
0 | 0 | 0 | 0s | 0s | makeSingular | Foswiki::Plugins::FindElsewherePlugin::Core::
0 | 0 | 0 | 0s | 0s | makeTopicLink | Foswiki::Plugins::FindElsewherePlugin::Core::
0 | 0 | 0 | 0s | 0s | putBackBlocks | Foswiki::Plugins::FindElsewherePlugin::Core::
0 | 0 | 0 | 0s | 0s | takeOutBlocks | Foswiki::Plugins::FindElsewherePlugin::Core::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # Copyright (C) 2002 Mike Barton, Marco Carnut, Peter HErnst | ||||
2 | # (C) 2003 Martin Cleaver, (C) 2004 Matt Wilkie (C) 2007 Crawford Currie | ||||
3 | # (C) 2008 Foswiki Contributors | ||||
4 | # | ||||
5 | # This program is free software; you can redistribute it and/or | ||||
6 | # modify it under the terms of the GNU General Public License | ||||
7 | # as published by the Free Software Foundation; either version 2 | ||||
8 | # of the License, or (at your option) any later version. | ||||
9 | # | ||||
10 | # This program is distributed in the hope that it will be useful, | ||||
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
13 | # GNU General Public License for more details, published at | ||||
14 | # http://www.gnu.org/copyleft/gpl.html | ||||
15 | # | ||||
16 | # ========================= | ||||
17 | # | ||||
18 | # This is the FindElsewhere Foswiki plugin, | ||||
19 | # see http://foswiki.org/Extensions/FindElsewherePlugin for details. | ||||
20 | |||||
21 | package Foswiki::Plugins::FindElsewherePlugin::Core; | ||||
22 | |||||
23 | 2 | 66µs | 2 | 47µs | # spent 32µs (16+16) within Foswiki::Plugins::FindElsewherePlugin::Core::BEGIN@23 which was called:
# once (16µs+16µs) by Foswiki::Plugins::FindElsewherePlugin::startRenderingHandler at line 23 # spent 32µs making 1 call to Foswiki::Plugins::FindElsewherePlugin::Core::BEGIN@23
# spent 16µs making 1 call to strict::import |
24 | |||||
25 | # spent 20µs (15+4) within Foswiki::Plugins::FindElsewherePlugin::Core::BEGIN@25 which was called:
# once (15µs+4µs) by Foswiki::Plugins::FindElsewherePlugin::startRenderingHandler at line 31 | ||||
26 | # Do a dynamic 'use locale' for this module | ||||
27 | 1 | 4µs | if( $Foswiki::useLocale || $Foswiki::cfg{UseLocale}) { | ||
28 | 1 | 600ns | require locale; | ||
29 | 1 | 4µs | 1 | 4µs | import locale (); # spent 4µs making 1 call to locale::import |
30 | } | ||||
31 | 1 | 46µs | 1 | 20µs | } # spent 20µs making 1 call to Foswiki::Plugins::FindElsewherePlugin::Core::BEGIN@25 |
32 | |||||
33 | 1 | 8µs | 1 | 99µs | # spent 112µs (13+99) within Foswiki::Plugins::FindElsewherePlugin::Core::BEGIN@33 which was called:
# once (13µs+99µs) by Foswiki::Plugins::FindElsewherePlugin::startRenderingHandler at line 37 # spent 99µs making 1 call to vars::import |
34 | $disabledFlag $disablePluralToSingular | ||||
35 | $webNameRegex $wikiWordRegex $abbrevRegex $singleMixedAlphaNumRegex | ||||
36 | $noAutolink $redirectable $initialised @webList %linkedWords $findAcronyms | ||||
37 | 1 | 1.59ms | 1 | 112µs | ); # spent 112µs making 1 call to Foswiki::Plugins::FindElsewherePlugin::Core::BEGIN@33 |
38 | |||||
39 | 1 | 500ns | $initialised = 0; | ||
40 | |||||
41 | 5 | 16µs | # spent 20µs within Foswiki::Plugins::FindElsewherePlugin::Core::_debug which was called 5 times, avg 4µs/call:
# 5 times (20µs+0s) by Foswiki::Plugins::FindElsewherePlugin::Core::handle at line 97, avg 4µs/call | ||
42 | # Uncomment for debug | ||||
43 | #Foswiki::Func::writeDebug( "FindElsewherePlugin: ".join(' ', @_)); | ||||
44 | } | ||||
45 | |||||
46 | # spent 7.55ms (74µs+7.47) within Foswiki::Plugins::FindElsewherePlugin::Core::_lazyInit which was called 5 times, avg 1.51ms/call:
# 5 times (74µs+7.47ms) by Foswiki::Plugins::FindElsewherePlugin::Core::handle at line 94, avg 1.51ms/call | ||||
47 | 5 | 20µs | return 1 if $initialised; | ||
48 | 1 | 200ns | $initialised = 1; | ||
49 | |||||
50 | 1 | 4µs | 1 | 41µs | my $otherWebs = Foswiki::Func::getPreferencesValue( "LOOKELSEWHEREWEBS" ); # spent 41µs making 1 call to Foswiki::Func::getPreferencesValue |
51 | 1 | 3µs | 1 | 7.26ms | unless( defined( $otherWebs)) { # spent 7.26ms making 1 call to Foswiki::Func::getPluginPreferencesValue |
52 | # Compatibility, deprecated | ||||
53 | $otherWebs = Foswiki::Func::getPluginPreferencesValue( "LOOKELSEWHEREWEBS" ); | ||||
54 | } | ||||
55 | |||||
56 | 1 | 500ns | unless( defined( $otherWebs )) { | ||
57 | # SMELL: Retained for compatibility, but would be much better | ||||
58 | # off without this, as we could use the absence of webs to mean the | ||||
59 | # plugin is disabled. | ||||
60 | $otherWebs = "System,Main"; | ||||
61 | } | ||||
62 | |||||
63 | 1 | 2µs | 1 | 25µs | $findAcronyms = Foswiki::Func::getPreferencesValue( "LOOKELSEWHEREFORACRONYMS" ) || "all"; # spent 25µs making 1 call to Foswiki::Func::getPreferencesValue |
64 | |||||
65 | 1 | 2µs | 1 | 33µs | $disablePluralToSingular = # spent 33µs making 1 call to Foswiki::Func::getPreferencesFlag |
66 | Foswiki::Func::getPreferencesFlag( "DISABLEPLURALTOSINGULAR" ); | ||||
67 | 1 | 200ns | unless( defined( $disablePluralToSingular)) { | ||
68 | # Compatibility, deprecated | ||||
69 | $disablePluralToSingular = | ||||
70 | Foswiki::Func::getPluginPreferencesFlag( "DISABLEPLURALTOSINGULAR" ); | ||||
71 | } | ||||
72 | |||||
73 | $redirectable = | ||||
74 | 1 | 1µs | 1 | 28µs | Foswiki::Func::getPreferencesFlag( "LOOKELSEWHEREFORLOCAL" ); # spent 28µs making 1 call to Foswiki::Func::getPreferencesFlag |
75 | |||||
76 | 1 | 4µs | @webList = split( /[,\s]+/, $otherWebs ); | ||
77 | |||||
78 | 1 | 4µs | 1 | 6µs | $webNameRegex = Foswiki::Func::getRegularExpression('webNameRegex'); # spent 6µs making 1 call to Foswiki::Func::getRegularExpression |
79 | 1 | 1µs | 1 | 2µs | $wikiWordRegex = Foswiki::Func::getRegularExpression('wikiWordRegex'); # spent 2µs making 1 call to Foswiki::Func::getRegularExpression |
80 | 1 | 900ns | 1 | 2µs | $abbrevRegex = Foswiki::Func::getRegularExpression('abbrevRegex'); # spent 2µs making 1 call to Foswiki::Func::getRegularExpression |
81 | |||||
82 | 1 | 1µs | 1 | 72µs | $noAutolink = Foswiki::Func::getPreferencesFlag('NOAUTOLINK'); # spent 72µs making 1 call to Foswiki::Func::getPreferencesFlag |
83 | |||||
84 | 1 | 2µs | 1 | 2µs | my $upperAlphaRegex = Foswiki::Func::getRegularExpression('upperAlpha'); # spent 2µs making 1 call to Foswiki::Func::getRegularExpression |
85 | 1 | 1µs | 1 | 1µs | my $lowerAlphaRegex = Foswiki::Func::getRegularExpression('lowerAlpha'); # spent 1µs making 1 call to Foswiki::Func::getRegularExpression |
86 | 1 | 900ns | 1 | 2µs | my $numericRegex = Foswiki::Func::getRegularExpression('numeric'); # spent 2µs making 1 call to Foswiki::Func::getRegularExpression |
87 | 1 | 13µs | $singleMixedAlphaNumRegex = qr/[$upperAlphaRegex$lowerAlphaRegex$numericRegex]/; | ||
88 | |||||
89 | # Plugin correctly initialized | ||||
90 | 1 | 5µs | return 1; | ||
91 | } | ||||
92 | |||||
93 | # spent 7.63ms (58µs+7.57) within Foswiki::Plugins::FindElsewherePlugin::Core::handle which was called 5 times, avg 1.53ms/call:
# 5 times (58µs+7.57ms) by Foswiki::Plugins::FindElsewherePlugin::startRenderingHandler at line 59 of /var/www/foswiki11/lib/Foswiki/Plugins/FindElsewherePlugin.pm, avg 1.53ms/call | ||||
94 | 5 | 11µs | 5 | 7.55ms | return unless _lazyInit(); # spent 7.55ms making 5 calls to Foswiki::Plugins::FindElsewherePlugin::Core::_lazyInit, avg 1.51ms/call |
95 | |||||
96 | 5 | 2µs | if ( $noAutolink ) { | ||
97 | 5 | 20µs | 5 | 20µs | _debug('NOAUTOLINK set'); # spent 20µs making 5 calls to Foswiki::Plugins::FindElsewherePlugin::Core::_debug, avg 4µs/call |
98 | 5 | 16µs | return; | ||
99 | } | ||||
100 | |||||
101 | unless (scalar(@webList)) { | ||||
102 | _debug('no webs'); | ||||
103 | # no point if there are no webs to search | ||||
104 | return; | ||||
105 | } | ||||
106 | |||||
107 | # Find instances of WikiWords not in this web, but in the otherWeb(s) | ||||
108 | # If the WikiWord is found in theWeb, put the word back unchanged | ||||
109 | # If the WikiWord is found in the otherWeb, link to it via [[otherWeb.WikiWord]] | ||||
110 | # If it isn't found there either, put the word back unchnaged | ||||
111 | |||||
112 | my $removed = {}; | ||||
113 | my $text = takeOutBlocks( $_[0], 'noautolink', $removed ); | ||||
114 | |||||
115 | # Match | ||||
116 | # 0) (Allowed preambles: "\s" and "(") | ||||
117 | # 1) [[something]] - (including [[something][something]], but non-greedy), | ||||
118 | # 2) WikiWordAsWebName.WikiWord, | ||||
119 | # 3) WikiWords, and | ||||
120 | # 4) WIK IWO RDS | ||||
121 | %linkedWords = (); | ||||
122 | $text =~ s/(\[\[.*?\]\]|(?:^|(?<=[\s\(,]))(?:$webNameRegex\.)?(?:$wikiWordRegex|$abbrevRegex))/findTopicElsewhere($_[1],$1)/geo; | ||||
123 | |||||
124 | putBackBlocks( \$text, $removed, 'noautolink' ); | ||||
125 | |||||
126 | $_[0] = $text; | ||||
127 | } | ||||
128 | |||||
129 | sub makeTopicLink { | ||||
130 | ##my($otherWeb, $theTopic) = @_; | ||||
131 | return "[[$_[0].$_[1]][$_[0]]]"; | ||||
132 | } | ||||
133 | |||||
134 | sub findTopicElsewhere { | ||||
135 | # This was copied and pruned from Foswiki::internalLink | ||||
136 | my( $theWeb, $theTopic ) = @_; | ||||
137 | my $original = $theTopic; | ||||
138 | my $linkText = $theTopic; | ||||
139 | my $nonForcedAcronym = 0; | ||||
140 | |||||
141 | if ($theTopic =~ /^\[\[($webNameRegex)\.($wikiWordRegex)\](?:\[(.*)\])?\]$/o) { | ||||
142 | if ($redirectable && $1 eq $theWeb) { | ||||
143 | # The topic is *supposed* to be in this web, but the web is | ||||
144 | # redirectable so we can ignore the web specifier | ||||
145 | # remove the web name and continue | ||||
146 | $theTopic = $2; | ||||
147 | $linkText = $3 || $theTopic; | ||||
148 | } else { | ||||
149 | # The topic is an explicit link to another web | ||||
150 | return $theTopic; | ||||
151 | } | ||||
152 | } elsif ($theTopic =~ /^\[\[($wikiWordRegex)\](?:\[(.*)\])?\]$/o) { | ||||
153 | # No web specifier, look elsewhere | ||||
154 | $theTopic = $1; | ||||
155 | $linkText = $2 || $theTopic; | ||||
156 | } elsif ($theTopic =~ /^\[\[($abbrevRegex)\](?:\[(.*)\])?\]$/o) { | ||||
157 | # No web specifier, look elsewhere | ||||
158 | $theTopic = $1; | ||||
159 | $linkText = $2 || $theTopic; | ||||
160 | } elsif ($theTopic =~ /^$abbrevRegex$/o) { | ||||
161 | $nonForcedAcronym = 1; | ||||
162 | } elsif ($theTopic =~ /^($webNameRegex)\.($wikiWordRegex)$/o) { | ||||
163 | if ($redirectable && $1 eq $theWeb) { | ||||
164 | $linkText = $theTopic = $2; | ||||
165 | } else { | ||||
166 | return $theTopic; | ||||
167 | } | ||||
168 | } | ||||
169 | |||||
170 | if ( $nonForcedAcronym ) { | ||||
171 | return $theTopic if $findAcronyms eq "none"; | ||||
172 | return $linkedWords{$theTopic} if ( $findAcronyms eq "all" && $linkedWords{$theTopic} ); | ||||
173 | return $theTopic if ( $findAcronyms eq "first" && $linkedWords{$theTopic} ); | ||||
174 | } | ||||
175 | |||||
176 | # Turn spaced-out names into WikiWords - upper case first letter of | ||||
177 | # whole link, and first of each word. | ||||
178 | $theTopic =~ s/^(.)/\U$1/o; | ||||
179 | $theTopic =~ s/\s($singleMixedAlphaNumRegex)/\U$1/go; | ||||
180 | $theTopic =~ s/\[\[($singleMixedAlphaNumRegex)(.*)\]\]/\u$1$2/o; | ||||
181 | |||||
182 | # Look in the current web, return if found | ||||
183 | my $exist = Foswiki::Func::topicExists( $theWeb, $theTopic ); | ||||
184 | |||||
185 | if( ! $exist ) { | ||||
186 | if( !$disablePluralToSingular && $theTopic =~ /s$/ ) { | ||||
187 | my $theTopicSingular = makeSingular( $theTopic ); | ||||
188 | if( Foswiki::Func::topicExists( $theWeb, $theTopicSingular ) ) { | ||||
189 | _debug("$theTopicSingular was found in $theWeb" ); | ||||
190 | return $original; # leave it as we found it | ||||
191 | } | ||||
192 | } | ||||
193 | } else { | ||||
194 | _debug("$theTopic was found in $theWeb: $linkText" ); | ||||
195 | return $original; # leave it as we found it | ||||
196 | } | ||||
197 | |||||
198 | # Look in the other webs, return when found | ||||
199 | my @topicLinks; | ||||
200 | |||||
201 | foreach ( @webList ) { | ||||
202 | my $otherWeb = $_; | ||||
203 | |||||
204 | # For systems running WebNameAsWikiName | ||||
205 | # If the $theTopic is a reference to a the name of | ||||
206 | # otherWeb, point at otherWeb.WebHome - MRJC | ||||
207 | if ($otherWeb eq $theTopic) { | ||||
208 | _debug("$theTopic is the name of another web $otherWeb."); | ||||
209 | return "[[$otherWeb.WebHome][$otherWeb]]"; | ||||
210 | } | ||||
211 | |||||
212 | my $exist = Foswiki::Func::topicExists( $otherWeb, $theTopic ); | ||||
213 | if( ! $exist ) { | ||||
214 | if( !$disablePluralToSingular && $theTopic =~ /s$/ ) { | ||||
215 | my $theTopicSingular = makeSingular( $theTopic ); | ||||
216 | if( Foswiki::Func::topicExists( $otherWeb, $theTopicSingular ) ) { | ||||
217 | _debug("$theTopicSingular was found in $otherWeb"); | ||||
218 | push(@topicLinks, makeTopicLink($otherWeb, $theTopic)); | ||||
219 | } | ||||
220 | } | ||||
221 | } | ||||
222 | else { | ||||
223 | _debug("$theTopic was found in $otherWeb"); | ||||
224 | push(@topicLinks, makeTopicLink($otherWeb,$theTopic)); | ||||
225 | } | ||||
226 | } | ||||
227 | |||||
228 | if (scalar(@topicLinks) > 0) { | ||||
229 | if (scalar(@topicLinks) == 1) { | ||||
230 | # Topic found in one place | ||||
231 | # If link text [[was in this form]], free it | ||||
232 | $linkText =~ s/\[\[(.*)\]\]/$1/o; | ||||
233 | |||||
234 | # Link to topic | ||||
235 | $topicLinks[0] =~ s/(\[\[.*?\]\[)(.*?)(\]\])/$1$linkText$3/o; | ||||
236 | $linkedWords{$theTopic} = $topicLinks[0]; | ||||
237 | return $topicLinks[0]; | ||||
238 | } else { | ||||
239 | # topic found in several places | ||||
240 | # If link text [[was in this form]] <em> it | ||||
241 | $linkText =~ s/\[\[(.*)\]\]/<em>$1<\/em>/go; | ||||
242 | |||||
243 | # If $linkText is a WikiWord, prepend with <nop> | ||||
244 | # (prevent double links) | ||||
245 | $linkText =~ s/($wikiWordRegex)/<nop\/>$1/go; | ||||
246 | my $renderedLink = "$linkText<sup>(".join(",", @topicLinks ).")</sup>"; | ||||
247 | $linkedWords{$theTopic} = $renderedLink; | ||||
248 | return $renderedLink; | ||||
249 | } | ||||
250 | } | ||||
251 | return $original; | ||||
252 | } | ||||
253 | |||||
254 | sub makeSingular { | ||||
255 | my ($theWord) = @_; | ||||
256 | |||||
257 | $theWord =~ s/ies$/y/o; # plurals like policy / policies | ||||
258 | $theWord =~ s/sses$/ss/o; # plurals like address / addresses | ||||
259 | $theWord =~ s/([Xx])es$/$1/o; # plurals like box / boxes | ||||
260 | $theWord =~ s/([A-Za-rt-z])s$/$1/o; # others, excluding ending ss like address(es) | ||||
261 | return $theWord; | ||||
262 | } | ||||
263 | |||||
264 | 1 | 300ns | my $placeholderMarker = 0; | ||
265 | |||||
266 | sub takeOutBlocks { | ||||
267 | my( $intext, $tag, $map ) = @_; | ||||
268 | |||||
269 | return $intext unless( $intext =~ m/<$tag\b/i ); | ||||
270 | |||||
271 | my $out = ''; | ||||
272 | my $depth = 0; | ||||
273 | my $scoop; | ||||
274 | my $tagParams; | ||||
275 | |||||
276 | foreach my $token ( split/(<\/?$tag[^>]*>)/i, $intext ) { | ||||
277 | if ($token =~ /<$tag\b([^>]*)?>/i) { | ||||
278 | $depth++; | ||||
279 | if ($depth eq 1) { | ||||
280 | $tagParams = $1; | ||||
281 | next; | ||||
282 | } | ||||
283 | } elsif ($token =~ /<\/$tag>/i) { | ||||
284 | if ($depth > 0) { | ||||
285 | $depth--; | ||||
286 | if ($depth eq 0) { | ||||
287 | my $placeholder = $tag.$placeholderMarker; | ||||
288 | $placeholderMarker++; | ||||
289 | $map->{$placeholder}{text} = $scoop; | ||||
290 | $map->{$placeholder}{params} = $tagParams; | ||||
291 | $out .= '<!--'.$Foswiki::TranslationToken.$placeholder. | ||||
292 | $Foswiki::TranslationToken.'-->'; | ||||
293 | $scoop = ''; | ||||
294 | next; | ||||
295 | } | ||||
296 | } | ||||
297 | } | ||||
298 | if ($depth > 0) { | ||||
299 | $scoop .= $token; | ||||
300 | } else { | ||||
301 | $out .= $token; | ||||
302 | } | ||||
303 | } | ||||
304 | |||||
305 | # unmatched tags | ||||
306 | if (defined($scoop) && ($scoop ne '')) { | ||||
307 | my $placeholder = $tag.$placeholderMarker; | ||||
308 | $placeholderMarker++; | ||||
309 | $map->{$placeholder}{text} = $scoop; | ||||
310 | $map->{$placeholder}{params} = $tagParams; | ||||
311 | $out .= '<!--'.$Foswiki::TranslationToken.$placeholder. | ||||
312 | $Foswiki::TranslationToken.'-->'; | ||||
313 | } | ||||
314 | |||||
315 | return $out; | ||||
316 | } | ||||
317 | |||||
318 | sub putBackBlocks { | ||||
319 | my( $text, $map, $tag, $newtag, $callback ) = @_; | ||||
320 | |||||
321 | $newtag = $tag if (!defined($newtag)); | ||||
322 | |||||
323 | foreach my $placeholder ( keys %$map ) { | ||||
324 | if( $placeholder =~ /^$tag\d+$/ ) { | ||||
325 | my $params = $map->{$placeholder}{params} || ''; | ||||
326 | my $val = $map->{$placeholder}{text}; | ||||
327 | $val = &$callback( $val ) if ( defined( $callback )); | ||||
328 | if ($newtag eq '') { | ||||
329 | $$text =~ s(<!--$Foswiki::TranslationToken$placeholder$Foswiki::TranslationToken-->)($val); | ||||
330 | } else { | ||||
331 | $$text =~ s(<!--$Foswiki::TranslationToken$placeholder$Foswiki::TranslationToken-->) | ||||
332 | (<$newtag$params>$val</$newtag>); | ||||
333 | } | ||||
334 | delete( $map->{$placeholder} ); | ||||
335 | } | ||||
336 | } | ||||
337 | } | ||||
338 | |||||
339 | 1 | 4µs | 1; |