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