Completed
Push — fix-2196 ( c55b8d )
by
unknown
03:25
created

Subscription::send_diff()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 51
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 37
nc 4
nop 6
dl 0
loc 51
rs 8.8981
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Class for handling (email) subscriptions
4
 *
5
 * @author  Adrian Lang <[email protected]>
6
 * @author  Andreas Gohr <[email protected]>
7
 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
8
 */
9
class Subscription {
10
11
    /**
12
     * Check if subscription system is enabled
13
     *
14
     * @return bool
15
     */
16
    public function isenabled() {
17
        return actionOK('subscribe');
18
    }
19
20
    /**
21
     * Return the subscription meta file for the given ID
22
     *
23
     * @author Adrian Lang <[email protected]>
24
     *
25
     * @param string $id The target page or namespace, specified by id; Namespaces
26
     *                   are identified by appending a colon.
27
     * @return string
28
     */
29
    protected function file($id) {
30
        $meta_fname = '.mlist';
31
        if((substr($id, -1, 1) === ':')) {
32
            $meta_froot = getNS($id);
33
            $meta_fname = '/'.$meta_fname;
34
        } else {
35
            $meta_froot = $id;
36
        }
37
        return metaFN((string) $meta_froot, $meta_fname);
38
    }
39
40
    /**
41
     * Lock subscription info
42
     *
43
     * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
44
     *
45
     * @author Adrian Lang <[email protected]>
46
     * @param string $id The target page or namespace, specified by id; Namespaces
47
     *                   are identified by appending a colon.
48
     * @return bool true, if you got a succesful lock
49
     */
50
    protected function lock($id) {
51
        global $conf;
52
53
        $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
54
55
        if(is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
56
            // looks like a stale lock - remove it
57
            @rmdir($lock);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
58
        }
59
60
        // try creating the lock directory
61
        if(!@mkdir($lock, $conf['dmode'])) {
62
            return false;
63
        }
64
65
        if(!empty($conf['dperm'])) chmod($lock, $conf['dperm']);
66
        return true;
67
    }
68
69
    /**
70
     * Unlock subscription info
71
     *
72
     * @author Adrian Lang <[email protected]>
73
     * @param string $id The target page or namespace, specified by id; Namespaces
74
     *                   are identified by appending a colon.
75
     * @return bool
76
     */
77
    protected function unlock($id) {
78
        global $conf;
79
        $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
80
        return @rmdir($lock);
81
    }
82
83
    /**
84
     * Construct a regular expression for parsing a subscription definition line
85
     *
86
     * @author Andreas Gohr <[email protected]>
87
     *
88
     * @param string|array $user
89
     * @param string|array $style
90
     * @param string|array $data
91
     * @return string complete regexp including delimiters
92
     * @throws Exception when no data is passed
93
     */
94
    protected function buildregex($user = null, $style = null, $data = null) {
95
        // always work with arrays
96
        $user = (array) $user;
97
        $style = (array) $style;
98
        $data = (array) $data;
99
100
        // clean
101
        $user = array_filter(array_map('trim', $user));
102
        $style = array_filter(array_map('trim', $style));
103
        $data = array_filter(array_map('trim', $data));
104
105
        // user names are encoded
106
        $user = array_map('auth_nameencode', $user);
107
108
        // quote
109
        $user = array_map('preg_quote_cb', $user);
110
        $style = array_map('preg_quote_cb', $style);
111
        $data = array_map('preg_quote_cb', $data);
112
113
        // join
114
        $user = join('|', $user);
115
        $style = join('|', $style);
116
        $data = join('|', $data);
117
118
        // any data at all?
119
        if($user.$style.$data === '') throw new Exception('no data passed');
120
121
        // replace empty values, set which ones are optional
122
        $sopt = '';
123
        $dopt = '';
124
        if($user === '') {
125
            $user = '\S+';
126
        }
127
        if($style === '') {
128
            $style = '\S+';
129
            $sopt = '?';
130
        }
131
        if($data === '') {
132
            $data = '\S+';
133
            $dopt = '?';
134
        }
135
136
        // assemble
137
        return "/^($user)(?:\\s+($style))$sopt(?:\\s+($data))$dopt$/";
138
    }
139
140
    /**
141
     * Recursively search for matching subscriptions
142
     *
143
     * This function searches all relevant subscription files for a page or
144
     * namespace.
145
     *
146
     * @author Adrian Lang <[email protected]>
147
     *
148
     * @param string         $page The target object’s (namespace or page) id
149
     * @param string|array   $user
150
     * @param string|array   $style
151
     * @param string|array   $data
152
     * @return array
153
     */
154
    public function subscribers($page, $user = null, $style = null, $data = null) {
155
        if(!$this->isenabled()) return array();
156
157
        // Construct list of files which may contain relevant subscriptions.
158
        $files = array(':' => $this->file(':'));
159
        do {
160
            $files[$page] = $this->file($page);
161
            $page = getNS(rtrim($page, ':')).':';
162
        } while($page !== ':');
163
164
        $re = $this->buildregex($user, $style, $data);
165
166
        // Handle files.
167
        $result = array();
168
        foreach($files as $target => $file) {
169
            if(!file_exists($file)) continue;
170
171
            $lines = file($file);
172
            foreach($lines as $line) {
173
                // fix old style subscription files
174
                if(strpos($line, ' ') === false) $line = trim($line)." every\n";
175
176
                // check for matching entries
177
                if(!preg_match($re, $line, $m)) continue;
178
179
                $u = rawurldecode($m[1]); // decode the user name
180
                if(!isset($result[$target])) $result[$target] = array();
181
                $result[$target][$u] = array($m[2], $m[3]); // add to result
182
            }
183
        }
184
        return array_reverse($result);
185
    }
186
187
    /**
188
     * Adds a new subscription for the given page or namespace
189
     *
190
     * This will automatically overwrite any existent subscription for the given user on this
191
     * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
192
     *
193
     * @param string $id The target page or namespace, specified by id; Namespaces
194
     *                   are identified by appending a colon.
195
     * @param string $user
196
     * @param string $style
197
     * @param string $data
198
     * @throws Exception when user or style is empty
199
     * @return bool
200
     */
201
    public function add($id, $user, $style, $data = '') {
202
        if(!$this->isenabled()) return false;
203
204
        // delete any existing subscription
205
        $this->remove($id, $user);
206
207
        $user  = auth_nameencode(trim($user));
208
        $style = trim($style);
209
        $data  = trim($data);
210
211
        if(!$user) throw new Exception('no subscription user given');
212
        if(!$style) throw new Exception('no subscription style given');
213
        if(!$data) $data = time(); //always add current time for new subscriptions
214
215
        $line = "$user $style $data\n";
216
        $file = $this->file($id);
217
        return io_saveFile($file, $line, true);
218
    }
219
220
    /**
221
     * Removes a subscription for the given page or namespace
222
     *
223
     * This removes all subscriptions matching the given criteria on the given page or
224
     * namespace. It will *not* modify any subscriptions that may exist in higher
225
     * namespaces.
226
     *
227
     * @param string         $id   The target object’s (namespace or page) id
228
     * @param string|array   $user
229
     * @param string|array   $style
230
     * @param string|array   $data
231
     * @return bool
232
     */
233
    public function remove($id, $user = null, $style = null, $data = null) {
234
        if(!$this->isenabled()) return false;
235
236
        $file = $this->file($id);
237
        if(!file_exists($file)) return true;
238
239
        $re = $this->buildregex($user, $style, $data);
240
        return io_deleteFromFile($file, $re, true);
241
    }
242
243
    /**
244
     * Get data for $INFO['subscribed']
245
     *
246
     * $INFO['subscribed'] is either false if no subscription for the current page
247
     * and user is in effect. Else it contains an array of arrays with the fields
248
     * “target”, “style”, and optionally “data”.
249
     *
250
     * @param string $id  Page ID, defaults to global $ID
251
     * @param string $user User, defaults to $_SERVER['REMOTE_USER']
252
     * @return array
253
     * @author Adrian Lang <[email protected]>
254
     */
255
    function user_subscription($id = '', $user = '') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
256
        if(!$this->isenabled()) return false;
257
258
        global $ID;
259
        /** @var Input $INPUT */
260
        global $INPUT;
261
        if(!$id) $id = $ID;
262
        if(!$user) $user = $INPUT->server->str('REMOTE_USER');
263
264
        $subs = $this->subscribers($id, $user);
265
        if(!count($subs)) return false;
266
267
        $result = array();
268
        foreach($subs as $target => $info) {
269
            $result[] = array(
270
                'target' => $target,
271
                'style' => $info[$user][0],
272
                'data' => $info[$user][1]
273
            );
274
        }
275
276
        return $result;
277
    }
