Failed Conditions
Push — refactorSubscriptions ( 75d664...451969 )
by Michael
07:30 queued 03:08
created

BulkSubscriptionSender::sendList()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 3
dl 0
loc 34
rs 9.376
c 0
b 0
f 0
1
<?php
2
3
4
namespace dokuwiki\Subscriptions;
5
6
7
use dokuwiki\ChangeLog\PageChangeLog;
8
use dokuwiki\Input\Input;
9
use DokuWiki_Auth_Plugin;
10
11
class BulkSubscriptionSender extends SubscriptionSender
12
{
13
14
    /**
15
     * Send digest and list subscriptions
16
     *
17
     * This sends mails to all subscribers that have a subscription for namespaces above
18
     * the given page if the needed $conf['subscribe_time'] has passed already.
19
     *
20
     * This function is called form lib/exe/indexer.php
21
     *
22
     * @param string $page
23
     *
24
     * @return int number of sent mails
25
     */
26
    public function sendBulk($page)
27
    {
28
        $subscriberManager = new SubscriberManager();
29
        if (!$subscriberManager->isenabled()) {
30
            return 0;
31
        }
32
33
        /** @var DokuWiki_Auth_Plugin $auth */
34
        global $auth;
35
        global $conf;
36
        global $USERINFO;
37
        /** @var Input $INPUT */
38
        global $INPUT;
39
        $count = 0;
40
41
        $subscriptions = $subscriberManager->subscribers($page, null, ['digest', 'list']);
42
43
        // remember current user info
44
        $olduinfo = $USERINFO;
45
        $olduser = $INPUT->server->str('REMOTE_USER');
46
47
        foreach ($subscriptions as $target => $users) {
48
            if (!$this->lock($target)) {
49
                continue;
50
            }
51
52
            foreach ($users as $user => $info) {
53
                list($style, $lastupdate) = $info;
54
55
                $lastupdate = (int)$lastupdate;
56
                if ($lastupdate + $conf['subscribe_time'] > time()) {
57
                    // Less than the configured time period passed since last
58
                    // update.
59
                    continue;
60
                }
61
62
                // Work as the user to make sure ACLs apply correctly
63
                $USERINFO = $auth->getUserData($user);
64
                $INPUT->server->set('REMOTE_USER', $user);
65
                if ($USERINFO === false) {
66
                    continue;
67
                }
68
                if (!$USERINFO['mail']) {
69
                    continue;
70
                }
71
72
                if (substr($target, -1, 1) === ':') {
73
                    // subscription target is a namespace, get all changes within
74
                    $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...
75
                } else {
76
                    // single page subscription, check ACL ourselves
77
                    if (auth_quickaclcheck($target) < AUTH_READ) {
78
                        continue;
79
                    }
80
                    $meta = p_get_metadata($target);
81
                    $changes = [$meta['last_change']];
82
                }
83
84
                // Filter out pages only changed in small and own edits
85
                $change_ids = [];
86
                foreach ($changes as $rev) {
87
                    $n = 0;
88
                    while (!is_null($rev) && $rev['date'] >= $lastupdate &&
89
                        ($INPUT->server->str('REMOTE_USER') === $rev['user'] ||
90
                            $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) {
91
                        $pagelog = new PageChangeLog($rev['id']);
92
                        $rev = $pagelog->getRevisions($n++, 1);
93
                        $rev = (count($rev) > 0) ? $rev[0] : null;
94
                    }
95
96
                    if (!is_null($rev) && $rev['date'] >= $lastupdate) {
97
                        // Some change was not a minor one and not by myself
98
                        $change_ids[] = $rev['id'];
99
                    }
100
                }
101
102
                // send it
103
                if ($style === 'digest') {
104
                    foreach ($change_ids as $change_id) {
105
                        $this->sendDigest(
106
                            $USERINFO['mail'],
107
                            $change_id,
108
                            $lastupdate
109
                        );
110
                        $count++;
111
                    }
112
                } else {
113
                    if ($style === 'list') {
114
                        $this->sendList($USERINFO['mail'], $change_ids, $target);
115
                        $count++;
116
                    }
117
                }
118
                // TODO: Handle duplicate subscriptions.
119
120
                // Update notification time.
121
                $subscriberManager->add($target, $user, $style, time());
122
            }
123
            $this->unlock($target);
124
        }
125
126
        // restore current user info
127
        $USERINFO = $olduinfo;
128
        $INPUT->server->set('REMOTE_USER', $olduser);
129
        return $count;
130
    }
131
132
    /**
133
     * Lock subscription info
134
     *
135
     * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
136
     *
137
     * @param string $id The target page or namespace, specified by id; Namespaces
138
     *                   are identified by appending a colon.
139
     *
140
     * @return bool true, if you got a succesful lock
141
     * @author Adrian Lang <[email protected]>
142
     */
143
    protected function lock($id)
144
    {
145
        global $conf;
146
147
        $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
148
149
        if (is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
150
            // looks like a stale lock - remove it
151
            @rmdir($lock);
152
        }
153
154
        // try creating the lock directory
155
        if (!@mkdir($lock, $conf['dmode'])) {
156
            return false;
157
        }
158
159
        if (!empty($conf['dperm'])) {
160
            chmod($lock, $conf['dperm']);
161
        }
162
        return true;
163
    }
164
165
    /**
166
     * Unlock subscription info
167
     *
168
     * @param string $id The target page or namespace, specified by id; Namespaces
169
     *                   are identified by appending a colon.
170
     *
171
     * @return bool
172
     * @author Adrian Lang <[email protected]>
173
     */
174
    protected function unlock($id)
175
    {
176
        global $conf;
177
        $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
178
        return @rmdir($lock);
179
    }
180
181
    /**
182
     * Send a digest mail
183
     *
184
     * Sends a digest mail showing a bunch of changes of a single page. Basically the same as sendPageDiff()
185
     * but determines the last known revision first
186
     *
187
     * @param string $subscriber_mail The target mail address
188
     * @param string $id              The ID
189
     * @param int    $lastupdate      Time of the last notification
190
     *
191
     * @return bool
192
     * @author Adrian Lang <[email protected]>
193
     *
194
     */
195
    protected function sendDigest($subscriber_mail, $id, $lastupdate)
196
    {
197
        $pagelog = new PageChangeLog($id);
198
        $n = 0;
199
        do {
200
            $rev = $pagelog->getRevisions($n++, 1);
201
            $rev = (count($rev) > 0) ? $rev[0] : null;
202
        } while (!is_null($rev) && $rev > $lastupdate);
203
204
        // TODO I'm not happy with the following line and passing $this->mailer around. Not sure how to solve it better
205
        $pageSubSender = new PageSubscriptionSender($this->mailer);
206
        return $pageSubSender->sendPageDiff(
207
            $subscriber_mail,
208
            'subscr_digest',
209
            $id,
210
            $rev
211
        );
212
    }
213
214
    /**
215
     * Send a list mail
216
     *
217
     * Sends a list mail showing a list of changed pages.
218
     *
219
     * @param string $subscriber_mail The target mail address
220
     * @param array  $ids             Array of ids
221
     * @param string $ns_id           The id of the namespace
222
     *
223
     * @return bool true if a mail was sent
224
     * @author Adrian Lang <[email protected]>
225
     *
226
     */
227
    protected function sendList($subscriber_mail, $ids, $ns_id)
228
    {
229
        if (count($ids) === 0) {
230
            return false;
231
        }
232
233
        $tlist = '';
234
        $hlist = '<ul>';
235
        foreach ($ids as $id) {
236
            $link = wl($id, [], true);
237
            $tlist .= '* ' . $link . NL;
238
            $hlist .= '<li><a href="' . $link . '">' . hsc($id) . '</a></li>' . NL;
239
        }
240
        $hlist .= '</ul>';
241
242
        $id = prettyprint_id($ns_id);
243
        $trep = [
244
            'DIFF' => rtrim($tlist),
245
            'PAGE' => $id,
246
            'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'),
247
        ];
248
        $hrep = [
249
            'DIFF' => $hlist,
250
        ];
251
252
        return $this->send(
253
            $subscriber_mail,
254
            'subscribe_list',
255
            $ns_id,
256
            'subscr_list',
257
            $trep,
258
            $hrep
259
        );
260
    }
261
}
262