Failed Conditions
Push — psr2 ( 9ddafc...b78f68 )
by Andreas
11:21 queued 07:11
created

Subscription::subscribers()   D

Complexity

Conditions 9
Paths 4

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 19
nc 4
nop 4
dl 0
loc 32
rs 4.909
c 0
b 0
f 0
1
<?php
2
3
use dokuwiki\ChangeLog\PageChangeLog;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, PageChangeLog.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
4
5
/**
6
 * Class for handling (email) subscriptions
7
 *
8
 * @author  Adrian Lang <[email protected]>
9
 * @author  Andreas Gohr <[email protected]>
10
 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
11
 */
12
class Subscription {
13
14
    /**
15
     * Check if subscription system is enabled
16
     *
17
     * @return bool
18
     */
19
    public function isenabled() {
20
        return actionOK('subscribe');
21
    }
22
23
    /**
24
     * Return the subscription meta file for the given ID
25
     *
26
     * @author Adrian Lang <[email protected]>
27
     *
28
     * @param string $id The target page or namespace, specified by id; Namespaces
29
     *                   are identified by appending a colon.
30
     * @return string
31
     */
32
    protected function file($id) {
33
        $meta_fname = '.mlist';
34
        if((substr($id, -1, 1) === ':')) {
35
            $meta_froot = getNS($id);
36
            $meta_fname = '/'.$meta_fname;
37
        } else {
38
            $meta_froot = $id;
39
        }
40
        return metaFN((string) $meta_froot, $meta_fname);
41
    }
42
43
    /**
44
     * Lock subscription info
45
     *
46
     * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
47
     *
48
     * @author Adrian Lang <[email protected]>
49
     * @param string $id The target page or namespace, specified by id; Namespaces
50
     *                   are identified by appending a colon.
51
     * @return bool true, if you got a succesful lock
52
     */
53
    protected function lock($id) {
54
        global $conf;
55
56
        $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
57
58
        if(is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
59
            // looks like a stale lock - remove it
60
            @rmdir($lock);
61
        }
62
63
        // try creating the lock directory
64
        if(!@mkdir($lock, $conf['dmode'])) {
65
            return false;
66
        }
67
68
        if(!empty($conf['dperm'])) chmod($lock, $conf['dperm']);
69
        return true;
70
    }
71
72
    /**
73
     * Unlock subscription info
74
     *
75
     * @author Adrian Lang <[email protected]>
76
     * @param string $id The target page or namespace, specified by id; Namespaces
77
     *                   are identified by appending a colon.
78
     * @return bool
79
     */
80
    protected function unlock($id) {
81
        global $conf;
82
        $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
83
        return @rmdir($lock);
84
    }
85
86
    /**
87
     * Construct a regular expression for parsing a subscription definition line
88
     *
89
     * @author Andreas Gohr <[email protected]>
90
     *
91
     * @param string|array $user
92
     * @param string|array $style
93
     * @param string|array $data
94
     * @return string complete regexp including delimiters
95
     * @throws Exception when no data is passed
96
     */
97
    protected function buildregex($user = null, $style = null, $data = null) {
98
        // always work with arrays
99
        $user = (array) $user;
100
        $style = (array) $style;
101
        $data = (array) $data;
102
103
        // clean
104
        $user = array_filter(array_map('trim', $user));
105
        $style = array_filter(array_map('trim', $style));
106
        $data = array_filter(array_map('trim', $data));
107
108
        // user names are encoded
109
        $user = array_map('auth_nameencode', $user);
110
111
        // quote
112
        $user = array_map('preg_quote_cb', $user);
113
        $style = array_map('preg_quote_cb', $style);
114
        $data = array_map('preg_quote_cb', $data);
115
116
        // join
117
        $user = join('|', $user);
118
        $style = join('|', $style);
119
        $data = join('|', $data);
120
121
        // any data at all?
122
        if($user.$style.$data === '') throw new Exception('no data passed');
123
124
        // replace empty values, set which ones are optional
125
        $sopt = '';
126
        $dopt = '';
127
        if($user === '') {
128
            $user = '\S+';
129
        }
130
        if($style === '') {
131
            $style = '\S+';
132
            $sopt = '?';
133
        }
134
        if($data === '') {
135
            $data = '\S+';
136
            $dopt = '?';
137
        }
138
139
        // assemble
140
        return "/^($user)(?:\\s+($style))$sopt(?:\\s+($data))$dopt$/";
141
    }
142
143
    /**
144
     * Recursively search for matching subscriptions
145
     *
146
     * This function searches all relevant subscription files for a page or
147
     * namespace.
148
     *
149
     * @author Adrian Lang <[email protected]>
150
     *
151
     * @param string         $page The target object’s (namespace or page) id
152
     * @param string|array   $user
153
     * @param string|array   $style
154
     * @param string|array   $data
155
     * @return array
156
     */
157
    public function subscribers($page, $user = null, $style = null, $data = null) {
158
        if(!$this->isenabled()) return array();
159
160
        // Construct list of files which may contain relevant subscriptions.
161
        $files = array(':' => $this->file(':'));
162
        do {
163
            $files[$page] = $this->file($page);
164
            $page = getNS(rtrim($page, ':')).':';
165
        } while($page !== ':');
166
167
        $re = $this->buildregex($user, $style, $data);
168
169
        // Handle files.
170
        $result = array();
171
        foreach($files as $target => $file) {
172
            if(!file_exists($file)) continue;
173
174
            $lines = file($file);
175
            foreach($lines as $line) {
176
                // fix old style subscription files
177
                if(strpos($line, ' ') === false) $line = trim($line)." every\n";
178
179
                // check for matching entries
180
                if(!preg_match($re, $line, $m)) continue;
181
182
                $u = rawurldecode($m[1]); // decode the user name
183
                if(!isset($result[$target])) $result[$target] = array();
184
                $result[$target][$u] = array($m[2], $m[3]); // add to result
185
            }
186
        }
187
        return array_reverse($result);
188
    }
189
190
    /**
191
     * Adds a new subscription for the given page or namespace
192
     *
193
     * This will automatically overwrite any existent subscription for the given user on this
194
     * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
195
     *
196
     * @param string $id The target page or namespace, specified by id; Namespaces
197
     *                   are identified by appending a colon.
198
     * @param string $user
199
     * @param string $style
200
     * @param string $data
201
     * @throws Exception when user or style is empty
202
     * @return bool
203
     */
204
    public function add($id, $user, $style, $data = '') {
205
        if(!$this->isenabled()) return false;
206
207
        // delete any existing subscription
208
        $this->remove($id, $user);
209
210
        $user  = auth_nameencode(trim($user));
211
        $style = trim($style);
212
        $data  = trim($data);
213
214
        if(!$user) throw new Exception('no subscription user given');
215
        if(!$style) throw new Exception('no subscription style given');
216
        if(!$data) $data = time(); //always add current time for new subscriptions
217
218
        $line = "$user $style $data\n";
219
        $file = $this->file($id);
220
        return io_saveFile($file, $line, true);
221
    }
222
223
    /**
224
     * Removes a subscription for the given page or namespace
225
     *
226
     * This removes all subscriptions matching the given criteria on the given page or
227
     * namespace. It will *not* modify any subscriptions that may exist in higher
228
     * namespaces.
229
     *
230
     * @param string         $id   The target object’s (namespace or page) id
231
     * @param string|array   $user
232
     * @param string|array   $style
233
     * @param string|array   $data
234
     * @return bool
235
     */
236
    public function remove($id, $user = null, $style = null, $data = null) {
237
        if(!$this->isenabled()) return false;
238
239
        $file = $this->file($id);
240
        if(!file_exists($file)) return true;
241
242
        $re = $this->buildregex($user, $style, $data);
243
        return io_deleteFromFile($file, $re, true);
244
    }
245
246
    /**
247
     * Get data for $INFO['subscribed']
248
     *
249
     * $INFO['subscribed'] is either false if no subscription for the current page
250
     * and user is in effect. Else it contains an array of arrays with the fields
251
     * “target”, “style”, and optionally “data”.
252
     *
253
     * @param string $id  Page ID, defaults to global $ID
254
     * @param string $user User, defaults to $_SERVER['REMOTE_USER']
255
     * @return array|false
256
     * @author Adrian Lang <[email protected]>
257
     */
258
    public function user_subscription($id = '', $user = '') {
259
        if(!$this->isenabled()) return false;
260
261
        global $ID;
262
        /** @var Input $INPUT */
263
        global $INPUT;
264
        if(!$id) $id = $ID;
265
        if(!$user) $user = $INPUT->server->str('REMOTE_USER');
266
267
        $subs = $this->subscribers($id, $user);
268
        if(!count($subs)) return false;
269
270
        $result = array();
271
        foreach($subs as $target => $info) {
272
            $result[] = array(
273
                'target' => $target,
274
                'style' => $info[$user][0],
275
                'data' => $info[$user][1]
276
            );
277
        }
278
279
        return $result;
280
    }
281
282
    /**
283
     * Send digest and list subscriptions
284
     *
285
     * This sends mails to all subscribers that have a subscription for namespaces above
286
     * the given page if the needed $conf['subscribe_time'] has passed already.
287
     *
288
     * This function is called form lib/exe/indexer.php
289
     *
290
     * @param string $page
291
     * @return int number of sent mails
292
     */
293
    public function send_bulk($page) {
294
        if(!$this->isenabled()) return 0;
295
296
        /** @var DokuWiki_Auth_Plugin $auth */
297
        global $auth;
298
        global $conf;
299
        global $USERINFO;
300
        /** @var Input $INPUT */
301
        global $INPUT;
302
        $count = 0;
303
304
        $subscriptions = $this->subscribers($page, null, array('digest', 'list'));
305
306
        // remember current user info
307
        $olduinfo = $USERINFO;
308
        $olduser = $INPUT->server->str('REMOTE_USER');
309
310
        foreach($subscriptions as $target => $users) {
311
            if(!$this->lock($target)) continue;
312
313
            foreach($users as $user => $info) {
314
                list($style, $lastupdate) = $info;
315
316
                $lastupdate = (int) $lastupdate;
317
                if($lastupdate + $conf['subscribe_time'] > time()) {
318
                    // Less than the configured time period passed since last
319
                    // update.
320
                    continue;
321
                }
322
323
                // Work as the user to make sure ACLs apply correctly
324
                $USERINFO = $auth->getUserData($user);
325
                $INPUT->server->set('REMOTE_USER',$user);
326
                if($USERINFO === false) continue;
327
                if(!$USERINFO['mail']) continue;
328
329
                if(substr($target, -1, 1) === ':') {
330
                    // subscription target is a namespace, get all changes within
331
                    $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...
332
                } else {
333
                    // single page subscription, check ACL ourselves
334
                    if(auth_quickaclcheck($target) < AUTH_READ) continue;
335
                    $meta = p_get_metadata($target);
336
                    $changes = array($meta['last_change']);
337
                }
338
339
                // Filter out pages only changed in small and own edits
340
                $change_ids = array();
341
                foreach($changes as $rev) {
342
                    $n = 0;
343
                    while(!is_null($rev) && $rev['date'] >= $lastupdate &&
344
                        ($INPUT->server->str('REMOTE_USER') === $rev['user'] ||
345
                            $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) {
346
                        $pagelog = new PageChangeLog($rev['id']);
347
                        $rev = $pagelog->getRevisions($n++, 1);
348
                        $rev = (count($rev) > 0) ? $rev[0] : null;
349
                    }
350
351
                    if(!is_null($rev) && $rev['date'] >= $lastupdate) {
352
                        // Some change was not a minor one and not by myself
353
                        $change_ids[] = $rev['id'];
354
                    }
355
                }
356
357
                // send it
358
                if($style === 'digest') {
359
                    foreach($change_ids as $change_id) {
360
                        $this->send_digest(
361
                            $USERINFO['mail'], $change_id,
362
                            $lastupdate
363
                        );
364
                        $count++;
365
                    }
366
                } elseif($style === 'list') {
367
                    $this->send_list($USERINFO['mail'], $change_ids, $target);
368
                    $count++;
369
                }
370
                // TODO: Handle duplicate subscriptions.
371
372
                // Update notification time.
373
                $this->add($target, $user, $style, time());
374
            }
375
            $this->unlock($target);
376
        }
377
378
        // restore current user info
379
        $USERINFO = $olduinfo;
380
        $INPUT->server->set('REMOTE_USER',$olduser);
381
        return $count;
382
    }
383
384
    /**
385
     * Send the diff for some page change
386
     *
387
     * @param string   $subscriber_mail The target mail address
388
     * @param string   $template        Mail template ('subscr_digest', 'subscr_single', 'mailtext', ...)
389
     * @param string   $id              Page for which the notification is
390
     * @param int|null $rev             Old revision if any
391
     * @param string   $summary         Change summary if any
392
     * @return bool                     true if successfully sent
393
     */
394
    public function send_diff($subscriber_mail, $template, $id, $rev = null, $summary = '') {
395
        global $DIFF_INLINESTYLES;
396
397
        // prepare replacements (keys not set in hrep will be taken from trep)
398
        $trep = array(
399
            'PAGE' => $id,
400
            'NEWPAGE' => wl($id, '', true, '&'),
401
            'SUMMARY' => $summary,
402
            'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
403
        );
404
        $hrep = array();
405
406
        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...
407
            $subject = 'changed';
408
            $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
409
410
            $old_content = rawWiki($id, $rev);
411
            $new_content = rawWiki($id);
412
413
            $df = new Diff(explode("\n", $old_content),
414
                           explode("\n", $new_content));
415
            $dformat = new UnifiedDiffFormatter();
416
            $tdiff = $dformat->format($df);
417
418
            $DIFF_INLINESTYLES = true;
419
            $df = new Diff(explode("\n", $old_content),
420
                           explode("\n", $new_content));
421
            $dformat = new InlineDiffFormatter();
422
            $hdiff = $dformat->format($df);
423
            $hdiff = '<table>'.$hdiff.'</table>';
424
            $DIFF_INLINESTYLES = false;
425
        } else {
426
            $subject = 'newpage';
427
            $trep['OLDPAGE'] = '---';
428
            $tdiff = rawWiki($id);
429
            $hdiff = nl2br(hsc($tdiff));
430
        }
431
432
        $trep['DIFF'] = $tdiff;
433
        $hrep['DIFF'] = $hdiff;
434
435
        $headers = array('Message-Id' => $this->getMessageID($id));
436
        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...
437
            $headers['In-Reply-To'] =  $this->getMessageID($id, $rev);
438
        }
439
440
        return $this->send(
441
            $subscriber_mail, $subject, $id,
442
            $template, $trep, $hrep, $headers
443
        );
444
    }
