xref: /plugin/structpublish/action/migration.php (revision 5b1fd00b9f767f78b08ea3240adf8c1fe925892a)
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        foreach ($pending as $version) {
64            $call = 'migration' . $version;
65            $ok = $ok && $this->$call($sqlite);
66        }
67
68        // update migration status in struct database
69        if ($ok) {
70            $sql = "REPLACE INTO opts (val,opt) VALUES ($version,'dbversion_structpublish')";
71            $ok = $ok && $sqlite->query($sql);
72        }
73
74        return $ok;
75    }
76
77    /**
78     * Read the current versions for struct and struct publish from the database
79     *
80     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
81     * @return array [structversion, structpublishversion]
82     */
83    protected function getDbVersions($sqlite)
84    {
85        $dbVersionStruct = null;
86        $dbVersionStructpublish = null;
87
88        $sql = 'SELECT opt, val FROM opts WHERE opt=? OR opt=?';
89        $vals = $sqlite->queryAll($sql, ['dbversion', 'dbversion_structpublish']);
90
91        foreach ($vals as $val) {
92            if ($val['opt'] === 'dbversion') {
93                $dbVersionStruct = $val['val'];
94            }
95            if ($val['opt'] === 'dbversion_structpublish') {
96                $dbVersionStructpublish = $val['val'];
97            }
98        }
99        return [$dbVersionStruct, $dbVersionStructpublish];
100    }
101
102    /**
103     * @return int
104     */
105    protected function getLatestVersion()
106    {
107        return (int) trim(file_get_contents(DOKU_PLUGIN . 'structpublish/db/latest.version', false));
108    }
109
110    /**
111     * Database setup
112     *
113     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
114     * @return bool
115     */
116    protected function migration1($sqlite)
117    {
118        $file = DOKU_PLUGIN . 'structpublish/db/json/structpublish0001.struct.json';
119        $schemaJson = file_get_contents($file);
120        $importer = new \dokuwiki\plugin\struct\meta\SchemaImporter('structpublish', $schemaJson);
121        $ok = (bool) $importer->build();
122
123        if ($ok) {
124            $sql = io_readFile(DOKU_PLUGIN . 'structpublish/db/update0001.sql', false);
125            $sqlArr = Tools::SQLstring2array($sql);
126            foreach ($sqlArr as $sql) {
127                $ok = $ok && $sqlite->query($sql);
128            }
129        }
130
131        return $ok;
132    }
133
134    /**
135     * Reset 'latest' flag to 0 for all rows except actually latest ones
136     * for each pid / status combination.
137     *
138     * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite
139     * @return bool
140     */
141    protected function migration2($sqlite)
142    {
143        $sql = "SELECT rid, pid, latest, col1, max(col4) FROM $this->table GROUP BY pid, col1";
144        $latest = $sqlite->queryAll($sql);
145        $rids = array_column($latest, 'rid');
146
147        $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')';
148
149        return (bool) $sqlite->query($sql);
150    }
151}
152