xref: /plugin/structpublish/action/migration.php (revision c4618b6c6265ede1d9a027588363ed678b625ef7)
187106851SAnna Dabrowska<?php
287106851SAnna Dabrowska
3f734c62fSAnna Dabrowskause dokuwiki\plugin\sqlite\Tools;
4f734c62fSAnna Dabrowska
587106851SAnna Dabrowskaclass action_plugin_structpublish_migration extends DokuWiki_Action_Plugin
687106851SAnna Dabrowska{
7bcee0e72SAnna Dabrowska    const MIN_DB_STRUCT = 19;
8bcee0e72SAnna Dabrowska
9d93c534aSAnna Dabrowska    /** @var string  */
10d93c534aSAnna Dabrowska    protected $table = 'data_structpublish';
11d93c534aSAnna Dabrowska
1287106851SAnna Dabrowska    /**
1387106851SAnna Dabrowska     * @inheritDoc
1487106851SAnna Dabrowska     */
1587106851SAnna Dabrowska    public function register(Doku_Event_Handler $controller)
1687106851SAnna Dabrowska    {
1787106851SAnna Dabrowska        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleMigrations');
1887106851SAnna Dabrowska    }
1987106851SAnna Dabrowska
2087106851SAnna Dabrowska    /**
21d93c534aSAnna Dabrowska     * Call our custom migrations. We do not use our own database,
22d93c534aSAnna Dabrowska     * so we cannot use the mechanism in sqlite init()
23d93c534aSAnna Dabrowska     * which processes updateXXXX.sql files
2487106851SAnna Dabrowska     *
2587106851SAnna Dabrowska     * @param Doku_Event $event
2687106851SAnna Dabrowska     * @return bool
278b0ba635SAndreas Gohr     * @throws Exception
2887106851SAnna Dabrowska     */
2987106851SAnna Dabrowska    public function handleMigrations(Doku_Event $event)
3087106851SAnna Dabrowska    {
3187106851SAnna Dabrowska        /** @var \helper_plugin_struct_db $helper */
3287106851SAnna Dabrowska        $helper = plugin_load('helper', 'struct_db');
33bcee0e72SAnna Dabrowska
34bcee0e72SAnna Dabrowska        // abort if struct is not installed
35bcee0e72SAnna Dabrowska        if (!$helper) {
36bcee0e72SAnna Dabrowska            throw new Exception('Plugin struct is required!');
37bcee0e72SAnna Dabrowska        }
38bcee0e72SAnna Dabrowska
3987106851SAnna Dabrowska        $sqlite = $helper->getDB();
4087106851SAnna Dabrowska
4187106851SAnna Dabrowska        list($dbVersionStruct, $dbVersionStructpublish) = $this->getDbVersions($sqlite);
42bcee0e72SAnna Dabrowska
43bcee0e72SAnna Dabrowska        // check if struct has required version
44bcee0e72SAnna Dabrowska        if ($dbVersionStruct < self::MIN_DB_STRUCT) {
458b0ba635SAndreas Gohr            throw new Exception('Plugin struct is outdated. Minimum required database version is ' . self::MIN_DB_STRUCT);
46bcee0e72SAnna Dabrowska        }
47bcee0e72SAnna Dabrowska
48bcee0e72SAnna Dabrowska        // check whether we are already up-to-date
49bcee0e72SAnna Dabrowska        $latestVersion = $this->getLatestVersion();
50bcee0e72SAnna Dabrowska        if (isset($dbVersionStructpublish) && (int) $dbVersionStructpublish >= $latestVersion) {
518b0ba635SAndreas Gohr            return true;
5287106851SAnna Dabrowska        }
5387106851SAnna Dabrowska
54bcee0e72SAnna Dabrowska        // check whether we have any pending migrations
55d93c534aSAnna Dabrowska        $pending = range(($dbVersionStructpublish ?: 0) + 1, $latestVersion);
5687106851SAnna Dabrowska        if (empty($pending)) {
578b0ba635SAndreas Gohr            return true;
5887106851SAnna Dabrowska        }
5987106851SAnna Dabrowska
6087106851SAnna Dabrowska        // execute the migrations
618b0ba635SAndreas Gohr        $ok = true;
62d93c534aSAnna Dabrowska
6387106851SAnna Dabrowska        foreach ($pending as $version) {
6487106851SAnna Dabrowska            $call = 'migration' . $version;
6587106851SAnna Dabrowska            $ok = $ok && $this->$call($sqlite);
6687106851SAnna Dabrowska        }
6787106851SAnna Dabrowska
68d93c534aSAnna Dabrowska        // update migration status in struct database
69d93c534aSAnna Dabrowska        if ($ok) {
70d93c534aSAnna Dabrowska            $sql = "REPLACE INTO opts (val,opt) VALUES ($version,'dbversion_structpublish')";
71d93c534aSAnna Dabrowska            $ok = $ok && $sqlite->query($sql);
72d93c534aSAnna Dabrowska        }
73d93c534aSAnna Dabrowska
7487106851SAnna Dabrowska        return $ok;
7587106851SAnna Dabrowska    }
7687106851SAnna Dabrowska
7787106851SAnna Dabrowska    /**
788b0ba635SAndreas Gohr     * Read the current versions for struct and struct publish from the database
798b0ba635SAndreas Gohr     *
80f734c62fSAnna Dabrowska     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
818b0ba635SAndreas Gohr     * @return array [structversion, structpublishversion]
8287106851SAnna Dabrowska     */
8387106851SAnna Dabrowska    protected function getDbVersions($sqlite)
8487106851SAnna Dabrowska    {
8587106851SAnna Dabrowska        $dbVersionStruct = null;
8687106851SAnna Dabrowska        $dbVersionStructpublish = null;
8787106851SAnna Dabrowska
8887106851SAnna Dabrowska        $sql = 'SELECT opt, val FROM opts WHERE opt=? OR opt=?';
89f734c62fSAnna Dabrowska        $vals = $sqlite->queryAll($sql, ['dbversion', 'dbversion_structpublish']);
9087106851SAnna Dabrowska
9187106851SAnna Dabrowska        foreach ($vals as $val) {
9287106851SAnna Dabrowska            if ($val['opt'] === 'dbversion') {
9387106851SAnna Dabrowska                $dbVersionStruct = $val['val'];
9487106851SAnna Dabrowska            }
9587106851SAnna Dabrowska            if ($val['opt'] === 'dbversion_structpublish') {
9687106851SAnna Dabrowska                $dbVersionStructpublish = $val['val'];
9787106851SAnna Dabrowska            }
9887106851SAnna Dabrowska        }
9987106851SAnna Dabrowska        return [$dbVersionStruct, $dbVersionStructpublish];
10087106851SAnna Dabrowska    }
10187106851SAnna Dabrowska
10287106851SAnna Dabrowska    /**
103bcee0e72SAnna Dabrowska     * @return int
104bcee0e72SAnna Dabrowska     */
105bcee0e72SAnna Dabrowska    protected function getLatestVersion()
106bcee0e72SAnna Dabrowska    {
107bcee0e72SAnna Dabrowska        return (int) trim(file_get_contents(DOKU_PLUGIN . 'structpublish/db/latest.version', false));
108bcee0e72SAnna Dabrowska    }
109bcee0e72SAnna Dabrowska
110bcee0e72SAnna Dabrowska    /**
111bcee0e72SAnna Dabrowska     * Database setup
11287106851SAnna Dabrowska     *
113f734c62fSAnna Dabrowska     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
11487106851SAnna Dabrowska     * @return bool
11587106851SAnna Dabrowska     */
116bcee0e72SAnna Dabrowska    protected function migration1($sqlite)
11787106851SAnna Dabrowska    {
1187ada453dSAnna Dabrowska        $file = DOKU_PLUGIN . 'structpublish/db/json/structpublish0001.struct.json';
1197ada453dSAnna Dabrowska        $schemaJson = file_get_contents($file);
1207ada453dSAnna Dabrowska        $importer = new \dokuwiki\plugin\struct\meta\SchemaImporter('structpublish', $schemaJson);
1217ada453dSAnna Dabrowska        $ok = (bool) $importer->build();
1227ada453dSAnna Dabrowska
1237ada453dSAnna Dabrowska        if ($ok) {
124bcee0e72SAnna Dabrowska            $sql = io_readFile(DOKU_PLUGIN . 'structpublish/db/update0001.sql', false);
125f734c62fSAnna Dabrowska            $sqlArr = Tools::SQLstring2array($sql);
126f734c62fSAnna Dabrowska            foreach ($sqlArr as $sql) {
127f734c62fSAnna Dabrowska                $ok = $ok && $sqlite->query($sql);
128f734c62fSAnna Dabrowska            }
129f734c62fSAnna Dabrowska        }
130677c897aSAnna Dabrowska
131677c897aSAnna Dabrowska        return $ok;
13287106851SAnna Dabrowska    }
133d93c534aSAnna Dabrowska
134d93c534aSAnna Dabrowska    /**
135d93c534aSAnna Dabrowska     * Reset 'latest' flag to 0 for all rows except actually latest ones
136d93c534aSAnna Dabrowska     * for each pid / status combination.
137d93c534aSAnna Dabrowska     *
138d93c534aSAnna Dabrowska     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
139d93c534aSAnna Dabrowska     * @return bool
140d93c534aSAnna Dabrowska     */
141d93c534aSAnna Dabrowska    protected function migration2($sqlite)
142d93c534aSAnna Dabrowska    {
143d93c534aSAnna Dabrowska        $sql = "SELECT rid, pid, latest, col1, max(col4) FROM $this->table GROUP BY pid, col1";
144d93c534aSAnna Dabrowska        $latest = $sqlite->queryAll($sql);
145d93c534aSAnna Dabrowska        $rids = array_column($latest, 'rid');
146d93c534aSAnna Dabrowska
147d93c534aSAnna Dabrowska        $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')';
148d93c534aSAnna Dabrowska
149d93c534aSAnna Dabrowska        return (bool) $sqlite->query($sql);
150d93c534aSAnna Dabrowska    }
151*c4618b6cSAnna Dabrowska
152*c4618b6cSAnna Dabrowska    /**
153*c4618b6cSAnna Dabrowska     * Set 'latest' flag to 0 for all rows except actually latest ones
154*c4618b6cSAnna Dabrowska     * for each page,
155*c4618b6cSAnna Dabrowska     *
156*c4618b6cSAnna Dabrowska     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
157*c4618b6cSAnna Dabrowska     * @return bool
158*c4618b6cSAnna Dabrowska     */
159*c4618b6cSAnna Dabrowska    protected function migration3($sqlite)
160*c4618b6cSAnna Dabrowska    {
161*c4618b6cSAnna Dabrowska        $sql = "WITH cte AS (
162*c4618b6cSAnna Dabrowska            SELECT rid, pid, col1 AS status, col4 as rev,
163*c4618b6cSAnna Dabrowska                   rank() OVER ( PARTITION BY pid
164*c4618b6cSAnna Dabrowska                       ORDER BY col4 DESC, col1 = 'draft', col1 = 'approved', col1 = 'published'
165*c4618b6cSAnna Dabrowska                   ) AS r
166*c4618b6cSAnna Dabrowska            FROM data_structpublish
167*c4618b6cSAnna Dabrowska        )
168*c4618b6cSAnna Dabrowska        SELECT rid, pid, status, rev
169*c4618b6cSAnna Dabrowska        FROM cte
170*c4618b6cSAnna Dabrowska        WHERE r  = 1
171*c4618b6cSAnna Dabrowska        ORDER BY pid ASC;";
172*c4618b6cSAnna Dabrowska
173*c4618b6cSAnna Dabrowska        $latest = $sqlite->queryAll($sql);
174*c4618b6cSAnna Dabrowska        $rids = array_column($latest, 'rid');
175*c4618b6cSAnna Dabrowska
176*c4618b6cSAnna Dabrowska        $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')';
177*c4618b6cSAnna Dabrowska
178*c4618b6cSAnna Dabrowska        return (bool) $sqlite->query($sql);
179*c4618b6cSAnna Dabrowska    }
18087106851SAnna Dabrowska}
181