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