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    public function handle_ajax(Doku_Event $event) {
99        if($event->data != 'plugin_move_rename') return;
100        $event->preventDefault();
101        $event->stopPropagation();
102
103        global $MSG;
104        global $INPUT;
105
106        $src = cleanID($INPUT->str('id'));
107        $dst = cleanID($INPUT->str('newid'));
108
109        /** @var helper_plugin_move_op $MoveOperator */
110        $MoveOperator = plugin_load('helper', 'move_op');
111
112        $JSON = new JSON();
113
114        header('Content-Type: application/json');
115
116        if($this->renameOkay($src) && $MoveOperator->movePage($src, $dst)) {
117            // all went well, redirect
118            echo $JSON->encode(array('redirect_url' => wl($dst, '', true, '&')));
119        } else {
120            if(isset($MSG[0])) {
121                $error = $MSG[0]; // first error
122            } else {
123                $error = $this->getLang('cantrename');
124            }
125            echo $JSON->encode(array('error' => $error));
126        }
127    }
128
129    /**
130     * Handle media renames in media manager
131     *
132     * @param Doku_Event $event
133     * @return void
134     */
135    public function handleAjaxMediaManager(Doku_Event $event)
136    {
137        if ($event->data !== 'plugin_move_rename_mediamanager') return;
138
139        if (!checkSecurityToken()) {
140            throw new \Exception('Security token did not match');
141        }
142
143        $event->preventDefault();
144        $event->stopPropagation();
145
146        global $INPUT;
147        global $MSG;
148        global $USERINFO;
149
150        $src = cleanID($INPUT->str('src'));
151        $dst = cleanID($INPUT->str('dst'));
152
153        /** @var helper_plugin_move_op $moveOperator */
154        $moveOperator = plugin_load('helper', 'move_op');
155
156        if ($src && $dst) {
157            header('Content-Type: application/json');
158
159            $response = [];
160
161            // check user/group restrictions
162            if (
163                !auth_isMember($this->getConf('allowrename'), $INPUT->server->str('REMOTE_USER'), (array) $USERINFO['grps'])
164            ) {
165                $response['error'] = $this->getLang('notallowed');
166                echo json_encode($response);
167                return;
168            }
169
170            $response['success'] = $moveOperator->moveMedia($src, $dst);
171
172            if ($response['success']) {
173                $ns = getNS($dst);
174                $response['redirect_url'] = wl($dst, ['do' => 'media', 'ns' => $ns], true, '&');
175            } else {
176                $response['error'] = sprintf($this->getLang('mediamoveerror'), $src);
177                if (isset($MSG)) {
178                    foreach ($MSG as $msg) {
179                        $response['error'] .= ' ' . $msg['msg'];
180                    }
181                }
182            }
183
184            echo json_encode($response);
185        }
186    }
187
188    /**
189     * Determines if it would be okay to show a rename page button for the given page and current user
190     *
191     * @param $id
192     * @return bool
193     */
194    public function renameOkay($id) {
195        global $conf;
196        global $ACT;
197        global $USERINFO;
198        if(!($ACT == 'show' || empty($ACT))) return false;
199        if(!page_exists($id)) return false;
200        if(auth_quickaclcheck($id) < AUTH_EDIT) return false;
201        if(checklock($id) !== false || @file_exists(wikiLockFN($id))) return false;
202        if(!$conf['useacl']) return true;
203        if(!isset($_SERVER['REMOTE_USER'])) return false;
204        if(!auth_isMember($this->getConf('allowrename'), $_SERVER['REMOTE_USER'], (array) $USERINFO['grps'])) return false;
205
206        return true;
207    }
208
209    /**
210     * Use this in your template to add a simple "move this page" link
211     *
212     * Alternatively give anything the class "plugin_move_page" - it will automatically be hidden and shown and
213     * trigger the page move dialog.
214     */
215    public function tpl() {
216        echo '<a href="" class="plugin_move_page">';
217        echo $this->getLang('renamepage');
218        echo '</a>';
219    }
220
221}
222