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\Page; 15 16if(!defined('DOKU_INC')) die(); 17 18class action_plugin_struct_move extends DokuWiki_Action_Plugin { 19 20 /** @var helper_plugin_sqlite */ 21 protected $db = null; 22 23 /** 24 * Registers a callback function for a given event 25 * 26 * @param Doku_Event_Handler $controller DokuWiki's event controller object 27 * @return void 28 */ 29 public function register(Doku_Event_Handler $controller) { 30 $controller->register_hook('PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this, 'handle_move'); 31 } 32 33 /** 34 * Renames all occurances of a page ID in the database 35 * 36 * @param Doku_Event $event event object by reference 37 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 38 * handler was registered] 39 * @return bool 40 */ 41 public function handle_move(Doku_Event $event, $param) { 42 /** @var helper_plugin_struct_db $hlp */ 43 $hlp = plugin_load('helper', 'struct_db'); 44 $this->db = $hlp->getDB(false); 45 if(!$this->db) return false; 46 $old = $event->data['src_id']; 47 $new = $event->data['dst_id']; 48 49 // prepare work 50 $this->db->query('BEGIN TRANSACTION'); 51 52 // general update of our meta tables 53 $this->updateDataTablePIDs($old, $new); 54 $this->updateAssignments($old, $new); 55 $this->updateTitles($old, $new); 56 57 // apply updates to all columns in all schemas depending on type 58 $schemas = Schema::getAll(); 59 foreach($schemas as $table) { 60 $schema = new Schema($table); 61 foreach($schema->getColumns() as $col) { 62 switch(get_class($col->getType())) { 63 case Page::class: 64 $this->updateColumnPage($schema, $col, $old, $new); 65 break; 66 case Lookup::class: 67 $this->updateColumnLookup($schema, $col, $old, $new); 68 break; 69 } 70 } 71 } 72 73 // FIXME we need to update Media Type fields on media move 74 75 // execute everything 76 $ok = $this->db->query('COMMIT TRANSACTION'); 77 if(!$ok) { 78 $this->db->query('ROLLBACK TRANSACTION'); 79 return false; 80 } 81 82 return true; 83 } 84 85 /** 86 * Update the pid column of ALL data tables 87 * 88 * (we don't trust the assigments are still there) 89 * 90 * @param string $old old page id 91 * @param string $new new page id 92 */ 93 protected function updateDataTablePIDs($old, $new) { 94 foreach(Schema::getAll('page') as $tbl) { 95 /** @noinspection SqlResolve */ 96 $sql = "UPDATE data_$tbl SET pid = ? WHERE pid = ?"; 97 $this->db->query($sql, array($new, $old)); 98 99 /** @noinspection SqlResolve */ 100 $sql = "UPDATE multi_$tbl SET pid = ? WHERE pid = ?"; 101 $this->db->query($sql, array($new, $old)); 102 } 103 } 104 105 /** 106 * Update the page-schema assignments 107 * 108 * @param string $old old page id 109 * @param string $new new page id 110 */ 111 protected function updateAssignments($old, $new) { 112 // assignments 113 $sql = "UPDATE schema_assignments SET pid = ? WHERE pid = ?"; 114 $this->db->query($sql, array($new, $old)); 115 // make sure assignments still match patterns; 116 $assignments = Assignments::getInstance(); 117 $assignments->reevaluatePageAssignments($new); 118 } 119 120 /** 121 * Update the Title information for the moved page 122 * 123 * @param string $old old page id 124 * @param string $new new page id 125 */ 126 protected function updateTitles($old, $new) { 127 $sql = "UPDATE titles SET pid = ? WHERE pid = ?"; 128 $this->db->query($sql, array($new, $old)); 129 } 130 131 /** 132 * Update a Page type column 133 * 134 * @param Schema $schema 135 * @param Column $col 136 * @param string $old old page id 137 * @param string $new new page id 138 */ 139 protected function updateColumnPage(Schema $schema, Column $col, $old, $new) { 140 $colref = $col->getColref(); 141 $table = $schema->getTable(); 142 143 if($col->isMulti()) { 144 /** @noinspection SqlResolve */ 145 $sql = "UPDATE multi_$table 146 SET value = REPLACE(value, ?, ?) 147 WHERE value LIKE ? 148 AND colref = $colref 149 AND latest = 1"; 150 151 } else { 152 /** @noinspection SqlResolve */ 153 $sql = "UPDATE data_$table 154 SET col$colref = REPLACE(col$colref, ?, ?) 155 WHERE col$colref LIKE ? 156 AND latest = 1"; 157 } 158 $this->db->query($sql, $old, $new, $old); // exact match 159 $this->db->query($sql, $old, $new, "$old#%"); // match with hashes 160 } 161 162 /** 163 * Update a Lookup type column 164 * 165 * Lookups contain a page id when the referenced schema is a data schema 166 * 167 * @param Schema $schema 168 * @param Column $col 169 * @param string $old old page id 170 * @param string $new new page id 171 */ 172 protected function updateColumnLookup(Schema $schema, Column $col, $old, $new) { 173 $tconf = $col->getType()->getConfig(); 174 $ref = new Schema($tconf['schema']); 175 if(!$ref->getId()) return; // this schema does not exist 176 if($ref->isLookup()) return; // a lookup is referenced, nothing to do 177 178 // after the checks it's basically the same as a Page type column 179 $this->updateColumnPage($schema, $col, $old, $new); 180 } 181} 182