1<?php 2 3use dokuwiki\plugin\sqlite\Tools; 4 5class action_plugin_structpublish_migration extends DokuWiki_Action_Plugin 6{ 7 public 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( 46 'Plugin struct is outdated. Minimum required database version is ' . self::MIN_DB_STRUCT 47 ); 48 } 49 50 // check whether we are already up-to-date 51 $latestVersion = $this->getLatestVersion(); 52 if (isset($dbVersionStructpublish) && (int) $dbVersionStructpublish >= $latestVersion) { 53 return true; 54 } 55 56 // check whether we have any pending migrations 57 $pending = range(($dbVersionStructpublish ?: 0) + 1, $latestVersion); 58 if (empty($pending)) { 59 return true; 60 } 61 62 // execute the migrations 63 $ok = true; 64 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 76 return $ok; 77 } 78 79 /** 80 * Read the current versions for struct and struct publish from the database 81 * 82 * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite 83 * @return array [structversion, structpublishversion] 84 */ 85 protected function getDbVersions($sqlite) 86 { 87 $dbVersionStruct = null; 88 $dbVersionStructpublish = null; 89 90 $sql = 'SELECT opt, val FROM opts WHERE opt=? OR opt=?'; 91 $vals = $sqlite->queryAll($sql, ['dbversion', 'dbversion_structpublish']); 92 93 foreach ($vals as $val) { 94 if ($val['opt'] === 'dbversion') { 95 $dbVersionStruct = $val['val']; 96 } 97 if ($val['opt'] === 'dbversion_structpublish') { 98 $dbVersionStructpublish = $val['val']; 99 } 100 } 101 return [$dbVersionStruct, $dbVersionStructpublish]; 102 } 103 104 /** 105 * @return int 106 */ 107 protected function getLatestVersion() 108 { 109 return (int) trim(file_get_contents(DOKU_PLUGIN . 'structpublish/db/latest.version', false)); 110 } 111 112 /** 113 * Database setup 114 * 115 * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite 116 * @return bool 117 */ 118 protected function migration1($sqlite) 119 { 120 $file = DOKU_PLUGIN . 'structpublish/db/json/structpublish0001.struct.json'; 121 $schemaJson = file_get_contents($file); 122 $importer = new \dokuwiki\plugin\struct\meta\SchemaImporter('structpublish', $schemaJson); 123 $ok = (bool) $importer->build(); 124 125 if ($ok) { 126 $sql = io_readFile(DOKU_PLUGIN . 'structpublish/db/update0001.sql', false); 127 $sqlArr = Tools::SQLstring2array($sql); 128 foreach ($sqlArr as $sql) { 129 $ok = $ok && $sqlite->query($sql); 130 } 131 } 132 133 return $ok; 134 } 135 136 /** 137 * Reset 'latest' flag to 0 for all rows except actually latest ones 138 * for each pid / status combination. 139 * 140 * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite 141 * @return bool 142 */ 143 protected function migration2($sqlite) 144 { 145 $sql = "SELECT rid, pid, latest, col1, max(col4) FROM $this->table GROUP BY pid, col1"; 146 $latest = $sqlite->queryAll($sql); 147 $rids = array_column($latest, 'rid'); 148 149 $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')'; 150 151 return (bool) $sqlite->query($sql); 152 } 153 154 /** 155 * Set 'latest' flag to 0 for all rows except actually latest ones 156 * for each page, 157 * 158 * @param \dokuwiki\plugin\sqlite\SQLiteDB $sqlite 159 * @return bool 160 */ 161 protected function migration3($sqlite) 162 { 163 $sql = "WITH cte AS ( 164 SELECT rid, pid, col1 AS status, col4 as rev, 165 rank() OVER ( PARTITION BY pid 166 ORDER BY col4 DESC, col1 = 'draft', col1 = 'approved', col1 = 'published' 167 ) AS r 168 FROM data_structpublish 169 ) 170 SELECT rid, pid, status, rev 171 FROM cte 172 WHERE r = 1 173 ORDER BY pid ASC;"; 174 175 $latest = $sqlite->queryAll($sql); 176 $rids = array_column($latest, 'rid'); 177 178 $sql = "UPDATE $this->table SET latest = 0 WHERE rid NOT IN (" . implode(', ', $rids) . ')'; 179 180 return (bool) $sqlite->query($sql); 181 } 182} 183