445
446
    /**
447
     * Send the diff for some media change
448
     *
449
     * @fixme this should embed thumbnails of images in HTML version
450
     *
451
     * @param string   $subscriber_mail The target mail address
452
     * @param string   $template        Mail template ('uploadmail', ...)
453
     * @param string   $id              Media file for which the notification is
454
     * @param int|bool $rev             Old revision if any
455
     */
456
    public function send_media_diff($subscriber_mail, $template, $id, $rev = false) {
457
        global $conf;
458
459
        $file = mediaFN($id);
460
        list($mime, /* $ext */) = mimetype($id);
461
462
        $trep = array(
463
            'MIME'  => $mime,
464
            'MEDIA' => ml($id,'',true,'&',true),
465
            'SIZE'  => filesize_h(filesize($file)),
466
        );
467
468
        if ($rev && $conf['mediarevisions']) {
469
            $trep['OLD'] = ml($id, "rev=$rev", true, '&', true);
470
        } else {
471
            $trep['OLD'] = '---';
472
        }
473
474
        $headers = array('Message-Id' => $this->getMessageID($id, @filemtime($file)));
475
        if ($rev) {
476
            $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...
477
        }
478
479
        $this->send($subscriber_mail, 'upload', $id, $template, $trep, null, $headers);
480
481
    }
