PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.6
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.6
2.7.1 2.7.2 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 All 141 releases
metasync / redirections / class-metasync-redirection-importer.php

class-metasync-redirection-importer.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.6, at redirections/class-metasync-redirection-importer.php

875 lines 29.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Import redirections from other SEO plugins.
5 *
6 * @link https://searchatlas.com
7 * @since 1.0.0
8 * @package Metasync
9 * @subpackage Metasync/redirections
10 * @author Engineering Team <[email protected]>
11 */
12
13 # Abort if this file is accessed directly.
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 class Metasync_Redirection_Importer
19 {
20 private $db_redirection;
21
22 /**
23 * Supported plugins for import
24 */
25 const SUPPORTED_PLUGINS = [
26 'redirection' => [
27 'name' => 'Redirection',
28 'slug' => 'redirection/redirection.php',
29 'table' => 'redirection_items'
30 ],
31 'yoast' => [
32 'name' => 'Yoast SEO Premium',
33 'slugs' => [
34 'wordpress-seo-premium/wp-seo-premium.php',
35 'wordpress-seo-premium-main/wp-seo-premium.php',
36 'wordpress-seo/wp-seo.php'
37 ],
38 'options' => [
39 'wpseo-premium-redirects-base',
40 'wpseo-premium-redirects-export-plain',
41 'wpseo-premium-redirects-export-regex',
42 'wpseo-premium-redirects',
43 'wpseo-premium-redirects-regex'
44 ]
45 ],
46 'rankmath' => [
47 'name' => 'Rank Math',
48 'slug' => 'seo-by-rank-math/rank-math.php',
49 'table' => 'rank_math_redirections'
50 ],
51 'aioseo' => [
52 'name' => 'All in One SEO',
53 'slug' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
54 'table' => 'aioseo_redirects'
55 ],
56 'simple301' => [
57 'name' => 'Simple 301 Redirects',
58 'slug' => 'simple-301-redirects/simple-301-redirects.php',
59 'option' => '301_redirects'
60 ]
61 ];
62
63 public function __construct(&$db_redirection)
64 {
65 $this->db_redirection = $db_redirection;
66 }
67
68 /**
69 * Get list of available plugins for import
70 *
71 * @return array List of plugins with their status
72 */
73 public function get_available_plugins()
74 {
75 global $wpdb;
76 $available = [];
77
78 foreach (self::SUPPORTED_PLUGINS as $key => $plugin) {
79 $status = [
80 'key' => $key,
81 'name' => $plugin['name'],
82 'installed' => false,
83 'has_data' => false,
84 'count' => 0
85 ];
86
87 # Check if plugin is installed/active
88 if (isset($plugin['slug'])) {
89 $status['installed'] = is_plugin_active($plugin['slug']) || $this->plugin_exists($plugin['slug']);
90 } elseif (isset($plugin['slugs'])) {
91 # Check multiple possible slugs (for Yoast)
92 foreach ($plugin['slugs'] as $slug) {
93 if (is_plugin_active($slug) || $this->plugin_exists($slug)) {
94 $status['installed'] = true;
95 break;
96 }
97 }
98 }
99
100 # Check if data exists
101 if (isset($plugin['table'])) {
102 $table_name = $wpdb->prefix . $plugin['table'];
103 if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name) {
104 $count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
105 if ($count > 0) {
106 $status['has_data'] = true;
107 $status['count'] = (int)$count;
108 }
109 }
110 } elseif (isset($plugin['option'])) {
111 $option_data = get_option($plugin['option'], []);
112 if (!empty($option_data)) {
113 $status['has_data'] = true;
114 $status['count'] = is_array($option_data) ? count($option_data) : 0;
115 }
116 } elseif (isset($plugin['options'])) {
117 # Check multiple options (for Yoast)
118 $total_count = 0;
119 foreach ($plugin['options'] as $option_name) {
120 $option_data = get_option($option_name, []);
121 if (!empty($option_data)) {
122 $status['has_data'] = true;
123 if (is_array($option_data)) {
124 $total_count += count($option_data);
125 }
126 }
127 }
128 if ($total_count > 0) {
129 $status['count'] = $total_count;
130 }
131 }
132
133 $available[] = $status;
134 }
135
136 return $available;
137 }
138
139 /**
140 * Check if plugin exists (even if not active)
141 */
142 private function plugin_exists($plugin_path)
143 {
144 $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_path;
145 return file_exists($plugin_file);
146 }
147
148 /**
149 * Import redirections from specified plugin
150 *
151 * @param string $plugin Plugin key
152 * @return array Result with success status and message
153 */
154 public function import_from_plugin($plugin)
155 {
156 if (!isset(self::SUPPORTED_PLUGINS[$plugin])) {
157 return [
158 'success' => false,
159 'message' => 'Unknown plugin specified.',
160 'imported' => 0,
161 'skipped' => 0
162 ];
163 }
164
165 $method = "import_from_{$plugin}";
166 if (method_exists($this, $method)) {
167 return $this->$method();
168 }
169
170 return [
171 'success' => false,
172 'message' => 'Import method not implemented.',
173 'imported' => 0,
174 'skipped' => 0
175 ];
176 }
177
178 /**
179 * Import from Redirection plugin
180 */
181 private function import_from_redirection()
182 {
183 global $wpdb;
184 $table_name = $wpdb->prefix . 'redirection_items';
185
186 if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") !== $table_name) {
187 return [
188 'success' => false,
189 'message' => 'Redirection plugin table not found.',
190 'imported' => 0,
191 'skipped' => 0
192 ];
193 }
194
195 $redirects = $wpdb->get_results("
196 SELECT * FROM $table_name
197 WHERE status = 'enabled'
198 ");
199
200 $imported = 0;
201 $skipped = 0;
202
203 foreach ($redirects as $redirect) {
204 $http_code = intval($redirect->action_code ?? 301);
205 $target_url = $redirect->action_data ?? '';
206
207 # For 410 and 451, target URL can be empty (they don't redirect)
208 if (empty($target_url) && !in_array($http_code, [410, 451])) {
209 $skipped++;
210 continue;
211 }
212
213 # Check if already exists
214 if ($this->redirection_exists($redirect->url)) {
215 $skipped++;
216 continue;
217 }
218
219 # Map Redirection plugin format to MetaSync format
220 $pattern_type = 'exact';
221 $regex_pattern = null;
222
223 # Check if it's a regex
224 if (isset($redirect->regex) && $redirect->regex == 1) {
225 $pattern_type = 'regex';
226 $regex_pattern = $redirect->url;
227 }
228
229 $sources = [
230 $redirect->url => $pattern_type
231 ];
232
233 $args = [
234 'sources_from' => serialize($sources),
235 'url_redirect_to' => $target_url,
236 'http_code' => $http_code,
237 'hits_count' => $redirect->hits ?? 0,
238 'status' => 'active',
239 'pattern_type' => $pattern_type,
240 'regex_pattern' => $regex_pattern,
241 'description' => 'Imported from Redirection plugin',
242 'created_at' => current_time('mysql'),
243 'updated_at' => current_time('mysql')
244 ];
245
246 if ($this->db_redirection->add($args)) {
247 $imported++;
248 }
249 }
250
251 return [
252 'success' => true,
253 'message' => "Successfully imported $imported redirections from Redirection plugin.",
254 'imported' => $imported,
255 'skipped' => $skipped
256 ];
257 }
258
259 /**
260 * Import from Yoast SEO Premium
261 *
262 * Yoast stores redirects in multiple options:
263 * - wpseo-premium-redirects-base (main storage since v3.1)
264 * - wpseo-premium-redirects-export-plain (plain redirects)
265 * - wpseo-premium-redirects-export-regex (regex redirects)
266 * - wpseo-premium-redirects (legacy plain)
267 * - wpseo-premium-redirects-regex (legacy regex)
268 */
269 private function import_from_yoast()
270 {
271 $imported = 0;
272 $skipped = 0;
273 $errors = [];
274
275 # Try to import from base option first (preferred method)
276 $base_redirects = get_option('wpseo-premium-redirects-base', []);
277
278 if (!empty($base_redirects) && is_array($base_redirects)) {
279 foreach ($base_redirects as $redirect) {
280 $result = $this->process_yoast_redirect_base($redirect);
281 if ($result === true) {
282 $imported++;
283 } elseif ($result === 'skipped') {
284 $skipped++;
285 } else {
286 $errors[] = $result;
287 }
288 }
289 }
290
291 # If no base redirects, try export options
292 if ($imported === 0 && $skipped === 0) {
293 # Import plain redirects
294 $plain_redirects = get_option('wpseo-premium-redirects-export-plain', []);
295 if (!empty($plain_redirects) && is_array($plain_redirects)) {
296 foreach ($plain_redirects as $origin => $redirect_data) {
297 $result = $this->process_yoast_redirect_export($origin, $redirect_data, 'plain');
298 if ($result === true) {
299 $imported++;
300 } elseif ($result === 'skipped') {
301 $skipped++;
302 } else {
303 $errors[] = $result;
304 }
305 }
306 }
307
308 # Import regex redirects
309 $regex_redirects = get_option('wpseo-premium-redirects-export-regex', []);
310 if (!empty($regex_redirects) && is_array($regex_redirects)) {
311 foreach ($regex_redirects as $origin => $redirect_data) {
312 $result = $this->process_yoast_redirect_export($origin, $redirect_data, 'regex');
313 if ($result === true) {
314 $imported++;
315 } elseif ($result === 'skipped') {
316 $skipped++;
317 } else {
318 $errors[] = $result;
319 }
320 }
321 }
322 }
323
324 # If still no redirects, try legacy options
325 if ($imported === 0 && $skipped === 0) {
326 # Legacy plain redirects
327 $legacy_plain = get_option('wpseo-premium-redirects', []);
328 if (!empty($legacy_plain) && is_array($legacy_plain)) {
329 foreach ($legacy_plain as $origin => $redirect_data) {
330 $result = $this->process_yoast_redirect_export($origin, $redirect_data, 'plain');
331 if ($result === true) {
332 $imported++;
333 } elseif ($result === 'skipped') {
334 $skipped++;
335 } else {
336 $errors[] = $result;
337 }
338 }
339 }
340
341 # Legacy regex redirects
342 $legacy_regex = get_option('wpseo-premium-redirects-regex', []);
343 if (!empty($legacy_regex) && is_array($legacy_regex)) {
344 foreach ($legacy_regex as $origin => $redirect_data) {
345 $result = $this->process_yoast_redirect_export($origin, $redirect_data, 'regex');
346 if ($result === true) {
347 $imported++;
348 } elseif ($result === 'skipped') {
349 $skipped++;
350 } else {
351 $errors[] = $result;
352 }
353 }
354 }
355 }
356
357 # Return results
358 if ($imported === 0 && $skipped === 0) {
359 return [
360 'success' => false,
361 'message' => 'No Yoast redirections found. ' . (!empty($errors) ? implode(' ', array_slice($errors, 0, 2)) : ''),
362 'imported' => 0,
363 'skipped' => 0,
364 'errors' => $errors
365 ];
366 }
367
368 return [
369 'success' => true,
370 'message' => $imported > 0
371 ? "Successfully imported $imported redirections from Yoast SEO."
372 : "All redirections already exist. Skipped $skipped duplicates.",
373 'imported' => $imported,
374 'skipped' => $skipped,
375 'errors' => $errors
376 ];
377 }
378
379 /**
380 * Process a redirect from Yoast base option
381 *
382 * @param mixed $redirect Redirect data from base option
383 * @return bool|string True on success, 'skipped' if exists, error message on failure
384 */
385 private function process_yoast_redirect_base($redirect)
386 {
387 try {
388 # Base format: ['origin' => string, 'url' => string, 'type' => int, 'format' => string]
389 if (!is_array($redirect)) {
390 return 'Invalid redirect format';
391 }
392
393 $origin = isset($redirect['origin']) ? trim($redirect['origin']) : '';
394 $target = isset($redirect['url']) ? trim($redirect['url']) : '';
395 $type = isset($redirect['type']) ? $redirect['type'] : 301;
396 $format = isset($redirect['format']) ? $redirect['format'] : 'plain';
397
398 # Validate origin
399 if (empty($origin)) {
400 return 'Empty origin URL';
401 }
402
403 # For 410 and 451, target URL can be empty (they don't redirect)
404 if (empty($target) && !in_array($type, [410, 451])) {
405 return 'Empty target URL for non-410/451 redirect';
406 }
407
408 # Check if already exists
409 if ($this->redirection_exists($origin)) {
410 return 'skipped';
411 }
412
413 # Determine pattern type
414 $pattern_type = ($format === 'regex') ? 'regex' : 'exact';
415 $regex_pattern = ($format === 'regex') ? $origin : null;
416
417 $sources = [
418 $origin => $pattern_type
419 ];
420
421 $args = [
422 'sources_from' => serialize($sources),
423 'url_redirect_to' => $target,
424 'http_code' => intval($type),
425 'hits_count' => 0,
426 'status' => 'active',
427 'pattern_type' => $pattern_type,
428 'regex_pattern' => $regex_pattern,
429 'description' => 'Imported from Yoast SEO',
430 'created_at' => current_time('mysql'),
431 'updated_at' => current_time('mysql')
432 ];
433
434 if ($this->db_redirection->add($args)) {
435 return true;
436 }
437
438 return 'Failed to insert redirect';
439
440 } catch (Exception $e) {
441 return 'Exception: ' . $e->getMessage();
442 }
443 }
444
445 /**
446 * Process a redirect from Yoast export options
447 *
448 * @param string $origin Source URL
449 * @param mixed $redirect_data Redirect data from export option
450 * @param string $format Format type ('plain' or 'regex')
451 * @return bool|string True on success, 'skipped' if exists, error message on failure
452 */
453 private function process_yoast_redirect_export($origin, $redirect_data, $format)
454 {
455 try {
456 $origin = trim($origin);
457
458 # Export format: ['url' => string, 'type' => int]
459 if (is_array($redirect_data)) {
460 $target = isset($redirect_data['url']) ? trim($redirect_data['url']) : '';
461 $type = isset($redirect_data['type']) ? $redirect_data['type'] : 301;
462 } elseif (is_string($redirect_data)) {
463 # Simple string format (target URL only)
464 $target = trim($redirect_data);
465 $type = 301;
466 } else {
467 return 'Invalid redirect data format';
468 }
469
470 # Validate origin
471 if (empty($origin)) {
472 return 'Empty origin URL';
473 }
474
475 # For 410 and 451, target URL can be empty (they don't redirect)
476 if (empty($target) && !in_array($type, [410, 451])) {
477 return 'Empty target URL for non-410/451 redirect';
478 }
479
480 # Check if already exists
481 if ($this->redirection_exists($origin)) {
482 return 'skipped';
483 }
484
485 # Determine pattern type
486 $pattern_type = ($format === 'regex') ? 'regex' : 'exact';
487 $regex_pattern = ($format === 'regex') ? $origin : null;
488
489 $sources = [
490 $origin => $pattern_type
491 ];
492
493 $args = [
494 'sources_from' => serialize($sources),
495 'url_redirect_to' => $target,
496 'http_code' => intval($type),
497 'hits_count' => 0,
498 'status' => 'active',
499 'pattern_type' => $pattern_type,
500 'regex_pattern' => $regex_pattern,
501 'description' => 'Imported from Yoast SEO',
502 'created_at' => current_time('mysql'),
503 'updated_at' => current_time('mysql')
504 ];
505
506 if ($this->db_redirection->add($args)) {
507 return true;
508 }
509
510 return 'Failed to insert redirect';
511
512 } catch (Exception $e) {
513 return 'Exception: ' . $e->getMessage();
514 }
515 }
516
517 /**
518 * Import from Rank Math
519 */
520 private function import_from_rankmath()
521 {
522 global $wpdb;
523 $table_name = $wpdb->prefix . 'rank_math_redirections';
524
525 if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") !== $table_name) {
526 return [
527 'success' => false,
528 'message' => 'Rank Math redirections table not found.',
529 'imported' => 0,
530 'skipped' => 0,
531 'errors' => ['Table not found: ' . $table_name]
532 ];
533 }
534
535 # Get all redirections (not just active ones, Rank Math uses different status field)
536 $redirects = $wpdb->get_results("SELECT * FROM $table_name");
537
538 if (empty($redirects)) {
539 return [
540 'success' => false,
541 'message' => 'No redirections found in Rank Math.',
542 'imported' => 0,
543 'skipped' => 0,
544 'errors' => []
545 ];
546 }
547
548 $imported = 0;
549 $skipped = 0;
550 $errors = [];
551
552 foreach ($redirects as $redirect) {
553 try {
554 # Rank Math uses 'sources' field - it's a serialized PHP array
555 $source_url = '';
556 $comparison_type = 'exact'; // Default to exact
557
558 if (isset($redirect->sources)) {
559 # First, try to unserialize (Rank Math format)
560 $unserialized = @maybe_unserialize($redirect->sources);
561
562 if (is_array($unserialized) && !empty($unserialized)) {
563 # Rank Math format: array of arrays with 'pattern', 'comparison', 'ignore' keys
564 $first_source = reset($unserialized);
565
566 if (is_array($first_source) && isset($first_source['pattern'])) {
567 # Extract the actual URL pattern
568 $source_url = $first_source['pattern'];
569
570 # Extract comparison type (exact, regex, contains, starts, ends)
571 if (isset($first_source['comparison'])) {
572 $comparison_type = $first_source['comparison'];
573 }
574 } elseif (is_string($first_source)) {
575 # Simple string format
576 $source_url = $first_source;
577 }
578 } elseif (is_string($unserialized)) {
579 # Direct string value
580 $source_url = $unserialized;
581 } else {
582 # Try as JSON
583 $parsed = json_decode($redirect->sources, true);
584 if (json_last_error() === JSON_ERROR_NONE && is_array($parsed)) {
585 $source_url = is_array($parsed) ? reset($parsed) : $parsed;
586 } else {
587 # Last resort - use as-is
588 $source_url = $redirect->sources;
589 }
590 }
591 } elseif (isset($redirect->url_from)) {
592 $source_url = $redirect->url_from;
593 }
594
595 # Clean up the source URL
596 $source_url = trim($source_url);
597
598 if (empty($source_url)) {
599 $errors[] = 'Empty source URL in redirect ID: ' . ($redirect->id ?? 'unknown');
600 $skipped++;
601 continue;
602 }
603
604 # Check if already exists
605 if ($this->redirection_exists($source_url)) {
606 $skipped++;
607 continue;
608 }
609
610 # Map Rank Math comparison types to MetaSync pattern types
611 # Rank Math: exact, regex, contains, starts, ends
612 # MetaSync: exact, regex, contain, start, end
613 $pattern_type_map = [
614 'exact' => 'exact',
615 'regex' => 'regex',
616 'contains' => 'contain',
617 'starts' => 'start',
618 'ends' => 'end'
619 ];
620
621 $pattern_type = isset($pattern_type_map[$comparison_type])
622 ? $pattern_type_map[$comparison_type]
623 : 'exact';
624
625 # For regex patterns, store the pattern
626 $regex_pattern = ($pattern_type === 'regex') ? $source_url : null;
627
628 $sources = [
629 $source_url => $pattern_type
630 ];
631
632 $target_url = $redirect->url_to ?? '';
633 $http_code = intval($redirect->header_code ?? 301);
634
635 # For 410 and 451, target URL can be empty (they don't redirect)
636 if (empty($target_url) && !in_array($http_code, [410, 451])) {
637 $errors[] = 'Empty target URL for source: ' . $source_url;
638 $skipped++;
639 continue;
640 }
641
642 $args = [
643 'sources_from' => serialize($sources),
644 'url_redirect_to' => $target_url,
645 'http_code' => $http_code,
646 'hits_count' => $redirect->hits ?? 0,
647 'status' => 'active',
648 'pattern_type' => $pattern_type,
649 'regex_pattern' => $regex_pattern,
650 'description' => 'Imported from Rank Math',
651 'created_at' => current_time('mysql'),
652 'updated_at' => current_time('mysql')
653 ];
654
655 $result = $this->db_redirection->add($args);
656
657 if ($result !== false && $result > 0) {
658 $imported++;
659 } else {
660 $errors[] = 'Failed to insert redirect: ' . $source_url . ' -> ' . $target_url;
661 if ($wpdb->last_error) {
662 $errors[] = 'DB Error: ' . $wpdb->last_error;
663 }
664 }
665
666 } catch (Exception $e) {
667 $errors[] = 'Exception: ' . $e->getMessage();
668 $skipped++;
669 }
670 }
671
672 # Return success if we processed redirections (even if all skipped)
673 $has_results = ($imported + $skipped) > 0;
674
675 return [
676 'success' => $has_results,
677 'message' => $imported > 0
678 ? "Successfully imported $imported redirections from Rank Math."
679 : ($skipped > 0
680 ? "All redirections already exist. Skipped $skipped duplicates."
681 : "No redirections found to import. " . (count($errors) > 0 ? implode(' ', array_slice($errors, 0, 2)) : '')),
682 'imported' => $imported,
683 'skipped' => $skipped,
684 'errors' => $errors
685 ];
686 }
687
688 /**
689 * Import from All in One SEO
690 */
691 private function import_from_aioseo()
692 {
693 global $wpdb;
694 $table_name = $wpdb->prefix . 'aioseo_redirects';
695
696 if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") !== $table_name) {
697 return [
698 'success' => false,
699 'message' => 'All in One SEO redirects table not found.',
700 'imported' => 0,
701 'skipped' => 0
702 ];
703 }
704
705 $redirects = $wpdb->get_results("
706 SELECT * FROM $table_name
707 WHERE enabled = 1
708 ");
709
710 $imported = 0;
711 $skipped = 0;
712
713 foreach ($redirects as $redirect) {
714 $http_code = intval($redirect->redirect_code ?? 301);
715 $target_url = $redirect->target_url ?? '';
716
717 # For 410 and 451, target URL can be empty (they don't redirect)
718 if (empty($target_url) && !in_array($http_code, [410, 451])) {
719 $skipped++;
720 continue;
721 }
722
723 # Check if already exists
724 if ($this->redirection_exists($redirect->source_url)) {
725 $skipped++;
726 continue;
727 }
728
729 # Determine pattern type
730 $pattern_type = 'exact';
731 $regex_pattern = null;
732
733 if (isset($redirect->regex) && $redirect->regex == 1) {
734 $pattern_type = 'regex';
735 $regex_pattern = $redirect->source_url;
736 }
737
738 $sources = [
739 $redirect->source_url => $pattern_type
740 ];
741
742 $args = [
743 'sources_from' => serialize($sources),
744 'url_redirect_to' => $target_url,
745 'http_code' => $http_code,
746 'hits_count' => $redirect->hits ?? 0,
747 'status' => 'active',
748 'pattern_type' => $pattern_type,
749 'regex_pattern' => $regex_pattern,
750 'description' => 'Imported from All in One SEO',
751 'created_at' => current_time('mysql'),
752 'updated_at' => current_time('mysql')
753 ];
754
755 if ($this->db_redirection->add($args)) {
756 $imported++;
757 }
758 }
759
760 return [
761 'success' => true,
762 'message' => "Successfully imported $imported redirections from All in One SEO.",
763 'imported' => $imported,
764 'skipped' => $skipped
765 ];
766 }
767
768 /**
769 * Import from Simple 301 Redirects
770 */
771 private function import_from_simple301()
772 {
773 # Simple 301 Redirects stores in options
774 $redirects = get_option('301_redirects', []);
775
776 if (empty($redirects)) {
777 return [
778 'success' => false,
779 'message' => 'No Simple 301 Redirects found.',
780 'imported' => 0,
781 'skipped' => 0
782 ];
783 }
784
785 $imported = 0;
786 $skipped = 0;
787
788 foreach ($redirects as $old_url => $new_url) {
789 # Skip empty entries
790 if (empty($old_url) || empty($new_url)) {
791 $skipped++;
792 continue;
793 }
794
795 # Check if already exists
796 if ($this->redirection_exists($old_url)) {
797 $skipped++;
798 continue;
799 }
800
801 $sources = [
802 $old_url => 'exact'
803 ];
804
805 $args = [
806 'sources_from' => serialize($sources),
807 'url_redirect_to' => $new_url,
808 'http_code' => 301,
809 'hits_count' => 0,
810 'status' => 'active',
811 'pattern_type' => 'exact',
812 'regex_pattern' => null,
813 'description' => 'Imported from Simple 301 Redirects',
814 'created_at' => current_time('mysql'),
815 'updated_at' => current_time('mysql')
816 ];
817
818 if ($this->db_redirection->add($args)) {
819 $imported++;
820 }
821 }
822
823 return [
824 'success' => true,
825 'message' => "Successfully imported $imported redirections from Simple 301 Redirects.",
826 'imported' => $imported,
827 'skipped' => $skipped
828 ];
829 }
830
831 /**
832 * Check if a redirection already exists
833 *
834 * @param string $source_url Source URL to check
835 * @return bool True if exists, false otherwise
836 */
837 private function redirection_exists($source_url)
838 {
839 global $wpdb;
840 $table_name = $wpdb->prefix . Metasync_Redirection_Database::$table_name;
841
842 $count = $wpdb->get_var($wpdb->prepare(
843 "SELECT COUNT(*) FROM $table_name WHERE sources_from LIKE %s",
844 '%' . $wpdb->esc_like($source_url) . '%'
845 ));
846
847 return $count > 0;
848 }
849
850 /**
851 * Get import statistics
852 *
853 * @return array Statistics about imports
854 */
855 public function get_import_stats()
856 {
857 global $wpdb;
858 $table_name = $wpdb->prefix . Metasync_Redirection_Database::$table_name;
859
860 $stats = [
861 'total_redirections' => 0,
862 'imported_redirections' => 0
863 ];
864
865 $stats['total_redirections'] = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
866
867 $stats['imported_redirections'] = $wpdb->get_var($wpdb->prepare(
868 "SELECT COUNT(*) FROM $table_name WHERE description LIKE %s",
869 'Imported from%'
870 ));
871
872 return $stats;
873 }
874 }
875