Failed Conditions
Push — refactorSubscriptions ( 74035a...75d664 )
by Michael
03:07
created

ChangesSubscriptionSender::sendBulk()   F

Complexity

Conditions 21
Paths 50

Size

Total Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
nc 50
nop 1
dl 0
loc 105
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
namespace dokuwiki\Subscriptions;
4
5
use Diff;
6
use dokuwiki\ChangeLog\PageChangeLog;
7
use dokuwiki\Input\Input;
8
use DokuWiki_Auth_Plugin;
9
use InlineDiffFormatter;
10
use UnifiedDiffFormatter;
11
12
class ChangesSubscriptionSender extends SubscriptionSender
13
{
14
15
    /**
16
     * Send digest and list subscriptions
17
     *
18
     * This sends mails to all subscribers that have a subscription for namespaces above
19
     * the given page if the needed $conf['subscribe_time'] has passed already.
20
     *
21
     * This function is called form lib/exe/indexer.php
22
     *
23
     * @param string $page
24
     *
25
     * @return int number of sent mails
26
     */
27
    public function sendBulk($page)
28
    {
29
        $subscriberManager = new SubscriberManager();
30
        if (!$subscriberManager->isenabled()) {
31
            return 0;
32
        }
33
34
        /** @var DokuWiki_Auth_Plugin $auth */
35
        global $auth;
36
        global $conf;
37
        global $USERINFO;
38
        /** @var Input $INPUT */
39
        global $INPUT;
40
        $count = 0;
41
42
        $subscriptions = $subscriberManager->subscribers($page, null, ['digest', 'list']);
43
44
        // remember current user info
45
        $olduinfo = $USERINFO;
46
        $olduser = $INPUT->server->str('REMOTE_USER');
47
48
        foreach ($subscriptions as $target => $users) {
49
            if (!$this->lock($target)) {
50
                continue;
51
            }
52
53
            foreach ($users as $user => $info) {
54
                list($style, $lastupdate) = $info;
55
56
                $lastupdate = (int)$lastupdate;
57
                if ($lastupdate + $conf['subscribe_time'] > time()) {
58
                    // Less than the configured time period passed since last
59
                    // update.
60
                    continue;
61
                }
62
63
                // Work as the user to make sure ACLs apply correctly
64
                $USERINFO = $auth->getUserData($user);
65
                $INPUT->server->set('REMOTE_USER', $user);
66
                if ($USERINFO === false) {
67
                    continue;
68
                }
69
                if (!$USERINFO['mail']) {
70
                    continue;
71
                }
72
73
                if (substr($target, -1, 1) === ':') {
74
                    // subscription target is a namespace, get all changes within
75
                    $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...
76
                } else {
77
                    // single page subscription, check ACL ourselves
78
                    if (auth_quickaclcheck($target) < AUTH_READ) {
79
                        continue;
80
                    }
81
                    $meta = p_get_metadata($target);
82
                    $changes = [$meta['last_change']];
83
                }
84
85
                // Filter out pages only changed in small and own edits
86
                $change_ids = [];
87
                foreach ($changes as $rev) {
88
                    $n = 0;
89
                    while (!is_null($rev) && $rev['date'] >= $lastupdate &&
90
                        ($INPUT->server->str('REMOTE_USER') === $rev['user'] ||
91
                            $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) {
92
                        $pagelog = new PageChangeLog($rev['id']);
93
                        $rev = $pagelog->getRevisions($n++, 1);
94
                        $rev = (count($rev) > 0) ? $rev[0] : null;
95
                    }
96
97
                    if (!is_null($rev) && $rev['date'] >= $lastupdate) {
98
                        // Some change was not a minor one and not by myself
99
                        $change_ids[] = $rev['id'];
100
                    }
101
                }
102
103
                // send it
104
                if ($style === 'digest') {
105
                    foreach ($change_ids as $change_id) {
106
                        $this->sendDigest(
107
                            $USERINFO['mail'],
108
                            $change_id,
109
                            $lastupdate
110
                        );
111
                        $count++;
112
                    }
113
                } else {
114
                    if ($style === 'list') {
115
                        $this->sendList($USERINFO['mail'], $change_ids, $target);
116
                        $count++;
117
                    }
118
                }
119
                // TODO: Handle duplicate subscriptions.
120
121
                // Update notification time.
122
                $subscriberManager->add($target, $user, $style, time());
123
            }
124
            $this->unlock($target);
125
        }
126
127
        // restore current user info
128
        $USERINFO = $olduinfo;
129
        $INPUT->server->set('REMOTE_USER', $olduser);
130
        return $count;
131
    }
132
133
    /**
134
     * Send the diff for some page change
135
     *
136
     * @param string   $subscriber_mail The target mail address
137
     * @param string   $template        Mail template ('subscr_digest', 'subscr_single', 'mailtext', ...)
138
     * @param string   $id              Page for which the notification is
139
     * @param int|null $rev             Old revision if any
140
     * @param string   $summary         Change summary if any
141
     *
142
     * @return bool                     true if successfully sent
143
     */
144
    public function sendPageDiff($subscriber_mail, $template, $id, $rev = null, $summary = '')
145
    {
146
        global $DIFF_INLINESTYLES;
147
148
        // prepare replacements (keys not set in hrep will be taken from trep)
149
        $trep = [
150
            'PAGE' => $id,
151
            'NEWPAGE' => wl($id, '', true, '&'),
152
            'SUMMARY' => $summary,
153
            'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'),
154
        ];
155
        $hrep = [];
156
157
        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...
158
            $subject = 'changed';
159
            $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
160
161
            $old_content = rawWiki($id, $rev);
162
            $new_content = rawWiki($id);
163
164
            $df = new Diff(
165
                explode("\n", $old_content),
166
                explode("\n", $new_content)
167
            );
168
            $dformat = new UnifiedDiffFormatter();
169
            $tdiff = $dformat->format($df);
170
171
            $DIFF_INLINESTYLES = true;
172
            $df = new Diff(
173
                explode("\n", $old_content),
174
                explode("\n", $new_content)
175
            );
176
            $dformat = new InlineDiffFormatter();
177
            $hdiff = $dformat->format($df);
178
            $hdiff = '<table>' . $hdiff . '</table>';
179
            $DIFF_INLINESTYLES = false;
180
        } else {
181
            $subject = 'newpage';
182
            $trep['OLDPAGE'] = '---';
183
            $tdiff = rawWiki($id);
184
            $hdiff = nl2br(hsc($tdiff));
185
        }
186
187
        $trep['DIFF'] = $tdiff;
188
        $hrep['DIFF'] = $hdiff;
189
190
        $headers = ['Message-Id' => $this->getMessageID($id)];
191
        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...
192
            $headers['In-Reply-To'] = $this->getMessageID($id, $rev);
193
        }