278
279
    /**
280
     * Send digest and list subscriptions
281
     *
282
     * This sends mails to all subscribers that have a subscription for namespaces above
283
     * the given page if the needed $conf['subscribe_time'] has passed already.
284
     *
285
     * This function is called form lib/exe/indexer.php
286
     *
287
     * @param string $page
288
     * @return int number of sent mails
289
     */
290
    public function send_bulk($page) {
291
        if(!$this->isenabled()) return 0;
292
293
        /** @var DokuWiki_Auth_Plugin $auth */
294
        global $auth;
295
        global $conf;
296
        global $USERINFO;
297
        /** @var Input $INPUT */
298
        global $INPUT;
299
        $count = 0;
300
301
        $subscriptions = $this->subscribers($page, null, array('digest', 'list'));
302
303
        // remember current user info
304
        $olduinfo = $USERINFO;
305
        $olduser = $INPUT->server->str('REMOTE_USER');
306
307
        foreach($subscriptions as $target => $users) {
308
            if(!$this->lock($target)) continue;
309
310
            foreach($users as $user => $info) {
311
                list($style, $lastupdate) = $info;
312
313
                $lastupdate = (int) $lastupdate;
314
                if($lastupdate + $conf['subscribe_time'] > time()) {
315
                    // Less than the configured time period passed since last
316
                    // update.
317
                    continue;
318
                }
319
320
                // Work as the user to make sure ACLs apply correctly
321
                $USERINFO = $auth->getUserData($user);
322
                $INPUT->server->set('REMOTE_USER',$user);
323
                if($USERINFO === false) continue;
324
                if(!$USERINFO['mail']) continue;
325
326
                if(substr($target, -1, 1) === ':') {
327
                    // subscription target is a namespace, get all changes within
328
                    $changes = getRecentsSince($lastupdate, null, getNS($target));
0 ignored issues
show
Security Bug introduced by
It seems like getNS($target) targeting getNS() can also be of type false; however, getRecentsSince() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
329
                } else {
330
                    // single page subscription, check ACL ourselves
331
                    if(auth_quickaclcheck($target) < AUTH_READ) continue;
332
                    $meta = p_get_metadata($target);
333
                    $changes = array($meta['last_change']);
334
                }
335
336
                // Filter out pages only changed in small and own edits
337
                $change_ids = array();
338
                foreach($changes as $rev) {
339
                    $n = 0;
340
                    while(!is_null($rev) && $rev['date'] >= $lastupdate &&
341
                        ($INPUT->server->str('REMOTE_USER') === $rev['user'] ||
342
                            $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) {
343
                        $pagelog = new PageChangeLog($rev['id']);
344
                        $rev = $pagelog->getRevisions($n++, 1);
345
                        $rev = (count($rev) > 0) ? $rev[0] : null;
346
                    }
347
348
                    if(!is_null($rev) && $rev['date'] >= $lastupdate) {
349
                        // Some change was not a minor one and not by myself
350
                        $change_ids[] = $rev['id'];
351
                    }
352
                }
353
354
                // send it
355
                if($style === 'digest') {
356
                    foreach($change_ids as $change_id) {
357
                        $this->send_digest(
358
                            $USERINFO['mail'], $change_id,
359
                            $lastupdate
360
                        );
361
                        $count++;
362
                    }
363
                } elseif($style === 'list') {
364
                    $this->send_list($USERINFO['mail'], $change_ids, $target);
365
                    $count++;
366
                }
367
                // TODO: Handle duplicate subscriptions.
368
369
                // Update notification time.
370
                $this->add($target, $user, $style, time());
371
            }
372
            $this->unlock($target);
373
        }
374
375
        // restore current user info
376
        $USERINFO = $olduinfo;
377
        $INPUT->server->set('REMOTE_USER',$olduser);
378
        return $count;
379
    }
380
381
    /**
382
     * Send the diff for some page change
383
     *
384
     * @param string   $subscriber_mail The target mail address
385
     * @param string   $template        Mail template ('subscr_digest', 'subscr_single', 'mailtext', ...)
386
     * @param string   $id              Page for which the notification is
387
     * @param int|null $rev             Old revision if any
388
     * @param string   $summary         Change summary if any
389
     * @param int|null $current_rev     New revision if any
390
     * @return bool                     true if successfully sent
391
     */
392
    public function send_diff($subscriber_mail, $template, $id, $rev = null, $summary = '', $current_rev = null) {
393
        global $DIFF_INLINESTYLES;
394
395
        // prepare replacements (keys not set in hrep will be taken from trep)
396
        $trep = array(
397
            'PAGE' => $id,
398
            'NEWPAGE' => wl($id, $current_rev?('rev='.$current_rev):'', true, '&'),
399
            'SUMMARY' => $summary,
400
            'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
401
        );
402
        $hrep = array();
403
404
        if($rev) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rev of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
405
            $subject = 'changed';
406
            $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
407
408
            $old_content = rawWiki($id, $rev);
409
            $new_content = rawWiki($id);
410
411
            $df = new Diff(explode("\n", $old_content),
412
                           explode("\n", $new_content));
413
            $dformat = new UnifiedDiffFormatter();
414
            $tdiff = $dformat->format($df);
415
416
            $DIFF_INLINESTYLES = true;
417
            $df = new Diff(explode("\n", $old_content),
418
                           explode("\n", $new_content));
419
            $dformat = new InlineDiffFormatter();
420
            $hdiff = $dformat->format($df);
421
            $hdiff = '<table>'.$hdiff.'</table>';
422
            $DIFF_INLINESTYLES = false;
423
        } else {
424
            $subject = 'newpage';
425
            $trep['OLDPAGE'] = '---';
426
            $tdiff = rawWiki($id);
427
            $hdiff = nl2br(hsc($tdiff));
428
        }
429
430
        $trep['DIFF'] = $tdiff;
431
        $hrep['DIFF'] = $hdiff;
432
433
        $headers = array('Message-Id' => $this->getMessageID($id));
434
        if ($rev) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rev of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
435
            $headers['In-Reply-To'] =  $this->getMessageID($id, $rev);
436
        }
