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