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_db $approve_db */
44        $approve_db = plugin_load('helper', 'approve_db');
45        if ($approve_db == null) {
46            msg('You must install approve plugin to use ireadit-approve integration.', -1);
47            return null;
48        }
49
50        return $approve_db->getLastDbRev($id, 'approved');
51    }
52
53    public function use_approve_here($id) {
54        /** @var helper_plugin_approve_acl $approve_acl */
55        $approve_acl = plugin_load('helper', 'approve_acl');
56        if ($approve_acl == null) {
57            msg('You must install approve plugin to use ireadit-approve integration.', -1);
58            return null;
59        }
60
61        return $approve_acl->useApproveHere($id);
62    }
63
64    public function get_approved_revs($id) {
65        /** @var helper_plugin_approve_db $approve_db */
66        $approve_db = plugin_load('helper', 'approve_db');
67        if ($approve_db == null) {
68            msg('You must install approve plugin to use ireadit-approve integration.', -1);
69            return null;
70        }
71        $revs = $approve_db->getPageRevisions($id);
72        $approved_revs = array_filter($revs, function ($rev) {
73            return $rev['status'] == 'approved';
74        });
75        return array_map(function ($row) {
76            return (int) $row['rev'];
77        }, $approved_revs);
78
79        try {
80            /** @var \helper_plugin_approve_db $db_helper */
81            $approve_db_helper = plugin_load('helper', 'approve_db');
82            if ($approve_db_helper == null) {
83                msg('You must install approve plugin to use ireadit-approve integration.', -1);
84                return [];
85            }
86            $approve_sqlite = $approve_db_helper->getDB();
87        } catch (Exception $e) {
88            msg($e->getMessage(), -1);
89            return [];
90        }
91
92        $res = $approve_sqlite->query('SELECT rev FROM revision WHERE page=? AND approved IS NOT NULL', $id);
93        return array_map(function ($row) {
94            return (int) $row['rev'];
95        }, $approve_sqlite->res2arr($res));
96    }
97
98    public function use_ireadit_here($id, $rev) {
99        if ($this->getConf('approve_integration') && $this->use_approve_here($id)) { // check if this is newest approve page
100            $last_approved_rev = $this->find_last_approved($id);
101            if ($rev == $last_approved_rev) { // this is last approved version
102                return true;
103            }
104        } elseif ($rev == p_get_metadata($id, 'last_change date')) { // check if it is last page revision
105            return true;
106        }
107        return false;
108    }
109
110    public function user_can_read_page($ireadit_data, $id, $rev, $user) {
111        if (!$this->use_ireadit_here($id, $rev)) return false;
112
113        try {
114            /** @var \helper_plugin_ireadit_db $db_helper */
115            $db_helper = plugin_load('helper', 'ireadit_db');
116            $sqlite = $db_helper->getDB();
117        } catch (Exception $e) {
118            msg($e->getMessage(), -1);
119            return false;
120        }
121
122        $res = $sqlite->query('SELECT user, timestamp FROM ireadit
123                                        WHERE page = ?
124                                        AND rev = ?
125                                        ORDER BY timestamp', $id, $rev);
126        $readers = $sqlite->res2arr($res);
127        $users_set = $this->users_set($ireadit_data);
128        return in_array($user, $users_set) && !in_array($user, array_column($readers, 'user'));
129    }
130
131    /**
132     * @param $user NULL means overview mode
133     * @return array
134     */
135    public function get_list($user=NULL) {
136        try {
137            /** @var \helper_plugin_ireadit_db $db_helper */
138            $db_helper = plugin_load('helper', 'ireadit_db');
139            $sqlite = $db_helper->getDB();
140        } catch (Exception $e) {
141            msg($e->getMessage(), -1);
142            return [];
143        }
144
145        $indexer = idx_get_indexer();
146        if ($user) {
147            $current_user_pages = $indexer->lookupKey('ireadit', $user);
148        } else {
149            $current_user_pages = $indexer->getPages('ireadit');
150        }
151
152        $pages = [];
153        foreach ($current_user_pages as $page) {
154            $current_rev = p_get_metadata($page, 'last_change date');
155
156            $pages[$page] = [
157                'current_rev' => $current_rev,
158                'last_read_rev' => NULL,
159                'timestamp' => NULL
160            ];
161        }
162        if ($user) {
163            $res = $sqlite->query('SELECT page, MAX(rev) as "rev", timestamp FROM ireadit WHERE user=? GROUP BY page',
164                $user);
165        } else {
166            $res = $sqlite->query('SELECT page, MAX(rev) as "rev", timestamp FROM ireadit GROUP BY page');
167        }
168        while ($row = $sqlite->res_fetch_assoc($res)) {
169            $page = $row['page'];
170            $rev = (int) $row['rev'];
171            $timestamp = $row['timestamp'];
172            if (isset($pages[$page])) {
173                $pages[$page]['last_read_rev'] = $rev;
174                $pages[$page]['timestamp'] = $timestamp;
175            }
176        }
177
178        if ($this->getConf('approve_integration')) {
179            foreach ($current_user_pages as $page) {
180                if (!$this->use_approve_here($page)) continue; // ignore the pages where approve doesn't apply
181                $approved_revs = $this->get_approved_revs($page);
182                if (count($approved_revs) == 0) { // page was never approved - don't list it
183                    unset($pages[$page]);
184                    continue;
185                }
186
187                $current_rev = max($approved_revs);
188                if ($user) {
189                    $res = $sqlite->query('SELECT rev, timestamp FROM ireadit WHERE user=? AND page=? ORDER BY rev DESC',
190                        $user, $page);
191                } else {
192                    $res = $sqlite->query('SELECT rev, timestamp FROM ireadit WHERE page=? ORDER BY rev DESC', $page);
193                }
194                $user_reads = $sqlite->res2arr($res);
195                $last_read_rev = NULL;
196                $last_read_timestamp = NULL;
197                foreach ($user_reads as $row) {
198                    $rev = (int) $row['rev'];
199                    if (in_array($rev, $approved_revs)) {
200                        $last_read_rev = $rev;
201                        $last_read_timestamp = $row['timestamp'];
202                        break;
203                    }
204                }
205
206                $pages[$page] = [
207                    'current_rev' => $current_rev,
208                    'last_read_rev' => $last_read_rev,
209                    'timestamp' => $last_read_timestamp
210                ]; // override default values
211            }
212        }
213
214        // apply states to pages
215        foreach ($pages as &$page) {
216            if ($page['current_rev'] == $page['last_read_rev']) {
217                $page['state'] = 'read';
218            } elseif ($page['last_read_rev'] == NULL) {
219                $page['state'] = 'unread';
220            } else {
221                $page['state'] = 'outdated';
222            }
223        }
224        return $pages;
225    }
226}
227