437
438
        return $this->send(
439
            $subscriber_mail, $subject, $id,
440
            $template, $trep, $hrep, $headers
441
        );
442
    }
443
444
    /**
445
     * Send the diff for some media change
446
     *
447
     * @fixme this should embed thumbnails of images in HTML version
448
     *
449
     * @param string   $subscriber_mail The target mail address
450
     * @param string   $template        Mail template ('uploadmail', ...)
451
     * @param string   $id              Media file for which the notification is
452
     * @param int|bool $rev             Old revision if any
453
     * @param int|bool $current_rev     New revision if any
454
     */
455
    public function send_media_diff($subscriber_mail, $template, $id, $rev = false, $current_rev = false) {
456
        global $conf;
457
458
        $file = mediaFN($id);
459
        list($mime, /* $ext */) = mimetype($id);
460
461
        $trep = array(
462
            'MIME'  => $mime,
463
            'MEDIA' => ml($id,$current_rev?('rev='.$current_rev):'',true,'&',true),
464
            'SIZE'  => filesize_h(filesize($file)),
465
        );
466
467
        if ($rev && $conf['mediarevisions']) {
468
            $trep['OLD'] = ml($id, "rev=$rev", true, '&', true);
469
        } else {
470
            $trep['OLD'] = '---';
471
        }
472
473
        $headers = array('Message-Id' => $this->getMessageID($id, @filemtime($file)));
474
        if ($rev) {
475
            $headers['In-Reply-To'] =  $this->getMessageID($id, $rev);
0 ignored issues
show
Documentation introduced by
$rev is of type integer|boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
476
        }
477
478
        $this->send($subscriber_mail, 'upload', $id, $template, $trep, null, $headers);
479
480
    }
