xref: /plugin/approve/action/migration.php (revision b2a20814e2132f0970dfea9e9c08b6f06c98837e)
1<?php
2/**
3 * DokuWiki Plugin bez (Action Component)
4 *
5 */
6
7// must be run within Dokuwiki
8
9if (!defined('DOKU_INC')) die();
10
11/**
12 * Class action_plugin_bez_migration
13 *
14 * Handle migrations that need more than just SQL
15 */
16class action_plugin_approve_migration extends DokuWiki_Action_Plugin
17{
18    /**
19     * @inheritDoc
20     */
21    public function register(Doku_Event_Handler $controller)
22    {
23        $controller->register_hook('PLUGIN_SQLITE_DATABASE_UPGRADE', 'AFTER', $this, 'handle_migrations');
24    }
25
26    /**
27     * Call our custom migrations when defined
28     *
29     * @param Doku_Event $event
30     * @param $param
31     */
32    public function handle_migrations(Doku_Event $event, $param)
33    {
34        if ($event->data['sqlite']->getAdapter()->getDbname() !== 'approve') {
35            return;
36        }
37        $to = $event->data['to'];
38
39        if (is_callable([$this, "migration$to"])) {
40            $event->result = call_user_func([$this, "migration$to"], $event->data);
41        }
42    }
43
44    /**
45     * Convenience function to run an INSERT ... ON CONFLICT IGNORE operation
46     *
47     * The function takes a key-value array with the column names in the key and the actual value in the value,
48     * build the appropriate query and executes it.
49     *
50     * @param string $table the table the entry should be saved to (will not be escaped)
51     * @param array $entry A simple key-value pair array (only values will be escaped)
52     * @return bool|SQLiteResult
53     */
54    protected function insertOrIgnore(helper_plugin_sqlite $sqlite, $table, $entry) {
55        $keys = join(',', array_keys($entry));
56        $vals = join(',', array_fill(0,count($entry),'?'));
57
58        $sql = "INSERT OR IGNORE INTO $table ($keys) VALUES ($vals)";
59        return $sqlite->query($sql, array_values($entry));
60    }
61
62    protected function migration1($data)
63    {
64        global $conf;
65
66        /** @var helper_plugin_sqlite $sqlite */
67        $sqlite = $data['sqlite'];
68        $db = $sqlite->getAdapter()->getDb();
69
70
71        $datadir = $conf['datadir'];
72        if (substr($datadir, -1) != '/') {
73            $datadir .= '/';
74        }
75
76        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($datadir));
77        $pages = [];
78        foreach ($rii as $file) {
79            if ($file->isDir()){
80                continue;
81            }
82
83            //remove start path and extension
84            $page = substr($file->getPathname(), strlen($datadir), -4);
85            $pages[] = str_replace('/', ':', $page);
86			$pages[] = str_replace('\\', ':', $page);
87        }
88
89        $db->beginTransaction();
90
91        $apr_namespaces = preg_split('/\s+/', $this->getConf('apr_namespaces', ''),
92            -1,PREG_SPLIT_NO_EMPTY);
93
94        if (!$apr_namespaces) {
95            $sqlite->storeEntry('maintainer',[
96                'namespace' => '**'
97            ]);
98        } else {
99            foreach ($apr_namespaces as $namespace) {
100                $namespace = rtrim($namespace, ':');
101                $namespace .= ':**';
102                $sqlite->storeEntry('maintainer',[
103                    'namespace' => $namespace
104                ]);
105            }
106        }
107
108        //store config
109        $no_apr_namespaces = $this->getConf('no_apr_namespaces', '');
110        $sqlite->storeEntry('config',[
111            'key' => 'no_apr_namespaces',
112            'value' => $no_apr_namespaces
113        ]);
114
115        $no_apr_namespaces_list = preg_split('/\s+/', $no_apr_namespaces,-1,
116            PREG_SPLIT_NO_EMPTY);
117        $no_apr_namespaces_list = array_map(function ($namespace) {
118            return trim($namespace, ':');
119        }, $no_apr_namespaces_list);
120
121
122        foreach ($pages as $page) {
123            //import historic data
124            $versions = p_get_metadata($page, 'plugin_approve_versions');
125            if (!$versions) {
126                $versions = $this->render_metadata_for_approved_page($page);
127            }
128
129//            $last_change_date = p_get_metadata($page, 'last_change date');
130            $last_change_date = @filemtime(wikiFN($page));
131            $last_version = $versions[0];
132
133            //remove current versions to not process it here
134            unset($versions[0]);
135            unset($versions[$last_change_date]);
136
137            $revision_editors = $this->revision_editors($page);
138            foreach ($versions as $rev => $version) {
139                $data = [
140                    'page' => $page,
141                    'rev' => $rev,
142                    'approved' => date('c', $rev),
143                    'approved_by' => $revision_editors[$rev],
144                    'version' => $version
145                ];
146                $sqlite->storeEntry('revision', $data);
147            }
148
149            //process current data
150            $summary = p_get_metadata($page, 'last_change sum');
151            $user = p_get_metadata($page, 'last_change user');
152            $data = [
153                'page' => $page,
154                'rev' => $last_change_date,
155                'current' => 1
156            ];
157            if ($this->getConf('ready_for_approval') &&
158                $summary == $this->getConf('sum ready for approval')) {
159                $data['ready_for_approval'] = date('c', $last_change_date);
160                $data['ready_for_approval_by'] = $user;
161            } elseif($summary == $this->getConf('sum approved')) {
162                $data['approved'] = date('c', $last_change_date);
163                $data['approved_by'] = $user;
164                $data['version'] = $last_version;
165            }
166            $sqlite->storeEntry('revision', $data);
167
168
169            //empty apr_namespaces - all match
170            if (!$apr_namespaces) {
171                $in_apr_namespace = true;
172            } else {
173                $in_apr_namespace = false;
174                foreach ($apr_namespaces as $namespace) {
175                    if (substr($page, 0, strlen($namespace)) == $namespace) {
176                        $in_apr_namespace = true;
177                        break;
178                    }
179                }
180            }
181
182            if ($in_apr_namespace) {
183                $hidden = '0';
184                foreach ($no_apr_namespaces_list as $namespace) {
185                    if (substr($page, 0, strlen($namespace)) == $namespace) {
186                        $hidden = '1';
187                        break;
188                    }
189                }
190                $sqlite->storeEntry('page', [
191                    'page' => $page,
192                    'hidden' => $hidden
193                ]);
194            }
195        }
196
197
198        $db->commit();
199
200        return true;
201    }
202
203    /**
204     * Calculate current version
205     *
206     * @param $id
207     * @return array
208     */
209    protected function render_metadata_for_approved_page($id, $currev=false) {
210        if (!$currev) $currev = @filemtime(wikiFN($id));
211
212        $version = $this->approved($id);
213        //version for current page
214        $curver = $version + 1;
215        $versions = array(0 => $curver, $currev => $curver);
216
217        $changelog = new PageChangeLog($id);
218        $first = 0;
219        $num = 100;
220        while (count($revs = $changelog->getRevisions($first, $num)) > 0) {
221            foreach ($revs as $rev) {
222                $revInfo = $changelog->getRevisionInfo($rev);
223                if ($revInfo['sum'] == $this->getConf('sum approved')) {
224                    $versions[$rev] = $version;
225                    $version -= 1;
226                }
227            }
228            $first += $num;
229        }
230
231//        p_set_metadata($id, array(ApproveConst::METADATA_VERSIONS_KEY => $versions));
232
233        return $versions;
234    }
235
236    /**
237     * Get the number of approved pages
238     * @param $id
239     * @return int
240     */
241    protected function approved($id) {
242        $count = 0;
243
244        $changelog = new PageChangeLog($id);
245        $first = 0;
246        $num = 100;
247        while (count($revs = $changelog->getRevisions($first, $num)) > 0) {
248            foreach ($revs as $rev) {
249                $revInfo = $changelog->getRevisionInfo($rev);
250                if ($revInfo['sum'] == $this->getConf('sum approved')) {
251                    $count += 1;
252                }
253            }
254            $first += $num;
255        }
256
257        return $count;
258    }
259
260    /**
261     * Calculate current version
262     *
263     * @param $id
264     * @return array
265     */
266    protected function revision_editors($id)
267    {
268        $currev = @filemtime(wikiFN($id));
269        $user = p_get_metadata($id, 'last_change user');
270
271        $revision_editors = array($currev => $user);
272
273        $changelog = new PageChangeLog($id);
274        $first = 0;
275        $num = 100;
276        while (count($revs = $changelog->getRevisions($first, $num)) > 0) {
277            foreach ($revs as $rev) {
278                $revInfo = $changelog->getRevisionInfo($rev);
279                $revision_editors[$rev] = $revInfo['user'];
280            }
281            $first += $num;
282        }
283
284        return $revision_editors;
285    }
286}
287