← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:14 2015

Filename/var/www/foswikidev/core/lib/Foswiki/PageCache.pm
StatementsExecuted 18 statements in 2.19ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1111.28ms1.36msFoswiki::PageCache::::BEGIN@61Foswiki::PageCache::BEGIN@61
11113µs27µsFoswiki::PageCache::::BEGIN@58Foswiki::PageCache::BEGIN@58
1119µs13µsFoswiki::PageCache::::BEGIN@59Foswiki::PageCache::BEGIN@59
1118µs37µsFoswiki::PageCache::::BEGIN@74Foswiki::PageCache::BEGIN@74
1118µs109µsFoswiki::PageCache::::BEGIN@63Foswiki::PageCache::BEGIN@63
1115µs5µsFoswiki::PageCache::::BEGIN@62Foswiki::PageCache::BEGIN@62
1114µs4µsFoswiki::PageCache::::BEGIN@64Foswiki::PageCache::BEGIN@64
1114µs4µsFoswiki::PageCache::::BEGIN@66Foswiki::PageCache::BEGIN@66
1114µs4µsFoswiki::PageCache::::BEGIN@60Foswiki::PageCache::BEGIN@60
0000s0sFoswiki::PageCache::::__ANON__[:654]Foswiki::PageCache::__ANON__[:654]
0000s0sFoswiki::PageCache::::__ANON__[:657]Foswiki::PageCache::__ANON__[:657]
0000s0sFoswiki::PageCache::::_handleDirtyAreaFoswiki::PageCache::_handleDirtyArea
0000s0sFoswiki::PageCache::::addDependencyFoswiki::PageCache::addDependency
0000s0sFoswiki::PageCache::::cachePageFoswiki::PageCache::cachePage
0000s0sFoswiki::PageCache::::deleteAllFoswiki::PageCache::deleteAll
0000s0sFoswiki::PageCache::::deleteDependenciesFoswiki::PageCache::deleteDependencies
0000s0sFoswiki::PageCache::::deletePageFoswiki::PageCache::deletePage
0000s0sFoswiki::PageCache::::finishFoswiki::PageCache::finish
0000s0sFoswiki::PageCache::::fireDependencyFoswiki::PageCache::fireDependency
0000s0sFoswiki::PageCache::::genVariationKeyFoswiki::PageCache::genVariationKey
0000s0sFoswiki::PageCache::::getDependenciesFoswiki::PageCache::getDependencies
0000s0sFoswiki::PageCache::::getPageFoswiki::PageCache::getPage
0000s0sFoswiki::PageCache::::getPageVariationFoswiki::PageCache::getPageVariation
0000s0sFoswiki::PageCache::::getWebDependenciesFoswiki::PageCache::getWebDependencies
0000s0sFoswiki::PageCache::::isCacheableFoswiki::PageCache::isCacheable
0000s0sFoswiki::PageCache::::newFoswiki::PageCache::new
0000s0sFoswiki::PageCache::::renderDirtyAreasFoswiki::PageCache::renderDirtyAreas
0000s0sFoswiki::PageCache::::setDependenciesFoswiki::PageCache::setDependencies
0000s0sFoswiki::PageCache::::setPageVariationFoswiki::PageCache::setPageVariation
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::PageCache
6
7This class is a purely virtual base class that implements the
8basic infrastructure required to cache pages as produced by
9the rendering engine. Once a page was computed, it will be
10cached for subsequent calls for the same output. In addition
11a Foswiki::PageCache has to ensure cache correctness, that is
12all content stored in the cache is up-to-date. It must not
13return any content being rendered on the base of data that has already
14changed in the meantine by actions performed by the Foswiki::Store.
15
16The Foswiki::Store informs the cache whenever any content has changed
17by calling Foswiki::PageCache::fireDependency($web, $topic). This
18will in turn delete any cache entries that used this $web.$topic as an
19ingredience to render the cached page. That's why there is a dependency
20graph part of the page cache.
21
22The dependency graph records all topics that have been touched while
23the current page is being computed. It also records the session and url
24parameters that were in use, part of which is the user name as well.
25
26An edge in the dependency graph consists of:
27
28 * from: the topic being rendered
29 * variation: an opaque key encoding the context in which the page was rendered
30 * to: the topic that has been used to render the "from" topic
31
32For every cached page there's a record of meta data describing it:
33
34 * topic: the web.topic being cached
35 * variation: the context which this page was rendered within
36 * md5: fingerprint of the data stored; this is used to get access to the stored
37 blob related to this page
38 * contenttype: to be used in the http header
39 * lastmodified: time when this page was cached in http-date format
40 * etag: tag used for browser-side caching
41 * status: http response status
42 * location: url in case the status is a 302 redirect
43 * expire: time when this cache entry is outdated
44 * isdirty: boolean flag indicating whether the cached page has got "dirtyareas"
45 and thus needs post-processing
46
47Whenever the Foswiki::Store informs the cache by firing a dependency for
48a given web.topic, the cache will remove those cache entries that have a dependency
49to the given web.topic. It thereby guarentees that whenever a page has been
50successfully retrieved from the cache, there is no "fresher" content available
51in the Foswiki::Store, and that this cache entry can be used instead without
52rendering the related yet again.
53
54=cut
55
56package Foswiki::PageCache;
57
58227µs240µs
# spent 27µs (13+14) within Foswiki::PageCache::BEGIN@58 which was called: # once (13µs+14µs) by Foswiki::UI::View::BEGIN@25 at line 58
use strict;
# spent 27µs making 1 call to Foswiki::PageCache::BEGIN@58 # spent 14µs making 1 call to strict::import
59229µs217µs
# spent 13µs (9+4) within Foswiki::PageCache::BEGIN@59 which was called: # once (9µs+4µs) by Foswiki::UI::View::BEGIN@25 at line 59
use warnings;
# spent 13µs making 1 call to Foswiki::PageCache::BEGIN@59 # spent 4µs making 1 call to warnings::import
60220µs14µs
# spent 4µs within Foswiki::PageCache::BEGIN@60 which was called: # once (4µs+0s) by Foswiki::UI::View::BEGIN@25 at line 60
use Foswiki::Time ();
# spent 4µs making 1 call to Foswiki::PageCache::BEGIN@60
612107µs11.36ms
# spent 1.36ms (1.28+74µs) within Foswiki::PageCache::BEGIN@61 which was called: # once (1.28ms+74µs) by Foswiki::UI::View::BEGIN@25 at line 61
use Foswiki::Attrs ();
# spent 1.36ms making 1 call to Foswiki::PageCache::BEGIN@61
62224µs15µs
# spent 5µs within Foswiki::PageCache::BEGIN@62 which was called: # once (5µs+0s) by Foswiki::UI::View::BEGIN@25 at line 62
use Foswiki::Plugins ();
# spent 5µs making 1 call to Foswiki::PageCache::BEGIN@62
63230µs2210µs
# spent 109µs (8+101) within Foswiki::PageCache::BEGIN@63 which was called: # once (8µs+101µs) by Foswiki::UI::View::BEGIN@25 at line 63
use Error qw( :try );
# spent 109µs making 1 call to Foswiki::PageCache::BEGIN@63 # spent 101µs making 1 call to Error::import
64241µs14µs
# spent 4µs within Foswiki::PageCache::BEGIN@64 which was called: # once (4µs+0s) by Foswiki::UI::View::BEGIN@25 at line 64
use CGI::Util ();
# spent 4µs making 1 call to Foswiki::PageCache::BEGIN@64
65
66
# spent 4µs within Foswiki::PageCache::BEGIN@66 which was called: # once (4µs+0s) by Foswiki::UI::View::BEGIN@25 at line 71
BEGIN {
6714µs if ( $Foswiki::cfg{UseLocale} ) {
68 require locale;
69 import locale();
70 }
71120µs14µs}
# spent 4µs making 1 call to Foswiki::PageCache::BEGIN@66
72
73# Enable output
7421.89ms266µs
# spent 37µs (8+29) within Foswiki::PageCache::BEGIN@74 which was called: # once (8µs+29µs) by Foswiki::UI::View::BEGIN@25 at line 74
use constant TRACE => 0;
# spent 37µs making 1 call to Foswiki::PageCache::BEGIN@74 # spent 29µs making 1 call to constant::import
75
76=begin TML
77
78---++ ClassMethod new( ) -> $object
79
80Construct a new page cache
81
82=cut
83
84sub new {
85 my ($class) = @_;
86
87 return bless( {}, $class );
88}
89
90=begin TML
91
92---++ ObjectMethod genVariationKey() -> $key
93
94Generate a key for the current webtopic being produced; this reads
95information from the current session and url params, as follows:
96 * the server serving the request (HTTP_HOST)
97 * the port number of the server serving the request (HTTP_PORT)
98 * the action used to render the page (view or rest)
99 * the language of the current session, if any
100 * all session parameters EXCEPT:
101 o Those starting with an underscore
102 o VALIDATION
103 o REMEMBER
104 o FOSWIKISTRIKEONE.*
105 o VALID_ACTIONS.*
106 o BREADCRUMB_TRAIL
107 o DGP_hash
108 * all HTTP request parameters EXCEPT:
109 o All those starting with an underscore
110 o refresh
111 o foswiki_redirect_cache
112 o logout
113 o topic
114 o cache_ignore
115 o cache_expire
116
117=cut
118
119sub genVariationKey {
120 my $this = shift;
121
122 my $variationKey = $this->{variationKey};
123 return $variationKey if defined $variationKey;
124
125 my $session = $Foswiki::Plugins::SESSION;
126 my $request = $session->{request};
127 my $action = substr( ( $request->{action} || 'view' ), 0, 4 );
128 my $serverName = $request->server_name || $Foswiki::cfg{DefaultUrlHost};
129 my $serverPort = $request->server_port || 80;
130 $variationKey = '::' . $serverName . '::' . $serverPort . '::' . $action;
131
132 # add a flag to distinguish compressed from uncompressed cache entries
133 $variationKey .= '::'
134 . (
135 (
136 $Foswiki::cfg{HttpCompress}
137 && $Foswiki::engine->isa('Foswiki::Engine::CLI')
138 )
139 ? 1
140 : 0
141 );
142
143 # add language tag
144 if ( $Foswiki::cfg{UserInterfaceInternationalisation} ) {
145 my $language = $session->i18n->language();
146 $variationKey .= "::language=$language" if $language;
147 }
148
149 # get information from the session object
150 my $sessionValues = $session->getLoginManager()->getSessionValues();
151 foreach my $key ( sort keys %$sessionValues ) {
152
153 # SMELL: add a setting to make exclusion of session variables configurable
154 next
155 if $key =~
156m/^(_.*|VALIDATION|REMEMBER|FOSWIKISTRIKEONE.*|VALID_ACTIONS.*|BREADCRUMB_TRAIL|DGP_hash|release_lock)$/;
157
158 #writeDebug("adding session key=$key");
159
160 my $val = $sessionValues->{$key};
161 next unless defined $val;
162
163 $variationKey .= '::' . $key . '=' . $val;
164 }
165
166 # get cache_ignore pattern
167 my @ignoreParams = $request->multi_param("cache_ignore");
168 push @ignoreParams,
169 (
170 "cache_expire", "cache_ignore",
171 "_.*", "refresh",
172 "foswiki_redirect_cache", "logout",
173 "topic"
174 );
175 my $ignoreParams = join( "|", @ignoreParams );
176
177 foreach my $key ( sort $request->multi_param() ) {
178
179 # filter out some params that are not relevant
180 next if $key =~ m/^($ignoreParams)$/;
181 my @vals = $request->multi_param($key);
182 foreach my $val (@vals) {
183 next unless defined $val; # wtf?
184 $variationKey .= '::' . $key . '=' . $val;
185 Foswiki::Func::writeDebug("adding urlparam key=$key val=$val")
186 if TRACE;
187 }
188 }
189
190 $variationKey =~ s/'/\\'/g;
191
192 Foswiki::Func::writeDebug("variation key = '$variationKey'") if TRACE;
193
194 # cache it
195 $this->{variationKey} = $variationKey;
196 return $variationKey;
197}
198
199=begin TML
200
201---++ ObjectMethod cachePage($contentType, $data) -> $boolean
202
203Cache a page. Every page is stored in a page bucket that contains all
204variations (stored for other users or other session parameters) of this page,
205as well as dependency and expiration information
206
207=cut
208
209sub cachePage {
210 my ( $this, $contentType, $data ) = @_;
211
212 my $session = $Foswiki::Plugins::SESSION;
213 my $request = $session->{request};
214 my $web = $session->{webName};
215 my $topic = $session->{topicName};
216 $web =~ s/\//./g;
217
218 Foswiki::Func::writeDebug("called cachePage($web, $topic)") if TRACE;
219 return undef unless $this->isCacheable( $web, $topic );
220
221 # delete page and all variations if we ask for a refresh copy
222 my $refresh = $request->param('refresh') || '';
223 my $variationKey = $this->genVariationKey();
224
225 # remove old entries
226 if ( $refresh =~ m/^(on|cache|all)$/ ) {
227 $this->deletePage( $web, $topic ); # removes all variations
228 }
229 else {
230 $this->deletePage( $web, $topic, $variationKey );
231 }
232
233 # prepare page variation
234 my $isDirty =
235 ( $data =~ m/<dirtyarea[^>]*?>/ )
236 ? 1
237 : 0; # SMELL: only for textual content type
238
239 Foswiki::Func::writeDebug("isDirty=$isDirty") if TRACE;
240
241 my $etag = '';
242 my $lastModified = '';
243 my $time = time();
244
245 unless ($isDirty) {
246 $data =~ s/([\t ]?)[ \t]*<\/?(nop|noautolink)\/?>/$1/gis;
247
248 # clean pages are stored utf8-encoded, whether plaintext or zip
249 $data = Foswiki::encode_utf8($data);
250 if ( $Foswiki::cfg{HttpCompress} ) {
251
252 # Cache compressed page
253 require Compress::Zlib;
254 $data = Compress::Zlib::memGzip($data);
255 }
256 $etag = $time;
257 $lastModified = Foswiki::Time::formatTime( $time, '$http', 'gmtime' );
258 }
259
260 my $headers = $session->{response}->headers();
261 my $status = $headers->{Status} || 200;
262 my $variation = {
263 contenttype => $contentType,
264 lastmodified => $lastModified,
265 data => $data,
266 etag => $etag,
267 isdirty => $isDirty,
268 status => $status,
269 };
270 $variation->{location} = $headers->{Location} if $status == 302;
271
272 # get cache-expiry preferences and add it to the bucket if available
273 my $expire = $request->param("cache_expire");
274 $expire = $session->{prefs}->getPreference('CACHEEXPIRE')
275 unless defined $expire;
276 $variation->{expire} = CGI::Util::expire_calc($expire)
277 if defined $expire;
278
279 if ( defined $variation->{expire} && $variation->{expire} !~ /^\d+$/ ) {
280 print STDERR
281"WARNING: expire value '$variation->{expire}' is not recognized as a proper cache expiration value\n";
282 $variation->{expire} = undef;
283 }
284
285 # store page variation
286 Foswiki::Func::writeDebug("PageCache: Stored data") if TRACE;
287 return undef
288 unless $this->setPageVariation( $web, $topic, $variationKey, $variation );
289
290 # assert newly autotetected dependencies
291 $this->setDependencies( $web, $topic, $variationKey );
292
293 return $variation;
294}
295
296=begin TML
297
298---++ ObjectMethod getPage($web, $topic)
299
300Retrieve a cached page for the given web.topic, using a variation
301key based on the current session.
302
303=cut
304
305sub getPage {
306 my ( $this, $web, $topic ) = @_;
307
308 $web =~ s/\//./g;
309
310 Foswiki::Func::writeDebug("getPage($web.$topic)") if TRACE;
311
312 # check url param
313 my $session = $Foswiki::Plugins::SESSION;
314 my $refresh = $session->{request}->param('refresh') || '';
315 if ( $refresh eq 'all' ) {
316
317 # SMELL: restrict this to admins; put this somewhere else
318 $this->deleteAll();
319 }
320
321 if ( $refresh eq 'fire' ) { # simulates a "save" of the current topic
322 $this->fireDependency( $web, $topic );
323 }
324 return undef if $refresh =~ m/^(on|cache|all|fire)$/;
325
326 # check cacheability
327 return undef unless $this->isCacheable( $web, $topic );
328
329 # check availability
330 my $variationKey = $this->genVariationKey();
331
332 my $variation = $this->getPageVariation( $web, $topic, $variationKey );
333
334 # check expiry date of this entry; return undef if it did expire, not
335 # deleted from cache as it will be recomputed during a normal view
336 # cycle
337 return undef
338 if defined($variation)
339 && defined( $variation->{expire} )
340 && $variation->{expire} < time();
341
342 return $variation;
343}
344
345=begin TML
346
347---++ ObjectMethod setPageVariation($web, $topici, $variationKey, $variation) -> $bool
348
349stores a rendered page
350
351=cut
352
353sub setPageVariation {
354 my ( $this, $web, $topic, $variationKey, $variation ) = @_;
355
356 die("virtual method");
357}
358
359=begin TML
360
361---++ ObjectMethod getPageVariation($web, $topic, $variationKey)
362
363retrievs a cache entry; returns undef if there is none.
364
365=cut
366
367sub getPageVariation {
368 die("virtual method");
369}
370
371=begin TML
372
373Checks whether the current page is cacheable. It first
374checks the "refresh" url parameter and then looks out
375for the "CACHEABLE" preference variable.
376
377=cut
378
379sub isCacheable {
380 my ( $this, $web, $topic ) = @_;
381
382 my $webTopic = $web . '.' . $topic;
383
384 my $isCacheable = $this->{isCacheable}{$webTopic};
385 return $isCacheable if defined $isCacheable;
386
387 #Foswiki::Func::writeDebug("... checking") if TRACE;
388
389 # by default we try to cache as much as possible
390 $isCacheable = 1;
391
392 my $session = $Foswiki::Plugins::SESSION;
393
394 # POSTs and HEADs aren't cacheable
395 my $method = $session->{request}->method;
396 $isCacheable = 0 if $method && $method =~ m/^(?:POST|HEAD)$/;
397
398 if ($isCacheable) {
399
400 # check prefs value
401 my $flag = $session->{prefs}->getPreference('CACHEABLE');
402 $isCacheable = 0 if defined $flag && !Foswiki::isTrue($flag);
403 }
404
405 # don't cache 401 Not authorized responses
406 my $headers = $session->{response}->headers();
407 my $status = $headers->{Status};
408 $isCacheable = 0 if $status && $status eq 401;
409
410 # TODO: give plugins a chance - create a callback to intercept cacheability
411
412 #Foswiki::Func::writeDebug("isCacheable=$isCacheable") if TRACE;
413 $this->{isCacheable}{$webTopic} = $isCacheable;
414 return $isCacheable;
415}
416
417=begin TML
418
419---++ ObjectMethod addDependency($web, $topic)
420
421Add a web.topic to the dependencies of the current page
422
423=cut
424
425sub addDependency {
426 my ( $this, $depWeb, $depTopic ) = @_;
427
428 # exclude invalid topic names
429 return unless $depTopic =~ m/^[[:upper:]]/;
430
431 # omit dependencies triggered from inside a dirtyarea
432 my $session = $Foswiki::Plugins::SESSION;
433 return if $session->inContext('dirtyarea');
434
435 $depWeb =~ s/\//\./g;
436 my $depWebTopic = $depWeb . '.' . $depTopic;
437
438 # exclude unwanted dependencies
439 if ( $depWebTopic =~ m/^($Foswiki::cfg{Cache}{DependencyFilter})$/ ) {
440
441#Foswiki::Func::writeDebug( "dependency on $depWebTopic ignored by filter $Foswiki::cfg{Cache}{DependencyFilter}") if TRACE;
442 return;
443 }
444 else {
445
446 #Foswiki::Func::writeDebug("addDependency($depWeb.$depTopic)") if TRACE;
447 }
448
449 # collect them; defer writing them to the database til we cache this page
450 $this->{deps}{$depWebTopic} = 1;
451}
452
453=begin TML
454
455---++ ObjectMethod getDependencies($web, $topic, $variationKey) -> \@deps
456
457Return dependencies for a given web.topic. if $variationKey is specified, only
458dependencies of this page variation will be returned.
459
460=cut
461
462sub getDependencies {
463 my ( $this, $web, $topic, $variationKey ) = @_;
464
465 die("virtual method");
466
467}
468
469=begin TML
470
471---++ ObjectMethod getWebDependencies($web) -> \@deps
472
473Returns dependencies that hold for all topics in a web.
474
475=cut
476
477sub getWebDependencies {
478 my ( $this, $web ) = @_;
479
480 unless ( defined $this->{webDeps} ) {
481 my $session = $Foswiki::Plugins::SESSION;
482 my $webDeps =
483 $session->{prefs}->getPreference( 'WEBDEPENDENCIES', $web )
484 || $Foswiki::cfg{Cache}{WebDependencies}
485 || '';
486
487 $this->{webDeps} = ();
488
489 # normalize topics
490 foreach my $dep ( split( /\s*,\s*/, $webDeps ) ) {
491 my ( $depWeb, $depTopic ) =
492 $session->normalizeWebTopicName( $web, $dep );
493
494 Foswiki::Func::writeDebug("found webdep $depWeb.$depTopic")
495 if TRACE;
496 $this->{webDeps}{ $depWeb . '.' . $depTopic } = 1;
497 }
498 }
499 my @result = keys %{ $this->{webDeps} };
500 return \@result;
501}
502
503=begin TML
504
505---++ ObjectMethod setDependencies($web, $topic, $variation, @topics)
506
507Stores the dependencies for the given web.topic topic. Setting the dependencies
508happens at the very end of a rendering process of a page while it is about
509to be cached.
510
511When the optional @topics parameter isn't provided, then all dependencies
512collected in the Foswiki::PageCache object will be used. These dependencies
513are collected during the rendering process.
514
515=cut
516
517sub setDependencies {
518 my ( $this, $web, $topic, $variationKey, @topicDeps ) = @_;
519
520 @topicDeps = keys %{ $this->{deps} } unless @topicDeps;
521
522 die("virtual method");
523}
524
525=begin TML
526
527---++ ObjectMethod deleteDependencies($web, $topic, $variation, $force)
528
529Remove a dependency from the graph. This operation is normally performed
530as part of a call to Foswiki::PageCache::deletePage().
531
532=cut
533
534sub deleteDependencies {
535 die("virtual method");
536}
537
538=begin TML
539
540---++ ObjectMethod deletePage($web, $topic, $variation, $force)
541
542Remove a page from the cache; this removes all of the information
543that we have about this page, including any dependencies that have
544been established while this page was created.
545
546If $variation is specified, only this variation of $web.$topic will
547be removed. When $variation is not specified, all page variations of $web.$topic
548will be removed.
549
550When $force is true, the deletion will take place immediately. Otherwise all
551delete requests might be delayed and committed as part of
552Foswiki::PageCache::finish().
553
554=cut
555
556sub deletePage {
557 die("virtual method");
558}
559
560=begin TML
561
562---++ ObjectMethod deleteAll()
563
564purges all of the cache
565
566=cut
567
568sub deleteAll {
569 die("virtual method");
570}
571
572=begin TML
573
574---++ ObjectMethod fireDependency($web, $topic)
575
576This method is called to remove all other cache entries that
577used the given $web.$topic as an ingredience to produce the page.
578
579A dependency is a directed edge starting from a page variation being rendered
580towards a depending page that has been used to produce it.
581
582While dependency edges are stored as they are collected during the rendering
583process, these edges are traversed in reverse order when a dependency is
584fired.
585
586In addition all manually asserted dependencies of topics in a web are deleted,
587as well as the given topic itself.
588
589=cut
590
591sub fireDependency {
592 die("virtual method");
593}
594
595=begin TML
596
597---++ ObjectMethod renderDirtyAreas($text)
598
599Extract dirty areas and render them; this happens after storing a
600page including the un-rendered dirty areas into the cache and after
601retrieving it again.
602
603=cut
604
605sub renderDirtyAreas {
606 my ( $this, $text ) = @_;
607
608 Foswiki::Func::writeDebug("called renderDirtyAreas") if TRACE;
609
610 my $session = $Foswiki::Plugins::SESSION;
611 $session->enterContext('dirtyarea');
612
613 # remember the current page length to recompute the content length below
614 my $found = 0;
615 my $topicObj =
616 new Foswiki::Meta( $session, $session->{webName}, $session->{topicName} );
617
618 # expand dirt
619 while ( $$text =~
620s/<dirtyarea([^>]*?)>(?!.*<dirtyarea)(.*?)<\/dirtyarea>/$this->_handleDirtyArea($1, $2, $topicObj)/ges
621 )
622 {
623 $found = 1;
624 }
625
626 $$text =~ s/([\t ]?)[ \t]*<\/?(nop|noautolink)\/?>/$1/gis if $found;
627
628 # remove any dirtyarea leftovers
629 $$text =~ s/<\/?dirtyarea>//g;
630
631 $session->leaveContext('dirtyarea');
632}
633
634# called by renderDirtyAreas() to process each dirty area in isolation
635sub _handleDirtyArea {
636 my ( $this, $args, $text, $topicObj ) = @_;
637
638 Foswiki::Func::writeDebug("called _handleDirtyArea($args)")
639 if TRACE;
640
641 #Foswiki::Func::writeDebug("in text=$text") if TRACE;
642
643 # add dirtyarea params
644 my $params = new Foswiki::Attrs($args);
645 my $session = $Foswiki::Plugins::SESSION;
646 my $prefs = $session->{prefs};
647
648 $prefs->pushTopicContext( $topicObj->web, $topicObj->topic );
649 $params->remove('_RAW');
650 $prefs->setSessionPreferences(%$params);
651 try {
652 $text = $topicObj->expandMacros($text);
653 $text = $topicObj->renderTML($text);
654 };
655 finally {
656 $prefs->popTopicContext();
657 };
658
659 #Foswiki::Func::writeDebug("out text='$text'") if TRACE;
660 return $text;
661}
662
663=begin TML
664
665---++ ObjectMethod finish()
666
667clean up finally
668
669=cut
670
671sub finish {
672}
673
67412µs1;
675__END__