xref: /plugin/structpublish/action/migration.php (revision 9ab8cbae39a6ffa37705f745bc76916ea212a92b)
187106851SAnna Dabrowska<?php
287106851SAnna Dabrowska
3*9ab8cbaeSanndause dokuwiki\Extension\ActionPlugin;
4*9ab8cbaeSanndause dokuwiki\Extension\EventHandler;
5*9ab8cbaeSanndause dokuwiki\Extension\Event;
6*9ab8cbaeSanndause dokuwiki\plugin\struct\meta\SchemaImporter;
7*9ab8cbaeSanndause dokuwiki\plugin\sqlite\SQLiteDB;
8f734c62fSAnna Dabrowskause dokuwiki\plugin\sqlite\Tools;
9f734c62fSAnna Dabrowska
10*9ab8cbaeSanndaclass action_plugin_structpublish_migration extends ActionPlugin
1187106851SAnna Dabrowska{
12f0c5cbdaSAnna Dabrowska    public const MIN_DB_STRUCT = 19;
13bcee0e72SAnna Dabrowska
14d93c534aSAnna Dabrowska    /** @var string  */
15d93c534aSAnna Dabrowska    protected $table = 'data_structpublish';
16d93c534aSAnna Dabrowska
1787106851SAnna Dabrowska    /**
1887106851SAnna Dabrowska     * @inheritDoc
1987106851SAnna Dabrowska     */
20*9ab8cbaeSannda    public function register(EventHandler $controller)
2187106851SAnna Dabrowska    {
2287106851SAnna Dabrowska        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleMigrations');
2387106851SAnna Dabrowska    }
2487106851SAnna Dabrowska
2587106851SAnna Dabrowska    /**
26d93c534aSAnna Dabrowska     * Call our custom migrations. We do not use our own database,
27d93c534aSAnna Dabrowska     * so we cannot use the mechanism in sqlite init()
28d93c534aSAnna Dabrowska     * which processes updateXXXX.sql files
2987106851SAnna Dabrowska     *
30*9ab8cbaeSannda     * @param Event $event
3187106851SAnna Dabrowska     * @return bool
328b0ba635SAndreas Gohr     * @throws Exception
3387106851SAnna Dabrowska     */
34*9ab8cbaeSannda    public function handleMigrations(Event $event)
3587106851SAnna Dabrowska    {
3687106851SAnna Dabrowska        /** @var \helper_plugin_struct_db $helper */
3787106851SAnna Dabrowska        $helper = plugin_load('helper', 'struct_db');
38bcee0e72SAnna Dabrowska
39bcee0e72SAnna Dabrowska        // abort if struct is not installed
40bcee0e72SAnna Dabrowska        if (!$helper) {
41bcee0e72SAnna Dabrowska            throw new Exception('Plugin struct is required!');
42bcee0e72SAnna Dabrowska        }
43bcee0e72SAnna Dabrowska
4487106851SAnna Dabrowska        $sqlite = $helper->getDB();
4587106851SAnna Dabrowska
46*9ab8cbaeSannda        [$dbVersionStruct, $dbVersionStructpublish] = $this->getDbVersions($sqlite);
47bcee0e72SAnna Dabrowska
48bcee0e72SAnna Dabrowska        // check if struct has required version
49bcee0e72SAnna Dabrowska        if ($dbVersionStruct < self::MIN_DB_STRUCT) {
5031e730e1SAnna Dabrowska            throw new Exception(
5131e730e1SAnna Dabrowska                'Plugin struct is outdated. Minimum required database version is ' . self::MIN_DB_STRUCT
5231e730e1SAnna Dabrowska            );
53bcee0e72SAnna Dabrowska        }
54bcee0e72SAnna Dabrowska
55bcee0e72SAnna Dabrowska        // check whether we are already up-to-date
56bcee0e72SAnna Dabrowska        $latestVersion = $this->getLatestVersion();
57bcee0e72SAnna Dabrowska        if (isset($dbVersionStructpublish) && (int) $dbVersionStructpublish >= $latestVersion) {
588b0ba635SAndreas Gohr            return true;
5987106851SAnna Dabrowska        }
6087106851SAnna Dabrowska
61bcee0e72SAnna Dabrowska        // check whether we have any pending migrations
62d93c534aSAnna Dabrowska        $pending = range(($dbVersionStructpublish ?: 0) + 1, $latestVersion);
63*9ab8cbaeSannda        if ($pending === []) {
648b0ba635SAndreas Gohr            return true;
6587106851SAnna Dabrowska        }
6687106851SAnna Dabrowska
6787106851SAnna Dabrowska        // execute the migrations
688b0ba635SAndreas Gohr        $ok = true;
69d93c534aSAnna Dabrowska
7087106851SAnna Dabrowska        foreach ($pending as $version) {
7187106851SAnna Dabrowska            $call = 'migration' . $version;
7287106851SAnna Dabrowska            $ok = $ok && $this->$call($sqlite);
7387106851SAnna Dabrowska        }
7487106851SAnna Dabrowska
75d93c534aSAnna Dabrowska        // update migration status in struct database
76d93c534aSAnna Dabrowska        if ($ok) {
77d93c534aSAnna Dabrowska            $sql = "REPLACE INTO opts (val,opt) VALUES ($version,'dbversion_structpublish')";
78d93c534aSAnna Dabrowska            $ok = $ok && $sqlite->query($sql);
79d93c534aSAnna Dabrowska        }
80d93c534aSAnna Dabrowska
8187106851SAnna Dabrowska        return $ok;
8287106851SAnna Dabrowska    }
8387106851SAnna Dabrowska
8487106851SAnna Dabrowska    /**
858b0ba635SAndreas Gohr     * Read the current versions for struct and struct publish from the database
868b0ba635SAndreas Gohr     *
87*9ab8cbaeSannda     * @param SQLiteDB $sqlite
888b0ba635SAndreas Gohr     * @return array [structversion, structpublishversion]
8987106851SAnna Dabrowska     */
9087106851SAnna Dabrowska    protected function getDbVersions($sqlite)
9187106851SAnna Dabrowska    {
9287106851SAnna Dabrowska        $dbVersionStruct = null;
9387106851SAnna Dabrowska        $dbVersionStructpublish = null;
9487106851SAnna Dabrowska
9587106851SAnna Dabrowska        $sql = 'SELECT opt, val FROM opts WHERE opt=? OR opt=?';
96f734c62fSAnna Dabrowska        $vals = $sqlite->queryAll($sql, ['dbversion', 'dbversion_structpublish']);
9787106851SAnna Dabrowska
9887106851SAnna Dabrowska        foreach ($vals as $val) {
9987106851SAnna Dabrowska            if ($val['opt'] === 'dbversion') {
10087106851SAnna Dabrowska                $dbVersionStruct = $val['val'];
10187106851SAnna Dabrowska            }
10287106851SAnna Dabrowska            if ($val['opt'] === 'dbversion_structpublish') {
10387106851SAnna Dabrowska                $dbVersionStructpublish = $val['val'];
10487106851SAnna Dabrowska            }
10587106851SAnna Dabrowska        }
10687106851SAnna Dabrowska        return [$dbVersionStruct, $dbVersionStructpublish];
10787106851SAnna Dabrowska    }
10887106851SAnna Dabrowska
10987106851SAnna Dabrowska    /**
110bcee0e72SAnna Dabrowska     * @return int
111bcee0e72SAnna Dabrowska     */
112bcee0e72SAnna Dabrowska    protected function getLatestVersion()
113bcee0e72SAnna Dabrowska    {
114bcee0e72SAnna Dabrowska        return (int) trim(file_get_contents(DOKU_PLUGIN . 'structpublish/db/latest.version', false));
115bcee0e72SAnna Dabrowska    }
116bcee0e72SAnna Dabrowska
117bcee0e72SAnna Dabrowska    /**
118bcee0e72SAnna Dabrowska     * Database setup
11987106851SAnna Dabrowska     *
120*9ab8cbaeSannda     * @param SQLiteDB $sqlite
12187106851SAnna Dabrowska     * @return bool
12287106851SAnna Dabrowska     */
123bcee0e72SAnna Dabrowska    protected function migration1($sqlite)
12487106851SAnna Dabrowska    {
1257ada453dSAnna Dabrowska        $file = DOKU_PLUGIN . 'structpublish/db/json/structpublish0001.struct.json';
1267ada453dSAnna Dabrowska        $schemaJson = file_get_contents($file);
127*9ab8cbaeSannda        $importer = new SchemaImporter('structpublish', $schemaJson);
1287ada453dSAnna Dabrowska        $ok = (bool) $importer->build();
1297ada453dSAnna Dabrowska
1307ada453dSAnna Dabrowska        if ($ok) {
131bcee0e72SAnna Dabrowska            $sql = io_readFile(DOKU_PLUGIN . 'structpublish/db/update0001.sql', false);
132f734c62fSAnna Dabrowska            $sqlArr = Tools::SQLstring2array($sql);
133f734c62fSAnna Dabrowska            foreach ($sqlArr as $sql) {
134f734c62fSAnna Dabrowska                $ok = $ok && $sqlite->query($sql);
135f734c62fSAnna Dabrowska            }
136f734c62fSAnna Dabrowska        }
137677c897aSAnna Dabrowska
138677c897aSAnna Dabrowska        return $ok;
13987106851SAnna Dabrowska    }
140d93c534aSAnna Dabrowska
141d93c534aSAnna Dabrowska    /**
142d93c534aSAnna Dabrowska     * Reset 'latest' flag to 0 for all rows except actually latest ones
143d93c534aSAnna Dabrowska     * for each pid / status combination.
144d93c534aSAnna Dabrowska     *
145*9ab8cbaeSannda     * @param SQLiteDB $sqlite
146d93c534aSAnna Dabrowska     * @return bool
147d93c534aSAnna Dabrowska     */
148d93c534aSAnna Dabrowska    protected function migration2($sqlite)
149d93c534aSAnna Dabrowska    {
150d93c534aSAnna Dabrowska        $sql = "SELECT rid, pid, latest, col1, max(col4) FROM $this->table GROUP BY pid, col1";
151d93c534aSAnna Dabrowska        $latest = $sqlite->queryAll($sql);
152d93c534aSAnna Dabrowska        $rids = array_column($latest, 'rid');
153d93c534aSAnna Dabrowska
154d93c534aSAnna Dabrowska        $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')';
155d93c534aSAnna Dabrowska
156d93c534aSAnna Dabrowska        return (bool) $sqlite->query($sql);
157d93c534aSAnna Dabrowska    }
158c4618b6cSAnna Dabrowska
159c4618b6cSAnna Dabrowska    /**
160c4618b6cSAnna Dabrowska     * Set 'latest' flag to 0 for all rows except actually latest ones
161c4618b6cSAnna Dabrowska     * for each page,
162c4618b6cSAnna Dabrowska     *
163*9ab8cbaeSannda     * @param SQLiteDB $sqlite
164c4618b6cSAnna Dabrowska     * @return bool
165c4618b6cSAnna Dabrowska     */
166c4618b6cSAnna Dabrowska    protected function migration3($sqlite)
167c4618b6cSAnna Dabrowska    {
168c4618b6cSAnna Dabrowska        $sql = "WITH cte AS (
169c4618b6cSAnna Dabrowska            SELECT rid, pid, col1 AS status, col4 as rev,
170c4618b6cSAnna Dabrowska                   rank() OVER ( PARTITION BY pid
171c4618b6cSAnna Dabrowska                       ORDER BY col4 DESC, col1 = 'draft', col1 = 'approved', col1 = 'published'
172c4618b6cSAnna Dabrowska                   ) AS r
173c4618b6cSAnna Dabrowska            FROM data_structpublish
174c4618b6cSAnna Dabrowska        )
175c4618b6cSAnna Dabrowska        SELECT rid, pid, status, rev
176c4618b6cSAnna Dabrowska        FROM cte
177c4618b6cSAnna Dabrowska        WHERE r  = 1
178c4618b6cSAnna Dabrowska        ORDER BY pid ASC;";
179c4618b6cSAnna Dabrowska
180c4618b6cSAnna Dabrowska        $latest = $sqlite->queryAll($sql);
181c4618b6cSAnna Dabrowska        $rids = array_column($latest, 'rid');
182c4618b6cSAnna Dabrowska
183c4618b6cSAnna Dabrowska        $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')';
184c4618b6cSAnna Dabrowska
185c4618b6cSAnna Dabrowska        return (bool) $sqlite->query($sql);
186c4618b6cSAnna Dabrowska    }
18787106851SAnna Dabrowska}
188