xref: /plugin/move/action/rename.php (revision edc7cb0e3e3b5ae4c77fb757596bba129e61f4f9) !
1<?php
2/**
3 * Move Plugin Page Rename Functionality
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <gohr@cosmocode.de>
7 */
8// must be run within Dokuwiki
9if(!defined('DOKU_INC')) die();
10
11/**
12 * Class action_plugin_move_rename
13 */
14class action_plugin_move_rename extends DokuWiki_Action_Plugin {
15
16    /**
17     * Register event handlers.
18     *
19     * @param Doku_Event_Handler $controller The plugin controller
20     */
21    public function register(Doku_Event_Handler $controller) {
22        $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'handle_init');
23
24        // TODO: DEPRECATED JAN 2018
25        $controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, 'handle_pagetools');
26
27        $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'addsvgbutton', array());
28        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax');
29        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxMediaManager');
30    }
31
32    /**
33     * set JavaScript info if renaming of current page is possible
34     */
35    public function handle_init() {
36        global $JSINFO;
37        global $INFO;
38        global $INPUT;
39        global $USERINFO;
40
41        if (isset($INFO['id'])) {
42            $JSINFO['move_renameokay'] = $this->renameOkay($INFO['id']);
43        } else {
44            $JSINFO['move_renameokay'] = false;
45        }
46
47        $JSINFO['move_allowrename'] = auth_isMember(
48            $this->getConf('allowrename'),
49            $INPUT->server->str('REMOTE_USER'),
50            $USERINFO['grps'] ?? []
51        );
52    }
53
54    /**
55     * Adds a button to the default template
56     *
57     * TODO: DEPRECATED JAN 2018
58     *
59     * @param Doku_Event $event
60     */
61    public function handle_pagetools(Doku_Event $event) {
62        if($event->data['view'] != 'main') return;
63        if (!$this->getConf('pagetools_integration')) {
64            return;
65        }
66
67        $newitem = '<li class="plugin_move_page"><a href=""><span>' . $this->getLang('renamepage') . '</span></a></li>';
68        $offset = count($event->data['items']) - 1;
69        $event->data['items'] =
70            array_slice($event->data['items'], 0, $offset, true) +
71            array('plugin_move' => $newitem) +
72            array_slice($event->data['items'], $offset, null, true);
73    }
74
75    /**
76     * Add 'rename' button to page tools, new SVG based mechanism
77     *
78     * @param Doku_Event $event
79     */
80    public function addsvgbutton(Doku_Event $event) {
81        global $INFO, $JSINFO;
82        if(
83            $event->data['view'] !== 'page' ||
84            !$this->getConf('pagetools_integration') ||
85            empty($JSINFO['move_renameokay'])
86        ) {
87            return;
88        }
89        if(!$INFO['exists']) {
90            return;
91        }
92        array_splice($event->data['items'], -1, 0, array(new \dokuwiki\plugin\move\MenuItem()));
93    }
94
95    /**
96     * Rename a single page
97     *
98     * This creates a plan and executes it right away. If the user selected to move media with the page,
99     * all media files used in the original page that are located in the same namespace are moved with the page
100     * to the new namespace.
101     *
102     */
103    public function handle_ajax(Doku_Event $event) {
104        if($event->data != 'plugin_move_rename') return;
105        $event->preventDefault();
106        $event->stopPropagation();
107
108        global $MSG;
109        global $INPUT;
110
111        $src = cleanID($INPUT->str('id'));
112        $dst = cleanID($INPUT->str('newid'));
113        $doMedia = $INPUT->bool('media');
114
115        header('Content-Type: application/json');
116
117        if(!$this->renameOkay($src)) {
118            echo json_encode(['error' => $this->getLang('cantrename')]);
119            return;
120        }
121
122        /** @var helper_plugin_move_plan $plan */
123        $plan = plugin_load('helper', 'move_plan');
124        if($plan->isCommited()) {
125            echo json_encode(['error' => $this->getLang('cantrename')]);
126            return;
127        }
128        $plan->setOption('autorewrite', true);
129        $plan->addPageMove($src, $dst); // add the page move to the plan
130
131        if($doMedia) { // move media with the page?
132            $srcNS = getNS($src);
133            $dstNS = getNS($dst);
134            $srcNSLen = strlen($srcNS);
135            // we don't do this for root namespace or if namespace hasn't changed
136            if ($srcNS != '' && $srcNS != $dstNS) {
137                $media = p_get_metadata($src, 'relation media');
138                if (is_array($media)) {
139                    foreach ($media as $file => $exists) {
140                        if(!$exists) continue;
141                        $mediaNS = getNS($file);
142                        if ($mediaNS == $srcNS) {
143                            $plan->addMediaMove($file, $dstNS . substr($file, $srcNSLen));
144                        }
145                    }
146                }
147            }
148        }
149
150        try {
151            // commit and execute the plan
152            $plan->commit();
153            do {
154                $next = $plan->nextStep();
155                if ($next === false) throw new \Exception('Move plan failed');
156            } while ($next > 0);
157            echo json_encode(array('redirect_url' => wl($dst, '', true, '&')));
158        } catch (\Exception $e) {
159            // error should be in $MSG
160            if(isset($MSG[0])) {
161                $error = $MSG[0]; // first error
162            } else {
163                $error = $this->getLang('cantrename') . ' ' . $e->getMessage();
164            }
165            echo json_encode(['error' => $error]);
166        }
167    }
168
169    /**
170     * Handle media renames in media manager
171     *
172     * @param Doku_Event $event
173     * @return void
174     */
175    public function handleAjaxMediaManager(Doku_Event $event)
176    {
177        if ($event->data !== 'plugin_move_rename_mediamanager') return;
178
179        if (!checkSecurityToken()) {
180            throw new \Exception('Security token did not match');
181        }
182
183        $event->preventDefault();
184        $event->stopPropagation();
185
186        global $INPUT;
187        global $MSG;
188        global $USERINFO;
189
190        $src = cleanID($INPUT->str('src'));
191        $dst = cleanID($INPUT->str('dst'));
192
193        /** @var helper_plugin_move_op $moveOperator */
194        $moveOperator = plugin_load('helper', 'move_op');
195
196        if ($src && $dst) {
197            header('Content-Type: application/json');
198
199            $response = [];
200
201            // check user/group restrictions
202            if (
203                !auth_isMember($this->getConf('allowrename'), $INPUT->server->str('REMOTE_USER'), (array) $USERINFO['grps'])
204            ) {
205                $response['error'] = $this->getLang('notallowed');
206                echo json_encode($response);
207                return;
208            }
209
210            $response['success'] = $moveOperator->moveMedia($src, $dst);
211
212            if ($response['success']) {
213                $ns = getNS($dst);
214                $response['redirect_url'] = wl($dst, ['do' => 'media', 'ns' => $ns], true, '&');
215            } else {
216                $response['error'] = sprintf($this->getLang('mediamoveerror'), $src);
217                if (isset($MSG)) {
218                    foreach ($MSG as $msg) {
219                        $response['error'] .= ' ' . $msg['msg'];
220                    }
221                }
222            }
223
224            echo json_encode($response);
225        }
226    }
227
228    /**
229     * Determines if it would be okay to show a rename page button for the given page and current user
230     *
231     * @param $id
232     * @return bool
233     */
234    public function renameOkay($id) {
235        global $conf;
236        global $ACT;
237        global $USERINFO;
238        if(!($ACT == 'show' || empty($ACT))) return false;
239        if(!page_exists($id)) return false;
240        if(auth_quickaclcheck($id) < AUTH_EDIT) return false;
241        if(checklock($id) !== false || @file_exists(wikiLockFN($id))) return false;
242        if(!$conf['useacl']) return true;
243        if(!isset($_SERVER['REMOTE_USER'])) return false;
244        if(!auth_isMember($this->getConf('allowrename'), $_SERVER['REMOTE_USER'], (array) $USERINFO['grps'])) return false;
245
246        return true;
247    }
248
249    /**
250     * Use this in your template to add a simple "move this page" link
251     *
252     * Alternatively give anything the class "plugin_move_page" - it will automatically be hidden and shown and
253     * trigger the page move dialog.
254     */
255    public function tpl() {
256        echo '<a href="" class="plugin_move_page">';
257        echo $this->getLang('renamepage');
258        echo '</a>';
259    }
260
261}
262