Failed Conditions
Push — psr2 ( 749c00...b8c09b )
by Andreas
06:25 queued 06:21
created

SubscriberManager::isenabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace dokuwiki\Subscriptions;
4
5
use dokuwiki\Input\Input;
6
use DokuWiki_Auth_Plugin;
7
use Exception;
8
9
class SubscriberManager
10
{
11
12
    /**
13
     * Check if subscription system is enabled
14
     *
15
     * @return bool
16
     */
17
    public function isenabled()
18
    {
19
        return actionOK('subscribe');
20
    }
21
22
    /**
23
     * Adds a new subscription for the given page or namespace
24
     *
25
     * This will automatically overwrite any existent subscription for the given user on this
26
     * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
27
     *
28
     * @throws Exception when user or style is empty
29
     *
30
     * @param string $id The target page or namespace, specified by id; Namespaces
31
     *                   are identified by appending a colon.
32
     * @param string $user
33
     * @param string $style
34
     * @param string $data
35
     *
36
     * @return bool
37
     */
38
    public function add($id, $user, $style, $data = '')
39
    {
40
        if (!$this->isenabled()) {
41
            return false;
42
        }
43
44
        // delete any existing subscription
45
        $this->remove($id, $user);
46
47
        $user = auth_nameencode(trim($user));
48
        $style = trim($style);
49
        $data = trim($data);
50
51
        if (!$user) {
52
            throw new Exception('no subscription user given');
53
        }
54
        if (!$style) {
55
            throw new Exception('no subscription style given');
56
        }
57
        if (!$data) {
58
            $data = time();
59
        } //always add current time for new subscriptions
60
61
        $line = "$user $style $data\n";
62
        $file = $this->file($id);
63
        return io_saveFile($file, $line, true);
64
    }
65
66
67
    /**
68
     * Removes a subscription for the given page or namespace
69
     *
70
     * This removes all subscriptions matching the given criteria on the given page or
71
     * namespace. It will *not* modify any subscriptions that may exist in higher
72
     * namespaces.
73
     *
74
     * @param string       $id The target object’s (namespace or page) id
75
     * @param string|array $user
76
     * @param string|array $style
77
     * @param string|array $data
78
     *
79
     * @return bool
80
     */
81
    public function remove($id, $user = null, $style = null, $data = null)
82
    {
83
        if (!$this->isenabled()) {
84
            return false;
85
        }
86
87
        $file = $this->file($id);
88
        if (!file_exists($file)) {
89
            return true;
90
        }
91
92
        $regexBuilder = new SubscriberRegexBuilder();
93
        $re = $regexBuilder->buildRegex($user, $style, $data);
94
        return io_deleteFromFile($file, $re, true);
95
    }
96
97
    /**
98
     * Get data for $INFO['subscribed']
99
     *
100
     * $INFO['subscribed'] is either false if no subscription for the current page
101
     * and user is in effect. Else it contains an array of arrays with the fields
102
     * “target”, “style”, and optionally “data”.
103
     *
104
     * @author Adrian Lang <[email protected]>
105
     *
106
     * @param string $id   Page ID, defaults to global $ID
107
     * @param string $user User, defaults to $_SERVER['REMOTE_USER']
108
     *
109
     * @return array|false
110
     */
111
    public function userSubscription($id = '', $user = '')
112
    {
113
        if (!$this->isenabled()) {
114
            return false;
115
        }
116
117
        global $ID;
118
        /** @var Input $INPUT */
119
        global $INPUT;
120
        if (!$id) {
121
            $id = $ID;
122
        }
123
        if (!$user) {
124
            $user = $INPUT->server->str('REMOTE_USER');
125
        }
126
127
        $subs = $this->subscribers($id, $user);
128
        if (!count($subs)) {
129
            return false;
130
        }
131
132
        $result = [];
133
        foreach ($subs as $target => $info) {
134
            $result[] = [
135
                'target' => $target,
136
                'style' => $info[$user][0],
137
                'data' => $info[$user][1],
138
            ];
139
        }
140
141
        return $result;
142
    }
143
144
    /**
145
     * Recursively search for matching subscriptions
146
     *
147
     * This function searches all relevant subscription files for a page or
148
     * namespace.
149
     *
150
     * @author Adrian Lang <[email protected]>
151
     *
152
     * @param string       $page The target object’s (namespace or page) id
153
     * @param string|array $user
154
     * @param string|array $style
155
     * @param string|array $data
156
     *
157
     * @return array
158
     */
159
    public function subscribers($page, $user = null, $style = null, $data = null)
160
    {
161
        if (!$this->isenabled()) {
162
            return [];
163
        }
164
165
        // Construct list of files which may contain relevant subscriptions.
166
        $files = [':' => $this->file(':')];
167
        do {
168
            $files[$page] = $this->file($page);
169
            $page = getNS(rtrim($page, ':')) . ':';
170
        } while ($page !== ':');
171
172
        $regexBuilder = new SubscriberRegexBuilder();
173
        $re = $regexBuilder->buildRegex($user, $style, $data);
174
175
        // Handle files.
176
        $result = [];
177
        foreach ($files as $target => $file) {
178
            if (!file_exists($file)) {
179
                continue;
180
            }
181
182
            $lines = file($file);
183
            foreach ($lines as $line) {
184
                // fix old style subscription files
185
                if (strpos($line, ' ') === false) {
186
                    $line = trim($line) . " every\n";
187
                }
188
189
                // check for matching entries
190
                if (!preg_match($re, $line, $m)) {
191
                    continue;
192
                }
193
194
                $u = rawurldecode($m[1]); // decode the user name
195
                if (!isset($result[$target])) {
196
                    $result[$target] = [];
197
                }
198
                $result[$target][$u] = [$m[2], $m[3]]; // add to result
199
            }
200
        }
201
        return array_reverse($result);
202
    }
203
204
    /**
205
     * Default callback for COMMON_NOTIFY_ADDRESSLIST
206
     *
207
     * Aggregates all email addresses of user who have subscribed the given page with 'every' style
208
     *
209
     * @author Adrian Lang <[email protected]>
210
     * @author Steven Danz <[email protected]>
211
     *
212
     * @todo   move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
213
     *         use an array for the addresses within it
214
     *
215
     * @param array &$data Containing the entries:
216
     *                     - $id (the page id),
217
     *                     - $self (whether the author should be notified,
218
     *                     - $addresslist (current email address list)
219
     *                     - $replacements (array of additional string substitutions, @KEY@ to be replaced by value)
220
     */
221
    public function notifyAddresses(&$data)
222
    {
223
        if (!$this->isenabled()) {
224
            return;
225
        }
226
227
        /** @var DokuWiki_Auth_Plugin $auth */
228
        global $auth;
229
        global $conf;
230
        /** @var \Input $INPUT */
231
        global $INPUT;
232
233
        $id = $data['id'];
234
        $self = $data['self'];
235
        $addresslist = $data['addresslist'];
236
237
        $subscriptions = $this->subscribers($id, null, 'every');
238
239
        $result = [];
240
        foreach ($subscriptions as $target => $users) {
241
            foreach ($users as $user => $info) {
242
                $userinfo = $auth->getUserData($user);
243
                if ($userinfo === false) {
244
                    continue;
245
                }
246
                if (!$userinfo['mail']) {
247
                    continue;
248
                }
249
                if (!$self && $user == $INPUT->server->str('REMOTE_USER')) {
250
                    continue;
251
                } //skip our own changes
252
253
                $level = auth_aclcheck($id, $user, $userinfo['grps']);
254
                if ($level >= AUTH_READ) {
255
                    if (strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
256
                        $result[$user] = $userinfo['mail'];
257
                    }
258
                }
259
            }
260
        }
261
        $data['addresslist'] = trim($addresslist . ',' . implode(',', $result), ',');
262
    }
263
264
    /**
265
     * Return the subscription meta file for the given ID
266
     *
267
     * @author Adrian Lang <[email protected]>
268
     *
269
     * @param string $id The target page or namespace, specified by id; Namespaces
270
     *                   are identified by appending a colon.
271
     *
272
     * @return string
273
     */
274
    protected function file($id)
275
    {
276
        $meta_fname = '.mlist';
277
        if ((substr($id, -1, 1) === ':')) {
278
            $meta_froot = getNS($id);
279
            $meta_fname = '/' . $meta_fname;
280
        } else {
281
            $meta_froot = $id;
282
        }
283
        return metaFN((string)$meta_froot, $meta_fname);
284
    }
285
}
286