1<?php
2
3namespace dokuwiki\Action;
4
5use dokuwiki\Action\Exception\ActionAbort;
6use dokuwiki\Action\Exception\ActionDisabledException;
7use dokuwiki\Subscriptions\SubscriberManager;
8use dokuwiki\Extension\Event;
9use dokuwiki\Ui;
10use Exception;
11
12/**
13 * Class Subscribe
14 *
15 * E-Mail subscription handling
16 *
17 * @package dokuwiki\Action
18 */
19class Subscribe extends AbstractUserAction
20{
21    /** @inheritdoc */
22    public function minimumPermission()
23    {
24        return AUTH_READ;
25    }
26
27    /** @inheritdoc */
28    public function checkPreconditions()
29    {
30        parent::checkPreconditions();
31
32        global $conf;
33        if (isset($conf['subscribers']) && !$conf['subscribers']) throw new ActionDisabledException();
34    }
35
36    /** @inheritdoc */
37    public function preProcess()
38    {
39        try {
40            $this->handleSubscribeData();
41        } catch (ActionAbort $e) {
42            throw $e;
43        } catch (Exception $e) {
44            msg($e->getMessage(), -1);
45        }
46    }
47
48    /** @inheritdoc */
49    public function tplContent()
50    {
51        (new Ui\Subscribe())->show();
52    }
53
54    /**
55     * Handle page 'subscribe'
56     *
57     * @author Adrian Lang <lang@cosmocode.de>
58     * @throws Exception if (un)subscribing fails
59     * @throws ActionAbort when (un)subscribing worked
60     */
61    protected function handleSubscribeData()
62    {
63        global $lang;
64        global $INFO;
65        global $INPUT;
66
67        // get and preprocess data.
68        $params = [];
69        foreach (['target', 'style', 'action'] as $param) {
70            if ($INPUT->has("sub_$param")) {
71                $params[$param] = $INPUT->str("sub_$param");
72            }
73        }
74
75        // any action given? if not just return and show the subscription page
76        if (empty($params['action']) || !checkSecurityToken()) return;
77
78        // Handle POST data, may throw exception.
79        Event::createAndTrigger('ACTION_HANDLE_SUBSCRIBE', $params, [$this, 'handlePostData']);
80
81        $target = $params['target'];
82        $style = $params['style'];
83        $action = $params['action'];
84
85        // Perform action.
86        $subManager = new SubscriberManager();
87        if ($action === 'unsubscribe') {
88            $ok = $subManager->remove($target, $INPUT->server->str('REMOTE_USER'), $style);
89        } else {
90            $ok = $subManager->add($target, $INPUT->server->str('REMOTE_USER'), $style);
91        }
92
93        if ($ok) {
94            msg(
95                sprintf(
96                    $lang["subscr_{$action}_success"],
97                    hsc($INFO['userinfo']['name']),
98                    prettyprint_id($target)
99                ),
100                1
101            );
102            throw new ActionAbort('redirect');
103        }
104
105        throw new Exception(
106            sprintf(
107                $lang["subscr_{$action}_error"],
108                hsc($INFO['userinfo']['name']),
109                prettyprint_id($target)
110            )
111        );
112    }
113
114    /**
115     * Validate POST data
116     *
117     * Validates POST data for a subscribe or unsubscribe request. This is the
118     * default action for the event ACTION_HANDLE_SUBSCRIBE.
119     *
120     * @author Adrian Lang <lang@cosmocode.de>
121     *
122     * @param array &$params the parameters: target, style and action
123     * @throws Exception
124     */
125    public function handlePostData(&$params)
126    {
127        global $INFO;
128        global $lang;
129        global $INPUT;
130
131        // Get and validate parameters.
132        if (!isset($params['target'])) {
133            throw new Exception('no subscription target given');
134        }
135        $target = $params['target'];
136        $valid_styles = ['every', 'digest'];
137        if (str_ends_with($target, ':')) {
138            // Allow “list” subscribe style since the target is a namespace.
139            $valid_styles[] = 'list';
140        }
141        $style = valid_input_set(
142            'style',
143            $valid_styles,
144            $params,
145            'invalid subscription style given'
146        );
147        $action = valid_input_set(
148            'action',
149            ['subscribe', 'unsubscribe'],
150            $params,
151            'invalid subscription action given'
152        );
153
154        // Check other conditions.
155        if ($action === 'subscribe') {
156            if ($INFO['userinfo']['mail'] === '') {
157                throw new Exception($lang['subscr_subscribe_noaddress']);
158            }
159        } elseif ($action === 'unsubscribe') {
160            $is = false;
161            foreach ($INFO['subscribed'] as $subscr) {
162                if ($subscr['target'] === $target) {
163                    $is = true;
164                }
165            }
166            if ($is === false) {
167                throw new Exception(
168                    sprintf(
169                        $lang['subscr_not_subscribed'],
170                        $INPUT->server->str('REMOTE_USER'),
171                        prettyprint_id($target)
172                    )
173                );
174            }
175            // subscription_set deletes a subscription if style = null.
176            $style = null;
177        }
178
179        $params = ['target' => $target, 'style' => $style, 'action' => $action];
180    }
181}
182