194
195
        return $this->send(
196
            $subscriber_mail,
197
            $subject,
198
            $id,
199
            $template,
200
            $trep,
201
            $hrep,
202
            $headers
203
        );
204
    }
205
206
    /**
207
     * Send the diff for some media change
208
     *
209
     * @fixme this should embed thumbnails of images in HTML version
210
     *
211
     * @param string   $subscriber_mail The target mail address
212
     * @param string   $template        Mail template ('uploadmail', ...)
213
     * @param string   $id              Media file for which the notification is
214
     * @param int|bool $rev             Old revision if any
215
     */
216
    public function sendMediaDiff($subscriber_mail, $template, $id, $rev = false)
217
    {
218
        global $conf;
219
220
        $file = mediaFN($id);
221
        list($mime, /* $ext */) = mimetype($id);
222
223
        $trep = [
224
            'MIME' => $mime,
225
            'MEDIA' => ml($id, '', true, '&', true),
226
            'SIZE' => filesize_h(filesize($file)),
227
        ];
228
229
        if ($rev && $conf['mediarevisions']) {
230
            $trep['OLD'] = ml($id, "rev=$rev", true, '&', true);
231
        } else {
232
            $trep['OLD'] = '---';
233
        }
234
235
        $headers = ['Message-Id' => $this->getMessageID($id, @filemtime($file))];
236
        if ($rev) {
237
            $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...
238
        }
239
240
        $this->send($subscriber_mail, 'upload', $id, $template, $trep, null, $headers);
241
    }
242
243
    /**
244
     * Get a valid message id for a certain $id and revision (or the current revision)
245
     *
246
     * @param string $id  The id of the page (or media file) the message id should be for
247
     * @param string $rev The revision of the page, set to the current revision of the page $id if not set
248
     *
249
     * @return string
250
     */
