1<?php
2
3/**
4 * Class action_plugin_tagging_main
5 */
6class action_plugin_tagging_main extends DokuWiki_Action_Plugin {
7
8    /**
9     * Register handlers
10     *
11     * @param Doku_Event_Handler $controller
12     */
13    public function register(Doku_Event_Handler $controller) {
14        $controller->register_hook(
15            'AJAX_CALL_UNKNOWN', 'BEFORE', $this,
16            'handle_ajax_call_unknown'
17        );
18
19        $controller->register_hook(
20            'ACTION_ACT_PREPROCESS', 'BEFORE', $this,
21            'handle_jump'
22        );
23
24        $controller->register_hook(
25            'DOKUWIKI_STARTED', 'AFTER', $this,
26            'js_add_security_token'
27        );
28
29        $controller->register_hook(
30            'PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this,
31            'update_moved_page'
32        );
33    }
34
35    /**
36     * Add sectok to JavaScript to secure ajax requests
37     *
38     * @param Doku_Event $event
39     * @param            $param
40     */
41    function js_add_security_token(Doku_Event $event, $param) {
42        global $JSINFO;
43        $JSINFO['sectok'] = getSecurityToken();
44    }
45
46    /**
47     * Handle our AJAX requests
48     *
49     * @param Doku_Event $event
50     * @param            $param
51     */
52    function handle_ajax_call_unknown(Doku_Event &$event, $param) {
53        $handled = true;
54
55        if ($event->data == 'plugin_tagging_save') {
56            $this->save();
57        } elseif ($event->data == 'plugin_tagging_autocomplete') {
58            $this->autocomplete();
59        } elseif ($event->data === 'plugin_tagging_admin_change') {
60            $this->admin_change();
61        } elseif ($event->data === 'plugin_tagging_html_pages') {
62            $this->getPagesHtml();
63        } elseif ($event->data === 'plugin_tagging_delete') {
64            $this->deleteTag();
65        } elseif ($event->data === 'plugin_tagging_rename') {
66            $this->renameTag();
67        } else {
68            $handled = false;
69        }
70        if (!$handled) {
71            return;
72        }
73
74        $event->preventDefault();
75        $event->stopPropagation();
76    }
77
78    /**
79     * Jump to a tag
80     *
81     * @param Doku_Event $event
82     * @param            $param
83     */
84    function handle_jump(Doku_Event &$event, $param) {
85        if (act_clean($event->data) != 'tagjmp') {
86            return;
87        }
88
89        $event->preventDefault();
90        $event->stopPropagation();
91        $event->data = 'show';
92
93        global $INPUT;
94        $tags = $INPUT->arr('tag', (array)$INPUT->str('tag'));
95        $lang = $INPUT->str('lang');
96
97        /** @var helper_plugin_tagging $hlp */
98        $hlp = plugin_load('helper', 'tagging');
99
100        foreach ($tags as $tag) {
101            $filter = array('tag' => $tag);
102            if ($lang) {
103                $filter['lang'] = $lang;
104            }
105            $pages = $hlp->findItems($filter, 'pid', 1);
106            if (!count($pages)) {
107                continue;
108            }
109
110            $pages = array_keys($pages);
111            $id = array_pop($pages);
112            send_redirect(wl($id, '', true, '&'));
113        }
114
115        $tags = array_map('hsc', $tags);
116        msg(sprintf($this->getLang('tagjmp_error'), join(', ', $tags)), -1);
117    }
118
119    /**
120     * Save new/changed tags
121     */
122    function save() {
123        global $INPUT;
124        global $INFO;
125
126        /** @var helper_plugin_tagging $hlp */
127        $hlp = plugin_load('helper', 'tagging');
128
129        $data = $INPUT->arr('tagging');
130        $id = $data['id'];
131        $INFO['writable'] = auth_quickaclcheck($id) >= AUTH_EDIT; // we also need this in findItems
132
133        if ($INFO['writable'] && $hlp->getUser()) {
134            $hlp->replaceTags(
135                $id, $hlp->getUser(),
136                preg_split(
137                    '/(\s*,\s*)|(\s*,?\s*\n\s*)/', $data['tags'], -1,
138                    PREG_SPLIT_NO_EMPTY
139                )
140            );
141            $hlp->updateElasticState($id);
142        }
143
144        $tags = $hlp->findItems(array('pid' => $id), 'tag');
145        $hlp->html_cloud($tags, 'tag', array($hlp, 'linkToSearch'), false);
146    }
147
148    /**
149     * Return autocompletion data
150     */
151    function autocomplete() {
152        global $INPUT;
153
154        /** @var helper_plugin_tagging $hlp */
155        $hlp = plugin_load('helper', 'tagging');
156
157        $search = $INPUT->str('term');
158        $tags = $hlp->findItems(array('tag' => '*' . $hlp->getDB()->escape_string($search) . '*'), 'tag');
159        arsort($tags);
160        $tags = array_keys($tags);
161
162        header('Content-Type: application/json');
163
164        echo json_encode(array_combine($tags, $tags));
165    }
166
167    /**
168     * Allow admins to change all tags (not only their own)
169     * We change the tag for every user
170     */
171    function admin_change() {
172        global $INPUT;
173
174        /** @var helper_plugin_tagging $hlp */
175        $hlp = plugin_load('helper', 'tagging');
176
177        header('Content-Type: application/json');
178
179        if (!auth_isadmin()) {
180            echo json_encode(array('status' => 'error', 'msg' => $this->getLang('no_admin')));
181            return;
182        }
183
184        if (!checkSecurityToken()) {
185            echo json_encode(array('status' => 'error', 'msg' => 'Security Token did not match. Possible CSRF attack.'));
186            return;
187        }
188
189        if (!$INPUT->has('id')) {
190            echo json_encode(array('status' => 'error', 'msg' => 'No page id given.'));
191            return;
192        }
193        $pid = $INPUT->str('id');
194
195        if (!$INPUT->has('oldValue') || !$INPUT->has('newValue')) {
196            echo json_encode(array('status' => 'error', 'msg' => 'No proper input. Give "oldValue" and "newValue"'));
197            return;
198        }
199
200
201        list($err, $msg) = $hlp->modifyPageTag($pid, $INPUT->str('oldValue'), $INPUT->str('newValue'));
202        if ($err) {
203            echo json_encode(array('status' => 'error', 'msg' => $msg));
204            return;
205        }
206
207        $tags = $hlp->findItems(array('pid' => $pid), 'tag');
208        $userTags = $hlp->findItems(array('pid' => $pid, 'tagger' => $hlp->getUser()), 'tag');
209        echo json_encode(array(
210            'status'          => 'ok',
211            'tags_edit_value' => implode(', ', array_keys($userTags)),
212            'html_cloud'      => $hlp->html_cloud($tags, 'tag', array($hlp, 'linkToSearch'), false, true),
213        ));
214    }
215
216    /**
217     * Management: delete all occurrences of a tag
218     */
219    public function deleteTag()
220    {
221        global $INPUT;
222        $data = $INPUT->arr('tagging');
223
224        /** @var helper_plugin_tagging $hlp */
225        $hlp = plugin_load('helper', 'tagging');
226        $hlp->deleteTags($data['tid']);
227
228        // update elasticsearch state for all relevant pages
229        $pids = $hlp->findItems(['tag' => $data['tid'][0]], 'pid');
230        if (!empty($pids)) {
231            foreach (array_keys($pids) as $pid) {
232                $hlp->updateElasticState($pid);
233            }
234        }
235    }
236
237    /**
238     * Management: rename all occurrences of a tag
239     */
240    public function renameTag()
241    {
242        global $INPUT;
243        $data = $INPUT->arr('tagging');
244
245        /** @var helper_plugin_tagging $hlp */
246        $hlp = plugin_load('helper', 'tagging');
247        $hlp->renameTag($data['oldValue'], $data['newValue']);
248
249        // update elasticsearch state for all relevant pages
250        $pids = $hlp->findItems(['tag' => $data['newValue']], 'pid');
251        if (!empty($pids)) {
252            foreach (array_keys($pids) as $pid) {
253                $hlp->updateElasticState($pid);
254            }
255        }
256    }
257
258    /**
259     * Tag dialog HTML: links to all pages with a given tag
260     *
261     * @return string
262     */
263    public function getPagesHtml()
264    {
265        global $INPUT;
266        $data = $INPUT->arr('tagging');
267
268        /** @var helper_plugin_tagging $hlp */
269        $hlp = plugin_load('helper', 'tagging');
270        echo $hlp->getPagesHtml($data['tid']);
271    }
272
273    /**
274     * Updates tagging database after a page has been moved/renamed by the move plugin
275     *
276     * @param Doku_Event $event
277     * @param $param
278     */
279    public function update_moved_page(Doku_Event $event, $param)
280    {
281        $src = $event->data['src_id'];
282        $dst = $event->data['dst_id'];
283
284        /** @var helper_plugin_tagging $hlp */
285        $hlp = plugin_load('helper', 'tagging');
286        $hlp->renamePage($src, $dst);
287    }
288}
289