xref: /plugin/acknowledge/action/ajax.php (revision c2e33bcede677b26c01ce484dd9ed10c198ffb08)
1<?php
2
3/**
4 * DokuWiki Plugin acknowledge (AJAX 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_ajax extends ActionPlugin
16{
17    /** @inheritDoc */
18    public function register(EventHandler $controller)
19    {
20        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxAcknowledge');
21        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxAutocomplete');
22        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxUserList');
23    }
24
25    /**
26     * @param Event $event
27     * @param $param
28     */
29    public function handleAjaxAcknowledge(Event $event, $param)
30    {
31        if ($event->data === 'plugin_acknowledge_acknowledge') {
32            $event->stopPropagation();
33            $event->preventDefault();
34
35            global $INPUT;
36            $id = $INPUT->str('id');
37
38            if (page_exists($id)) {
39                echo $this->html();
40            }
41        }
42    }
43
44    /**
45     * @param Event $event
46     * @return void
47     */
48    public function handleAjaxAutocomplete(Event $event)
49    {
50        if ($event->data === 'plugin_acknowledge_autocomplete') {
51            if (!checkSecurityToken()) return;
52
53            global $INPUT;
54
55            $event->stopPropagation();
56            $event->preventDefault();
57
58            /** @var helper_plugin_acknowledge $hlp */
59            $hlp = $this->loadHelper('acknowledge');
60
61            $found = [];
62
63            if ($INPUT->has('user')) {
64                $search = $INPUT->str('user');
65                $knownUsers = $hlp->getUsers();
66                $found = array_filter($knownUsers, function ($user) use ($search) {
67                    return (strstr(strtolower($user['label']), strtolower($search))) !== false ? $user : null;
68                });
69            }
70
71            if ($INPUT->has('pg')) {
72                $search = $INPUT->str('pg');
73                $pages = ft_pageLookup($search, true);
74                $found = array_map(function ($id, $title) {
75                    return ['value' => $id, 'label' => $title ?? $id];
76                }, array_keys($pages), array_values($pages));
77            }
78
79            header('Content-Type: application/json');
80
81            echo json_encode($found);
82        }
83    }
84
85    /**
86     * Returns the full user list for a report section (loaded on demand)
87     *
88     * @param Event $event
89     * @return void
90     */
91    public function handleAjaxUserList(Event $event)
92    {
93        if ($event->data !== 'plugin_acknowledge_userlist') return;
94
95        $event->stopPropagation();
96        $event->preventDefault();
97
98        if (!auth_ismanager()) return;
99
100        global $INPUT;
101        $id = $INPUT->str('id');
102        $status = $INPUT->str('status');
103
104        if (!page_exists($id)) return;
105        if (!in_array($status, ['current', 'due'], true)) return;
106
107        /** @var helper_plugin_acknowledge $helper */
108        $helper = plugin_load('helper', 'acknowledge');
109
110        if (!$helper->getPageAssignees($id)) return;
111
112        echo $this->userListHtml($helper->getPageAcknowledgements($id, '', $status));
113    }
114
115    /**
116     * Returns the acknowledgment form/confirmation and optionally management report
117     *
118     * @return string The HTML to display
119     */
120    protected function html()
121    {
122        global $INPUT;
123        $id = $INPUT->str('id');
124        $user = $INPUT->server->str('REMOTE_USER');
125        if ($id === '' || $user === '') return '';
126
127        /** @var helper_plugin_acknowledge $helper */
128        $helper = plugin_load('helper', 'acknowledge');
129
130        return $this->bannerHtml($id, $user, $helper) . $this->reportHtml($id, $helper);
131    }
132
133    /**
134     * Returns the personal acknowledgement banner
135     *
136     * @param string $id
137     * @param string $user
138     * @param helper_plugin_acknowledge $helper
139     * @return string
140     */
141    protected function bannerHtml($id, $user, helper_plugin_acknowledge $helper)
142    {
143        global $INPUT;
144        global $USERINFO;
145
146        // only display for users assigned to the page
147        if (!$helper->isUserAssigned($id, $user, $USERINFO['grps'])) {
148            return '';
149        }
150
151        // if the approve plugin is active, only show if the page is approved
152        if ($helper->isBlockedByApprove($id)) {
153            return '';
154        }
155
156        if ($INPUT->bool('ack')) {
157            $helper->saveAcknowledgement($id, $user);
158        }
159
160        $ack = $helper->hasUserAcknowledged($id, $user);
161
162        $html = '<div class="plugin-acknowledge-box ack' . ($ack ? ' done' : '') . '">';
163        $html .= '<div class="ack-icon">';
164        $html .= inlineSVG(__DIR__ . '/../admin.svg');
165        $html .= '</div>';
166
167        $html .= '<div class="content">';
168        if ($ack) {
169            $html .= '<h4>';
170            $html .= $this->getLang('ackOk');
171            $html .= '</h4>';
172            $html .= sprintf($this->getLang('ackGranted'), dformat($ack));
173        } else {
174            $html .= '<h4>' . $this->getLang('ackRequired') . '</h4>';
175            $latest = $helper->getLatestUserAcknowledgement($id, $user);
176            if ($latest) {
177                $html .= '<a href="'
178                    . wl($id, ['do' => 'diff', 'at' => $latest], false, '&') . '">'
179                    . sprintf($this->getLang('ackDiff'), dformat($latest))
180                    . '</a><br>';
181            }
182
183            $form = new Form(['id' => 'ackForm']);
184            $form->addCheckbox('ack', $this->getLang('ackText'))->attr('required', 'required');
185            $form->addHTML(
186                '<br><button type="submit" name="acksubmit" id="ack-submit">'
187                . $this->getLang('ackButton')
188                . '</button>'
189            );
190
191            $html .= $form->toHTML();
192        }
193        $html .= '</div>'; // content
194        $html .= '</div>'; // box
195
196        return $html;
197    }
198
199    /**
200     * Returns the manager/admin report box
201     *
202     * @param string $id
203     * @param helper_plugin_acknowledge $helper
204     * @return string
205     */
206    protected function reportHtml($id, helper_plugin_acknowledge $helper)
207    {
208        $mode = $this->getConf('onpage_report');
209        if ($mode === 'off') return '';
210
211        if (!auth_ismanager()) return '';
212
213        if (!$helper->getPageAssignees($id)) return '';
214
215        $html = '<div class="plugin-acknowledge-box report">';
216
217        $html .= '<div class="ack-icon">';
218        $html .= inlineSVG(__DIR__ . '/../admin.svg');
219        $html .= '</div>';
220
221        $html .= '<div class="content">';
222        $html .= '<h3>' . $this->getLang('reportTitle') . '</h3>';
223
224        // resolve group membership once, derive both counts arithmetically
225        $counts = $helper->getPageAcknowledgementCounts($id);
226
227        if ($mode === 'acknowledged' || $mode === 'both') {
228            $html .= '<p>' . $this->getLang('reportAcknowledgedTitle') . '</p>';
229            $html .= $this->userCountHtml($id, 'current', $counts['current']);
230        }
231
232        if ($mode === 'pending' || $mode === 'both') {
233            $html .= '<p>' . $this->getLang('reportPendingTitle') . '</p>';
234            $html .= $this->userCountHtml($id, 'due', $counts['due']);
235        }
236
237        $html .= '</div>'; // content
238        $html .= '</div>'; // box
239
240        return $html;
241    }
242
243    /**
244     * Renders a clickable user count that loads the full user list on demand
245     *
246     * @param string $id
247     * @param string $status acknowledgement status ('current' or 'due')
248     * @param int $count
249     * @return string
250     */
251    protected function userCountHtml($id, $status, $count)
252    {
253        if (!$count) {
254            return '<p>' . $this->getLang('reportNobody') . '</p>';
255        }
256
257        return '<p><a href="#" class="plugin-acknowledge-loadusers"'
258            . ' data-id="' . hsc($id) . '" data-status="' . hsc($status) . '">'
259            . sprintf($this->getLang('reportUserCount'), $count)
260            . '</a></p>';
261    }
262
263    /**
264     * Renders a list of users from acknowledgement records.
265     *
266     * @param array $rows
267     * @return string
268     */
269    protected function userListHtml($rows)
270    {
271        if (!$rows) {
272            return '<p>' . $this->getLang('reportNobody') . '</p>';
273        }
274
275        $html = '<ul>';
276        foreach ($rows as $row) {
277            $html .= '<li>';
278            $html .= userlink($row['user']);
279
280            if (!empty($row['ack'])) {
281                $html .= ' ' . $this->getLang('reportAckedOn') . ' ' . hsc(dformat($row['ack']));
282            }
283            $html .= '</li>';
284        }
285        $html .= '</ul>';
286
287        return $html;
288    }
289}
290