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
92        /** @var helper_plugin_ireadit $helper */
93        $helper = plugin_load('helper', 'ireadit');
94
95        //remove old rows
96        $sqlite->query('DELETE FROM ireadit WHERE timestamp IS NULL');
97
98        $res = $sqlite->query('SELECT page,meta FROM meta');
99        while ($row = $sqlite->res_fetch_assoc($res)) {
100            $page = $row['page'];
101            $meta = json_decode($row['meta'], true);
102            $user_set = $helper->users_set($meta);
103            $last_change_date = p_get_metadata($page, 'last_change date');
104
105            $users = $auth->retrieveUsers();
106            foreach ($users as $user => $info) {
107                $res2 = $sqlite->query('SELECT user FROM ireadit WHERE page=? AND rev=? AND user=?', $page, $last_change_date, $user);
108                $existsAlready = $sqlite->res2single($res2);
109                if (!$existsAlready && in_array($user, $user_set)) {
110                    $sqlite->storeEntry('ireadit', [
111                        'page' => $page,
112                        'rev' => $last_change_date,
113                        'user' => $user
114                    ]);
115                }
116            }
117        }
118    }
119
120    protected function migration3($data)
121    {
122        global $conf;
123
124        /** @var helper_plugin_sqlite $sqlite */
125        $sqlite = $data['sqlite'];
126
127        $res = $sqlite->query('SELECT page,meta FROM meta');
128        while ($row = $sqlite->res_fetch_assoc($res)) {
129            $last_change_date = p_get_metadata($row['page'], 'last_change date');
130            $sqlite->storeEntry('meta2', [
131                'page' => $row['page'],
132                'meta' => $row['meta'],
133                'last_change_date' => $last_change_date
134            ]);
135        }
136        $sqlite->query('DROP TABLE meta');
137        $sqlite->query('ALTER TABLE meta2 RENAME TO meta');
138    }
139
140    protected function migration2($data)
141    {
142        global $conf;
143
144        /** @var helper_plugin_sqlite $sqlite */
145        $sqlite = $data['sqlite'];
146        $db = $sqlite->getAdapter()->getPdo();
147
148        /* @var \helper_plugin_ireadit $helper */
149        $helper = plugin_load('helper', 'ireadit');
150
151
152        $datadir = $conf['datadir'];
153        if (substr($datadir, -1) != '/') {
154            $datadir .= '/';
155        }
156
157        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($datadir));
158        $pages = [];
159        foreach ($rii as $file) {
160            if ($file->isDir()){
161                continue;
162            }
163
164            //remove start path and extension
165            $page = substr($file->getPathname(), strlen($datadir), -4);
166            $pages[] = str_replace('/', ':', $page);
167        }
168
169        $db->beginTransaction();
170
171        foreach ($pages as $page) {
172            //import historic data
173            $meta = p_get_metadata($page, 'plugin ireadit');
174            if (!$meta) continue;
175
176            $sqlite->storeEntry('meta', [
177                'page' => $page,
178                'meta' => json_encode($meta)
179            ]);
180        }
181        $db->commit();
182
183
184    }
185
186    protected function migration1($data)
187    {
188        global $conf;
189
190        /** @var helper_plugin_sqlite $sqlite */
191        $sqlite = $data['sqlite'];
192        $db = $sqlite->getAdapter()->getPdo();
193
194        /* @var \helper_plugin_ireadit $helper */
195        $helper = plugin_load('helper', 'ireadit');
196
197
198        $datadir = $conf['datadir'];
199        if (substr($datadir, -1) != '/') {
200            $datadir .= '/';
201        }
202
203        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($datadir));
204        $pages = [];
205        foreach ($rii as $file) {
206            if ($file->isDir()){
207                continue;
208            }
209
210            //remove start path and extension
211            $page = substr($file->getPathname(), strlen($datadir), -4);
212            $pages[] = str_replace('/', ':', $page);
213        }
214        $db->beginTransaction();
215
216        foreach ($pages as $page) {
217            //import historic data
218            $meta = p_get_metadata($page, 'plugin_ireadit');
219            if (!$meta) continue; //no metadata
220
221            foreach ($meta as $rev => $data) {
222                if ($rev === '' || count($data) == 0) continue;
223                foreach ($data as $user_read) {
224                    $sqlite->storeEntry('ireadit', [
225                        'page' => $page,
226                        'rev' => $rev,
227                        'user' => $user_read['client'],
228                        'timestamp' => date('c', $user_read['time'])
229                    ]);
230                }
231            }
232
233            //import current data
234            $content = file_get_contents($datadir . str_replace(':', '/', $page) . '.txt');
235            $status = preg_match('/~~IREADIT.*~~/', $content, $matches);
236            //no ireadit on page
237            if ($status !== 1) continue;
238
239            $match = trim(substr($matches[0], strlen('~~IREADIT'), -2));
240            $splits = preg_split('/\s+/', $match, -1, PREG_SPLIT_NO_EMPTY);
241
242            $users = [];
243            $groups = [];
244            foreach ($splits as $split) {
245                if ($split[0] == '@') {
246                    $group = substr($split, 1);
247                    $groups[] = $group;
248                } else {
249                    $users[] = $split;
250                }
251            }
252
253            $usersToInsert = $helper->users_set($users, $groups);
254
255            if ($usersToInsert) {
256                $last_change_date = p_get_metadata($page, 'last_change date');
257                foreach ($usersToInsert as $user) {
258                    $this->insertOrIgnore($sqlite,'ireadit', [
259                        'page' => $page,
260                        'rev' => $last_change_date,
261                        'user' => $user
262                    ]);
263                }
264            }
265
266        }
267        $db->commit();
268
269        return true;
270    }
271}
272