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