1<?php 2/** 3 * DokuWiki Plugin struct (Action Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 7 */ 8 9// must be run within Dokuwiki 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 17if(!defined('DOKU_INC')) die(); 18 19class action_plugin_struct_move extends DokuWiki_Action_Plugin { 20 21 /** @var helper_plugin_sqlite */ 22 protected $db = null; 23 24 /** 25 * Registers a callback function for a given event 26 * 27 * @param Doku_Event_Handler $controller DokuWiki's event controller object 28 * @return void 29 */ 30 public function register(Doku_Event_Handler $controller) { 31 $controller->register_hook('PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this, 'handle_move', true); 32 $controller->register_hook('PLUGIN_MOVE_MEDIA_RENAME', 'AFTER', $this, 'handle_move', false); 33 } 34 35 /** 36 * Renames all occurances of a page ID in the database 37 * 38 * @param Doku_Event $event event object by reference 39 * @param bool $ispage is this a page move operation? 40 * @return bool 41 */ 42 public function handle_move(Doku_Event $event, $ispage) { 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 foreach(Schema::getAll('page') as $tbl) { 104 /** @noinspection SqlResolve */ 105 $sql = "UPDATE data_$tbl SET pid = ? WHERE pid = ?"; 106 $this->db->query($sql, array($new, $old)); 107 108 /** @noinspection SqlResolve */ 109 $sql = "UPDATE multi_$tbl SET pid = ? WHERE pid = ?"; 110 $this->db->query($sql, array($new, $old)); 111 } 112 } 113 114 /** 115 * Update the page-schema assignments 116 * 117 * @param string $old old page id 118 * @param string $new new page id 119 */ 120 protected function updateAssignments($old, $new) { 121 // assignments 122 $sql = "UPDATE schema_assignments SET pid = ? WHERE pid = ?"; 123 $this->db->query($sql, array($new, $old)); 124 // make sure assignments still match patterns; 125 $assignments = Assignments::getInstance(); 126 $assignments->reevaluatePageAssignments($new); 127 } 128 129 /** 130 * Update the Title information for the moved page 131 * 132 * @param string $old old page id 133 * @param string $new new page id 134 */ 135 protected function updateTitles($old, $new) { 136 $sql = "UPDATE titles SET pid = ? WHERE pid = ?"; 137 $this->db->query($sql, array($new, $old)); 138 } 139 140 /** 141 * Update the ID in a given column 142 * 143 * @param Schema $schema 144 * @param Column $col 145 * @param string $old old page id 146 * @param string $new new page id 147 * @param bool $hashes could the ID have a hash part? (for Page type) 148 */ 149 protected function updateColumnID(Schema $schema, Column $col, $old, $new, $hashes = false) { 150 $colref = $col->getColref(); 151 $table = $schema->getTable(); 152 153 if($col->isMulti()) { 154 /** @noinspection SqlResolve */ 155 $sql = "UPDATE multi_$table 156 SET value = REPLACE(value, ?, ?) 157 WHERE value LIKE ? 158 AND colref = $colref 159 AND latest = 1"; 160 161 } else { 162 /** @noinspection SqlResolve */ 163 $sql = "UPDATE data_$table 164 SET col$colref = REPLACE(col$colref, ?, ?) 165 WHERE col$colref LIKE ? 166 AND latest = 1"; 167 } 168 $this->db->query($sql, $old, $new, $old); // exact match 169 if($hashes) { 170 $this->db->query($sql, $old, $new, "$old#%"); // match with hashes 171 } 172 } 173 174 /** 175 * Update a Lookup type column 176 * 177 * Lookups contain a page id when the referenced schema is a data schema 178 * 179 * @param Schema $schema 180 * @param Column $col 181 * @param string $old old page id 182 * @param string $new new page id 183 */ 184 protected function updateColumnLookup(Schema $schema, Column $col, $old, $new) { 185 $tconf = $col->getType()->getConfig(); 186 $ref = new Schema($tconf['schema']); 187 if(!$ref->getId()) return; // this schema does not exist 188 // FIXME does this make sense at all? it's always a lookup, isn't it? 189 if(!$ref->getTimeStamp()) return; // a lookup is referenced, nothing to do 190// var_dump($ref) && die(); 191 192// $this->updateColumnID($schema, $col, $old, $new); 193 } 194 195} 196