xref: /plugin/structpublish/action/migration.php (revision d93c534ab70dab6913cef90024efe0b98384883d)
1<?php
2
3use dokuwiki\plugin\sqlite\Tools;
4
5class action_plugin_structpublish_migration extends DokuWiki_Action_Plugin
6{
7    const MIN_DB_STRUCT = 19;
8
9    /** @var string  */
10    protected $table = 'data_structpublish';
11
12    /**
13     * @inheritDoc
14     */
15    public function register(Doku_Event_Handler $controller)
16    {
17        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleMigrations');
18    }
19
20    /**
21     * Call our custom migrations. We do not use our own database,
22     * so we cannot use the mechanism in sqlite init()
23     * which processes updateXXXX.sql files
24     *
25     * @param Doku_Event $event
26     * @return bool
27     * @throws Exception
28     */
29    public function handleMigrations(Doku_Event $event)
30    {
31        /** @var \helper_plugin_struct_db $helper */
32        $helper = plugin_load('helper', 'struct_db');
33
34        // abort if struct is not installed
35        if (!$helper) {
36            throw new Exception('Plugin struct is required!');
37        }
38
39        $sqlite = $helper->getDB();
40
41        list($dbVersionStruct, $dbVersionStructpublish) = $this->getDbVersions($sqlite);
42
43        // check if struct has required version
44        if ($dbVersionStruct < self::MIN_DB_STRUCT) {
45            throw new Exception('Plugin struct is outdated. Minimum required database version is ' . self::MIN_DB_STRUCT);
46        }
47
48        // check whether we are already up-to-date
49        $latestVersion = $this->getLatestVersion();
50        if (isset($dbVersionStructpublish) && (int) $dbVersionStructpublish >= $latestVersion) {
51            return true;
52        }
53
54        // check whether we have any pending migrations
55        $pending = range(($dbVersionStructpublish ?: 0) + 1, $latestVersion);
56        if (empty($pending)) {
57            return true;
58        }
59
60        // execute the migrations
61        $ok = true;
62
63        $sqlite->getPdo()->beginTransaction();
64        try {
65            foreach ($pending as $version) {
66                $call = 'migration' . $version;
67                $ok = $ok && $this->$call($sqlite);
68            }
69
70            // update migration status in struct database
71            if ($ok) {
72                $sql = "REPLACE INTO opts (val,opt) VALUES ($version,'dbversion_structpublish')";
73                $ok = $ok && $sqlite->query($sql);
74            }
75            $sqlite->getPdo()->commit();
76        } catch (Exception $e) {
77            $sqlite->getPdo()->rollBack();
78        }
79
80        return $ok;
81    }
82
83    /**
84     * Read the current versions for struct and struct publish from the database
85     *
86     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
87     * @return array [structversion, structpublishversion]
88     */
89    protected function getDbVersions($sqlite)
90    {
91        $dbVersionStruct = null;
92        $dbVersionStructpublish = null;
93
94        $sql = 'SELECT opt, val FROM opts WHERE opt=? OR opt=?';
95        $vals = $sqlite->queryAll($sql, ['dbversion', 'dbversion_structpublish']);
96
97        foreach ($vals as $val) {
98            if ($val['opt'] === 'dbversion') {
99                $dbVersionStruct = $val['val'];
100            }
101            if ($val['opt'] === 'dbversion_structpublish') {
102                $dbVersionStructpublish = $val['val'];
103            }
104        }
105        return [$dbVersionStruct, $dbVersionStructpublish];
106    }
107
108    /**
109     * @return int
110     */
111    protected function getLatestVersion()
112    {
113        return (int) trim(file_get_contents(DOKU_PLUGIN . 'structpublish/db/latest.version', false));
114    }
115
116    /**
117     * Database setup
118     *
119     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
120     * @return bool
121     */
122    protected function migration1($sqlite)
123    {
124        $file = DOKU_PLUGIN . 'structpublish/db/json/structpublish0001.struct.json';
125        $schemaJson = file_get_contents($file);
126        $importer = new \dokuwiki\plugin\struct\meta\SchemaImporter('structpublish', $schemaJson);
127        $ok = (bool) $importer->build();
128
129        if ($ok) {
130            $sql = io_readFile(DOKU_PLUGIN . 'structpublish/db/update0001.sql', false);
131            $sqlArr = Tools::SQLstring2array($sql);
132            foreach ($sqlArr as $sql) {
133                $ok = $ok && $sqlite->query($sql);
134            }
135        }
136
137        return $ok;
138    }
139
140    /**
141     * Reset 'latest' flag to 0 for all rows except actually latest ones
142     * for each pid / status combination.
143     *
144     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
145     * @return bool
146     */
147    protected function migration2($sqlite)
148    {
149        $sql = "SELECT rid, pid, latest, col1, max(col4) FROM $this->table GROUP BY pid, col1";
150        $latest = $sqlite->queryAll($sql);
151        $rids = array_column($latest, 'rid');
152
153        $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')';
154
155        return (bool) $sqlite->query($sql);
156    }
157}
158