1<?php 2 3use dokuwiki\plugin\sqlite\SQLiteDB; 4 5/** 6 * DokuWiki Plugin structstatus (Action Component) 7 * 8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9 * @author Andreas Gohr <dokuwiki@cosmocode.de> 10 */ 11class action_plugin_structstatus extends DokuWiki_Action_Plugin { 12 13 /** 14 * Registers a callback function for a given event 15 * 16 * @param Doku_Event_Handler $controller DokuWiki's event controller object 17 * @return void 18 */ 19 public function register(Doku_Event_Handler $controller) { 20 21 $controller->register_hook('PLUGIN_STRUCT_TYPECLASS_INIT', 'BEFORE', $this, 'handleInit'); 22 $controller->register_hook('PLUGIN_STRUCT_TYPECLASS_INIT', 'AFTER', $this, 'handleMigrations'); 23 24 } 25 26 /** 27 * @param Doku_Event $event 28 * @return void 29 */ 30 31 public function handleInit(Doku_Event $event) { 32 $event->data['Status'] = 'dokuwiki\\plugin\\structstatus\\Status'; 33 } 34 35 /** 36 * Call our custom migrations when defined 37 * 38 * @param Doku_Event $event 39 * @return bool 40 */ 41 public function handleMigrations(Doku_Event $event) 42 { 43 /** @var \helper_plugin_struct_db $helper */ 44 $helper = plugin_load('helper', 'struct_db'); 45 $sqlite = $helper->getDB(); 46 47 // check whether we are already up-to-date 48 list($dbVersionStruct, $dbVersionStructStatus) = $this->getDbVersions($sqlite); 49 if (isset($dbVersionStructStatus) && $dbVersionStructStatus === $dbVersionStruct) { 50 return true; 51 } 52 53 // check whether we have any pending migrations for the current version of struct db 54 $pending = array_filter(array_map(function ($version) use ($dbVersionStruct) { 55 return $version >= $dbVersionStruct && 56 is_callable([$this, "migration$version"]) ? $version : null; 57 }, $this->diffVersions($dbVersionStruct, $dbVersionStructStatus))); 58 if (empty($pending)) { 59 return true; 60 } 61 62 // execute the migrations 63 $sql = "SELECT MAX(id) AS id, tbl FROM schemas GROUP BY tbl"; 64 $schemas= $sqlite->queryAll($sql); 65 66 $sqlite->getPdo()->beginTransaction(); 67 try { 68 69 foreach ($pending as $version) { 70 $call = 'migration' . $version; 71 foreach ($schemas as $schema) { 72 $this->$call($sqlite, $schema); 73 } 74 } 75 76 // update migration status in struct database 77 $sql = 'REPLACE INTO opts(opt,val) VALUES ("dbversion_structstatus", ' . $version . ')'; 78 $sqlite->query($sql); 79 80 $sqlite->getPdo()->commit(); 81 } catch (Exception $e) { 82 $sqlite->getPdo()->rollBack(); 83 return false; 84 } 85 86 return true; 87 } 88 89 /** 90 * Detect which migrations should be executed. Start conservatively with version 1. 91 * 92 * @param int $dbVersionStruct Current version of struct DB as found in 'opts' table 93 * @param int|null $dbVersionStructStatus Current version in 'opts', may not exist yet 94 * @return int[] 95 */ 96 protected function diffVersions($dbVersionStruct, $dbVersionStructStatus) 97 { 98 $pluginDbVersion = $dbVersionStructStatus ?: 1; 99 return range($pluginDbVersion, $dbVersionStruct); 100 } 101 102 /** 103 * Converts integer ids used in struct before dbversion 17 104 * to composite ids ["",int] 105 * 106 * @param SQLiteDB $sqlite 107 * @param array $schema 108 * @return bool 109 */ 110 protected function migration17($sqlite, $schema) 111 { 112 $name = $schema['tbl']; 113 $sid = $schema['id']; 114 115 $s = $this->getLookupColsSql($sid); 116 $cols = $sqlite->queryAll($s); 117 118 if ($cols) { 119 foreach ($cols as $col) { 120 $colno = $col['COL']; 121 $s = "UPDATE data_$name SET col$colno = '[" . '""' . ",'||col$colno||']' WHERE col$colno != '' AND CAST(col$colno AS DECIMAL) = col$colno"; 122 $ok = true && $sqlite->query($s); 123 if (!$ok) return false; 124 // multi_ 125 $s = "UPDATE multi_$name SET value = '[" . '""' . ",'||value||']' WHERE colref=$colno AND CAST(value AS DECIMAL) = value"; 126 $ok = $ok && $sqlite->query($s); 127 if (!$ok) return false; 128 } 129 } 130 131 return true; 132 } 133 134 /** 135 * @param SQLiteDB $sqlite 136 * @return array 137 */ 138 protected function getDbVersions($sqlite) 139 { 140 $dbVersionStruct = null; 141 $dbVersionStructStatus = null; 142 143 $sql = 'SELECT opt, val FROM opts WHERE opt=? OR opt=?'; 144 $vals = $sqlite->queryAll($sql, ['dbversion', 'dbversion_structstatus']); 145 146 foreach ($vals as $val) { 147 if ($val['opt'] === 'dbversion') { 148 $dbVersionStruct = $val['val']; 149 } 150 if ($val['opt'] === 'dbversion_structstatus') { 151 $dbVersionStructStatus = $val['val']; 152 } 153 } 154 return [$dbVersionStruct, $dbVersionStructStatus]; 155 } 156 157 /** 158 * Returns a select statement to fetch our columns in the current schema 159 * 160 * @param int $sid Id of the schema 161 * @return string SQL statement 162 */ 163 protected function getLookupColsSql($sid) 164 { 165 return "SELECT C.colref AS COL, T.class AS TYPE 166 FROM schema_cols AS C 167 LEFT OUTER JOIN types AS T 168 ON C.tid = T.id 169 WHERE C.sid = $sid 170 AND (TYPE = 'Status') 171 "; 172 } 173} 174 175// vim:ts=4:sw=4:et: 176