481
482
    /**
483
     * Send a notify mail on new registration
484
     *
485
     * @author Andreas Gohr <[email protected]>
486
     *
487
     * @param string $login    login name of the new user
488
     * @param string $fullname full name of the new user
489
     * @param string $email    email address of the new user
490
     * @return bool true if a mail was sent
491
     */
492
    public function send_register($login, $fullname, $email) {
493
        global $conf;
494
        if(empty($conf['registernotify'])) return false;
495
496
        $trep = array(
497
            'NEWUSER' => $login,
498
            'NEWNAME' => $fullname,
499
            'NEWEMAIL' => $email,
500
        );
501
502
        return $this->send(
503
            $conf['registernotify'],
504
            'new_user',
505
            $login,
506
            'registermail',
507
            $trep
508
        );
509
    }
510
511
    /**
512
     * Send a digest mail
513
     *
514
     * Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff()
515
     * but determines the last known revision first
516
     *
517
     * @author Adrian Lang <[email protected]>
518
     *
519
     * @param string $subscriber_mail The target mail address
520
     * @param string $id              The ID
521
     * @param int    $lastupdate      Time of the last notification
522
     * @return bool
523
     */
524
    protected function send_digest($subscriber_mail, $id, $lastupdate) {
525
        $pagelog = new PageChangeLog($id);
526
        $n = 0;
527
        do {
528
            $rev = $pagelog->getRevisions($n++, 1);
529
            $rev = (count($rev) > 0) ? $rev[0] : null;
530
        } while(!is_null($rev) && $rev > $lastupdate);
531
532
        return $this->send_diff(
533
            $subscriber_mail,
534
            'subscr_digest',
535
            $id, $rev
536
        );
537
    }
