PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.14
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.14
2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 All 139 releases
metasync / wp-mcp-server / tools / class-mcp-tool-redirects.php

class-mcp-tool-redirects.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.14, at wp-mcp-server/tools/class-mcp-tool-redirects.php

400 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MCP Tool: Redirect Management
4 *
5 * Provides tools for managing WordPress redirects.
6 *
7 * @package MetaSync
8 * @subpackage MCP_Server/Tools
9 */
10
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 /**
16 * Create Redirect Tool
17 */
18 class MCP_Tool_Create_Redirect extends MCP_Tool_Base {
19
20 public function get_name() {
21 return 'wordpress_create_redirect';
22 }
23
24 public function get_description() {
25 return 'Create a 301 or 302 redirect from one URL to another';
26 }
27
28 public function get_input_schema() {
29 return [
30 'type' => 'object',
31 'properties' => [
32 'source' => [
33 'type' => 'string',
34 'description' => 'Source URL path (e.g., /old-page or https://example.com/old-page)'
35 ],
36 'destination' => [
37 'type' => 'string',
38 'description' => 'Destination URL (full URL or path)'
39 ],
40 'type' => [
41 'type' => 'integer',
42 'description' => 'Redirect HTTP code',
43 'enum' => [301, 302],
44 'default' => 301
45 ],
46 'description' => [
47 'type' => 'string',
48 'description' => 'Optional description of the redirect',
49 'default' => ''
50 ]
51 ],
52 'required' => ['source', 'destination']
53 ];
54 }
55
56 public function execute($params) {
57 $this->validate_params($params);
58 $this->require_capability('manage_options');
59
60 // Load database class
61 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'redirections/class-metasync-redirection-database.php';
62 $db = new Metasync_Redirection_Database();
63
64 $source = $this->sanitize_url($params['source']);
65 $destination = $this->sanitize_url($params['destination']);
66 $http_code = isset($params['type']) ? $this->sanitize_integer($params['type']) : 301;
67 $description = isset($params['description']) ? $this->sanitize_textarea($params['description']) : '';
68
69 if (!get_option('metasync_allow_external_redirects', 0) && !empty($destination) && wp_validate_redirect($destination, '') !== $destination) {
70 return [
71 'error' => 'external_destination',
72 'message' => 'Destination URL must be on this site. Enable "Allow External Redirects" in plugin settings to permit off-site redirects.',
73 ];
74 }
75
76 // Parse source to get path
77 $source_path = parse_url($source, PHP_URL_PATH) ?: $source;
78
79 // Loop detection — refuse to create a redirect whose chain resolves back to the source
80 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'redirections/class-metasync-redirection.php';
81 $db_ref = $db;
82 $redirection_helper = new Metasync_Redirection($db_ref);
83 $loop_chain = [];
84 if ($redirection_helper->would_create_loop($source_path, $destination, $loop_chain)) {
85 return [
86 'error' => 'loop_detected',
87 'chain' => $loop_chain,
88 ];
89 }
90
91 // Create redirect
92 $result = $db->add([
93 'sources_from' => [$source_path],
94 'url_redirect_to' => $destination,
95 'http_code' => $http_code,
96 'description' => $description,
97 'status' => 'active',
98 'pattern_type' => 'exact'
99 ]);
100
101 if ($result === false) {
102 throw new Exception('Failed to create redirect');
103 }
104
105 // Non-blocking destination reachability check — surface a warning, never block creation.
106 $dest_warning = $redirection_helper->destination_resolves_warning($destination, $http_code);
107
108 $response_data = [
109 'redirect_id' => $result,
110 'source' => $source_path,
111 'destination' => $destination,
112 'type' => $http_code,
113 'status' => 'active'
114 ];
115 if ($dest_warning !== null) {
116 $response_data['warning'] = $dest_warning;
117 }
118
119 return $this->success($response_data, 'Redirect created successfully');
120 }
121 }
122
123 /**
124 * List Redirects Tool
125 */
126 class MCP_Tool_List_Redirects extends MCP_Tool_Base {
127
128 public function get_name() {
129 return 'wordpress_list_redirects';
130 }
131
132 public function get_description() {
133 return 'List all redirects with optional status filter';
134 }
135
136 public function get_input_schema() {
137 return [
138 'type' => 'object',
139 'properties' => [
140 'status' => [
141 'type' => 'string',
142 'description' => 'Filter by status',
143 'enum' => ['active', 'inactive', 'all'],
144 'default' => 'active'
145 ],
146 'limit' => [
147 'type' => 'integer',
148 'description' => 'Maximum number of results',
149 'default' => 50,
150 'minimum' => 1,
151 'maximum' => 200
152 ]
153 ],
154 'required' => []
155 ];
156 }
157
158 public function execute($params) {
159 $this->validate_params($params);
160 $this->require_capability('manage_options');
161
162 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'redirections/class-metasync-redirection-database.php';
163 $db = new Metasync_Redirection_Database();
164
165 $status = isset($params['status']) ? $params['status'] : 'active';
166
167 // Get redirects
168 if ($status === 'active') {
169 $redirects = $db->getAllActiveRecords();
170 } else {
171 $redirects = $db->getAllRecords();
172 }
173
174 // Filter by status if needed
175 if ($status !== 'all' && $status !== 'active') {
176 $redirects = array_filter($redirects, function($r) use ($status) {
177 return $r->status === $status;
178 });
179 }
180
181 // Limit results
182 $limit = isset($params['limit']) ? $this->sanitize_integer($params['limit']) : 50;
183 $redirects = array_slice($redirects, 0, $limit);
184
185 // Format results (sources_from may be JSON or legacy serialized in DB)
186 $result = array_map(function($redirect) {
187 $sources_raw = $redirect->sources_from;
188 $source = null;
189 if ( ! empty( $sources_raw ) ) {
190 // Try JSON first; fall back to unserialize for legacy DB records
191 $sources = json_decode( $sources_raw, true );
192 if ( $sources === null && $sources_raw !== 'null' ) {
193 $sources = unserialize( $sources_raw, ['allowed_classes' => false] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
194 }
195 if ( is_array( $sources ) && ! empty( $sources ) ) {
196 $keys = array_keys( $sources );
197 $source = is_int( $keys[0] ) ? $sources[ $keys[0] ] : $keys[0];
198 }
199 }
200 return [
201 'id' => $redirect->id,
202 'source' => $source,
203 'destination' => $redirect->url_redirect_to,
204 'type' => $redirect->http_code,
205 'hits' => $redirect->hits_count,
206 'status' => $redirect->status,
207 'pattern_type' => $redirect->pattern_type ?? 'exact',
208 'description' => $redirect->description ?? '',
209 'created_at' => $redirect->created_at ?? null
210 ];
211 }, $redirects);
212
213 return $this->success([
214 'redirects' => $result,
215 'total' => count($result)
216 ]);
217 }
218 }
219
220 /**
221 * Delete Redirect Tool
222 */
223 class MCP_Tool_Delete_Redirect extends MCP_Tool_Base {
224
225 public function get_name() {
226 return 'wordpress_delete_redirect';
227 }
228
229 public function get_description() {
230 return 'Delete a redirect by ID';
231 }
232
233 public function get_input_schema() {
234 return [
235 'type' => 'object',
236 'properties' => [
237 'redirect_id' => [
238 'type' => 'integer',
239 'description' => 'Redirect ID to delete',
240 'minimum' => 1
241 ]
242 ],
243 'required' => ['redirect_id']
244 ];
245 }
246
247 public function execute($params) {
248 $this->validate_params($params);
249 $this->require_capability('manage_options');
250
251 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'redirections/class-metasync-redirection-database.php';
252 $db = new Metasync_Redirection_Database();
253
254 $redirect_id = $this->sanitize_integer($params['redirect_id']);
255
256 // Verify redirect exists
257 $redirect = $db->find($redirect_id);
258 if (!$redirect) {
259 throw new Exception(sprintf("Redirect not found: %d", absint($redirect_id)));
260 }
261
262 // Delete redirect
263 $result = $db->delete([$redirect_id]);
264
265 if ($result === false) {
266 throw new Exception('Failed to delete redirect');
267 }
268
269 return $this->success([
270 'redirect_id' => $redirect_id,
271 'deleted' => true
272 ], 'Redirect deleted successfully');
273 }
274 }
275
276 /**
277 * Update Redirect Tool
278 */
279 class MCP_Tool_Update_Redirect extends MCP_Tool_Base {
280
281 public function get_name() {
282 return 'wordpress_update_redirect';
283 }
284
285 public function get_description() {
286 return 'Update an existing redirect';
287 }
288
289 public function get_input_schema() {
290 return [
291 'type' => 'object',
292 'properties' => [
293 'redirect_id' => [
294 'type' => 'integer',
295 'description' => 'Redirect ID to update',
296 'minimum' => 1
297 ],
298 'destination' => [
299 'type' => 'string',
300 'description' => 'New destination URL (optional)'
301 ],
302 'type' => [
303 'type' => 'integer',
304 'description' => 'New redirect HTTP code (optional)',
305 'enum' => [301, 302]
306 ],
307 'status' => [
308 'type' => 'string',
309 'description' => 'New status (optional)',
310 'enum' => ['active', 'inactive']
311 ]
312 ],
313 'required' => ['redirect_id']
314 ];
315 }
316
317 public function execute($params) {
318 $this->validate_params($params);
319 $this->require_capability('manage_options');
320
321 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'redirections/class-metasync-redirection-database.php';
322 $db = new Metasync_Redirection_Database();
323
324 $redirect_id = $this->sanitize_integer($params['redirect_id']);
325
326 // Verify redirect exists
327 $redirect = $db->find($redirect_id);
328 if (!$redirect) {
329 throw new Exception(sprintf("Redirect not found: %d", absint($redirect_id)));
330 }
331
332 // Build update args
333 $update_args = ['updated_at' => current_time('mysql')];
334
335 if (isset($params['destination'])) {
336 $destination = $this->sanitize_url($params['destination']);
337 if (!get_option('metasync_allow_external_redirects', 0) && !empty($destination) && wp_validate_redirect($destination, '') !== $destination) {
338 return [
339 'error' => 'external_destination',
340 'message' => 'Destination URL must be on this site. Enable "Allow External Redirects" in plugin settings to permit off-site redirects.',
341 ];
342 }
343 $update_args['url_redirect_to'] = $destination;
344 }
345
346 if (isset($params['type'])) {
347 $update_args['http_code'] = $this->sanitize_integer($params['type']);
348 }
349
350 if (isset($params['status'])) {
351 $update_args['status'] = $this->sanitize_string($params['status']);
352 }
353
354 // Update redirect
355 $result = $db->update($update_args, $redirect_id);
356
357 if ($result === false) {
358 throw new Exception('Failed to update redirect');
359 }
360
361 // Non-blocking destination reachability check — surface a warning, never block the update.
362 $dest_warning = null;
363 if (isset($params['destination'])) {
364 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'redirections/class-metasync-redirection.php';
365 $db_ref = $db;
366 $effective_code = isset($update_args['http_code']) ? (int) $update_args['http_code'] : (int) $redirect->http_code;
367 $effective_pattern = isset($redirect->pattern_type) ? $redirect->pattern_type : 'exact';
368 $dest_warning = (new Metasync_Redirection($db_ref))->destination_resolves_warning($destination, $effective_code, $effective_pattern);
369 }
370
371 // Get updated redirect (sources_from may be JSON or legacy serialized in DB)
372 $updated = $db->find($redirect_id);
373 $source = null;
374 if ( ! empty( $updated->sources_from ) ) {
375 // Try JSON first; fall back to unserialize for legacy DB records
376 $sources = json_decode( $updated->sources_from, true );
377 if ( $sources === null && $updated->sources_from !== 'null' ) {
378 $sources = unserialize( $updated->sources_from, ['allowed_classes' => false] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
379 }
380 if ( is_array( $sources ) && ! empty( $sources ) ) {
381 $keys = array_keys( $sources );
382 $source = is_int( $keys[0] ) ? $sources[ $keys[0] ] : $keys[0];
383 }
384 }
385
386 $success_data = [
387 'redirect_id' => $redirect_id,
388 'source' => $source,
389 'destination' => $updated->url_redirect_to,
390 'type' => $updated->http_code,
391 'status' => $updated->status
392 ];
393 if ($dest_warning !== null) {
394 $success_data['warning'] = $dest_warning;
395 }
396
397 return $this->success($success_data, 'Redirect updated successfully');
398 }
399 }
400