xref: /plugin/approve/action/migration.php (revision c7d53eaba29e207f5d28ea550cd66a04c154a49d)
1<?php
2/**
3 * DokuWiki Plugin bez (Action Component)
4 *
5 */
6
7// must be run within Dokuwiki
8
9use dokuwiki\plugin\approve\meta\ApproveConst;
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_approve_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() !== 'approve') {
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 migration1($data)
65    {
66        global $conf;
67
68        /** @var helper_plugin_sqlite $sqlite */
69        $sqlite = $data['sqlite'];
70        $db = $sqlite->getAdapter()->getDb();
71
72
73        $datadir = $conf['datadir'];
74        if (substr($datadir, -1) != '/') {
75            $datadir .= '/';
76        }
77
78        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($datadir));
79        $pages = [];
80        foreach ($rii as $file) {
81            if ($file->isDir()){
82                continue;
83            }
84
85            //remove start path and extension
86            $page = substr($file->getPathname(), strlen($datadir), -4);
87            $pages[] = str_replace('/', ':', $page);
88        }
89
90        $db->beginTransaction();
91
92        $apr_namespaces = preg_split('/\s+/', $this->getConf('apr_namespaces'),
93            -1,PREG_SPLIT_NO_EMPTY);
94
95        if (!$apr_namespaces) {
96            $sqlite->storeEntry('maintainer',[
97                'namespace' => '**'
98            ]);
99        } else {
100            foreach ($apr_namespaces as $namespace) {
101                $namespace = rtrim($namespace, ':');
102                $namespace .= ':**';
103                $sqlite->storeEntry('maintainer',[
104                    'namespace' => $namespace
105                ]);
106            }
107        }
108
109        //store config
110        $no_apr_namespaces = $this->getConf('no_apr_namespaces');
111        $sqlite->storeEntry('config',[
112            'key' => 'no_apr_namespaces',
113            'value' => $no_apr_namespaces
114        ]);
115
116        $no_apr_namespaces_list = preg_split('/\s+/', $no_apr_namespaces,-1,
117            PREG_SPLIT_NO_EMPTY);
118        $no_apr_namespaces_list = array_map(function ($namespace) {
119            return trim($namespace, ':');
120        }, $no_apr_namespaces_list);
121
122
123        foreach ($pages as $page) {
124            //import historic data
125            $versions = p_get_metadata($page, ApproveConst::METADATA_VERSIONS_KEY);
126            if (!$versions) {
127                $versions = $this->render_metadata_for_approved_page($page);
128            }
129
130//            $last_change_date = p_get_metadata($page, 'last_change date');
131            $last_change_date = @filemtime(wikiFN($page));
132            $last_version = $versions[0];
133
134            //remove current versions to not process it here
135            unset($versions[0]);
136            unset($versions[$last_change_date]);
137
138            $revision_editors = $this->revision_editors($page);
139            foreach ($versions as $rev => $version) {
140                $data = [
141                    'page' => $page,
142                    'rev' => $rev,
143                    'approved' => date('c', $rev),
144                    'approved_by' => $revision_editors[$rev],
145                    'version' => $version
146                ];
147                $sqlite->storeEntry('revision', $data);
148            }
149
150            //process current data
151            $summary = p_get_metadata($page, 'last_change sum');
152            $user = p_get_metadata($page, 'last_change user');
153            $data = [
154                'page' => $page,
155                'rev' => $last_change_date,
156                'current' => 1
157            ];
158            if ($this->getConf('ready_for_approval') &&
159                $summary == $this->getConf('sum ready for approval')) {
160                $data['ready_for_approval'] = date('c', $last_change_date);
161                $data['ready_for_approval_by'] = $user;
162            } elseif($summary == $this->getConf('sum approved')) {
163                $data['approved'] = date('c', $last_change_date);
164                $data['approved_by'] = $user;
165                $data['version'] = $last_version;
166            }
167            $sqlite->storeEntry('revision', $data);
168
169
170            //empty apr_namespaces - all match
171            if (!$apr_namespaces) {
172                $in_apr_namespace = true;
173            } else {
174                $in_apr_namespace = false;
175                foreach ($apr_namespaces as $namespace) {
176                    if (substr($page, 0, strlen($namespace)) == $namespace) {
177                        $in_apr_namespace = true;
178                        break;
179                    }
180                }
181            }
182
183            if ($in_apr_namespace) {
184                $hidden = '0';
185                foreach ($no_apr_namespaces_list as $namespace) {
186                    if (substr($page, 0, strlen($namespace)) == $namespace) {
187                        $hidden = '1';
188                        break;
189                    }
190                }
191                $sqlite->storeEntry('page', [
192                    'page' => $page,
193                    'hidden' => $hidden
194                ]);
195            }
196        }
197
198
199        $db->commit();
200
201        return true;
202    }
203
204    /**
205     * Calculate current version
206     *
207     * @param $id
208     * @return array
209     */
210    protected function render_metadata_for_approved_page($id, $currev=false) {
211        if (!$currev) $currev = @filemtime(wikiFN($id));
212
213        $version = $this->approved($id);
214        //version for current page
215        $curver = $version + 1;
216        $versions = array(0 => $curver, $currev => $curver);
217
218        $changelog = new PageChangeLog($id);
219        $first = 0;
220        $num = 100;
221        while (count($revs = $changelog->getRevisions($first, $num)) > 0) {
222            foreach ($revs as $rev) {
223                $revInfo = $changelog->getRevisionInfo($rev);
224                if ($revInfo['sum'] == $this->getConf('sum approved')) {
225                    $versions[$rev] = $version;
226                    $version -= 1;
227                }
228            }
229            $first += $num;
230        }
231
232//        p_set_metadata($id, array(ApproveConst::METADATA_VERSIONS_KEY => $versions));
233
234        return $versions;
235    }
236
237    /**
238     * Get the number of approved pages
239     * @param $id
240     * @return int
241     */
242    protected function approved($id) {
243        $count = 0;
244
245        $changelog = new PageChangeLog($id);
246        $first = 0;
247        $num = 100;
248        while (count($revs = $changelog->getRevisions($first, $num)) > 0) {
249            foreach ($revs as $rev) {
250                $revInfo = $changelog->getRevisionInfo($rev);
251                if ($revInfo['sum'] == $this->getConf('sum approved')) {
252                    $count += 1;
253                }
254            }
255            $first += $num;
256        }
257
258        return $count;
259    }
260
261    /**
262     * Calculate current version
263     *
264     * @param $id
265     * @return array
266     */
267    protected function revision_editors($id)
268    {
269        $currev = @filemtime(wikiFN($id));
270        $user = p_get_metadata($id, 'last_change user');
271
272        $revision_editors = array($currev => $user);
273
274        $changelog = new PageChangeLog($id);
275        $first = 0;
276        $num = 100;
277        while (count($revs = $changelog->getRevisions($first, $num)) > 0) {
278            foreach ($revs as $rev) {
279                $revInfo = $changelog->getRevisionInfo($rev);
280                $revision_editors[$rev] = $revInfo['user'];
281            }
282            $first += $num;
283        }
284
285        return $revision_editors;
286    }
287}
288