1<?php
2/**
3 * DokuWiki Plugin acknowledge (Helper Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Andreas Gohr, Anna Dabrowska <dokuwiki@cosmocode.de>
7 */
8
9
10class helper_plugin_acknowledge extends DokuWiki_Plugin
11{
12
13    /**
14     * @return helper_plugin_sqlite|null
15     */
16    public function getDB()
17    {
18        /** @var \helper_plugin_sqlite $sqlite */
19        $sqlite = plugin_load('helper', 'sqlite');
20        if ($sqlite === null) {
21            msg($this->getLang('error sqlite plugin missing'), -1);
22            return null;
23        }
24        if (!$sqlite->init('acknowledgement', __DIR__ . '/db')) {
25            return null;
26        }
27
28        $this->registerUDF($sqlite);
29
30        return $sqlite;
31    }
32
33    /**
34     * Register user defined functions
35     *
36     * @param helper_plugin_sqlite $sqlite
37     */
38    protected function registerUDF($sqlite)
39    {
40        $sqlite->create_function('AUTH_ISMEMBER', [$this, 'auth_isMember'], -1);
41    }
42
43    /**
44     * Wrapper function for auth_isMember which accepts groups as string
45     *
46     * @param string $memberList
47     * @param string $user
48     * @param string $groups
49     * @return bool
50     */
51    public function auth_isMember($memberList, $user, $groups)
52    {
53        return auth_isMember($memberList, $user, explode('///', $groups));
54    }
55
56    /**
57     * Delete a page
58     *
59     * Cascades to delete all assigned data, etc.
60     *
61     * @param string $page Page ID
62     */
63    public function removePage($page)
64    {
65        $sqlite = $this->getDB();
66        if (!$sqlite) return;
67
68        $sql = "DELETE FROM pages WHERE page = ?";
69        $sqlite->query($sql, $page);
70    }
71
72    /**
73     * Update last modified date of page if content has changed
74     *
75     * @param string $page Page ID
76     * @param int $lastmod timestamp of last non-minor change
77     */
78    public function storePageDate($page, $lastmod, $newContent)
79    {
80        $changelog = new \dokuwiki\ChangeLog\PageChangeLog($page);
81        $revs = $changelog->getRevisions(0, 1);
82
83        // compare content
84        $oldContent = str_replace(NL, '', io_readFile(wikiFN($page, $revs[0])));
85        $newContent = str_replace(NL, '', $newContent);
86        if ($oldContent === $newContent) return;
87
88        $sqlite = $this->getDB();
89        if (!$sqlite) return;
90
91        $sql = "REPLACE INTO pages (page, lastmod) VALUES (?,?)";
92        $sqlite->query($sql, $page, $lastmod);
93    }
94
95    /**
96     * @param string $page Page ID
97     * @param string $assignees comma separated list of users and groups
98     */
99    public function setAssignees($page, $assignees)
100    {
101        $sqlite = $this->getDB();
102        if (!$sqlite) return;
103
104        $sql = "REPLACE INTO assignments ('page', 'assignee') VALUES (?,?)";
105        $sqlite->query($sql, $page, $assignees);
106    }
107
108    /**
109     * Clears assignments for a page
110     *
111     * @param string $page Page ID
112     */
113    public function clearAssignments($page)
114    {
115        $sqlite = $this->getDB();
116        if (!$sqlite) return;
117
118        $sql = "DELETE FROM assignments WHERE page = ?";
119        $sqlite->query($sql, $page);
120    }
121
122
123    /**
124     * Is the given user one of the assignees for this page
125     *
126     * @param string $page Page ID
127     * @param string $user user name to check
128     * @param string[] $groups groups this user is in
129     * @return bool
130     */
131    public function isUserAssigned($page, $user, $groups)
132    {
133        $sqlite = $this->getDB();
134        if (!$sqlite) return false;
135
136
137        $sql = "SELECT assignee FROM assignments WHERE page = ?";
138        $result = $sqlite->query($sql, $page);
139        $assignees = (string)$sqlite->res2single($result);
140        $sqlite->res_close($result);
141
142        return auth_isMember($assignees, $user, $groups);
143    }
144
145    /**
146     * Has the given user acknowledged the given page?
147     *
148     * @param string $page
149     * @param string $user
150     * @return bool|int timestamp of acknowledgement or false
151     */
152    public function hasUserAcknowledged($page, $user)
153    {
154        $sqlite = $this->getDB();
155        if (!$sqlite) return false;
156
157        $sql = "SELECT ack
158                  FROM acks A, pages B
159                 WHERE A.page = B.page
160                   AND A.page = ?
161                   AND A.user = ?
162                   AND A.ack >= B.lastmod";
163
164        $result = $sqlite->query($sql, $page, $user);
165        $acktime = $sqlite->res2single($result);
166        $sqlite->res_close($result);
167
168        return $acktime ? (int)$acktime : false;
169    }
170
171    /**
172     * Timestamp of the latest acknowledgment of the given page
173     * by the given user
174     *
175     * @param string $page
176     * @param string $user
177     * @return bool|string
178     */
179    public function getLatestUserAcknowledgement($page, $user)
180    {
181        $sqlite = $this->getDB();
182        if (!$sqlite) return false;
183
184        $sql = "SELECT MAX(ack)
185                  FROM acks
186                 WHERE page = ?
187                   AND user = ?";
188
189        $result = $sqlite->query($sql, $page, $user);
190        $latestAck = $sqlite->res2single($result);
191        $sqlite->res_close($result);
192
193        return $latestAck;
194    }
195
196    /**
197     * Save user's acknowledgement for a given page
198     *
199     * @param string $page
200     * @param string $user
201     * @return bool
202     */
203    public function saveAcknowledgement($page, $user)
204    {
205        $sqlite = $this->getDB();
206        if (!$sqlite) return false;
207
208        $sql = "INSERT INTO acks (page, user, ack) VALUES (?,?, strftime('%s','now'))";
209
210        $result = $sqlite->query($sql, $page, $user);
211        $sqlite->res_close($result);
212        return true;
213
214    }
215
216    /**
217     * Fetch all assignments for a given user, with additional page information,
218     * filtering already granted acknowledgements.
219     *
220     * @param string $user
221     * @param array $groups
222     * @return array|bool
223     */
224    public function getUserAssignments($user, $groups)
225    {
226        $sqlite = $this->getDB();
227        if (!$sqlite) return false;
228
229        $sql = "SELECT A.page, A.assignee, B.lastmod, C.user, C.ack FROM assignments A
230                JOIN pages B
231                ON A.page = B.page
232                LEFT JOIN acks C
233                ON A.page = C.page AND ( (C.user = ? AND C.ack > B.lastmod) )
234                WHERE AUTH_ISMEMBER(A.assignee, ? , ?)
235                AND ack IS NULL";
236
237        $result = $sqlite->query($sql, $user, $user, implode('///', $groups));
238        $assignments = $sqlite->res2arr($result);
239        $sqlite->res_close($result);
240
241        return $assignments;
242    }
243
244    /**
245     * Returns all acknowledgements
246     *
247     * @return array|bool
248     */
249    public function getAcknowledgements()
250    {
251        $sqlite = $this->getDB();
252        if (!$sqlite) return false;
253
254        $sql = 'SELECT page, user, max(ack) AS ack FROM acks GROUP BY user,page ORDER BY ack DESC';
255        $result = $sqlite->query($sql);
256        $acknowledgements = $sqlite->res2arr($result);
257        $sqlite->res_close($result);
258
259        return $acknowledgements;
260    }
261}
262
263