482
483
    /**
484
     * Send a notify mail on new registration
485
     *
486
     * @author Andreas Gohr <[email protected]>
487
     *
488
     * @param string $login    login name of the new user
489
     * @param string $fullname full name of the new user
490
     * @param string $email    email address of the new user
491
     * @return bool true if a mail was sent
492
     */
493
    public function send_register($login, $fullname, $email) {
494
        global $conf;
495
        if(empty($conf['registernotify'])) return false;
496
497
        $trep = array(
498
            'NEWUSER' => $login,
499
            'NEWNAME' => $fullname,
500
            'NEWEMAIL' => $email,
501
        );
502
503
        return $this->send(
504
            $conf['registernotify'],
505
            'new_user',
506
            $login,
507
            'registermail',
508
            $trep
509
        );
510
    }
511
512
    /**
513
     * Send a digest mail
514
     *
515
     * Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff()
516
     * but determines the last known revision first
517
     *
518
     * @author Adrian Lang <[email protected]>
519
     *
520
     * @param string $subscriber_mail The target mail address
521
     * @param string $id              The ID
522
     * @param int    $lastupdate      Time of the last notification
523
     * @return bool
524
     */
525
    protected function send_digest($subscriber_mail, $id, $lastupdate) {
526
        $pagelog = new PageChangeLog($id);
527
        $n = 0;
528
        do {
529
            $rev = $pagelog->getRevisions($n++, 1);
530
            $rev = (count($rev) > 0) ? $rev[0] : null;
531
        } while(!is_null($rev) && $rev > $lastupdate);
532
533
        return $this->send_diff(
534
            $subscriber_mail,
535
            'subscr_digest',
536
            $id, $rev
537
        );
538
    }
