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