1<?php
2
3/**
4 * DokuWiki Plugin acknowledge (Action Component)
5 *
6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
7 * @author  Andreas Gohr, Anna Dabrowska <dokuwiki@cosmocode.de>
8 */
9
10use dokuwiki\Extension\ActionPlugin;
11use dokuwiki\Extension\EventHandler;
12use dokuwiki\Extension\Event;
13use dokuwiki\Form\Form;
14
15class action_plugin_acknowledge extends ActionPlugin
16{
17    /** @inheritDoc */
18    public function register(EventHandler $controller)
19    {
20        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handlePageSave');
21        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxAcknowledge');
22        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxAutocomplete');
23        $controller->register_hook('PLUGIN_SQLITE_DATABASE_UPGRADE', 'AFTER', $this, 'handleUpgrade');
24    }
25
26    /**
27     * Manage page meta data
28     *
29     * Store page last modified date
30     * Handle page deletions
31     * Handle page creations
32     *
33     * @param Event $event
34     * @param $param
35     */
36    public function handlePageSave(Event $event, $param)
37    {
38        /** @var helper_plugin_acknowledge $helper */
39        $helper = plugin_load('helper', 'acknowledge');
40
41        if ($event->data['changeType'] === DOKU_CHANGE_TYPE_DELETE) {
42            $helper->removePage($event->data['id']); // this cascades to assignments
43        } elseif ($event->data['changeType'] !== DOKU_CHANGE_TYPE_MINOR_EDIT) {
44            $helper->storePageDate($event->data['id'], $event->data['newRevision'], $event->data['newContent']);
45        }
46
47        // Remove page assignees here because the syntax might have been removed
48        // they are readded on metadata rendering if still there
49        $helper->clearPageAssignments($event->data['id']);
50
51        if ($event->data['changeType'] === DOKU_CHANGE_TYPE_CREATE) {
52            // new pages need to have their auto assignments updated based on the existing patterns
53            $helper->setAutoAssignees($event->data['id']);
54        }
55    }
56
57    /**
58     * @param Event $event
59     * @param $param
60     */
61    public function handleAjaxAcknowledge(Event $event, $param)
62    {
63        if ($event->data === 'plugin_acknowledge_acknowledge') {
64            $event->stopPropagation();
65            $event->preventDefault();
66
67            global $INPUT;
68            $id = $INPUT->str('id');
69
70            if (page_exists($id)) {
71                echo $this->html();
72            }
73        }
74    }
75
76    /**
77     * @param Event $event
78     * @return void
79     */
80    public function handleAjaxAutocomplete(Event $event)
81    {
82        if ($event->data === 'plugin_acknowledge_autocomplete') {
83            if (!checkSecurityToken()) return;
84
85            global $INPUT;
86
87            $event->stopPropagation();
88            $event->preventDefault();
89
90            /** @var helper_plugin_acknowledge $hlp */
91            $hlp = $this->loadHelper('acknowledge');
92
93            $found = [];
94
95            if ($INPUT->has('user')) {
96                $search = $INPUT->str('user');
97                $knownUsers = $hlp->getUsers();
98                $found = array_filter($knownUsers, function ($user) use ($search) {
99                    return (strstr(strtolower($user['label']), strtolower($search))) !== false ? $user : null;
100                });
101            }
102
103            if ($INPUT->has('pg')) {
104                $search = $INPUT->str('pg');
105                $pages = ft_pageLookup($search, true);
106                $found = array_map(function ($id, $title) {
107                    return ['value' => $id, 'label' => $title ?? $id];
108                }, array_keys($pages), array_values($pages));
109            }
110
111            header('Content-Type: application/json');
112
113            echo json_encode($found);
114        }
115    }
116
117    /**
118     * Handle Migration events
119     *
120     * @param Event $event
121     * @param $param
122     * @return void
123     */
124    public function handleUpgrade(Event $event, $param)
125    {
126        if ($event->data['sqlite']->getAdapter()->getDbname() !== 'acknowledgement') {
127            return;
128        }
129        $to = $event->data['to'];
130        if ($to !== 3) return; // only handle upgrade to version 3
131
132        /** @var helper_plugin_acknowledge $helper */
133        $helper = plugin_load('helper', 'acknowledge');
134        $helper->updatePageIndex();
135    }
136
137    /**
138     * Returns the acknowledgment form/confirmation
139     *
140     * @return string The HTML to display
141     */
142    protected function html()
143    {
144        global $INPUT;
145        global $USERINFO;
146        $id = $INPUT->str('id');
147        $ackSubmitted = $INPUT->bool('ack');
148        $user = $INPUT->server->str('REMOTE_USER');
149        if ($id === '' || $user === '') return '';
150
151        /** @var helper_plugin_acknowledge $helper */
152        $helper = plugin_load('helper', 'acknowledge');
153
154        // only display for users assigned to the page
155        if (!$helper->isUserAssigned($id, $user, $USERINFO['grps'])) {
156            return '';
157        }
158
159        if ($ackSubmitted) {
160            $helper->saveAcknowledgement($id, $user);
161        }
162
163        $ack = $helper->hasUserAcknowledged($id, $user);
164
165        $html = '<div class="' . ($ack ? 'ack' : 'noack') . '">';
166        $html .= inlineSVG(__DIR__ . '/admin.svg');
167        $html .= '</div>';
168
169        if ($ack) {
170            $html .= '<div>';
171            $html .= '<h4>';
172            $html .= $this->getLang('ackOk');
173            $html .= '</h4>';
174            $html .= sprintf($this->getLang('ackGranted'), dformat($ack));
175            $html .= '</div>';
176        } else {
177            $html .= '<div>';
178            $html .= '<h4>' . $this->getLang('ackRequired') . '</h4>';
179            $latest = $helper->getLatestUserAcknowledgement($id, $user);
180            if ($latest) {
181                $html .= '<a href="'
182                    . wl($id, ['do' => 'diff', 'at' => $latest], false, '&') . '">'
183                    . sprintf($this->getLang('ackDiff'), dformat($latest))
184                    . '</a><br>';
185            }
186
187            $form = new Form(['id' => 'ackForm']);
188            $form->addCheckbox('ack', $this->getLang('ackText'))->attr('required', 'required');
189            $form->addHTML(
190                '<br><button type="submit" name="acksubmit" id="ack-submit">'
191                . $this->getLang('ackButton')
192                . '</button>'
193            );
194
195            $html .= $form->toHTML();
196            $html .= '</div>';
197        }
198
199        return $html;
200    }
201}
202