539
540
    /**
541
     * Send a list mail
542
     *
543
     * Sends a list mail showing a list of changed pages.
544
     *
545
     * @author Adrian Lang <[email protected]>
546
     *
547
     * @param string $subscriber_mail The target mail address
548
     * @param array  $ids             Array of ids
549
     * @param string $ns_id           The id of the namespace
550
     * @return bool true if a mail was sent
551
     */
552
    protected function send_list($subscriber_mail, $ids, $ns_id) {
553
        if(count($ids) === 0) return false;
554
555
        $tlist = '';
556
        $hlist = '<ul>';
557
        foreach($ids as $id) {
558
            $link = wl($id, array(), true);
559
            $tlist .= '* '.$link.NL;
560
            $hlist .= '<li><a href="'.$link.'">'.hsc($id).'</a></li>'.NL;
561
        }
562
        $hlist .= '</ul>';
563
564
        $id = prettyprint_id($ns_id);
565
        $trep = array(
566
            'DIFF' => rtrim($tlist),
567
            'PAGE' => $id,
568
            'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
569
        );
570
        $hrep = array(
571
            'DIFF' => $hlist
572
        );
573
574
        return $this->send(
575
            $subscriber_mail,
576
            'subscribe_list',
577
            $ns_id,
578
            'subscr_list', $trep, $hrep
579
        );
580
    }
