Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Contrib/MailerContrib.pm |
Statements | Executed 54071 statements in 508ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1070 | 2 | 2 | 333ms | 175s | parsePageList (recurses: max depth 1, inclusive time 171s) | Foswiki::Contrib::MailerContrib::
26472 | 4 | 1 | 143ms | 143ms | CORE:subst (opcode) | Foswiki::Contrib::MailerContrib::
2 | 1 | 1 | 30.9ms | 175s | _isSubscribedToTopic | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 3.21ms | 7.09ms | BEGIN@25 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 3.14ms | 6.10ms | BEGIN@18 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 2.30ms | 2.46ms | BEGIN@26 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 424µs | 498µs | BEGIN@27 | Foswiki::Contrib::MailerContrib::
2 | 1 | 1 | 95µs | 175s | isSubscribedTo | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 35µs | 44µs | BEGIN@15 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 26µs | 344µs | BEGIN@19 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 21µs | 44µs | BEGIN@16 | Foswiki::Contrib::MailerContrib::
2 | 1 | 1 | 19µs | 19µs | initContrib | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 10µs | 10µs | BEGIN@21 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 9µs | 9µs | BEGIN@23 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 9µs | 9µs | BEGIN@22 | Foswiki::Contrib::MailerContrib::
1 | 1 | 1 | 9µs | 9µs | BEGIN@24 | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | _generateChangeDetail | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | _processSubscriptions | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | _processWeb | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | _sendChangesMails | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | _sendNewsletterMail | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | _sendNewsletterMails | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | changeSubscription | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | mailNotify | Foswiki::Contrib::MailerContrib::
0 | 0 | 0 | 0s | 0s | relativeURL | Foswiki::Contrib::MailerContrib::
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 | |||||
7 | Package of support for extended Web<nop>Notify notification, supporting per-topic notification and notification of changes to children. | ||||
8 | |||||
9 | Also supported is a simple API that can be used to change the Web<nop>Notify topic from other code. | ||||
10 | |||||
11 | =cut | ||||
12 | |||||
13 | package Foswiki::Contrib::MailerContrib; | ||||
14 | |||||
15 | 2 | 58µs | 2 | 54µ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 # spent 44µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@15
# spent 9µs making 1 call to strict::import |
16 | 2 | 53µs | 2 | 66µ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 # spent 44µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@16
# spent 22µs making 1 call to warnings::import |
17 | |||||
18 | 2 | 180µs | 1 | 6.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 # spent 6.10ms making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@18 |
19 | 2 | 57µs | 2 | 662µ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 # spent 344µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@19
# spent 318µs making 1 call to CGI::import |
20 | |||||
21 | 2 | 40µs | 1 | 10µ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 # spent 10µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@21 |
22 | 2 | 38µs | 1 | 9µ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 # spent 9µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@22 |
23 | 2 | 38µs | 1 | 9µ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 # spent 9µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@23 |
24 | 2 | 38µs | 1 | 9µ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 # spent 9µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@24 |
25 | 2 | 185µs | 1 | 7.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 # spent 7.09ms making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@25 |
26 | 2 | 176µs | 1 | 2.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 # spent 2.46ms making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@26 |
27 | 2 | 4.99ms | 1 | 498µ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 # spent 498µs making 1 call to Foswiki::Contrib::MailerContrib::BEGIN@27 |
28 | |||||
29 | 1 | 2µs | our $VERSION = '$Rev$'; | ||
30 | 1 | 2µs | our $RELEASE = '2.5.1'; | ||
31 | 1 | 1µs | our $SHORTDESCRIPTION = 'Supports email notification of changes'; | ||
32 | |||||
33 | 1 | 1µs | our $verbose = 0; | ||
34 | 1 | 1µs | our $nonews = 0; | ||
35 | 1 | 1µs | our $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 | ||||
39 | 2 | 23µ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 | |||||
52 | Main entry point. | ||||
53 | |||||
54 | Process the Web<nop>Notify topics in each web and generate and issue | ||||
55 | notification mails. Designed to be invoked from the command line; should | ||||
56 | only be called by =mailnotify= scripts. | ||||
57 | |||||
58 | =cut | ||||
59 | |||||
60 | sub 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 | |||||
103 | Modify 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 | |||||
111 | sub 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 | |||||
131 | Returns true if all topics mentioned in the =$topicList= are subscribed to by =$who=. | ||||
132 | |||||
133 | Can ignore all valid special characters that can be used on the WebNotify topic | ||||
134 | such 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 | ||||
139 | 8 | 93µs | my ( $defaultWeb, $who, $topicList ) = @_; | ||
140 | |||||
141 | my $subscribed = { | ||||
142 | currentWeb => $defaultWeb, | ||||
143 | topicSub => \&_isSubscribedToTopic | ||||
144 | }; | ||||
145 | |||||
146 | 2 | 175s | my $ret = parsePageList( $subscribed, $who, $topicList ); # spent 175s making 2 calls to Foswiki::Contrib::MailerContrib::parsePageList, avg 87.7s/call | ||
147 | |||||
148 | 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 | ||||
153 | 14 | 30.9ms | my ( $subscribed, $who, $unsubscribe, $topic, $options, $childDepth ) = @_; | ||
154 | |||||
155 | require Foswiki::Contrib::MailerContrib::WebNotify; | ||||
156 | 2 | 156µ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.. | ||||
160 | 2 | 175s | 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} ); | ||||
163 | 2 | 21µs | my $subscriber = $wn->getSubscriber($who); # spent 21µs making 2 calls to Foswiki::Contrib::MailerContrib::WebNotify::getSubscriber, avg 10µs/call | ||
164 | |||||
165 | 2 | 54µ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) | ||||
168 | 2 | 8µs | 2 | 21.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 { | ||||
174 | 2 | 12µs | push( @{ $subscribed->{not_subscribed} }, $stopic ); | ||
175 | } | ||||
176 | } | ||||
177 | |||||
178 | =begin TML | ||||
179 | |||||
180 | ---++ parsePageList ( $object, $who, $spec, $unsubscribe ) -> unprocessable remainder of =$spec= line | ||||
181 | Calls =$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 | ||||
191 | 5350 | 32.1ms | my ( $object, $who, $spec, $unsubscribe ) = @_; | ||
192 | |||||
193 | #ASSERT(defined($object->{topicSub})); | ||||
194 | |||||
195 | return $spec if ( !$object || !defined( $object->{topicSub} ) ); | ||||
196 | |||||
197 | 1070 | 2.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 | ||||
204 | 1070 | 5.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 | { | ||||
207 | 36498 | 413ms | my ( $us, $webTopic, $options, $childDepth ) = | ||
208 | ( $unsubscribe || $1 || '+', $2, $3, $4 || 0 ); | ||||
209 | 12166 | 27.7ms | $webTopic =~ s/^(['"])(.*)\1$/$2/; # remove quotes # spent 27.7ms making 12166 calls to Foswiki::Contrib::MailerContrib::CORE:subst, avg 2µs/call | ||
210 | 12166 | 26.2ms | 24332 | 346s | &{ $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 | } | ||||
215 | return $spec; | ||||
216 | } | ||||
217 | |||||
218 | # PRIVATE: Read the webnotify, and notify changes | ||||
219 | sub _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 | ||||
251 | sub _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 | ||||
345 | sub _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 | |||||
401 | sub _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 | |||||
448 | sub relativeURL { | ||||
449 | my ( $base, $link ) = @_; | ||||
450 | return URI->new_abs( $link, URI->new($base) )->as_string; | ||||
451 | } | ||||
452 | |||||
453 | sub _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 | |||||
463 | sub _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 | |||||
600 | 1 | 8µs | 1; | ||
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 |