xref: /plugin/struct/action/move.php (revision 06fee43a4802b3338be3c05d8b6be3f01981337a)
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        if($ref->isLookup()) return; // a lookup is referenced, nothing to do
189
190        $this->updateColumnID($schema, $col, $old, $new);
191    }
192
193}
194