581
582
    /**
583
     * Helper function for sending a mail
584
     *
585
     * @author Adrian Lang <[email protected]>
586
     *
587
     * @param string $subscriber_mail The target mail address
588
     * @param string $subject         The lang id of the mail subject (without the
589
     *                                prefix “mail_”)
590
     * @param string $context         The context of this mail, eg. page or namespace id
591
     * @param string $template        The name of the mail template
592
     * @param array  $trep            Predefined parameters used to parse the
593
     *                                template (in text format)
594
     * @param array  $hrep            Predefined parameters used to parse the
595
     *                                template (in HTML format), null to default to $trep
596
     * @param array  $headers         Additional mail headers in the form 'name' => 'value'
597
     * @return bool
598
     */
599
    protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null, $headers = array()) {
600
        global $lang;
601
        global $conf;
602
603
        $text = rawLocale($template);
604
        $subject = $lang['mail_'.$subject].' '.$context;
605
        $mail = new Mailer();
606
        $mail->bcc($subscriber_mail);
607
        $mail->subject($subject);
608
        $mail->setBody($text, $trep, $hrep);
609
        if(in_array($template, array('subscr_list', 'subscr_digest'))){
610
            $mail->from($conf['mailfromnobody']);
611
        }
612
        if(isset($trep['SUBSCRIBE'])) {
613
            $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false);
614
        }
