1<?php
2
3/**
4 * Plugin BatchEdit: User request
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author     Mykola Ostrovskyy <dwpforge@gmail.com>
8 */
9
10require_once(DOKU_PLUGIN . 'batchedit/engine.php');
11
12/**
13 * As of DW 2023-04-04 the \dokuwiki\File\Resolver class is abstact with only
14 * two concrete implementations (PageResolver, MediaResolver). This child class
15 * is used to access basic namespace resolution from the abstract base.
16 */
17class BatcheditNamespaceResolver extends \dokuwiki\File\Resolver {
18}
19
20class BatcheditRequest {
21
22    const COMMAND_WELCOME = 'welcome';
23    const COMMAND_PREVIEW = 'preview';
24    const COMMAND_APPLY = 'apply';
25
26    private $command;
27    private $sessionId;
28    private $namespace;
29    private $regexp;
30    private $replacement;
31    private $summary;
32    private $minorEdit;
33    private $appliedMatches;
34
35    /**
36     *
37     */
38    public function __construct($config) {
39        $this->command = $this->parseCommand();
40
41        if ($this->command == self::COMMAND_WELCOME) {
42            return;
43        }
44
45        $config->update($this->parseOptions());
46
47        $this->sessionId = $this->parseSessionId();
48        $this->namespace = $this->parseNamespace();
49        $this->regexp = $this->parseRegexp($config);
50        $this->replacement = $this->parseReplacement();
51        $this->summary = $this->parseSummary();
52        $this->minorEdit = isset($_REQUEST['minor']);
53
54        if ($this->command == self::COMMAND_APPLY || $config->getConf('keepmarks')) {
55            $this->appliedMatches = $this->parseAppliedMatches();
56        }
57    }
58
59    /**
60     *
61     */
62    public function getCommand() {
63        return $this->command;
64    }
65
66    /**
67     *
68     */
69    public function getSessionId() {
70        return $this->sessionId;
71    }
72
73    /**
74     *
75     */
76    public function getNamespace() {
77        return $this->namespace;
78    }
79
80    /**
81     *
82     */
83    public function getRegexp() {
84        return $this->regexp;
85    }
86
87    /**
88     *
89     */
90    public function getReplacement() {
91        return $this->replacement;
92    }
93
94    /**
95     *
96     */
97    public function getSummary() {
98        return $this->summary;
99    }
100
101    /**
102     *
103     */
104    public function getMinorEdit() {
105        return $this->minorEdit;
106    }
107
108    /**
109     *
110     */
111    public function getAppliedMatches() {
112        return $this->appliedMatches;
113    }
114
115    /**
116     *
117     */
118    private function parseCommand() {
119        if (!isset($_REQUEST['cmd'])) {
120            return self::COMMAND_WELCOME;
121        }
122
123        if (!is_array($_REQUEST['cmd'])) {
124            throw new BatcheditException('err_invreq');
125        }
126
127        $command = key($_REQUEST['cmd']);
128
129        if (($command != 'preview') && ($command != 'apply')) {
130            throw new BatcheditException('err_invreq');
131        }
132
133        return $command;
134    }
135
136    /**
137     *
138     */
139    private function parseOptions() {
140        if (!isset($_REQUEST['searchmode'])) {
141            throw new BatcheditException('err_invreq');
142        }
143
144        $options = array();
145
146        $options['searchmode'] = $_REQUEST['searchmode'];
147        $options['matchcase'] = isset($_REQUEST['matchcase']);
148        $options['multiline'] = isset($_REQUEST['multiline']);
149        $options['advregexp'] = isset($_REQUEST['advregexp']);
150        $options['matchctx'] = isset($_REQUEST['matchctx']);
151        $options['ctxchars'] = isset($_REQUEST['ctxchars']) ? $_REQUEST['ctxchars'] : '';
152        $options['ctxlines'] = isset($_REQUEST['ctxlines']) ? $_REQUEST['ctxlines'] : '';
153        $options['searchlimit'] = isset($_REQUEST['searchlimit']);
154        $options['searchmax'] = isset($_REQUEST['searchmax']) ? $_REQUEST['searchmax'] : '';
155        $options['keepmarks'] = isset($_REQUEST['keepmarks']);
156        $options['markpolicy'] = isset($_REQUEST['markpolicy']) ? $_REQUEST['markpolicy'] : '';
157        $options['tplpatterns'] = isset($_REQUEST['tplpatterns']);
158        $options['checksummary'] = isset($_REQUEST['checksummary']);
159
160        return $options;
161    }
162
163    /**
164     *
165     */
166    private function parseSessionId() {
167        if (!isset($_REQUEST['session'])) {
168            return '';
169        }
170
171        return $_REQUEST['session'];
172    }
173
174    /**
175     *
176     */
177    private function parseNamespace() {
178        if (!isset($_REQUEST['namespace'])) {
179            throw new BatcheditException('err_invreq');
180        }
181
182        $namespace = trim($_REQUEST['namespace']);
183
184        if ($namespace != '') {
185            global $ID;
186            $resolver = new BatcheditNamespaceResolver($ID);
187            $resolved = array();
188
189            foreach (explode(',', $namespace) as $ns) {
190                $ns = trim($ns) . ':';
191
192                if ($ns[0] == '-') {
193                    $resolved[] = '-' . $resolver->resolveId(substr($ns, 1));
194                }
195                else {
196                    $resolved[] = $resolver->resolveId($ns);
197                }
198            }
199
200            $namespace = implode(',', $resolved);
201        }
202
203        return $namespace;
204    }
205
206    /**
207     *
208     */
209    private function parseRegexp($config) {
210        if (!isset($_REQUEST['search'])) {
211            throw new BatcheditException('err_invreq');
212        }
213
214        $regexp = trim($_REQUEST['search']);
215
216        if ($regexp == '') {
217            throw new BatcheditException('err_nosearch');
218        }
219
220        if ($config->getConf('searchmode') == 'regexp') {
221            if ($config->getConf('advregexp')) {
222                if (preg_match('/^([^\w\\\\]|_).+?\1[imsxADSUXJu]*$/s', $regexp) != 1) {
223                    throw new BatcheditException('err_invregexp');
224                }
225            }
226            else {
227                $regexp = "\033" . $regexp . "\033um";
228            }
229        }
230        else {
231            $regexp = "\033" . preg_quote($regexp) . "\033";
232        }
233
234        $regexp = str_replace("\r\n", "\n", $regexp);
235
236        if (!$config->getConf('matchcase')) {
237            $regexp .= 'i';
238        }
239
240        return $regexp;
241    }
242
243    /**
244     *
245     */
246    private function parseReplacement() {
247        if (!isset($_REQUEST['replace'])) {
248            throw new BatcheditException('err_invreq');
249        }
250
251        $replace = str_replace("\r\n", "\n", $_REQUEST['replace']);
252
253        $unescape = function($matches) {
254            static $unescaped = array('n' => "\n", 'r' => "\r", 't' => "\t");
255
256            if (strlen($matches[1]) % 2) {
257                return substr($matches[1], 1) . $unescaped[$matches[2]];
258            }
259            else {
260                return $matches[0];
261            }
262        };
263
264        return preg_replace_callback('/(\\\\+)([nrt])/', $unescape, $replace);
265    }
266
267    /**
268     *
269     */
270    private function parseSummary() {
271        if (!isset($_REQUEST['summary'])) {
272            throw new BatcheditException('err_invreq');
273        }
274
275        return $_REQUEST['summary'];
276    }
277
278    /**
279     *
280     */
281    private function parseAppliedMatches() {
282        if (!isset($_REQUEST['apply'])) {
283            return array();
284        }
285
286        $matchIds = json_decode($_REQUEST['apply']);
287
288        if (!is_array($matchIds)) {
289            throw new BatcheditException('err_invreq');
290        }
291
292        return $matchIds;
293    }
294}
295