1<?php
2/**
3 * DokuWiki Plugin bez (Action Component)
4 *
5 */
6
7use dokuwiki\Cache\CacheRenderer;
8
9// must be run within Dokuwiki
10
11if (!defined('DOKU_INC')) die();
12
13/**
14 * Class action_plugin_bez_migration
15 *
16 * Handle migrations that need more than just SQL
17 */
18class action_plugin_ireadit_migration extends DokuWiki_Action_Plugin
19{
20    /**
21     * @inheritDoc
22     */
23    public function register(Doku_Event_Handler $controller)
24    {
25        $controller->register_hook('PLUGIN_SQLITE_DATABASE_UPGRADE', 'AFTER', $this, 'handle_migrations');
26    }
27
28    /**
29     * Call our custom migrations when defined
30     *
31     * @param Doku_Event $event
32     * @param $param
33     */
34    public function handle_migrations(Doku_Event $event, $param)
35    {
36        if ($event->data['sqlite']->getAdapter()->getDbname() !== 'ireadit') {
37            return;
38        }
39        $to = $event->data['to'];
40
41        if (is_callable([$this, "migration$to"])) {
42            $event->result = call_user_func([$this, "migration$to"], $event->data);
43        }
44    }
45
46    /**
47     * Convenience function to run an INSERT ... ON CONFLICT IGNORE operation
48     *
49     * The function takes a key-value array with the column names in the key and the actual value in the value,
50     * build the appropriate query and executes it.
51     *
52     * @param string $table the table the entry should be saved to (will not be escaped)
53     * @param array $entry A simple key-value pair array (only values will be escaped)
54     * @return bool|SQLiteResult
55     */
56    protected function insertOrIgnore(helper_plugin_sqlite $sqlite, $table, $entry) {
57        $keys = join(',', array_keys($entry));
58        $vals = join(',', array_fill(0,count($entry),'?'));
59
60        $sql = "INSERT OR IGNORE INTO $table ($keys) VALUES ($vals)";
61        return $sqlite->query($sql, array_values($entry));
62    }
63
64    protected function migration7($data)
65    {
66        global $conf;
67        $data = array();
68        search($data, $conf['datadir'], 'search_allpages', array('skipacl' => true));
69        foreach($data as $val) {
70            //import current data
71            $wikiFN = wikiFN($val['id']);
72            $content = file_get_contents($wikiFN);
73            $status = preg_match('/~~IREADIT.*~~/', $content, $matches);
74            // we use ireadit here
75            if ($status === 1) {
76                $cachefile = new CacheRenderer($val['id'], wikiFN($val['id']), 'metadata');
77                $cachefile->removeCache();
78                p_get_metadata($val['id'], 'plugin_ireadit=0.2'); // render metadata
79                idx_addPage($val['id'], false, true); // regenerate index for the plugin
80            }
81        }
82    }
83
84    protected function migration5($data)
85    {
86        /** @var DokuWiki_Auth_Plugin */
87        global $auth;
88
89        /** @var helper_plugin_sqlite $sqlite */
90        $sqlite = $data['sqlite'];
91        $db = $sqlite->getAdapter()->getDb();
92
93        /** @var helper_plugin_ireadit $helper */
94        $helper = plugin_load('helper', 'ireadit');
95
96        //remove old rows
97        $sqlite->query('DELETE FROM ireadit WHERE timestamp IS NULL');
98
99        $res = $sqlite->query('SELECT page,meta FROM meta');
100        while ($row = $sqlite->res_fetch_assoc($res)) {
101            $page = $row['page'];
102            $meta = json_decode($row['meta'], true);
103            $user_set = $helper->users_set($meta);
104            $last_change_date = p_get_metadata($page, 'last_change date');
105
106            $users = $auth->retrieveUsers();
107            foreach ($users as $user => $info) {
108                $res2 = $sqlite->query('SELECT user FROM ireadit WHERE page=? AND rev=? AND user=?', $page, $last_change_date, $user);
109                $existsAlready = $sqlite->res2single($res2);
110                if (!$existsAlready && in_array($user, $user_set)) {
111                    $sqlite->storeEntry('ireadit', [
112                        'page' => $page,
113                        'rev' => $last_change_date,
114                        'user' => $user
115                    ]);
116                }
117            }
118        }
119    }
120
121    protected function migration3($data)
122    {
123        global $conf;
124
125        /** @var helper_plugin_sqlite $sqlite */
126        $sqlite = $data['sqlite'];
127        $db = $sqlite->getAdapter()->getDb();
128
129        $res = $sqlite->query('SELECT page,meta FROM meta');
130        while ($row = $sqlite->res_fetch_assoc($res)) {
131            $last_change_date = p_get_metadata($row['page'], 'last_change date');
132            $sqlite->storeEntry('meta2', [
133                'page' => $row['page'],
134                'meta' => $row['meta'],
135                'last_change_date' => $last_change_date
136            ]);
137        }
138        $sqlite->query('DROP TABLE meta');
139        $sqlite->query('ALTER TABLE meta2 RENAME TO meta');
140    }
141
142    protected function migration2($data)
143    {
144        global $conf;
145
146        /** @var helper_plugin_sqlite $sqlite */
147        $sqlite = $data['sqlite'];
148        $db = $sqlite->getAdapter()->getDb();
149
150        /* @var \helper_plugin_ireadit $helper */
151        $helper = plugin_load('helper', 'ireadit');
152
153
154        $datadir = $conf['datadir'];
155        if (substr($datadir, -1) != '/') {
156            $datadir .= '/';
157        }
158
159        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($datadir));
160        $pages = [];
161        foreach ($rii as $file) {
162            if ($file->isDir()){
163                continue;
164            }
165
166            //remove start path and extension
167            $page = substr($file->getPathname(), strlen($datadir), -4);
168            $pages[] = str_replace('/', ':', $page);
169        }
170
171        $db->beginTransaction();
172
173        foreach ($pages as $page) {
174            //import historic data
175            $meta = p_get_metadata($page, 'plugin ireadit');
176            if (!$meta) continue;
177
178            $sqlite->storeEntry('meta', [
179                'page' => $page,
180                'meta' => json_encode($meta)
181            ]);
182        }
183        $db->commit();
184
185
186    }
187
188    protected function migration1($data)
189    {
190        global $conf;
191
192        /** @var helper_plugin_sqlite $sqlite */
193        $sqlite = $data['sqlite'];
194        $db = $sqlite->getAdapter()->getDb();
195
196        /* @var \helper_plugin_ireadit $helper */
197        $helper = plugin_load('helper', 'ireadit');
198
199
200        $datadir = $conf['datadir'];
201        if (substr($datadir, -1) != '/') {
202            $datadir .= '/';
203        }
204
205        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($datadir));
206        $pages = [];
207        foreach ($rii as $file) {
208            if ($file->isDir()){
209                continue;
210            }
211
212            //remove start path and extension
213            $page = substr($file->getPathname(), strlen($datadir), -4);
214            $pages[] = str_replace('/', ':', $page);
215        }
216        $db->beginTransaction();
217
218        foreach ($pages as $page) {
219            //import historic data
220            $meta = p_get_metadata($page, 'plugin_ireadit');
221            if (!$meta) continue; //no metadata
222
223            foreach ($meta as $rev => $data) {
224                if ($rev === '' || count($data) == 0) continue;
225                foreach ($data as $user_read) {
226                    $sqlite->storeEntry('ireadit', [
227                        'page' => $page,
228                        'rev' => $rev,
229                        'user' => $user_read['client'],
230                        'timestamp' => date('c', $user_read['time'])
231                    ]);
232                }
233            }
234
235            //import current data
236            $content = file_get_contents($datadir . str_replace(':', '/', $page) . '.txt');
237            $status = preg_match('/~~IREADIT.*~~/', $content, $matches);
238            //no ireadit on page
239            if ($status !== 1) continue;
240
241            $match = trim(substr($matches[0], strlen('~~IREADIT'), -2));
242            $splits = preg_split('/\s+/', $match, -1, PREG_SPLIT_NO_EMPTY);
243
244            $users = [];
245            $groups = [];
246            foreach ($splits as $split) {
247                if ($split[0] == '@') {
248                    $group = substr($split, 1);
249                    $groups[] = $group;
250                } else {
251                    $users[] = $split;
252                }
253            }
254
255            $usersToInsert = $helper->users_set($users, $groups);
256
257            if ($usersToInsert) {
258                $last_change_date = p_get_metadata($page, 'last_change date');
259                foreach ($usersToInsert as $user) {
260                    $this->insertOrIgnore($sqlite,'ireadit', [
261                        'page' => $page,
262                        'rev' => $last_change_date,
263                        'user' => $user
264                    ]);
265                }
266            }
267
268        }
269        $db->commit();
270
271        return true;
272    }
273}
274