615
616
        foreach ($headers as $header => $value) {
617
            $mail->setHeader($header, $value);
618
        }
619
620
        return $mail->send();
621
    }
622
623
    /**
624
     * Get a valid message id for a certain $id and revision (or the current revision)
625
     *
626
     * @param string $id  The id of the page (or media file) the message id should be for
627
     * @param string $rev The revision of the page, set to the current revision of the page $id if not set
628
     * @return string
629
     */
630
    protected function getMessageID($id, $rev = null) {
631
        static $listid = null;
632
        if (is_null($listid)) {
633
            $server = parse_url(DOKU_URL, PHP_URL_HOST);
634
            $listid = join('.', array_reverse(explode('/', DOKU_BASE))).$server;
635
            $listid = urlencode($listid);
636
            $listid = strtolower(trim($listid, '.'));
637
        }
638
639
        if (is_null($rev)) {
640
            $rev = @filemtime(wikiFN($id));
641
        }
642
643
        return "<$id?rev=$rev@$listid>";
644
    }
645
646
    /**
647
     * Default callback for COMMON_NOTIFY_ADDRESSLIST
648
     *
649
     * Aggregates all email addresses of user who have subscribed the given page with 'every' style
650
     *
651
     * @author Steven Danz <[email protected]>
652
     * @author Adrian Lang <[email protected]>
653
     *
654
     * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
655
     *       use an array for the addresses within it
656
     *
657
     * @param array &$data Containing the entries:
658
     *    - $id (the page id),
659
     *    - $self (whether the author should be notified,
660
     *    - $addresslist (current email address list)
661
     *    - $replacements (array of additional string substitutions, @KEY@ to be replaced by value)
662
     */
663
    public function notifyaddresses(&$data) {
664
        if(!$this->isenabled()) return;
665
666
        /** @var DokuWiki_Auth_Plugin $auth */
667
        global $auth;
668
        global $conf;
669
        /** @var Input $INPUT */
670
        global $INPUT;
671
672
        $id = $data['id'];
673
        $self = $data['self'];
674
        $addresslist = $data['addresslist'];
675
676
        $subscriptions = $this->subscribers($id, null, 'every');
677
678
        $result = array();
679
        foreach($subscriptions as $target => $users) {
680
            foreach($users as $user => $info) {
681
                $userinfo = $auth->getUserData($user);
682
                if($userinfo === false) continue;
683
                if(!$userinfo['mail']) continue;
684
                if(!$self && $user == $INPUT->server->str('REMOTE_USER')) continue; //skip our own changes
685
686
                $level = auth_aclcheck($id, $user, $userinfo['grps']);
687
                if($level >= AUTH_READ) {
688
                    if(strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
689
                        $result[$user] = $userinfo['mail'];
690
                    }
691
                }
692
            }
693
        }
694
        $data['addresslist'] = trim($addresslist.','.implode(',', $result), ',');
695
    }
696
}
697