251
    protected function getMessageID($id, $rev = null)
252
    {
253
        static $listid = null;
254
        if (is_null($listid)) {
255
            $server = parse_url(DOKU_URL, PHP_URL_HOST);
256
            $listid = join('.', array_reverse(explode('/', DOKU_BASE))) . $server;
257
            $listid = urlencode($listid);
258
            $listid = strtolower(trim($listid, '.'));
259
        }
260
261
        if (is_null($rev)) {
262
            $rev = @filemtime(wikiFN($id));
263
        }
264
265
        return "<$id?rev=$rev@$listid>";
266
    }
267
268
    /**
269
     * Lock subscription info
270
     *
271
     * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
272
     *
273
     * @param string $id The target page or namespace, specified by id; Namespaces
274
     *                   are identified by appending a colon.
275
     *
276
     * @return bool true, if you got a succesful lock
277
     * @author Adrian Lang <[email protected]>
278
     */
279
    protected function lock($id)
280
    {
281
        global $conf;
282
283
        $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
284
285
        if (is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
286
            // looks like a stale lock - remove it
287
            @rmdir($lock);
288
        }
289
290
        // try creating the lock directory
291
        if (!@mkdir($lock, $conf['dmode'])) {
292
            return false;
293
        }
294
295
        if (!empty($conf['dperm'])) {
296
            chmod($lock, $conf['dperm']);
297
        }
298
        return true;
299
    }
300
301
    /**
302
     * Unlock subscription info
303
     *
304
     * @param string $id The target page or namespace, specified by id; Namespaces
305
     *                   are identified by appending a colon.
306
     *
307
     * @return bool
308
     * @author Adrian Lang <[email protected]>
309
     */
310
    protected function unlock($id)
311
    {
312
        global $conf;
313
        $lock = $conf['lockdir'] . '/_subscr_' . md5($id) . '.lock';
314
        return @rmdir($lock);
315
    }
316
317
    /**
318
     * Send a digest mail
319
     *
320
     * Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff()
321
     * but determines the last known revision first
322
     *
323
     * @param string $subscriber_mail The target mail address
324
     * @param string $id              The ID
325
     * @param int    $lastupdate      Time of the last notification
326
     *
327
     * @return bool
328
     * @author Adrian Lang <[email protected]>
329
     *
330
     */
331
    protected function sendDigest($subscriber_mail, $id, $lastupdate)
332
    {
333
        $pagelog = new PageChangeLog($id);
334
        $n = 0;
335
        do {
336
            $rev = $pagelog->getRevisions($n++, 1);
337
            $rev = (count($rev) > 0) ? $rev[0] : null;
338
        } while (!is_null($rev) && $rev > $lastupdate);
339
340
        return $this->sendPageDiff(
341
            $subscriber_mail,
342
            'subscr_digest',
343
            $id,
344
            $rev
345
        );
346
    }
347
348
    /**
349
     * Send a list mail
350
     *
351
     * Sends a list mail showing a list of changed pages.
352
     *
353
     * @param string $subscriber_mail The target mail address
354
     * @param array  $ids             Array of ids
355
     * @param string $ns_id           The id of the namespace
356
     *
357
     * @return bool true if a mail was sent
358
     * @author Adrian Lang <[email protected]>
359
     *
360
     */
361
    protected function sendList($subscriber_mail, $ids, $ns_id)
362
    {
363
        if (count($ids) === 0) {
364
            return false;
365
        }
366
367
        $tlist = '';
368
        $hlist = '<ul>';
369
        foreach ($ids as $id) {
370
            $link = wl($id, [], true);
371
            $tlist .= '* ' . $link . NL;
372
            $hlist .= '<li><a href="' . $link . '">' . hsc($id) . '</a></li>' . NL;
373
        }
374
        $hlist .= '</ul>';
375
376
        $id = prettyprint_id($ns_id);
377
        $trep = [
378
            'DIFF' => rtrim($tlist),
379
            'PAGE' => $id,
380
            'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'),
381
        ];
382
        $hrep = [
383
            'DIFF' => $hlist,
384
        ];
385
386
        return $this->send(
387
            $subscriber_mail,
388
            'subscribe_list',
389
            $ns_id,
390
            'subscr_list',
391
            $trep,
392
            $hrep
393
        );
394
    }
395
}
396