← Index
NYTProf Performance Profile   « block view • line view • sub view »
For /usr/local/src/github.com/foswiki/core/bin/view
  Run on Sun Dec 4 17:17:59 2011
Reported on Sun Dec 4 17:27:22 2011

Filename/usr/local/src/github.com/foswiki/core/lib/Foswiki/Contrib/MailerContrib.pm
StatementsExecuted 54071 statements in 508ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
107022333ms175sFoswiki::Contrib::MailerContrib::::parsePageListFoswiki::Contrib::MailerContrib::parsePageList (recurses: max depth 1, inclusive time 171s)
2647241143ms143msFoswiki::Contrib::MailerContrib::::CORE:substFoswiki::Contrib::MailerContrib::CORE:subst (opcode)
21130.9ms175sFoswiki::Contrib::MailerContrib::::_isSubscribedToTopicFoswiki::Contrib::MailerContrib::_isSubscribedToTopic
1113.21ms7.09msFoswiki::Contrib::MailerContrib::::BEGIN@25Foswiki::Contrib::MailerContrib::BEGIN@25
1113.14ms6.10msFoswiki::Contrib::MailerContrib::::BEGIN@18Foswiki::Contrib::MailerContrib::BEGIN@18
1112.30ms2.46msFoswiki::Contrib::MailerContrib::::BEGIN@26Foswiki::Contrib::MailerContrib::BEGIN@26
111424µs498µsFoswiki::Contrib::MailerContrib::::BEGIN@27Foswiki::Contrib::MailerContrib::BEGIN@27
21195µs175sFoswiki::Contrib::MailerContrib::::isSubscribedToFoswiki::Contrib::MailerContrib::isSubscribedTo
11135µs44µsFoswiki::Contrib::MailerContrib::::BEGIN@15Foswiki::Contrib::MailerContrib::BEGIN@15
11126µs344µsFoswiki::Contrib::MailerContrib::::BEGIN@19Foswiki::Contrib::MailerContrib::BEGIN@19
11121µs44µsFoswiki::Contrib::MailerContrib::::BEGIN@16Foswiki::Contrib::MailerContrib::BEGIN@16
21119µs19µsFoswiki::Contrib::MailerContrib::::initContribFoswiki::Contrib::MailerContrib::initContrib
11110µs10µsFoswiki::Contrib::MailerContrib::::BEGIN@21Foswiki::Contrib::MailerContrib::BEGIN@21
1119µs9µsFoswiki::Contrib::MailerContrib::::BEGIN@23Foswiki::Contrib::MailerContrib::BEGIN@23
1119µs9µsFoswiki::Contrib::MailerContrib::::BEGIN@22Foswiki::Contrib::MailerContrib::BEGIN@22
1119µs9µsFoswiki::Contrib::MailerContrib::::BEGIN@24Foswiki::Contrib::MailerContrib::BEGIN@24
0000s0sFoswiki::Contrib::MailerContrib::::_generateChangeDetailFoswiki::Contrib::MailerContrib::_generateChangeDetail
0000s0sFoswiki::Contrib::MailerContrib::::_processSubscriptionsFoswiki::Contrib::MailerContrib::_processSubscriptions
0000s0sFoswiki::Contrib::MailerContrib::::_processWebFoswiki::Contrib::MailerContrib::_processWeb
0000s0sFoswiki::Contrib::MailerContrib::::_sendChangesMailsFoswiki::Contrib::MailerContrib::_sendChangesMails
0000s0sFoswiki::Contrib::MailerContrib::::_sendNewsletterMailFoswiki::Contrib::MailerContrib::_sendNewsletterMail
0000s0sFoswiki::Contrib::MailerContrib::::_sendNewsletterMailsFoswiki::Contrib::MailerContrib::_sendNewsletterMails
0000s0sFoswiki::Contrib::MailerContrib::::changeSubscriptionFoswiki::Contrib::MailerContrib::changeSubscription
0000s0sFoswiki::Contrib::MailerContrib::::mailNotifyFoswiki::Contrib::MailerContrib::mailNotify
0000s0sFoswiki::Contrib::MailerContrib::::relativeURLFoswiki::Contrib::MailerContrib::relativeURL
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::Contrib::MailerContrib
6
7Package of support for extended Web<nop>Notify notification, supporting per-topic notification and notification of changes to children.
8
9Also supported is a simple API that can be used to change the Web<nop>Notify topic from other code.
10
11=cut
12
13package Foswiki::Contrib::MailerContrib;
14
15258µs254µs
# spent 44µs (35+9) within Foswiki::Contrib::MailerContrib::BEGIN@15 which was called: # once (35µs+9µs) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 15
use strict;
# spent 44µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@15 # spent 9µs making 1 call to strict::import
16253µs266µs
# spent 44µs (21+23) within Foswiki::Contrib::MailerContrib::BEGIN@16 which was called: # once (21µs+23µs) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 16
use warnings;
# spent 44µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@16 # spent 22µs making 1 call to warnings::import
17
182180µs16.10ms
# spent 6.10ms (3.14+2.96) within Foswiki::Contrib::MailerContrib::BEGIN@18 which was called: # once (3.14ms+2.96ms) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 18
use URI ();
# spent 6.10ms making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@18
19257µs2662µs
# spent 344µs (26+318) within Foswiki::Contrib::MailerContrib::BEGIN@19 which was called: # once (26µs+318µs) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 19
use CGI qw(-any);
# spent 344µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@19 # spent 318µs making 1 call to CGI::import
20
21240µs110µs
# spent 10µs within Foswiki::Contrib::MailerContrib::BEGIN@21 which was called: # once (10µs+0s) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 21
use Foswiki ();
# spent 10µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@21
22238µs19µs
# spent 9µs within Foswiki::Contrib::MailerContrib::BEGIN@22 which was called: # once (9µs+0s) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 22
use Foswiki::Plugins ();
# spent 9µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@22
23238µs19µs
# spent 9µs within Foswiki::Contrib::MailerContrib::BEGIN@23 which was called: # once (9µs+0s) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 23
use Foswiki::Time ();
# spent 9µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@23
24238µs19µs
# spent 9µs within Foswiki::Contrib::MailerContrib::BEGIN@24 which was called: # once (9µs+0s) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 24
use Foswiki::Func ();
# spent 9µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@24
252185µs17.09ms
# spent 7.09ms (3.21+3.88) within Foswiki::Contrib::MailerContrib::BEGIN@25 which was called: # once (3.21ms+3.88ms) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 25
use Foswiki::Contrib::MailerContrib::WebNotify ();
# spent 7.09ms making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@25
262176µs12.46ms
# spent 2.46ms (2.30+163µs) within Foswiki::Contrib::MailerContrib::BEGIN@26 which was called: # once (2.30ms+163µs) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 26
use Foswiki::Contrib::MailerContrib::Change ();
# spent 2.46ms making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@26
2724.99ms1498µs
# spent 498µs (424+74) within Foswiki::Contrib::MailerContrib::BEGIN@27 which was called: # once (424µs+74µs) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 27
use Foswiki::Contrib::MailerContrib::UpData ();
# spent 498µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@27
28
2912µsour $VERSION = '$Rev$';
3012µsour $RELEASE = '2.5.1';
3111µsour $SHORTDESCRIPTION = 'Supports email notification of changes';
32
3311µsour $verbose = 0;
3411µsour $nonews = 0;
3511µsour $nochanges = 0;
36
37# PROTECTED STATIC ensure the contrib is internally initialised
38
# spent 19µs within Foswiki::Contrib::MailerContrib::initContrib which was called 2 times, avg 9µs/call: # 2 times (19µs+0s) by Foswiki::Contrib::MailerContrib::WebNotify::new at line 47 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Contrib/MailerContrib/WebNotify.pm, avg 9µs/call
sub initContrib {
39223µs $Foswiki::cfg{MailerContrib}{EmailFilterIn} ||=
40 $Foswiki::regex{emailAddrRegex};
41}
42
43=begin TML
44
45---++ StaticMethod mailNotify($webs, $verbose, $exwebs, $nonewsmode, $nochangesmode)
46 * =$webs= - filter list of names webs to process. Wildcards (*) may be used.
47 * =$verbose= - true to get verbose (debug) output.
48 * =$exwebs= - filter list of webs to exclude.
49 * =$nonewsmode= - the notify script was called with the =-nonews= option so we skip news mode
50 * =$nochangesmode= - the notify script was called with the =-nochanges= option
51
52Main entry point.
53
54Process the Web<nop>Notify topics in each web and generate and issue
55notification mails. Designed to be invoked from the command line; should
56only be called by =mailnotify= scripts.
57
58=cut
59
60sub mailNotify {
61 my ( $webs, $noisy, $exwebs, $nonewsmode, $nochangesmode ) = @_;
62
63 $verbose = $noisy;
64 $nonews = $nonewsmode || 0;
65 $nochanges = $nochangesmode || 0;
66
67 my $webstr;
68 if ( defined($webs) ) {
69 $webstr = join( '|', @$webs );
70 }
71 $webstr = '*' unless ($webstr);
72 $webstr =~ s/\*/\.\*/g;
73
74 my $exwebstr = '';
75 if ( defined($exwebs) ) {
76 $exwebstr = join( '|', @$exwebs );
77 }
78 $exwebstr =~ s/\*/\.\*/g;
79
80 my $context = Foswiki::Func::getContext();
81
82 $context->{command_line} = 1;
83
84 # absolute URL context for email generation
85 $context->{absolute_urls} = 1;
86
87 initContrib();
88
89 my $report = '';
90 foreach my $web ( Foswiki::Func::getListOfWebs('user ') ) {
91 if ( $web =~ /^($webstr)$/ && $web !~ /^($exwebstr)$/ ) {
92 _processWeb($web);
93 }
94 }
95
96 $context->{absolute_urls} = 0;
97}
98
99=begin TML
100
101---++ StaticMethod changeSubscription($web, $who, $topicList, $unsubscribe)
102
103Modify a user's subscription in =WebNotify= for a web.
104 * =$web= - web to edit the WebNotify for
105 * =$who= - the user's wikiname
106 * =$topicList= - list of topics to (un)subscribe to(from)
107 * =$unsubscribe= - false to subscribe, true to unsubscribe
108
109=cut
110
111sub changeSubscription {
112 my ( $defaultWeb, $who, $topicList, $unsubscribe ) = @_;
113
114#we can get away with a normalise on a list of topics, so long as the list starts with a topic
115 my ( $web, $t ) =
116 Foswiki::Func::normalizeWebTopicName( $defaultWeb, $topicList );
117
118 #TODO: this limits us to subscribing to one web.
119 my $wn =
120 Foswiki::Contrib::MailerContrib::WebNotify->new( $web,
121 $Foswiki::cfg{NotifyTopicName}, 1 );
122 $wn->parsePageSubscriptions( $who, $topicList, $unsubscribe );
123 $wn->writeWebNotify();
124 return;
125}
126
127=begin TML
128
129---++ isSubscribedTo ($web, $who, $topicList) -> boolean
130
131Returns true if all topics mentioned in the =$topicList= are subscribed to by =$who=.
132
133Can ignore all valid special characters that can be used on the WebNotify topic
134such as NewsTopic! , TopicAndChildren (2)
135
136=cut
137
138
# spent 175s (95µs+175) within Foswiki::Contrib::MailerContrib::isSubscribedTo which was called 2 times, avg 87.7s/call: # 2 times (95µs+175s) by Foswiki::Plugins::SubscribePlugin::_SUBSCRIBE at line 71 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Plugins/SubscribePlugin.pm, avg 87.7s/call
sub isSubscribedTo {
139212µs my ( $defaultWeb, $who, $topicList ) = @_;
140
141212µs my $subscribed = {
142 currentWeb => $defaultWeb,
143 topicSub => \&_isSubscribedToTopic
144 };
145
146222µs2175s my $ret = parsePageList( $subscribed, $who, $topicList );
# spent 175s making 2 calls to Foswiki::Contrib::MailerContrib::parsePageList, avg 87.7s/call
147
148247µs return ( !defined( $subscribed->{not_subscribed} )
149 || ( 0 == scalar( $subscribed->{not_subscribed} ) ) );
150}
151
152
# spent 175s (30.9ms+175) within Foswiki::Contrib::MailerContrib::_isSubscribedToTopic which was called 2 times, avg 87.7s/call: # 2 times (30.9ms+175s) by Foswiki::Contrib::MailerContrib::parsePageList at line 210, avg 87.7s/call
sub _isSubscribedToTopic {
15327µs my ( $subscribed, $who, $unsubscribe, $topic, $options, $childDepth ) = @_;
154
15524µs require Foswiki::Contrib::MailerContrib::WebNotify;
156225µs2156µs my ( $sweb, $stopic ) =
# spent 156µs making 2 calls to Foswiki::Func::normalizeWebTopicName, avg 78µs/call
157 Foswiki::Func::normalizeWebTopicName( $subscribed->{currentWeb}, $topic );
158
159 #TODO: extract this code so we only create $wn objects for each web once..
160241µs2175s my $wn =
# spent 175s making 2 calls to Foswiki::Contrib::MailerContrib::WebNotify::new, avg 87.7s/call
161 Foswiki::Contrib::MailerContrib::WebNotify->new( $sweb,
162 $Foswiki::cfg{NotifyTopicName} );
163213µs221µs my $subscriber = $wn->getSubscriber($who);
# spent 21µs making 2 calls to Foswiki::Contrib::MailerContrib::WebNotify::getSubscriber, avg 10µs/call
164
165232µs254µs my $db = Foswiki::Contrib::MailerContrib::UpData->new($sweb);
# spent 54µs making 2 calls to Foswiki::Contrib::MailerContrib::UpData::new, avg 27µs/call
166
167 #TODO: need to check $childDepth topics too (somehow)
168230.8ms221.5ms if ( $subscriber->isSubscribedTo( $stopic, $db )
# spent 21.5ms making 2 calls to Foswiki::Contrib::MailerContrib::Subscriber::isSubscribedTo, avg 10.8ms/call
169 && ( !$subscriber->isUnsubscribedFrom( $stopic, $db ) ) )
170 {
171 push( @{ $subscribed->{subscribed} }, $stopic );
172 }
173 else {
174419µs push( @{ $subscribed->{not_subscribed} }, $stopic );
175 }
176}
177
178=begin TML
179
180---++ parsePageList ( $object, $who, $spec, $unsubscribe ) -> unprocessable remainder of =$spec= line
181Calls =$object->{topicSub}= once per identified topic entry.
182 * =$object= (a hashref) may be a hashref that has the field =topicSub=,
183 which _may_ be a sub ref as follows:
184 =&topicSub($object, $who, $unsubscribe, $webTopic, $options, $childDepth)=
185 * =$unsubscribe= can be set to '-' to force an unsubscription
186 (used by SubscribePlugin)
187
188=cut
189
190
# spent 175s (333ms+175) within Foswiki::Contrib::MailerContrib::parsePageList which was called 1070 times, avg 164ms/call: # 1068 times (333ms+-333ms) by Foswiki::Contrib::MailerContrib::WebNotify::parsePageSubscriptions at line 417 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Contrib/MailerContrib/WebNotify.pm, avg 0s/call # 2 times (214µs+175s) by Foswiki::Contrib::MailerContrib::isSubscribedTo at line 146, avg 87.7s/call
sub parsePageList {
19110702.62ms my ( $object, $who, $spec, $unsubscribe ) = @_;
192
193 #ASSERT(defined($object->{topicSub}));
194
19510701.95ms return $spec if ( !$object || !defined( $object->{topicSub} ) );
196
19710707.72ms10702.34ms $spec =~ s/,/ /g;
# spent 2.34ms making 1070 calls to Foswiki::Contrib::MailerContrib::CORE:subst, avg 2µs/call
198
199 # $1: + or -, optional
200 # $2: the wildcarded topic specifier (may be quoted)
201 # TODO: refine the $2 regex to be proper web.topic/topic/* style..
202 # $3: options
203 # $4: child depth
204107012.1ms10705.57ms while ( $spec =~
# spent 5.57ms making 1070 calls to Foswiki::Contrib::MailerContrib::CORE:subst, avg 5µs/call
205 s/^\s*([+-])?\s*([*\w.]+|'.*?'|".*?")([!?]?)\s*(?:\((\d+)\))?// )
206 {
2071216659.4ms my ( $us, $webTopic, $options, $childDepth ) =
208 ( $unsubscribe || $1 || '+', $2, $3, $4 || 0 );
2091216683.5ms1216627.7ms $webTopic =~ s/^(['"])(.*)\1$/$2/; # remove quotes
# spent 27.7ms making 12166 calls to Foswiki::Contrib::MailerContrib::CORE:subst, avg 2µs/call
21024332296ms24332346s &{ $object->{topicSub} }( $object, $who, $us, $webTopic, $options,
# spent 175s making 2 calls to Foswiki::Contrib::MailerContrib::_isSubscribedToTopic, avg 87.7s/call # spent 170s making 12164 calls to Foswiki::Contrib::MailerContrib::WebNotify::_subscribeTopic, avg 14.0ms/call # spent 107ms making 12166 calls to Foswiki::Contrib::MailerContrib::CORE:subst, avg 9µs/call
211 $childDepth );
212
213 #go
214 }
21510707.64ms return $spec;
216}
217
218# PRIVATE: Read the webnotify, and notify changes
219sub _processWeb {
220 my ( $web, $nonews, $nochanges ) = @_;
221
222 if ( !Foswiki::Func::webExists($web) ) {
223
224 # print STDERR "**** ERROR mailnotifier cannot find web $web\n";
225 return '';
226 }
227
228 print "Processing $web\n" if $verbose;
229
230 my $report = '';
231
232 # Read the webnotify and load subscriptions
233 my $wn =
234 Foswiki::Contrib::MailerContrib::WebNotify->new( $web,
235 $Foswiki::cfg{NotifyTopicName} );
236 if ( $wn->isEmpty() ) {
237 print "\t$web has no subscribers\n" if $verbose;
238 }
239 else {
240
241 # create a DB object for parent pointers
242 print $wn->stringify(1) if $verbose;
243 my $db = Foswiki::Contrib::MailerContrib::UpData->new($web);
244 $report .= _processSubscriptions( $web, $wn, $db );
245 }
246
247 return $report;
248}
249
250# Process subscriptions in $notify
251sub _processSubscriptions {
252 my ( $web, $notify, $db ) = @_;
253
254 my $metadir = Foswiki::Func::getWorkArea('MailerContrib');
255 my $notmeta = $web;
256 $notmeta =~ s#/#.#g;
257 $notmeta = "$metadir/$notmeta";
258
259 my $timeOfLastNotify = 0;
260 if ( open( F, '<', $notmeta ) ) {
261 local $/ = undef;
262 $timeOfLastNotify = <F>;
263 close(F);
264 }
265
266 if ($verbose) {
267 print "\tLast notification was at "
268 . Foswiki::Time::formatTime( $timeOfLastNotify, 'iso' ) . "\n";
269 }
270
271 my $timeOfLastChange = 0;
272
273 # Hash indexed on email address, each entry contains a hash
274 # of topics already processed in the change set for this email.
275 # Each subhash maps the topic name to the index of the change
276 # record for this topic in the array of Change objects for this
277 # email in %changeset.
278 my %seenset;
279
280 # Hash indexed on email address, each entry contains an array
281 # indexed by the index stored in %seenSet. Each entry in the array
282 # is a ref to a Change object.
283 my %changeset;
284
285 # Hash indexed on topic name, mapping to email address, used to
286 # record simple newsletter subscriptions.
287 my %allSet;
288
289 # + 1 because the 'since' check is >=
290 my $it = Foswiki::Func::eachChangeSince( $web, $timeOfLastNotify + 1 );
291 while ( $it->hasNext() ) {
292 my $change = $it->next();
293 next if $change->{more} && $change->{more} =~ /minor/;
294
295 next unless Foswiki::Func::topicExists( $web, $change->{topic} );
296
297 $timeOfLastChange = $change->{time} unless ($timeOfLastChange);
298
299 print "\tChange to $change->{topic} at "
300 . Foswiki::Time::formatTime( $change->{time}, 'iso' )
301 . ". New revision is $change->{revision}\n"
302 if ($verbose);
303
304 # Formulate a change record, irrespective of
305 # whether any subscriber is interested
306 $change =
307 Foswiki::Contrib::MailerContrib::Change->new( $web, $change->{topic},
308 $change->{user}, $change->{time}, $change->{revision} );
309
310 # Now, find subscribers to this change and extend the change set
311 $notify->processChange( $change, $db, \%changeset, \%seenset,
312 \%allSet );
313 }
314
315 # For each topic, see if there's a compulsory subscription independent
316 # of the time since last notify
317 foreach my $topic ( Foswiki::Func::getTopicList($web) ) {
318 $notify->processCompulsory( $topic, $db, \%allSet );
319 }
320
321 # Now generate emails for each recipient
322 my $report = '';
323
324 if ( !$nochanges ) {
325 $report .=
326 _sendChangesMails( $web, \%changeset,
327 Foswiki::Time::formatTime($timeOfLastNotify) );
328 }
329
330 if ( !$nonews ) {
331 $report .= _sendNewsletterMails( $web, \%allSet );
332 }
333
334 if ( $timeOfLastChange != 0 ) {
335 if ( open( F, '>', $notmeta ) ) {
336 print F $timeOfLastChange;
337 close(F);
338 }
339 }
340
341 return $report;
342}
343
344# PRIVATE generate and send an email for each user
345sub _sendChangesMails {
346 my ( $web, $changeset, $lastTime ) = @_;
347 my $report = '';
348
349 # We read the mailnotify template in the context (skin and web) or the
350 # WebNotify topic we are currently processing
351 Foswiki::Func::pushTopicContext( $web, $Foswiki::cfg{NotifyTopicName} );
352 my $skin = Foswiki::Func::getSkin();
353 my $template = Foswiki::Func::readTemplate( 'mailnotify', $skin );
354 Foswiki::Func::popTopicContext();
355
356 my $mailtmpl = Foswiki::Func::expandTemplate('MailNotifyBody');
357 $mailtmpl =
358 Foswiki::Func::expandCommonVariables( $mailtmpl,
359 $Foswiki::cfg{HomeTopicName}, $web );
360 if ( $Foswiki::cfg{RemoveImgInMailnotify} ) {
361
362 # change images to [alt] text if there, else remove image
363 $mailtmpl =~ s/<img\s[^>]*\balt=\"([^\"]+)[^>]*>/[$1]/goi;
364 $mailtmpl =~ s/<img src=.*?[^>]>//goi;
365 }
366
367 my $sentMails = 0;
368
369 foreach my $email ( keys %{$changeset} ) {
370
371 my $mail = $mailtmpl;
372
373 $mail =~ s/%EMAILTO%/$email/g;
374 $mail =~ s/%(HTML|PLAIN|DIFF)_TEXT%/
375 _generateChangeDetail($email, $changeset, $1, $web)/ge;
376 $mail =~ s/%LASTDATE%/$lastTime/ge;
377
378 my $base = $Foswiki::cfg{DefaultUrlHost} . $Foswiki::cfg{ScriptUrlPath};
379 $mail =~ s/(href=\")([^"]+)/$1.relativeURL($base,$2)/goei;
380 $mail =~ s/(action=\")([^"]+)/$1.relativeURL($base,$2)/goei;
381
382 # remove <nop> and <noautolink> tags
383 $mail =~ s/( ?) *<\/?(nop|noautolink)\/?>\n?/$1/gois;
384
385 my $error = Foswiki::Func::sendEmail( $mail, 5 );
386
387 if ($error) {
388 print STDERR "Error sending mail for $web: $error\n";
389 $report .= $error . "\n";
390 }
391 else {
392 print "Notified $email of changes in $web\n" if $verbose;
393 $sentMails++;
394 }
395 }
396 $report .= "\t$sentMails change notifications from $web\n";
397
398 return $report;
399}
400
401sub _generateChangeDetail {
402 my ( $email, $changeset, $style, $web ) = @_;
403
404 my @wns = Foswiki::Func::emailToWikiNames($email);
405 my @ep = ( $Foswiki::cfg{HomeTopicName}, $web );
406
407 # If there is only one user with this email, we can load preferences
408 # for them by expanding preferences in the context of their home
409 # topic.
410 if ( scalar(@wns) == 1
411 && Foswiki::Func::topicExists( $Foswiki::cfg{UsersWebName}, $wns[0] )
412 && defined &Foswiki::Meta::load )
413 {
414 my ( $ww, $wt ) =
415 Foswiki::Func::normalizeWebTopicName( undef, $wns[0] );
416 my $userTopic =
417 Foswiki::Meta->load( $Foswiki::Plugins::SESSION, $ww, $wt );
418 my $uStyle = $userTopic->getPreference('PREFERRED_MAIL_CHANGE_FORMAT');
419 $style = $uStyle if $uStyle && $uStyle =~ /^(HTML|PLAIN|DIFF)$/;
420 }
421
422 my $template = Foswiki::Func::expandTemplate( $style . ':middle' );
423 my $text = '';
424 foreach my $change ( sort { $a->{TIME} cmp $b->{TIME} }
425 @{ $changeset->{$email} } )
426 {
427 if ( $style eq 'HTML' ) {
428 $text .= Foswiki::Func::expandCommonVariables(
429 $change->expandHTML($template), @ep );
430 }
431 elsif ( $style eq 'PLAIN' ) {
432 $text .= Foswiki::Func::expandCommonVariables(
433 $change->expandPlain($template), @ep );
434 }
435 elsif ( $style eq 'DIFF' ) {
436
437 # Note: no macro expansion; this is a verbatim format
438 $text .= $change->expandDiff($template);
439 }
440 }
441 return Foswiki::Func::expandCommonVariables(
442 Foswiki::Func::expandTemplate( $style . ':before' ), @ep )
443 . $text
444 . Foswiki::Func::expandCommonVariables(
445 Foswiki::Func::expandTemplate( $style . ':after' ), @ep );
446}
447
448sub relativeURL {
449 my ( $base, $link ) = @_;
450 return URI->new_abs( $link, URI->new($base) )->as_string;
451}
452
453sub _sendNewsletterMails {
454 my ( $web, $allSet ) = @_;
455
456 my $report = '';
457 foreach my $topic ( keys %$allSet ) {
458 $report .= _sendNewsletterMail( $web, $topic, $allSet->{$topic} );
459 }
460 return $report;
461}
462
463sub _sendNewsletterMail {
464 my ( $web, $topic, $emails ) = @_;
465 my $wikiName = Foswiki::Func::getWikiName();
466
467 # SMELL: this code is almost identical to PublishContrib
468
469 # Read topic data.
470 my ( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic );
471
472 # SMELL: Have to hack into the core to set internal preferences :-(
473 my %old =
474 map { $_ => undef } qw(BASEWEB BASETOPIC INCLUDINGWEB INCLUDINGTOPIC);
475 if ( defined $Foswiki::Plugins::SESSION->{SESSION_TAGS} ) {
476
477 # In 1.0.6 and earlier, have to handle some session tags ourselves
478 # because pushTopicContext doesn't do it. **
479 foreach my $macro ( keys %old ) {
480 $old{$macro} = Foswiki::Func::getPreferencesValue($macro);
481 }
482 }
483 Foswiki::Func::pushTopicContext( $web, $topic );
484
485 # See ** above
486 if ( defined $Foswiki::Plugins::SESSION->{SESSION_TAGS} ) {
487 my $stags = $Foswiki::Plugins::SESSION->{SESSION_TAGS};
488 $stags->{BASEWEB} = $web;
489 $stags->{BASETOPIC} = $topic;
490 $stags->{INCLUDINGWEB} = $web;
491 $stags->{INCLUDINGTOPIC} = $topic;
492 }
493
494 # Only required pre-1.1
495 Foswiki::Func::getContext()->{can_render_meta} = $meta;
496
497 # Get the skin for this topic
498 my $skin = Foswiki::Func::getSkin();
499 Foswiki::Func::readTemplate( 'newsletter', $skin );
500 my $header = Foswiki::Func::expandTemplate('NEWS:header');
501 my $body = Foswiki::Func::expandTemplate('NEWS:body');
502 my $footer = Foswiki::Func::expandTemplate('NEWS:footer');
503
504 my ( $revdate, $revuser, $maxrev );
505 ( $revdate, $revuser, $maxrev ) = $meta->getRevisionInfo();
506
507 # Handle standard formatting.
508 $body =~ s/%TEXT%/$text/g;
509
510 # Don't render the header, it is preformatted
511 $header = Foswiki::Func::expandCommonVariables( $header, $topic, $web );
512 my $tmpl = "$body\n$footer";
513 $tmpl = Foswiki::Func::expandCommonVariables( $tmpl, $topic, $web );
514 $tmpl = Foswiki::Func::renderText( $tmpl, "", $meta );
515
516 # REFACTOR OPPORTUNITY: stop factor me into getTWikiRendering()
517 # SMELL: this code is identical to PublishContrib!
518
519 # New tags
520 my $newTmpl = '';
521 my $tagSeen = 0;
522 my $publish = 1;
523 foreach my $s ( split( /(%STARTPUBLISH%|%STOPPUBLISH%)/, $tmpl ) ) {
524 if ( $s eq '%STARTPUBLISH%' ) {
525 $publish = 1;
526 $newTmpl = '' unless ($tagSeen);
527 $tagSeen = 1;
528 }
529 elsif ( $s eq '%STOPPUBLISH%' ) {
530 $publish = 0;
531 $tagSeen = 1;
532 }
533 elsif ($publish) {
534 $newTmpl .= $s;
535 }
536 }
537 $tmpl = $header . $newTmpl;
538 $tmpl =~ s/.*?<\/nopublish>//gs;
539 $tmpl =~ s/%MAXREV%/$maxrev/g;
540 $tmpl =~ s/%CURRREV%/$maxrev/g;
541 $tmpl =~ s/%REVTITLE%//g;
542 $tmpl =~ s|( ?) *</*nop/*>\n?|$1|gois;
543
544 # Remove <base.../> tag
545 $tmpl =~ s/<base[^>]+\/>//;
546
547 # Remove <base...>...</base> tag
548 $tmpl =~ s/<base[^>]+>.*?<\/base>//;
549
550 # Rewrite absolute URLs
551 my $base = $Foswiki::cfg{DefaultUrlHost} . $Foswiki::cfg{ScriptUrlPath};
552 $tmpl =~ s/(href=\")([^"]+)/$1.relativeURL($base,$2)/goei;
553 $tmpl =~ s/(action=\")([^"]+)/$1.relativeURL($base,$2)/goei;
554
555 my $report = '';
556 my $sentMails = 0;
557
558 my %targets = map { $_ => 1 } @$emails;
559
560 foreach my $email ( keys %targets ) {
561 my $mail = $tmpl;
562
563 $mail =~ s/%EMAILTO%/$email/go;
564
565 my $base = $Foswiki::cfg{DefaultUrlHost} . $Foswiki::cfg{ScriptUrlPath};
566 $mail =~ s/(href=\")([^"]+)/$1.relativeURL($base,$2)/goei;
567 $mail =~ s/(action=\")([^"]+)/$1.relativeURL($base,$2)/goei;
568
569 # remove <nop> and <noautolink> tags
570 $mail =~ s/( ?) *<\/?(nop|noautolink)\/?>\n?/$1/gois;
571
572 my $error = Foswiki::Func::sendEmail( $mail, 5 );
573
574 if ($error) {
575 print STDERR "Error sending mail for $web: $error\n";
576 $report .= $error . "\n";
577 }
578 else {
579 print "Sent newletter for $web to $email\n" if $verbose;
580 $sentMails++;
581 }
582 }
583 $report .= "\t$sentMails newsletters from $web\n";
584
585 Foswiki::Func::popTopicContext();
586
587 # SMELL: See ** above
588 if ( defined $Foswiki::Plugins::SESSION->{SESSION_TAGS} ) {
589
590 # In 1.0.6 and earlier, have to handle some session tags ourselves
591 # because pushTopicContext doesn't do it. **
592 foreach my $macro ( keys %old ) {
593 $Foswiki::Plugins::SESSION->{SESSION_TAGS}{$macro} = $old{$macro};
594 }
595 }
596
597 return $report;
598}
599
60018µs1;
601__END__
 
# spent 143ms within Foswiki::Contrib::MailerContrib::CORE:subst which was called 26472 times, avg 5µs/call: # 12166 times (107ms+0s) by Foswiki::Contrib::MailerContrib::parsePageList at line 210, avg 9µs/call # 12166 times (27.7ms+0s) by Foswiki::Contrib::MailerContrib::parsePageList at line 209, avg 2µs/call # 1070 times (5.57ms+0s) by Foswiki::Contrib::MailerContrib::parsePageList at line 204, avg 5µs/call # 1070 times (2.34ms+0s) by Foswiki::Contrib::MailerContrib::parsePageList at line 197, avg 2µs/call
sub Foswiki::Contrib::MailerContrib::CORE:subst; # opcode