538
539
    /**
540
     * Send a list mail
541
     *
542
     * Sends a list mail showing a list of changed pages.
543
     *
544
     * @author Adrian Lang <[email protected]>
545
     *
546
     * @param string $subscriber_mail The target mail address
547
     * @param array  $ids             Array of ids
548
     * @param string $ns_id           The id of the namespace
549
     * @return bool true if a mail was sent
550
     */
551
    protected function send_list($subscriber_mail, $ids, $ns_id) {
552
        if(count($ids) === 0) return false;
553
554
        $tlist = '';
555
        $hlist = '<ul>';
556
        foreach($ids as $id) {
557
            $link = wl($id, array(), true);
558
            $tlist .= '* '.$link.NL;
559
            $hlist .= '<li><a href="'.$link.'">'.hsc($id).'</a></li>'.NL;
560
        }
561
        $hlist .= '</ul>';
562
563
        $id = prettyprint_id($ns_id);
564
        $trep = array(
565
            'DIFF' => rtrim($tlist),
566
            'PAGE' => $id,
567
            'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
568
        );
569
        $hrep = array(
570
            'DIFF' => $hlist
571
        );
572
573
        return $this->send(
574
            $subscriber_mail,
575
            'subscribe_list',
576
            $ns_id,
577
            'subscr_list', $trep, $hrep
578
        );
579
    }
