1<?php
2/**
3 * DokuWiki Plugin struct (Helper Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Szymon Olewniczak <dokuwiki@cosmocode.de>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) {
11    die();
12}
13
14class helper_plugin_ireadit extends DokuWiki_Plugin
15{
16    /**
17     * @param array $users
18     * @param array $groups
19     * @return array
20     */
21    public function users_set($ireadit_data) {
22        global $auth;
23
24        $users = $ireadit_data['users'];
25        $groups = $ireadit_data['groups'];
26        $set = [];
27        if (empty($users) && empty($groups)) {
28            $set = $auth->retrieveUsers();
29        } else {
30            $all_users = $auth->retrieveUsers();
31            foreach ($all_users as $user => $info) {
32                if (in_array($user, $users)) {
33                    $set[$user] = true;
34                } elseif (array_intersect($groups, $info['grps'])) {
35                    $set[$user] = true;
36                }
37            }
38        }
39        return array_keys($set);
40    }
41
42    public function find_last_approved($id) {
43        /** @var helper_plugin_approve $approve_helper */
44        $approve_helper = plugin_load('helper', 'approve');
45        if ($approve_helper == null) {
46            msg('You must install approve plugin to use ireadit-approve integration.', -1);
47            return null;
48        }
49
50        try {
51            /** @var \helper_plugin_approve_db $db_helper */
52            $approve_db_helper = plugin_load('helper', 'approve_db');
53            $approve_sqlite = $approve_db_helper->getDB();
54        } catch (Exception $e) {
55            msg($e->getMessage(), -1);
56            return null;
57        }
58
59        return $approve_helper->find_last_approved($approve_sqlite, $id);
60    }
61
62    public function use_approve_here($id) {
63        /** @var helper_plugin_approve $approve_helper */
64        $approve_helper = plugin_load('helper', 'approve');
65        if ($approve_helper == null) {
66            msg('You must install approve plugin to use ireadit-approve integration.', -1);
67            return null;
68        }
69
70        try {
71            /** @var \helper_plugin_approve_db $db_helper */
72            $approve_db_helper = plugin_load('helper', 'approve_db');
73            $approve_sqlite = $approve_db_helper->getDB();
74        } catch (Exception $e) {
75            msg($e->getMessage(), -1);
76            return null;
77        }
78
79        return $approve_helper->use_approve_here($approve_sqlite, $id);
80    }
81
82    public function get_approved_revs($id) {
83        try {
84            /** @var \helper_plugin_approve_db $db_helper */
85            $approve_db_helper = plugin_load('helper', 'approve_db');
86            if ($approve_db_helper == null) {
87                msg('You must install approve plugin to use ireadit-approve integration.', -1);
88                return [];
89            }
90            $approve_sqlite = $approve_db_helper->getDB();
91        } catch (Exception $e) {
92            msg($e->getMessage(), -1);
93            return [];
94        }
95
96        $res = $approve_sqlite->query('SELECT rev FROM revision WHERE page=? AND approved IS NOT NULL', $id);
97        return array_map(function ($row) {
98            return (int) $row['rev'];
99        }, $approve_sqlite->res2arr($res));
100    }
101
102    public function use_ireadit_here($id, $rev) {
103        if ($this->getConf('approve_integration') && $this->use_approve_here($id)) { // check if this is newest approve page
104            $last_approved_rev = $this->find_last_approved($id);
105            if ($rev == $last_approved_rev) { // this is last approved version
106                return true;
107            }
108        } elseif ($rev == p_get_metadata($id, 'last_change date')) { // check if it is last page revision
109            return true;
110        }
111        return false;
112    }
113
114    public function user_can_read_page($ireadit_data, $id, $rev, $user) {
115        if (!$this->use_ireadit_here($id, $rev)) return false;
116
117        try {
118            /** @var \helper_plugin_ireadit_db $db_helper */
119            $db_helper = plugin_load('helper', 'ireadit_db');
120            $sqlite = $db_helper->getDB();
121        } catch (Exception $e) {
122            msg($e->getMessage(), -1);
123            return false;
124        }
125
126        $res = $sqlite->query('SELECT user, timestamp FROM ireadit
127                                        WHERE page = ?
128                                        AND rev = ?
129                                        ORDER BY timestamp', $id, $rev);
130        $readers = $sqlite->res2arr($res);
131        $users_set = $this->users_set($ireadit_data);
132        return in_array($user, $users_set) && !in_array($user, array_column($readers, 'user'));
133    }
134
135    /**
136     * @param $user NULL means overview mode
137     * @return array
138     */
139    public function get_list($user=NULL) {
140        try {
141            /** @var \helper_plugin_ireadit_db $db_helper */
142            $db_helper = plugin_load('helper', 'ireadit_db');
143            $sqlite = $db_helper->getDB();
144        } catch (Exception $e) {
145            msg($e->getMessage(), -1);
146            return [];
147        }
148
149        $indexer = idx_get_indexer();
150        if ($user) {
151            $current_user_pages = $indexer->lookupKey('ireadit', $user);
152        } else {
153            $current_user_pages = $indexer->getPages('ireadit');
154        }
155
156        $pages = [];
157        foreach ($current_user_pages as $page) {
158            $current_rev = p_get_metadata($page, 'last_change date');
159
160            $pages[$page] = [
161                'current_rev' => $current_rev,
162                'last_read_rev' => NULL,
163                'timestamp' => NULL
164            ];
165        }
166        if ($user) {
167            $res = $sqlite->query('SELECT page, MAX(rev) as "rev", timestamp FROM ireadit WHERE user=? GROUP BY page',
168                $user);
169        } else {
170            $res = $sqlite->query('SELECT page, MAX(rev) as "rev", timestamp FROM ireadit GROUP BY page');
171        }
172        while ($row = $sqlite->res_fetch_assoc($res)) {
173            $page = $row['page'];
174            $rev = (int) $row['rev'];
175            $timestamp = $row['timestamp'];
176            if (isset($pages[$page])) {
177                $pages[$page]['last_read_rev'] = $rev;
178                $pages[$page]['timestamp'] = $timestamp;
179            }
180        }
181
182        if ($this->getConf('approve_integration')) {
183            foreach ($current_user_pages as $page) {
184                if (!$this->use_approve_here($page)) continue; // ignore the pages where approve doesn't apply
185                $approved_revs = $this->get_approved_revs($page);
186                if (count($approved_revs) == 0) { // page was never approved - don't list it
187                    unset($pages[$page]);
188                    continue;
189                }
190
191                $current_rev = max($approved_revs);
192                if ($user) {
193                    $res = $sqlite->query('SELECT rev, timestamp FROM ireadit WHERE user=? AND page=? ORDER BY rev DESC',
194                        $user, $page);
195                } else {
196                    $res = $sqlite->query('SELECT rev, timestamp FROM ireadit WHERE page=? ORDER BY rev DESC', $page);
197                }
198                $user_reads = $sqlite->res2arr($res);
199                $last_read_rev = NULL;
200                $last_read_timestamp = NULL;
201                foreach ($user_reads as $row) {
202                    $rev = (int) $row['rev'];
203                    if (in_array($rev, $approved_revs)) {
204                        $last_read_rev = $rev;
205                        $last_read_timestamp = $row['timestamp'];
206                        break;
207                    }
208                }
209
210                $pages[$page] = [
211                    'current_rev' => $current_rev,
212                    'last_read_rev' => $last_read_rev,
213                    'timestamp' => $last_read_timestamp
214                ]; // override default values
215            }
216        }
217
218        // apply states to pages
219        foreach ($pages as &$page) {
220            if ($page['current_rev'] == $page['last_read_rev']) {
221                $page['state'] = 'read';
222            } elseif ($page['last_read_rev'] == NULL) {
223                $page['state'] = 'unread';
224            } else {
225                $page['state'] = 'outdated';
226            }
227        }
228        return $pages;
229    }
230}
231