Filename | /var/www/foswikidev/core/lib/Foswiki/Contrib/MailerContrib/WebNotify.pm |
Statements | Executed 3402 statements in 7.57ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
2 | 1 | 1 | 2.71ms | 74.2ms | _load | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 1.02ms | 1.10ms | BEGIN@22 | Foswiki::Contrib::MailerContrib::WebNotify::
62 | 1 | 1 | 990µs | 4.82ms | subscribe | Foswiki::Contrib::MailerContrib::WebNotify::
180 | 1 | 1 | 958µs | 2.15ms | _subscribeTopic | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 891µs | 1.06ms | BEGIN@23 | Foswiki::Contrib::MailerContrib::WebNotify::
62 | 1 | 1 | 574µs | 9.27ms | parsePageSubscriptions | Foswiki::Contrib::MailerContrib::WebNotify::
64 | 2 | 2 | 550µs | 763µs | getSubscriber | Foswiki::Contrib::MailerContrib::WebNotify::
2 | 1 | 1 | 55µs | 74.5ms | new | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 16µs | 30µs | BEGIN@15 | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 10µs | 14µs | BEGIN@16 | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 10µs | 42µs | BEGIN@25 | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 10µs | 36µs | BEGIN@18 | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 4µs | 4µs | BEGIN@20 | Foswiki::Contrib::MailerContrib::WebNotify::
1 | 1 | 1 | 3µs | 3µs | BEGIN@21 | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | _emailWarn | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | _optimise | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | getSubscribers | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | isEmpty | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | processChange | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | processCompulsory | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | stringify | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | unsubscribe | Foswiki::Contrib::MailerContrib::WebNotify::
0 | 0 | 0 | 0s | 0s | writeWebNotify | Foswiki::Contrib::MailerContrib::WebNotify::
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::WebNotify | ||||
6 | Object that represents the contents of a %NOTIFYTOPIC% topic in a Foswiki web. | ||||
7 | |||||
8 | Note that =$Foswiki::Plugins::SESSION= is used to find the Foswiki session, and | ||||
9 | must be set up before this class is used. | ||||
10 | |||||
11 | =cut | ||||
12 | |||||
13 | package Foswiki::Contrib::MailerContrib::WebNotify; | ||||
14 | |||||
15 | 2 | 29µs | 2 | 43µs | # spent 30µs (16+13) within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@15 which was called:
# once (16µs+13µs) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 15 # spent 30µs making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@15
# spent 13µs making 1 call to strict::import |
16 | 2 | 25µs | 2 | 18µs | # spent 14µs (10+4) within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@16 which was called:
# once (10µs+4µs) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 16 # spent 14µs making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@16
# spent 4µs making 1 call to warnings::import |
17 | |||||
18 | 2 | 28µs | 2 | 61µs | # spent 36µs (10+26) within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@18 which was called:
# once (10µs+26µs) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 18 # spent 36µs making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@18
# spent 26µs making 1 call to Exporter::import |
19 | |||||
20 | 2 | 20µs | 1 | 4µs | # spent 4µs within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@20 which was called:
# once (4µs+0s) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 20 # spent 4µs making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@20 |
21 | 2 | 19µs | 1 | 3µs | # spent 3µs within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@21 which was called:
# once (3µs+0s) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 21 # spent 3µs making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@21 |
22 | 2 | 104µs | 1 | 1.10ms | # spent 1.10ms (1.02+89µs) within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@22 which was called:
# once (1.02ms+89µs) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 22 # spent 1.10ms making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@22 |
23 | 2 | 108µs | 1 | 1.06ms | # spent 1.06ms (891µs+164µs) within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@23 which was called:
# once (891µs+164µs) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 23 # spent 1.06ms making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@23 |
24 | |||||
25 | 2 | 1.92ms | 2 | 73µs | # spent 42µs (10+31) within Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@25 which was called:
# once (10µs+31µs) by Foswiki::Contrib::MailerContrib::BEGIN@27 at line 25 # spent 42µs making 1 call to Foswiki::Contrib::MailerContrib::WebNotify::BEGIN@25
# spent 31µs making 1 call to constant::import |
26 | |||||
27 | =begin TML | ||||
28 | |||||
29 | ---++ ClassMethod new($web, $topic) | ||||
30 | * =$web= - web name | ||||
31 | * =$topic= - topic name | ||||
32 | * =$noexpandgroups= - True will prevent expansion of group subscriptions | ||||
33 | (False is best for checking subscriptions, but True is best for | ||||
34 | writing results back to $topic) | ||||
35 | |||||
36 | Create a new object by parsing the content of the given topic in the | ||||
37 | given web. This is the normal way to load a %NOTIFYTOPIC% topic. If the | ||||
38 | topic does not exist, it will create an empty object. | ||||
39 | |||||
40 | =cut | ||||
41 | |||||
42 | # spent 74.5ms (55µs+74.5) within Foswiki::Contrib::MailerContrib::WebNotify::new which was called 2 times, avg 37.3ms/call:
# 2 times (55µs+74.5ms) by Foswiki::Contrib::MailerContrib::_isSubscribedToTopic at line 159 of /var/www/foswikidev/core/lib/Foswiki/Contrib/MailerContrib.pm, avg 37.3ms/call | ||||
43 | 2 | 4µs | my ( $class, $web, $topic, $noexpandgroups ) = @_; | ||
44 | |||||
45 | 2 | 9µs | my $this = bless( {}, $class ); | ||
46 | |||||
47 | # Ensure the contrib is initialised | ||||
48 | 2 | 5µs | 2 | 10µs | Foswiki::Contrib::MailerContrib::initContrib(); # spent 10µs making 2 calls to Foswiki::Contrib::MailerContrib::initContrib, avg 5µs/call |
49 | |||||
50 | 2 | 2µs | $this->{web} = $web; | ||
51 | 2 | 1µs | $this->{topic} = $topic || $Foswiki::cfg{NotifyTopicName} || 'WebNotify'; | ||
52 | 2 | 2µs | $this->{pretext} = ''; | ||
53 | 2 | 1µs | $this->{posttext} = ''; | ||
54 | 2 | 1µs | $this->{noexpandgroups} = $noexpandgroups; | ||
55 | |||||
56 | 2 | 16µs | 4 | 74.5ms | if ( Foswiki::Func::topicExists( $web, $topic ) ) { # spent 74.2ms making 2 calls to Foswiki::Contrib::MailerContrib::WebNotify::_load, avg 37.1ms/call
# spent 234µs making 2 calls to Foswiki::Func::topicExists, avg 117µs/call |
57 | $this->_load(); | ||||
58 | } | ||||
59 | |||||
60 | 2 | 8µs | return $this; | ||
61 | } | ||||
62 | |||||
63 | =begin TML | ||||
64 | |||||
65 | ---++ ObjectMethod writeWebNotify() | ||||
66 | Write the object to the %NOTIFYTOPIC% topic it was read from. | ||||
67 | If there is a problem writing the topic (e.g. it is locked), or the | ||||
68 | current user is not authorised to write it, then the method will | ||||
69 | throw an exception. | ||||
70 | |||||
71 | =cut | ||||
72 | |||||
73 | sub writeWebNotify { | ||||
74 | my $this = shift; | ||||
75 | |||||
76 | # First remove overlaps | ||||
77 | $this->_optimise(); | ||||
78 | |||||
79 | # Then chuck it out | ||||
80 | Foswiki::Func::saveTopic( $this->{web}, $this->{topic}, undef, | ||||
81 | $this->stringify(), { minor => 1 } ); | ||||
82 | } | ||||
83 | |||||
84 | =begin TML | ||||
85 | |||||
86 | ---++ ObjectMethod getSubscriber($name, $noAdd) | ||||
87 | * =$name= - Name of subscriber (wikiname with no web or email address) | ||||
88 | * =$noAdd= - If false or undef, a new subscriber will be created for this name | ||||
89 | Get a subscriber from the list of subscribers, and return a reference | ||||
90 | to the Subscriber object. If $noAdd is true, and the subscriber is not | ||||
91 | found, undef will be returned. Otherwise a new Subscriber object will | ||||
92 | be added if necessary. | ||||
93 | |||||
94 | =cut | ||||
95 | |||||
96 | # spent 763µs (550+212) within Foswiki::Contrib::MailerContrib::WebNotify::getSubscriber which was called 64 times, avg 12µs/call:
# 62 times (534µs+205µs) by Foswiki::Contrib::MailerContrib::WebNotify::subscribe at line 157, avg 12µs/call
# 2 times (16µs+7µs) by Foswiki::Contrib::MailerContrib::_isSubscribedToTopic at line 164 of /var/www/foswikidev/core/lib/Foswiki/Contrib/MailerContrib.pm, avg 12µs/call | ||||
97 | 64 | 36µs | my ( $this, $name, $noAdd ) = @_; | ||
98 | |||||
99 | 64 | 44µs | my $subscriber = $this->{subscribers}{$name}; | ||
100 | 64 | 22µs | unless ( $noAdd || defined($subscriber) ) { | ||
101 | 56 | 187µs | 56 | 212µs | $subscriber = new Foswiki::Contrib::MailerContrib::Subscriber($name); # spent 212µs making 56 calls to Foswiki::Contrib::MailerContrib::Subscriber::new, avg 4µs/call |
102 | 56 | 110µs | $this->{subscribers}{$name} = $subscriber; | ||
103 | } | ||||
104 | 64 | 133µs | return $subscriber; | ||
105 | } | ||||
106 | |||||
107 | # Optimise the webnotify to remove overlaps in subscription lists | ||||
108 | sub _optimise { | ||||
109 | my $this = shift; | ||||
110 | foreach my $subscriber ( values %{ $this->{subscribers} } ) { | ||||
111 | $subscriber->optimise(); | ||||
112 | } | ||||
113 | } | ||||
114 | |||||
115 | =begin TML | ||||
116 | |||||
117 | ---++ ObjectMethod getSubscribers() | ||||
118 | Get a list of all subscriber names (unsorted) | ||||
119 | |||||
120 | =cut | ||||
121 | |||||
122 | sub getSubscribers { | ||||
123 | my ($this) = @_; | ||||
124 | |||||
125 | return keys %{ $this->{subscribers} }; | ||||
126 | } | ||||
127 | |||||
128 | =begin TML | ||||
129 | |||||
130 | ---++ ObjectMethod subscribe($name, $topics, $depth, $options) | ||||
131 | * =$name= - Name of subscriber (wikiname with no web or email address) | ||||
132 | * =$topics= - wildcard expression giving topics to subscribe to | ||||
133 | * =$depth= - Child depth to scan (default 0) | ||||
134 | * =$options= - Bitmap of Mailer::Const options | ||||
135 | Add a subscription, adding the subscriber if necessary. | ||||
136 | |||||
137 | =cut | ||||
138 | |||||
139 | # spent 4.82ms (990µs+3.83) within Foswiki::Contrib::MailerContrib::WebNotify::subscribe which was called 62 times, avg 78µs/call:
# 62 times (990µs+3.83ms) by Foswiki::Contrib::MailerContrib::WebNotify::parsePageSubscriptions at line 486, avg 78µs/call | ||||
140 | 62 | 38µs | my ( $this, $name, $topics, $depth, $opts ) = @_; | ||
141 | |||||
142 | 62 | 12µs | $opts ||= 0; | ||
143 | |||||
144 | 62 | 41µs | my @names = ($name); | ||
145 | 62 | 27µs | unless ( $this->{noexpandgroups} ) { | ||
146 | 62 | 70µs | 62 | 1.35ms | my $it = Foswiki::Func::eachGroupMember($name); # spent 1.35ms making 62 calls to Foswiki::Func::eachGroupMember, avg 22µs/call |
147 | 62 | 20µs | if ($it) { | ||
148 | @names = (); | ||||
149 | while ( $it->hasNext() ) { | ||||
150 | my $member = $it->next(); | ||||
151 | push( @names, $member ); | ||||
152 | } | ||||
153 | } | ||||
154 | } | ||||
155 | |||||
156 | 62 | 162µs | foreach my $n (@names) { | ||
157 | 62 | 88µs | 62 | 739µs | my $subscriber = $this->getSubscriber($n); # spent 739µs making 62 calls to Foswiki::Contrib::MailerContrib::WebNotify::getSubscriber, avg 12µs/call |
158 | 62 | 174µs | 62 | 1.58ms | my $sub = # spent 1.58ms making 62 calls to Foswiki::Contrib::MailerContrib::Subscription::new, avg 26µs/call |
159 | new Foswiki::Contrib::MailerContrib::Subscription( $topics, $depth, | ||||
160 | $opts ); | ||||
161 | 62 | 147µs | 62 | 157µs | $subscriber->subscribe($sub); # spent 157µs making 62 calls to Foswiki::Contrib::MailerContrib::Subscriber::subscribe, avg 3µs/call |
162 | } | ||||
163 | } | ||||
164 | |||||
165 | =begin TML | ||||
166 | |||||
167 | ---++ ObjectMethod unsubscribe($name, $topics, $depth) | ||||
168 | * =$name= - Name of subscriber (wikiname with no web or email address) | ||||
169 | * =$topics= - wildcard expression giving topics to subscribe to | ||||
170 | * =$depth= - Child depth to scan (default 0) | ||||
171 | Add an unsubscription, adding the subscriber if necessary. An unsubscription | ||||
172 | is a specific request to ignore notifications for a topic for this | ||||
173 | particular subscriber. | ||||
174 | |||||
175 | =cut | ||||
176 | |||||
177 | sub unsubscribe { | ||||
178 | my ( $this, $name, $topics, $depth ) = @_; | ||||
179 | |||||
180 | my @names = ($name); | ||||
181 | unless ( $this->{noexpandgroups} ) { | ||||
182 | my $it = Foswiki::Func::eachGroupMember($name); | ||||
183 | if ($it) { | ||||
184 | @names = (); | ||||
185 | while ( $it->hasNext() ) { | ||||
186 | my $member = $it->next(); | ||||
187 | push( @names, $member ); | ||||
188 | } | ||||
189 | } | ||||
190 | } | ||||
191 | |||||
192 | foreach my $n (@names) { | ||||
193 | my $subscriber = $this->getSubscriber($n); | ||||
194 | my $sub = | ||||
195 | new Foswiki::Contrib::MailerContrib::Subscription( $topics, $depth, | ||||
196 | 0 ); | ||||
197 | $subscriber->unsubscribe($sub); | ||||
198 | } | ||||
199 | } | ||||
200 | |||||
201 | =begin TML | ||||
202 | |||||
203 | ---++ ObjectMethod stringify([$subscribersOnly]) -> string | ||||
204 | Return a string representation of this object, in %NOTIFYTOPIC% format. | ||||
205 | |||||
206 | Optional =$subscribersOnly= parameter to only print the parsed subscription list. | ||||
207 | Used when running a mailnotify, where printing out the entire WebNotify topic is confusing, | ||||
208 | as it's different from the actual topic contents, but doesn't inform the user why. | ||||
209 | |||||
210 | =cut | ||||
211 | |||||
212 | sub stringify { | ||||
213 | my ( $this, $subscribersOnly ) = @_; | ||||
214 | |||||
215 | my $page = ''; | ||||
216 | |||||
217 | $page .= $this->{pretext} if ( !$subscribersOnly ); | ||||
218 | $page .= "---\n" if TRACE && $page !~ /---\n*$/; | ||||
219 | foreach my $name ( sort keys %{ $this->{subscribers} } ) { | ||||
220 | my $subscriber = $this->{subscribers}{$name}; | ||||
221 | $page .= $subscriber->stringify() . "\n"; | ||||
222 | } | ||||
223 | $page .= "---\n" if TRACE && $this->{posttext} !~ /^\n*---/; | ||||
224 | $page .= $this->{posttext} if ( !$subscribersOnly ); | ||||
225 | |||||
226 | return $page; | ||||
227 | } | ||||
228 | |||||
229 | =begin TML | ||||
230 | |||||
231 | ---++ ObjectMethod processChange($change, $db, $changeSet, $seenSet, $allSet) | ||||
232 | * =$change= - ref of a Foswiki::Contrib::Mailer::Change | ||||
233 | * =$db= - Foswiki::Contrib::MailerContrib::UpData database of parent references | ||||
234 | * =$changeSet= - ref of a hash mapping name&emails to sets of changes | ||||
235 | * =$seenSet= - ref of a hash recording indices of topics already seen for | ||||
236 | each name&email addressee | ||||
237 | * =$allSet= - ref of a hash that maps topics to name&email ids for news subscriptions | ||||
238 | Find all subscribers that are interested in the given change. Only the most | ||||
239 | recent change to each topic listed in the .changes file is retained. This | ||||
240 | method does _not_ change this object. | ||||
241 | |||||
242 | =cut | ||||
243 | |||||
244 | sub processChange { | ||||
245 | my ( $this, $change, $db, $changeSet, $seenSet, $allSet ) = @_; | ||||
246 | |||||
247 | my $topic = $change->{TOPIC}; | ||||
248 | my $web = $change->{WEB}; | ||||
249 | my %authors = map { $_ => 1 } @{ | ||||
250 | Foswiki::Contrib::MailerContrib::Subscriber::getEmailAddressesForUser( | ||||
251 | $change->{author} | ||||
252 | ) | ||||
253 | }; | ||||
254 | print STDERR "Process change to $web.$topic\n" if TRACE; | ||||
255 | |||||
256 | foreach my $name ( keys %{ $this->{subscribers} } ) { | ||||
257 | my $subscriber = $this->{subscribers}{$name}; | ||||
258 | my $subs = $subscriber->isSubscribedTo( $topic, $db ); | ||||
259 | if ( $subs && !$subscriber->isUnsubscribedFrom( $topic, $db ) ) { | ||||
260 | |||||
261 | # Check access to the old rev | ||||
262 | my $cuid = Foswiki::Func::getCanonicalUserID($name); | ||||
263 | |||||
264 | # Check access is allowed to the most recent revision | ||||
265 | my $to = | ||||
266 | Foswiki::Meta->load( $Foswiki::Plugins::SESSION, $web, $topic, | ||||
267 | $change->{CURR_REV} ); | ||||
268 | unless ( $to->haveAccess( 'VIEW', $cuid ) ) { | ||||
269 | print STDERR | ||||
270 | "$name has no permission to view r$change->{CURR_REV} of $web.$topic\n" | ||||
271 | if TRACE; | ||||
272 | next; | ||||
273 | } | ||||
274 | |||||
275 | $to = | ||||
276 | Foswiki::Meta->load( $Foswiki::Plugins::SESSION, $web, $topic, | ||||
277 | $change->{BASE_REV} ); | ||||
278 | unless ( $to->haveAccess( 'VIEW', $cuid ) ) { | ||||
279 | print STDERR | ||||
280 | "$name has no permission to view r$change->{BASE_REV} of $web.$topic\n" | ||||
281 | if TRACE; | ||||
282 | next; | ||||
283 | } | ||||
284 | |||||
285 | print "$name will be notified of changes to $topic\n" if TRACE; | ||||
286 | my $emails = $subscriber->getEmailAddresses(); | ||||
287 | if ( $emails && scalar(@$emails) ) { | ||||
288 | foreach my $email (@$emails) { | ||||
289 | my $id = "$name&$email"; | ||||
290 | |||||
291 | # Skip this change if the subscriber is the author | ||||
292 | # of the change, and we are not always sending | ||||
293 | next | ||||
294 | if ( | ||||
295 | !( | ||||
296 | $subs->{options} & | ||||
297 | Foswiki::Contrib::MailerContrib::Subscription::ALWAYS | ||||
298 | ) | ||||
299 | && $authors{$email} | ||||
300 | ); | ||||
301 | print STDERR "\tusing email $email for $name\n" if TRACE; | ||||
302 | if ( $subs->{options} & | ||||
303 | Foswiki::Contrib::MailerContrib::Subscription::FULL_TOPIC | ||||
304 | ) | ||||
305 | { | ||||
306 | |||||
307 | #print "Add $id to allSet for $topic\n"; | ||||
308 | push( @{ $allSet->{$topic} }, $id ); | ||||
309 | } | ||||
310 | else { | ||||
311 | my $at = $seenSet->{$id}{$topic}; | ||||
312 | if ($at) { | ||||
313 | |||||
314 | #print "Merge $id to changeset for $topic\n"; | ||||
315 | $changeSet->{$id}[ $at - 1 ]->merge($change); | ||||
316 | } | ||||
317 | else { | ||||
318 | |||||
319 | #print "Add $id to changeset for $topic\n"; | ||||
320 | $seenSet->{$id}{$topic} = | ||||
321 | push( @{ $changeSet->{$id} }, $change ); | ||||
322 | } | ||||
323 | } | ||||
324 | } | ||||
325 | } | ||||
326 | else { | ||||
327 | $this->_emailWarn( $subscriber, $name, $web ); | ||||
328 | } | ||||
329 | } | ||||
330 | } | ||||
331 | } | ||||
332 | |||||
333 | =begin TML | ||||
334 | |||||
335 | ---++ ObjectMethod processCompulsory($topic, $db, \%allSet) | ||||
336 | * =$topic= - topic name | ||||
337 | * =$db= - Foswiki::Contrib::MailerContrib::UpData database | ||||
338 | of parent references | ||||
339 | * =\%allSet= - ref of a hash that maps topics to name&email addresses | ||||
340 | for news subscriptions | ||||
341 | |||||
342 | =cut | ||||
343 | |||||
344 | sub processCompulsory { | ||||
345 | my ( $this, $topic, $db, $allSet ) = @_; | ||||
346 | |||||
347 | foreach my $name ( keys %{ $this->{subscribers} } ) { | ||||
348 | my $subscriber = $this->{subscribers}{$name}; | ||||
349 | my $subs = $subscriber->isSubscribedTo( $topic, $db ); | ||||
350 | next unless $subs; | ||||
351 | next | ||||
352 | unless ( $subs->{options} & | ||||
353 | Foswiki::Contrib::MailerContrib::Subscription::ALWAYS ); | ||||
354 | unless ( $subscriber->isUnsubscribedFrom( $topic, $db ) ) { | ||||
355 | my $emails = $subscriber->getEmailAddresses(); | ||||
356 | if ($emails) { | ||||
357 | foreach my $address (@$emails) { | ||||
358 | push( @{ $allSet->{$topic} }, "$name&$address" ); | ||||
359 | } | ||||
360 | } | ||||
361 | } | ||||
362 | } | ||||
363 | } | ||||
364 | |||||
365 | =begin TML | ||||
366 | |||||
367 | ---++ ObjectMethod isEmpty() -> boolean | ||||
368 | Return true if there are no subscribers | ||||
369 | |||||
370 | =cut | ||||
371 | |||||
372 | sub isEmpty { | ||||
373 | my $this = shift; | ||||
374 | return ( scalar( keys %{ $this->{subscribers} } ) == 0 ); | ||||
375 | } | ||||
376 | |||||
377 | # PRIVATE parse a webnotify topic extracting formatted lines | ||||
378 | # spent 74.2ms (2.71+71.5) within Foswiki::Contrib::MailerContrib::WebNotify::_load which was called 2 times, avg 37.1ms/call:
# 2 times (2.71ms+71.5ms) by Foswiki::Contrib::MailerContrib::WebNotify::new at line 56, avg 37.1ms/call | ||||
379 | 2 | 1µs | my $this = shift; | ||
380 | |||||
381 | 2 | 10µs | 2 | 1.22ms | my ( $meta, $text ) = # spent 1.22ms making 2 calls to Foswiki::Func::readTopic, avg 610µs/call |
382 | Foswiki::Func::readTopic( $this->{web}, $this->{topic} ); | ||||
383 | 2 | 500ns | my $in_pre = 1; | ||
384 | 2 | 1µs | $this->{pretext} = ''; | ||
385 | 2 | 800ns | $this->{posttext} = ''; | ||
386 | 2 | 1µs | $this->{meta} = $meta; | ||
387 | |||||
388 | # join \ terminated lines | ||||
389 | 2 | 18µs | $text =~ s/\\\r?\n//gs; | ||
390 | 2 | 16µs | my $webRE = qr/(?:$Foswiki::cfg{UsersWebName}\.)?/; | ||
391 | 2 | 76µs | my $legacyRE = qr{ | ||
392 | ^\s+\*\s$webRE | ||||
393 | ($Foswiki::regex{wikiWordRegex}) | ||||
394 | \s+-\s+ | ||||
395 | ($Foswiki::cfg{MailerContrib}{EmailFilterIn}+) | ||||
396 | \s*$ | ||||
397 | }x; | ||||
398 | 2 | 69µs | my $stdRE = qr{^\s+\*\s$webRE | ||
399 | ( | ||||
400 | $Foswiki::regex{wikiWordRegex} | ||||
401 | | '.*?' | ||||
402 | | ".*?" | ||||
403 | | $Foswiki::cfg{MailerContrib}{EmailFilterIn} | ||||
404 | ) | ||||
405 | \s*(:.*)?$ | ||||
406 | }x; | ||||
407 | 2 | 72µs | foreach my $baseline ( split( /\r?\n/, $text ) ) { | ||
408 | 88 | 169µs | 88 | 60.3ms | my $line = # spent 60.3ms making 88 calls to Foswiki::Func::expandCommonVariables, avg 685µs/call |
409 | Foswiki::Func::expandCommonVariables( $baseline, $this->{topic}, | ||||
410 | $this->{web}, $meta ); | ||||
411 | 88 | 1.53ms | 8 | 541µs | if ( $line =~ /$legacyRE/ # spent 541µs making 8 calls to utf8::SWASHNEW, avg 68µs/call |
412 | && $1 ne $Foswiki::cfg{DefaultUserWikiName} ) | ||||
413 | { | ||||
414 | |||||
415 | # Main.WikiName - email@domain (legacy format) | ||||
416 | $this->subscribe( $2, '*', 0, 0 ); | ||||
417 | $in_pre = 0; | ||||
418 | } | ||||
419 | elsif ($line =~ /$stdRE/ | ||||
420 | && $1 ne $Foswiki::cfg{DefaultUserWikiName} ) | ||||
421 | { | ||||
422 | 62 | 32µs | my $subscriber = $1; | ||
423 | |||||
424 | # Get the topic list from the last bracket matched. Have to do it | ||||
425 | # this awkward way because the email filter may contain braces | ||||
426 | 62 | 30µs | my $topics = $+; | ||
427 | |||||
428 | # email addresses can't start with : | ||||
429 | 62 | 106µs | $topics = undef unless ( $topics =~ s/^:// ); | ||
430 | 62 | 71µs | $subscriber =~ s/^(['"])(.*)\1$/$2/; # remove quotes | ||
431 | |||||
432 | # CDot: I don't understand how this can ever be tainted, but the | ||||
433 | # unit tests fail without this untaint. The subscriber is | ||||
434 | # validated, and should be untainted, by the conditional regex. | ||||
435 | 62 | 97µs | 62 | 200µs | $subscriber = Foswiki::Sandbox::untaintUnchecked($subscriber); # spent 200µs making 62 calls to Foswiki::Sandbox::untaintUnchecked, avg 3µs/call |
436 | |||||
437 | 62 | 91µs | 62 | 9.27ms | if ( defined $topics ) { # spent 9.27ms making 62 calls to Foswiki::Contrib::MailerContrib::WebNotify::parsePageSubscriptions, avg 150µs/call |
438 | $this->parsePageSubscriptions( $subscriber, $topics ); | ||||
439 | } | ||||
440 | else { | ||||
441 | $this->subscribe( $subscriber, '*', 0, 0 ); | ||||
442 | } | ||||
443 | 62 | 36µs | $in_pre = 0; | ||
444 | } | ||||
445 | else { | ||||
446 | 26 | 18µs | if ($in_pre) { | ||
447 | $this->{pretext} .= "$baseline\n"; | ||||
448 | } | ||||
449 | else { | ||||
450 | 22 | 37µs | $this->{posttext} .= "$baseline\n"; | ||
451 | } | ||||
452 | } | ||||
453 | } | ||||
454 | 2 | 10µs | print STDERR "LOADED " . $this->stringify() if TRACE; | ||
455 | } | ||||
456 | |||||
457 | =begin TML | ||||
458 | |||||
459 | ---++ ObjectMethod parsePageSubscriptions($who, $spec, $unsubscribe) | ||||
460 | |||||
461 | Parse a pages list for a single user (corresponding to one line in | ||||
462 | WebNotify), adding subscriptions for user $who as appropriate | ||||
463 | If $unsubscribe is set to '-' by SubscribePlugin to force a '-' operation | ||||
464 | |||||
465 | =cut | ||||
466 | |||||
467 | # If we see a simple topic specification with no unusual options, and | ||||
468 | # at childDepth 0, we can collect it into a group subscription for | ||||
469 | # efficiency. | ||||
470 | 1 | 300ns | our @simple_subscriptions; | ||
471 | |||||
472 | # spent 9.27ms (574µs+8.70) within Foswiki::Contrib::MailerContrib::WebNotify::parsePageSubscriptions which was called 62 times, avg 150µs/call:
# 62 times (574µs+8.70ms) by Foswiki::Contrib::MailerContrib::WebNotify::_load at line 437, avg 150µs/call | ||||
473 | 62 | 36µs | my ( $this, $who, $spec, $unsubscribe ) = @_; | ||
474 | |||||
475 | 62 | 51µs | $this->{topicSub} = \&_subscribeTopic; | ||
476 | |||||
477 | 62 | 38µs | local @simple_subscriptions = (); | ||
478 | 62 | 77µs | 62 | 0s | my $ret = # spent 3.88ms making 62 calls to Foswiki::Contrib::MailerContrib::parsePageList, avg 63µs/call, recursion: max depth 1, sum of overlapping time 3.88ms |
479 | Foswiki::Contrib::MailerContrib::parsePageList( $this, $who, $spec, | ||||
480 | $unsubscribe ); | ||||
481 | 62 | 14µs | if ( $ret =~ m/\S/ ) { | ||
482 | Foswiki::Func::writeWarning("Badly formatted page list at $who: $spec"); | ||||
483 | return -1; | ||||
484 | } | ||||
485 | |||||
486 | 62 | 149µs | 62 | 4.82ms | if ( scalar(@simple_subscriptions) ) { # spent 4.82ms making 62 calls to Foswiki::Contrib::MailerContrib::WebNotify::subscribe, avg 78µs/call |
487 | |||||
488 | $this->subscribe( $who, join( ' ', @simple_subscriptions ), 0, 0 ); | ||||
489 | } | ||||
490 | 62 | 142µs | return; | ||
491 | } | ||||
492 | |||||
493 | # helper for parsePageSubscriptions | ||||
494 | # spent 2.15ms (958µs+1.19) within Foswiki::Contrib::MailerContrib::WebNotify::_subscribeTopic which was called 180 times, avg 12µs/call:
# 180 times (958µs+1.19ms) by Foswiki::Contrib::MailerContrib::parsePageList at line 212 of /var/www/foswikidev/core/lib/Foswiki/Contrib/MailerContrib.pm, avg 12µs/call | ||||
495 | 180 | 113µs | my ( $this, $who, $unsubscribe, $webTopic, $options, $childDepth ) = @_; | ||
496 | |||||
497 | 180 | 239µs | 180 | 1.19ms | my ( $web, $topic ) = # spent 1.19ms making 180 calls to Foswiki::Func::normalizeWebTopicName, avg 7µs/call |
498 | Foswiki::Func::normalizeWebTopicName( $this->{web}, $webTopic ); | ||||
499 | |||||
500 | 180 | 31µs | my $opts = 0; | ||
501 | 180 | 29µs | my $kids = $childDepth || 0; | ||
502 | 180 | 23µs | if ($options) { | ||
503 | $opts |= Foswiki::Contrib::MailerContrib::Subscription::FULL_TOPIC; | ||||
504 | if ( $options =~ /!/ ) { | ||||
505 | $opts |= Foswiki::Contrib::MailerContrib::Subscription::ALWAYS; | ||||
506 | } | ||||
507 | } | ||||
508 | 180 | 159µs | if ( $unsubscribe && $unsubscribe eq '-' ) { | ||
509 | $this->unsubscribe( $who, $topic, $kids ); | ||||
510 | } | ||||
511 | elsif ( !$opts && !$childDepth ) { | ||||
512 | |||||
513 | push( @simple_subscriptions, $topic ); | ||||
514 | } | ||||
515 | else { | ||||
516 | |||||
517 | # Otherwise we have to add a separate subscription record | ||||
518 | $this->subscribe( $who, $topic, $kids, $opts ); | ||||
519 | } | ||||
520 | |||||
521 | #TODO: howto find & report errors? | ||||
522 | 180 | 351µs | return ''; | ||
523 | } | ||||
524 | |||||
525 | # PRIVATE emailWarn to warn when an email address cannot be found | ||||
526 | # for a subscriber. | ||||
527 | sub _emailWarn { | ||||
528 | my ( $this, $subscriber, $name, $web ) = @_; | ||||
529 | |||||
530 | # Make sure we only warn once. Don't want to see this for every | ||||
531 | # Topic we are notifying on. | ||||
532 | unless ( defined $this->{nomail}{$name} ) { | ||||
533 | $this->{nomail}{$name} = 1; | ||||
534 | Foswiki::Func::writeWarning( "Failed to find permitted email for '" | ||||
535 | . $subscriber->stringify() | ||||
536 | . "' when processing web '$web'" ); | ||||
537 | } | ||||
538 | } | ||||
539 | |||||
540 | 1 | 2µs | 1; | ||
541 | __END__ |