580
581
    /**
582
     * Helper function for sending a mail
583
     *
584
     * @author Adrian Lang <[email protected]>
585
     *
586
     * @param string $subscriber_mail The target mail address
587
     * @param string $subject         The lang id of the mail subject (without the
588
     *                                prefix “mail_”)
589
     * @param string $context         The context of this mail, eg. page or namespace id
590
     * @param string $template        The name of the mail template
591
     * @param array  $trep            Predefined parameters used to parse the
592
     *                                template (in text format)
593
     * @param array  $hrep            Predefined parameters used to parse the
594
     *                                template (in HTML format), null to default to $trep
595
     * @param array  $headers         Additional mail headers in the form 'name' => 'value'
596
     * @return bool
597
     */
598
    protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null, $headers = array()) {
599
        global $lang;
600
        global $conf;
601
602
        $text = rawLocale($template);
603
        $subject = $lang['mail_'.$subject].' '.$context;
604
        $mail = new Mailer();
605
        $mail->bcc($subscriber_mail);
606
        $mail->subject($subject);
607
        $mail->setBody($text, $trep, $hrep);
608
        if(in_array($template, array('subscr_list', 'subscr_digest'))){
609
            $mail->from($conf['mailfromnobody']);
610
        }
611
        if(isset($trep['SUBSCRIBE'])) {
612
            $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false);
613
        }
614
615
        foreach ($headers as $header => $value) {
616
            $mail->setHeader($header, $value);
617
        }
618
619
        return $mail->send();
620
    }
621
622
    /**
623
     * Get a valid message id for a certain $id and revision (or the current revision)
624
     *
625
     * @param string $id  The id of the page (or media file) the message id should be for
626
     * @param string $rev The revision of the page, set to the current revision of the page $id if not set
627
     * @return string
628
     */
629
    protected function getMessageID($id, $rev = null) {
630
        static $listid = null;
631
        if (is_null($listid)) {
632
            $server = parse_url(DOKU_URL, PHP_URL_HOST);
633
            $listid = join('.', array_reverse(explode('/', DOKU_BASE))).$server;
634
            $listid = urlencode($listid);
635
            $listid = strtolower(trim($listid, '.'));
636
        }
637
638
        if (is_null($rev)) {
639
            $rev = @filemtime(wikiFN($id));
640
        }
641
642
        return "<$id?rev=$rev@$listid>";
643
    }
644
645
    /**
646
     * Default callback for COMMON_NOTIFY_ADDRESSLIST
647
     *
648
     * Aggregates all email addresses of user who have subscribed the given page with 'every' style
649
     *
650
     * @author Steven Danz <[email protected]>
651
     * @author Adrian Lang <[email protected]>
652
     *
653
     * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
654
     *       use an array for the addresses within it
655
     *
656
     * @param array &$data Containing the entries:
657
     *    - $id (the page id),
658
     *    - $self (whether the author should be notified,
659
     *    - $addresslist (current email address list)
660
     *    - $replacements (array of additional string substitutions, @KEY@ to be replaced by value)
661
     */
662
    public function notifyaddresses(&$data) {
663
        if(!$this->isenabled()) return;
664
665
        /** @var DokuWiki_Auth_Plugin $auth */
666
        global $auth;
667
        global $conf;
668
        /** @var Input $INPUT */
669
        global $INPUT;
670
671
        $id = $data['id'];
672
        $self = $data['self'];
673
        $addresslist = $data['addresslist'];
674
675
        $subscriptions = $this->subscribers($id, null, 'every');
676
677
        $result = array();
678
        foreach($subscriptions as $target => $users) {
679
            foreach($users as $user => $info) {
680
                $userinfo = $auth->getUserData($user);
681
                if($userinfo === false) continue;
682
                if(!$userinfo['mail']) continue;
683
                if(!$self && $user == $INPUT->server->str('REMOTE_USER')) continue; //skip our own changes
684
685
                $level = auth_aclcheck($id, $user, $userinfo['grps']);
686
                if($level >= AUTH_READ) {
687
                    if(strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
688
                        $result[$user] = $userinfo['mail'];
689
                    }
690
                }
691
            }
692
        }
693
        $data['addresslist'] = trim($addresslist.','.implode(',', $result), ',');
694
    }
695
}
696