1<?php 2 3/** 4 * DokuWiki Plugin struct (Action Component) 5 * 6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 7 * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 8 */ 9 10use dokuwiki\plugin\struct\meta\Assignments; 11use dokuwiki\plugin\struct\meta\Column; 12use dokuwiki\plugin\struct\meta\Schema; 13use dokuwiki\plugin\struct\types\Lookup; 14use dokuwiki\plugin\struct\types\Media; 15use dokuwiki\plugin\struct\types\Page; 16 17class action_plugin_struct_move extends DokuWiki_Action_Plugin 18{ 19 /** @var helper_plugin_sqlite */ 20 protected $db = null; 21 22 /** 23 * Registers a callback function for a given event 24 * 25 * @param Doku_Event_Handler $controller DokuWiki's event controller object 26 * @return void 27 */ 28 public function register(Doku_Event_Handler $controller) 29 { 30 $controller->register_hook('PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this, 'handleMove', true); 31 $controller->register_hook('PLUGIN_MOVE_MEDIA_RENAME', 'AFTER', $this, 'handleMove', false); 32 } 33 34 /** 35 * Renames all occurrences of a page ID in the database 36 * 37 * @param Doku_Event $event event object by reference 38 * @param bool $ispage is this a page move operation? 39 * @return bool 40 */ 41 public function handleMove(Doku_Event $event, $ispage) 42 { 43 /** @var helper_plugin_struct_db $hlp */ 44 $hlp = plugin_load('helper', 'struct_db'); 45 $this->db = $hlp->getDB(false); 46 if (!$this->db) return false; 47 $old = $event->data['src_id']; 48 $new = $event->data['dst_id']; 49 50 // prepare work 51 $this->db->query('BEGIN TRANSACTION'); 52 53 // general update of our meta tables 54 if ($ispage) { 55 $this->updateDataTablePIDs($old, $new); 56 $this->updateAssignments($old, $new); 57 $this->updateTitles($old, $new); 58 } 59 60 // apply updates to all columns in all schemas depending on type 61 $schemas = Schema::getAll(); 62 foreach ($schemas as $table) { 63 $schema = new Schema($table); 64 foreach ($schema->getColumns() as $col) { 65 if ($ispage) { 66 switch (get_class($col->getType())) { 67 case Page::class: 68 $this->updateColumnID($schema, $col, $old, $new, true); 69 break; 70 case Lookup::class: 71 $this->updateColumnLookup($schema, $col, $old, $new); 72 break; 73 } 74 } else { 75 switch (get_class($col->getType())) { 76 case Media::class: 77 $this->updateColumnID($schema, $col, $old, $new); 78 break; 79 } 80 } 81 } 82 } 83 84 // execute everything 85 $ok = $this->db->query('COMMIT TRANSACTION'); 86 if (!$ok) { 87 $this->db->query('ROLLBACK TRANSACTION'); 88 return false; 89 } 90 91 return true; 92 } 93 94 /** 95 * Update the pid column of ALL data tables 96 * 97 * (we don't trust the assigments are still there) 98 * 99 * @param string $old old page id 100 * @param string $new new page id 101 */ 102 protected function updateDataTablePIDs($old, $new) 103 { 104 foreach (Schema::getAll() as $tbl) { 105 /** @noinspection SqlResolve */ 106 $sql = "UPDATE data_$tbl SET pid = ? WHERE pid = ?"; 107 $this->db->query($sql, array($new, $old)); 108 109 /** @noinspection SqlResolve */ 110 $sql = "UPDATE multi_$tbl SET pid = ? WHERE pid = ?"; 111 $this->db->query($sql, array($new, $old)); 112 } 113 } 114 115 /** 116 * Update the page-schema assignments 117 * 118 * @param string $old old page id 119 * @param string $new new page id 120 */ 121 protected function updateAssignments($old, $new) 122 { 123 // assignments 124 $sql = "UPDATE schema_assignments SET pid = ? WHERE pid = ?"; 125 $this->db->query($sql, array($new, $old)); 126 // make sure assignments still match patterns; 127 $assignments = Assignments::getInstance(); 128 $assignments->reevaluatePageAssignments($new); 129 } 130 131 /** 132 * Update the Title information for the moved page 133 * 134 * @param string $old old page id 135 * @param string $new new page id 136 */ 137 protected function updateTitles($old, $new) 138 { 139 $sql = "UPDATE titles SET pid = ? WHERE pid = ?"; 140 $this->db->query($sql, array($new, $old)); 141 } 142 143 /** 144 * Update the ID in a given column 145 * 146 * @param Schema $schema 147 * @param Column $col 148 * @param string $old old page id 149 * @param string $new new page id 150 * @param bool $hashes could the ID have a hash part? (for Page type) 151 */ 152 protected function updateColumnID(Schema $schema, Column $col, $old, $new, $hashes = false) 153 { 154 $colref = $col->getColref(); 155 $table = $schema->getTable(); 156 157 if ($col->isMulti()) { 158 /** @noinspection SqlResolve */ 159 $sql = "UPDATE multi_$table 160 SET value = REPLACE(value, ?, ?) 161 WHERE value LIKE ? 162 AND colref = $colref 163 AND latest = 1"; 164 } else { 165 /** @noinspection SqlResolve */ 166 $sql = "UPDATE data_$table 167 SET col$colref = REPLACE(col$colref, ?, ?) 168 WHERE col$colref LIKE ? 169 AND latest = 1"; 170 } 171 $this->db->query($sql, $old, $new, $old); // exact match 172 if ($hashes) { 173 $this->db->query($sql, $old, $new, "$old#%"); // match with hashes 174 } 175 if (get_class($col->getType()) === Lookup::class) { 176 $this->db->query($sql, $old, $new, "[\"$old\",%]"); // match JSON string 177 if ($hashes) { 178 $this->db->query($sql, $old, $new, "[\"$old#%\",%]"); // match JSON string with hash 179 } 180 } 181 } 182 183 /** 184 * Update a Lookup type column 185 * 186 * Lookups contain a page id when the referenced schema is a data schema 187 * 188 * @param Schema $schema 189 * @param Column $col 190 * @param string $old old page id 191 * @param string $new new page id 192 */ 193 protected function updateColumnLookup(Schema $schema, Column $col, $old, $new) 194 { 195 $tconf = $col->getType()->getConfig(); 196 $ref = new Schema($tconf['schema']); 197 if (!$ref->getId()) return; // this schema does not exist 198 if (!$ref->getTimeStamp()) return; // a lookup is referenced, nothing to do 199 $this->updateColumnID($schema, $col, $old, $new); 200 } 201} 202