PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.10.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / api / class-settings-management-endpoint.php

class-settings-management-endpoint.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.10.0, at includes/api/class-settings-management-endpoint.php

2,014 lines 67.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings Management API Endpoints Class
4 *
5 * REST API endpoints for centralized settings management across all SEO managers
6 * including global settings CRUD, validation and schema management, import/export
7 * functionality, and backup/restore operations. Provides comprehensive API access
8 * to Settings Manager functionality with proper authentication and validation.
9 *
10 * @package ThinkRank
11 * @subpackage API
12 * @since 1.0.0
13 */
14
15 declare(strict_types=1);
16
17 namespace ThinkRank\API;
18
19 use ThinkRank\Core\Settings_Manager;
20 use ThinkRank\SEO\Site_Identity_Manager;
21 use ThinkRank\SEO\Performance_Monitoring_Manager;
22 use ThinkRank\SEO\AI_Content_Analyzer;
23 use ThinkRank\SEO\Content_Optimization_Manager;
24 use ThinkRank\SEO\Schema_Management_System;
25 use ThinkRank\SEO\Social_Meta_Manager;
26 use ThinkRank\SEO\Sitemap_Generator;
27 use WP_REST_Controller;
28 use WP_REST_Request;
29 use WP_REST_Response;
30 use WP_Error;
31
32 /**
33 * Settings Management API Endpoints Class
34 *
35 * Provides REST API endpoints for centralized settings management operations
36 * including global settings CRUD, validation, import/export, backup/restore,
37 * and cross-manager settings coordination with proper authentication and validation.
38 *
39 * @since 1.0.0
40 */
41 class Settings_Management_Endpoint extends WP_REST_Controller {
42
43 /**
44 * Settings Manager instance
45 *
46 * @since 1.0.0
47 * @var Settings_Manager
48 */
49 private Settings_Manager $settings_manager;
50
51 /**
52 * SEO Manager instances for integration
53 *
54 * @since 1.0.0
55 * @var array
56 */
57 private array $seo_managers;
58
59 /**
60 * API namespace
61 *
62 * @since 1.0.0
63 * @var string
64 */
65 protected $namespace = 'thinkrank/v1';
66
67 /**
68 * API resource base
69 *
70 * @since 1.0.0
71 * @var string
72 */
73 protected $rest_base = 'settings-management';
74
75 /**
76 * Supported setting categories
77 *
78 * @since 1.0.0
79 * @var array
80 */
81 private array $setting_categories = [
82 'site_identity' => 'Site Identity & Global SEO',
83 'content_analysis' => 'AI Content Analysis',
84 'content_optimization' => 'Content Optimization',
85 'performance_monitoring' => 'Performance Monitoring',
86 'schema_management' => 'Schema Management',
87 'social_media' => 'Social Media & Open Graph',
88 'sitemap' => 'XML Sitemap Management',
89 'integrations' => 'External Integrations',
90 'analytics_integration' => 'Analytics Integration',
91 'seo_analytics' => 'SEO Analytics & Intelligence',
92 'global_defaults' => 'Global Default Settings'
93 ];
94
95 /**
96 * Constructor
97 *
98 * @since 1.0.0
99 */
100 public function __construct() {
101 $this->settings_manager = new Settings_Manager();
102
103 // Initialize SEO manager instances for integration
104 $this->seo_managers = [
105 'site_identity' => new Site_Identity_Manager(),
106 'performance_monitoring' => new Performance_Monitoring_Manager(),
107 'ai_content_analyzer' => new AI_Content_Analyzer(),
108 'content_optimization' => new Content_Optimization_Manager(),
109 'schema_management' => new Schema_Management_System(),
110 'social_media' => new Social_Meta_Manager(),
111 'sitemap' => new Sitemap_Generator(),
112 'analytics_integration' => new Performance_Monitoring_Manager()
113 ];
114 }
115
116 /**
117 * Register API routes
118 *
119 * @since 1.0.0
120 */
121 public function register_routes(): void {
122 // Global settings management
123 register_rest_route(
124 $this->namespace,
125 '/' . $this->rest_base . '/global',
126 [
127 [
128 'methods' => 'GET',
129 'callback' => [$this, 'get_global_settings'],
130 'permission_callback' => [$this, 'check_read_permissions']
131 ],
132 [
133 'methods' => 'POST',
134 'callback' => [$this, 'update_global_settings'],
135 'permission_callback' => [$this, 'check_manage_permissions'],
136 'args' => $this->get_global_settings_args()
137 ]
138 ]
139 );
140
141 // Category-specific settings
142 register_rest_route(
143 $this->namespace,
144 '/' . $this->rest_base . '/category/(?P<category>[a-zA-Z0-9_-]+)',
145 [
146 [
147 'methods' => 'GET',
148 'callback' => [$this, 'get_category_settings'],
149 'permission_callback' => [$this, 'check_read_permissions'],
150 'args' => [
151 'category' => [
152 'required' => true,
153 'type' => 'string',
154 'enum' => array_keys($this->setting_categories)
155 ]
156 ]
157 ],
158 [
159 'methods' => 'POST',
160 'callback' => [$this, 'update_category_settings'],
161 'permission_callback' => [$this, 'check_manage_permissions'],
162 'args' => $this->get_category_settings_args()
163 ]
164 ]
165 );
166
167 // Settings validation and schema
168 register_rest_route(
169 $this->namespace,
170 '/' . $this->rest_base . '/validate',
171 [
172 [
173 'methods' => 'POST',
174 'callback' => [$this, 'validate_settings'],
175 'permission_callback' => [$this, 'check_read_permissions'],
176 'args' => $this->get_validation_args()
177 ]
178 ]
179 );
180
181 // Settings schema management
182 register_rest_route(
183 $this->namespace,
184 '/' . $this->rest_base . '/schema',
185 [
186 [
187 'methods' => 'GET',
188 'callback' => [$this, 'get_settings_schema'],
189 'permission_callback' => [$this, 'check_read_permissions']
190 ]
191 ]
192 );
193
194 // Settings import/export
195 register_rest_route(
196 $this->namespace,
197 '/' . $this->rest_base . '/export',
198 [
199 [
200 'methods' => 'POST',
201 'callback' => [$this, 'export_settings'],
202 'permission_callback' => [$this, 'check_manage_permissions'],
203 'args' => $this->get_export_args()
204 ]
205 ]
206 );
207
208 register_rest_route(
209 $this->namespace,
210 '/' . $this->rest_base . '/import',
211 [
212 [
213 'methods' => 'POST',
214 'callback' => [$this, 'import_settings'],
215 'permission_callback' => [$this, 'check_manage_permissions'],
216 'args' => $this->get_import_args()
217 ]
218 ]
219 );
220
221 // Settings backup/restore
222 register_rest_route(
223 $this->namespace,
224 '/' . $this->rest_base . '/backup',
225 [
226 [
227 'methods' => 'POST',
228 'callback' => [$this, 'create_settings_backup'],
229 'permission_callback' => [$this, 'check_manage_permissions'],
230 'args' => $this->get_backup_args()
231 ]
232 ]
233 );
234
235 register_rest_route(
236 $this->namespace,
237 '/' . $this->rest_base . '/restore',
238 [
239 [
240 'methods' => 'POST',
241 'callback' => [$this, 'restore_settings_backup'],
242 'permission_callback' => [$this, 'check_manage_permissions'],
243 'args' => $this->get_restore_args()
244 ]
245 ]
246 );
247
248 // Settings conflicts resolution
249 register_rest_route(
250 $this->namespace,
251 '/' . $this->rest_base . '/conflicts',
252 [
253 [
254 'methods' => 'GET',
255 'callback' => [$this, 'detect_settings_conflicts'],
256 'permission_callback' => [$this, 'check_read_permissions']
257 ],
258 [
259 'methods' => 'POST',
260 'callback' => [$this, 'resolve_settings_conflicts'],
261 'permission_callback' => [$this, 'check_manage_permissions'],
262 'args' => $this->get_conflict_resolution_args()
263 ]
264 ]
265 );
266
267 // Settings reset
268 register_rest_route(
269 $this->namespace,
270 '/' . $this->rest_base . '/reset',
271 [
272 [
273 'methods' => 'POST',
274 'callback' => [$this, 'reset_settings'],
275 'permission_callback' => [$this, 'check_manage_permissions'],
276 'args' => $this->get_reset_args()
277 ]
278 ]
279 );
280
281 // Bulk operations
282 register_rest_route(
283 $this->namespace,
284 '/' . $this->rest_base . '/bulk',
285 [
286 [
287 'methods' => 'POST',
288 'callback' => [$this, 'bulk_operations'],
289 'permission_callback' => [$this, 'check_manage_permissions'],
290 'args' => $this->get_bulk_operations_args()
291 ]
292 ]
293 );
294
295 // Database maintenance operations
296 register_rest_route(
297 $this->namespace,
298 '/' . $this->rest_base . '/maintenance/performance-indexes',
299 [
300 [
301 'methods' => 'POST',
302 'callback' => [$this, 'add_performance_indexes'],
303 'permission_callback' => [$this, 'check_manage_permissions']
304 ]
305 ]
306 );
307 }
308
309 /**
310 * Get global settings across all categories
311 *
312 * @since 1.0.0
313 *
314 * @param WP_REST_Request $request Request object
315 * @return WP_REST_Response Response object
316 */
317 public function get_global_settings(WP_REST_Request $request): WP_REST_Response {
318 try {
319 $include_categories = $request->get_param('categories') ?? array_keys($this->setting_categories);
320 $include_schema = $request->get_param('include_schema') ?? false;
321
322 $global_settings = [];
323 $settings_schema = [];
324
325 foreach ($include_categories as $category) {
326 if (!isset($this->setting_categories[$category])) {
327 continue;
328 }
329
330 // Get settings for each category using Settings Manager
331 $category_settings = $this->settings_manager->get_settings($category);
332 $global_settings[$category] = $category_settings;
333
334 // Get schema if requested
335 if ($include_schema && isset($this->seo_managers[$category])) {
336 $settings_schema[$category] = $this->seo_managers[$category]->get_settings_schema($category);
337 }
338 }
339
340 // Get global metadata
341 $metadata = [
342 'total_categories' => count($this->setting_categories),
343 'loaded_categories' => count($global_settings),
344 'last_updated' => $this->get_last_settings_update(),
345 'settings_version' => $this->get_settings_version()
346 ];
347
348 return new WP_REST_Response([
349 'success' => true,
350 'data' => [
351 'settings' => $global_settings,
352 'schema' => $settings_schema,
353 'metadata' => $metadata,
354 'categories' => $this->setting_categories
355 ],
356 'message' => 'Global settings retrieved successfully'
357 ], 200);
358
359 } catch (\Exception $e) {
360 return new WP_REST_Response([
361 'success' => false,
362 'error' => 'Failed to retrieve global settings: ' . $e->getMessage()
363 ], 500);
364 }
365 }
366
367 /**
368 * Update global settings across multiple categories
369 *
370 * @since 1.0.0
371 *
372 * @param WP_REST_Request $request Request object
373 * @return WP_REST_Response|WP_Error Response object or error
374 */
375 public function update_global_settings(WP_REST_Request $request) {
376 try {
377 $settings = $request->get_param('settings');
378 $validate_before_update = $request->get_param('validate') ?? true;
379
380 // Validate settings structure
381 if (empty($settings) || !is_array($settings)) {
382 return new WP_Error(
383 'invalid_settings',
384 'Settings must be provided as an array',
385 ['status' => 400]
386 );
387 }
388
389 $validation_results = [];
390 $update_results = [];
391
392 // Validate all settings before updating if requested
393 if ($validate_before_update) {
394 foreach ($settings as $category => $category_settings) {
395 if (!isset($this->setting_categories[$category])) {
396 continue;
397 }
398
399 if (isset($this->seo_managers[$category])) {
400 $validation = $this->seo_managers[$category]->validate_settings($category_settings);
401 $validation_results[$category] = $validation;
402
403 if (!$validation['valid']) {
404 return new WP_Error(
405 'validation_failed',
406 "Settings validation failed for category: {$category}",
407 [
408 'status' => 400,
409 'validation_results' => $validation_results
410 ]
411 );
412 }
413 }
414 }
415 }
416
417 // Update settings for each category
418 foreach ($settings as $category => $category_settings) {
419 if (!isset($this->setting_categories[$category])) {
420 continue;
421 }
422
423 try {
424 // Update using Settings Manager
425 $update_success = $this->settings_manager->update_settings($category_settings, $category);
426
427 // Also update through specific SEO manager if available
428 if (isset($this->seo_managers[$category])) {
429 $manager_update = $this->seo_managers[$category]->save_settings('site', null, $category_settings);
430 $update_success = $update_success && $manager_update;
431 }
432
433 $update_results[$category] = [
434 'success' => $update_success,
435 'settings_count' => count($category_settings)
436 ];
437
438 } catch (\Exception $e) {
439 $update_results[$category] = [
440 'success' => false,
441 'error' => $e->getMessage()
442 ];
443 }
444 }
445
446 // Update settings version and timestamp
447 $this->update_settings_metadata();
448
449 // Get updated settings
450 $updated_settings = [];
451 foreach (array_keys($settings) as $category) {
452 if (isset($this->setting_categories[$category])) {
453 $updated_settings[$category] = $this->settings_manager->get_settings($category);
454 }
455 }
456
457 return new WP_REST_Response([
458 'success' => true,
459 'data' => [
460 'updated_settings' => $updated_settings,
461 'validation_results' => $validation_results,
462 'update_results' => $update_results,
463 'settings_version' => $this->get_settings_version()
464 ],
465 'message' => 'Global settings updated successfully'
466 ], 200);
467
468 } catch (\Exception $e) {
469 return new WP_Error(
470 'update_failed',
471 'Global settings update failed: ' . $e->getMessage(),
472 ['status' => 500]
473 );
474 }
475 }
476
477 /**
478 * Get settings for specific category
479 *
480 * @since 1.0.0
481 *
482 * @param WP_REST_Request $request Request object
483 * @return WP_REST_Response|WP_Error Response object or error
484 */
485 public function get_category_settings(WP_REST_Request $request) {
486 try {
487 $category = $request->get_param('category');
488 $include_schema = $request->get_param('include_schema') ?? false;
489
490 // Validate category
491 if (!isset($this->setting_categories[$category])) {
492 return new WP_Error(
493 'invalid_category',
494 'Invalid settings category provided',
495 ['status' => 400]
496 );
497 }
498
499 // Get category settings
500 $category_settings = $this->settings_manager->get_settings($category);
501
502 // Get schema if requested
503 $schema = [];
504 if ($include_schema && isset($this->seo_managers[$category])) {
505 $schema = $this->seo_managers[$category]->get_settings_schema($category);
506 }
507
508 // Get category metadata
509 $metadata = [
510 'category' => $category,
511 'category_name' => $this->setting_categories[$category],
512 'settings_count' => count($category_settings),
513 'last_updated' => $this->get_category_last_update($category),
514 'has_manager' => isset($this->seo_managers[$category])
515 ];
516
517 return new WP_REST_Response([
518 'success' => true,
519 'data' => [
520 'settings' => $category_settings,
521 'schema' => $schema,
522 'metadata' => $metadata
523 ],
524 'message' => "Settings for category '{$category}' retrieved successfully"
525 ], 200);
526
527 } catch (\Exception $e) {
528 return new WP_Error(
529 'retrieval_failed',
530 'Category settings retrieval failed: ' . $e->getMessage(),
531 ['status' => 500]
532 );
533 }
534 }
535
536 /**
537 * Update settings for specific category
538 *
539 * @since 1.0.0
540 *
541 * @param WP_REST_Request $request Request object
542 * @return WP_REST_Response|WP_Error Response object or error
543 */
544 public function update_category_settings(WP_REST_Request $request) {
545 try {
546 $category = $request->get_param('category');
547 $request_data = $request->get_param('settings');
548 $validate_before_update = $request->get_param('validate') ?? true;
549
550 // Extract only the actual settings data, not metadata
551 if (isset($request_data['settings'])) {
552 // If settings are nested under 'settings' key, use that
553 $settings = $request_data['settings'];
554 } else {
555 // Otherwise use the data directly
556 $settings = $request_data;
557 }
558
559 // Validate category
560 if (!isset($this->setting_categories[$category])) {
561 return new WP_Error(
562 'invalid_category',
563 'Invalid settings category provided',
564 ['status' => 400]
565 );
566 }
567
568 // Validate settings
569 if (empty($settings) || !is_array($settings)) {
570 return new WP_Error(
571 'invalid_settings',
572 'Settings must be provided as an array',
573 ['status' => 400]
574 );
575 }
576
577 $validation_result = ['valid' => true];
578
579 // Validate settings if requested
580 if ($validate_before_update && isset($this->seo_managers[$category])) {
581 $validation_result = $this->seo_managers[$category]->validate_settings($settings);
582
583 if (!$validation_result['valid']) {
584 return new WP_Error(
585 'validation_failed',
586 "Settings validation failed for category: {$category}",
587 [
588 'status' => 400,
589 'validation_errors' => $validation_result['errors'],
590 'validation_warnings' => $validation_result['warnings']
591 ]
592 );
593 }
594 }
595
596 // Update settings
597 $update_success = $this->settings_manager->update_settings($settings, $category);
598
599 // Also update through specific SEO manager if available
600 if (isset($this->seo_managers[$category])) {
601 $context_type = $request->get_param('context_type') ?? 'site';
602 $context_id = $request->get_param('context_id') ?? null;
603 $manager_update = $this->seo_managers[$category]->save_settings($context_type, $context_id, $settings);
604 $update_success = $update_success && $manager_update;
605 }
606
607 if (!$update_success) {
608 return new WP_Error(
609 'update_failed',
610 "Failed to update settings for category: {$category}",
611 ['status' => 500]
612 );
613 }
614
615 // Clear analytics cache when GSC/GA settings change so fresh data is fetched
616 if ($category === 'seo_analytics') {
617 foreach (['7d', '30d', '90d'] as $range) {
618 delete_transient("analytics_dashboard_v4_{$range}");
619 delete_transient("seo_opportunities_{$range}");
620 delete_transient("seo_insights_{$range}");
621 }
622 delete_transient('indexing_status');
623 }
624
625 // Update category metadata
626 $this->update_category_metadata($category);
627
628 // Get updated settings
629 $updated_settings = $this->settings_manager->get_settings($category);
630
631 return new WP_REST_Response([
632 'success' => true,
633 'data' => [
634 'category' => $category,
635 'updated_settings' => $updated_settings,
636 'validation_result' => $validation_result,
637 'settings_count' => count($updated_settings)
638 ],
639 'message' => "Settings for category '{$category}' updated successfully"
640 ], 200);
641
642 } catch (\Exception $e) {
643 return new WP_Error(
644 'update_failed',
645 'Category settings update failed: ' . $e->getMessage(),
646 ['status' => 500]
647 );
648 }
649 }
650
651 /**
652 * Validate settings across categories
653 *
654 * @since 1.0.0
655 *
656 * @param WP_REST_Request $request Request object
657 * @return WP_REST_Response Response object
658 */
659 public function validate_settings(WP_REST_Request $request): WP_REST_Response {
660 try {
661 $settings = $request->get_param('settings');
662 $categories = $request->get_param('categories') ?? array_keys($this->setting_categories);
663
664 $validation_results = [];
665 $overall_valid = true;
666
667 foreach ($categories as $category) {
668 if (!isset($this->setting_categories[$category])) {
669 continue;
670 }
671
672 $category_settings = $settings[$category] ?? [];
673
674 if (isset($this->seo_managers[$category])) {
675 $validation = $this->seo_managers[$category]->validate_settings($category_settings);
676 $validation_results[$category] = $validation;
677
678 if (!$validation['valid']) {
679 $overall_valid = false;
680 }
681 } else {
682 // Basic validation for categories without specific managers
683 $validation_results[$category] = [
684 'valid' => true,
685 'errors' => [],
686 'warnings' => [],
687 'suggestions' => []
688 ];
689 }
690 }
691
692 // Check for cross-category conflicts
693 $conflict_analysis = $this->analyze_cross_category_conflicts($settings);
694
695 return new WP_REST_Response([
696 'success' => true,
697 'data' => [
698 'validation_results' => $validation_results,
699 'overall_valid' => $overall_valid,
700 'conflict_analysis' => $conflict_analysis,
701 'validated_categories' => count($validation_results),
702 'validation_timestamp' => current_time('mysql')
703 ],
704 'message' => 'Settings validation completed'
705 ], 200);
706
707 } catch (\Exception $e) {
708 return new WP_REST_Response([
709 'success' => false,
710 'error' => 'Settings validation failed: ' . $e->getMessage()
711 ], 500);
712 }
713 }
714
715 /**
716 * Get settings schema for all categories
717 *
718 * @since 1.0.0
719 *
720 * @param WP_REST_Request $request Request object
721 * @return WP_REST_Response Response object
722 */
723 public function get_settings_schema(WP_REST_Request $request): WP_REST_Response {
724 try {
725 $categories = $request->get_param('categories') ?? array_keys($this->setting_categories);
726
727 $schema_data = [];
728
729 foreach ($categories as $category) {
730 if (!isset($this->setting_categories[$category])) {
731 continue;
732 }
733
734 if (isset($this->seo_managers[$category])) {
735 $schema_data[$category] = [
736 'schema' => $this->seo_managers[$category]->get_settings_schema($category),
737 'defaults' => $this->seo_managers[$category]->get_default_settings($category),
738 'category_name' => $this->setting_categories[$category]
739 ];
740 } else {
741 $schema_data[$category] = [
742 'schema' => [],
743 'defaults' => [],
744 'category_name' => $this->setting_categories[$category]
745 ];
746 }
747 }
748
749 return new WP_REST_Response([
750 'success' => true,
751 'data' => [
752 'schema' => $schema_data,
753 'categories' => $this->setting_categories,
754 'schema_version' => $this->get_schema_version(),
755 'generated_at' => current_time('mysql')
756 ],
757 'message' => 'Settings schema retrieved successfully'
758 ], 200);
759
760 } catch (\Exception $e) {
761 return new WP_REST_Response([
762 'success' => false,
763 'error' => 'Failed to retrieve settings schema: ' . $e->getMessage()
764 ], 500);
765 }
766 }
767
768 /**
769 * Export settings
770 *
771 * @since 1.0.0
772 *
773 * @param WP_REST_Request $request Request object
774 * @return WP_REST_Response|WP_Error Response object or error
775 */
776 public function export_settings(WP_REST_Request $request) {
777 try {
778 $categories = $request->get_param('categories') ?? array_keys($this->setting_categories);
779 $format = $request->get_param('format') ?? 'json';
780 $include_metadata = $request->get_param('include_metadata') ?? true;
781
782 // Validate format
783 if (!in_array($format, ['json', 'yaml', 'xml'], true)) {
784 return new WP_Error(
785 'invalid_format',
786 'Invalid export format. Supported formats: json, yaml, xml',
787 ['status' => 400]
788 );
789 }
790
791 $export_data = [];
792
793 // Export settings for each category
794 foreach ($categories as $category) {
795 if (!isset($this->setting_categories[$category])) {
796 continue;
797 }
798
799 $export_data[$category] = $this->settings_manager->get_settings($category);
800 }
801
802 // Add metadata if requested
803 $metadata = [];
804 if ($include_metadata) {
805 $metadata = [
806 'export_timestamp' => current_time('mysql'),
807 'export_version' => $this->get_settings_version(),
808 'wordpress_version' => get_bloginfo('version'),
809 'thinkrank_version' => '1.0.0',
810 'site_url' => home_url(),
811 'exported_categories' => $categories
812 ];
813 }
814
815 // Format export data
816 $formatted_export = $this->format_export_data($export_data, $metadata, $format);
817
818 return new WP_REST_Response([
819 'success' => true,
820 'data' => [
821 'export_data' => $formatted_export,
822 'format' => $format,
823 'metadata' => $metadata,
824 'exported_categories' => count($export_data)
825 ],
826 'message' => 'Settings exported successfully'
827 ], 200);
828
829 } catch (\Exception $e) {
830 return new WP_Error(
831 'export_failed',
832 'Settings export failed: ' . $e->getMessage(),
833 ['status' => 500]
834 );
835 }
836 }
837
838 /**
839 * Import settings
840 *
841 * @since 1.0.0
842 *
843 * @param WP_REST_Request $request Request object
844 * @return WP_REST_Response|WP_Error Response object or error
845 */
846 public function import_settings(WP_REST_Request $request) {
847 try {
848 $import_data = $request->get_param('import_data');
849 $format = $request->get_param('format') ?? 'json';
850 $validate_before_import = $request->get_param('validate') ?? true;
851 $overwrite_existing = $request->get_param('overwrite_existing') ?? false;
852
853 // Validate import data
854 if (empty($import_data)) {
855 return new WP_Error(
856 'missing_import_data',
857 'Import data is required',
858 ['status' => 400]
859 );
860 }
861
862 // Parse import data based on format
863 $parsed_data = $this->parse_import_data($import_data, $format);
864
865 if (!$parsed_data) {
866 return new WP_Error(
867 'invalid_import_data',
868 'Failed to parse import data',
869 ['status' => 400]
870 );
871 }
872
873 $import_results = [];
874 $validation_results = [];
875
876 // Validate imported settings if requested
877 if ($validate_before_import) {
878 foreach ($parsed_data as $category => $category_settings) {
879 if (!isset($this->setting_categories[$category])) {
880 continue;
881 }
882
883 if (isset($this->seo_managers[$category])) {
884 $validation = $this->seo_managers[$category]->validate_settings($category_settings);
885 $validation_results[$category] = $validation;
886
887 if (!$validation['valid']) {
888 return new WP_Error(
889 'import_validation_failed',
890 "Import validation failed for category: {$category}",
891 [
892 'status' => 400,
893 'validation_results' => $validation_results
894 ]
895 );
896 }
897 }
898 }
899 }
900
901 // Import settings for each category
902 foreach ($parsed_data as $category => $category_settings) {
903 if (!isset($this->setting_categories[$category])) {
904 $import_results[$category] = [
905 'success' => false,
906 'error' => 'Invalid category'
907 ];
908 continue;
909 }
910
911 try {
912 // Check if settings exist and handle overwrite
913 $existing_settings = $this->settings_manager->get_settings($category);
914
915 if (!empty($existing_settings) && !$overwrite_existing) {
916 $import_results[$category] = [
917 'success' => false,
918 'error' => 'Settings exist and overwrite is disabled'
919 ];
920 continue;
921 }
922
923 // Import settings
924 $import_success = $this->settings_manager->update_settings($category_settings, $category);
925
926 // Also update through specific SEO manager if available
927 if (isset($this->seo_managers[$category])) {
928 $manager_update = $this->seo_managers[$category]->save_settings('site', null, $category_settings);
929 $import_success = $import_success && $manager_update;
930 }
931
932 $import_results[$category] = [
933 'success' => $import_success,
934 'settings_count' => count($category_settings)
935 ];
936
937 } catch (\Exception $e) {
938 $import_results[$category] = [
939 'success' => false,
940 'error' => $e->getMessage()
941 ];
942 }
943 }
944
945 // Update settings metadata
946 $this->update_settings_metadata();
947
948 return new WP_REST_Response([
949 'success' => true,
950 'data' => [
951 'import_results' => $import_results,
952 'validation_results' => $validation_results,
953 'imported_categories' => count($import_results),
954 'successful_imports' => count(array_filter($import_results, function($result) {
955 return $result['success'];
956 }))
957 ],
958 'message' => 'Settings import completed'
959 ], 200);
960
961 } catch (\Exception $e) {
962 return new WP_Error(
963 'import_failed',
964 'Settings import failed: ' . $e->getMessage(),
965 ['status' => 500]
966 );
967 }
968 }
969
970 /**
971 * Create settings backup
972 *
973 * @since 1.0.0
974 *
975 * @param WP_REST_Request $request Request object
976 * @return WP_REST_Response|WP_Error Response object or error
977 */
978 public function create_settings_backup(WP_REST_Request $request) {
979 try {
980 $backup_name = $request->get_param('backup_name') ?? 'backup_' . gmdate('Y-m-d_H-i-s');
981 $categories = $request->get_param('categories') ?? array_keys($this->setting_categories);
982 $description = $request->get_param('description') ?? '';
983
984 // Create backup data
985 $backup_data = [];
986 foreach ($categories as $category) {
987 if (isset($this->setting_categories[$category])) {
988 $backup_data[$category] = $this->settings_manager->get_settings($category);
989 }
990 }
991
992 // Create backup metadata
993 $backup_metadata = [
994 'backup_name' => $backup_name,
995 'description' => $description,
996 'created_at' => current_time('mysql'),
997 'created_by' => get_current_user_id(),
998 'categories' => $categories,
999 'settings_version' => $this->get_settings_version(),
1000 'wordpress_version' => get_bloginfo('version')
1001 ];
1002
1003 // Save backup
1004 $backup_id = $this->save_settings_backup($backup_data, $backup_metadata);
1005
1006 if (!$backup_id) {
1007 return new WP_Error(
1008 'backup_failed',
1009 'Failed to create settings backup',
1010 ['status' => 500]
1011 );
1012 }
1013
1014 return new WP_REST_Response([
1015 'success' => true,
1016 'data' => [
1017 'backup_id' => $backup_id,
1018 'backup_name' => $backup_name,
1019 'backup_metadata' => $backup_metadata,
1020 'backed_up_categories' => count($backup_data)
1021 ],
1022 'message' => 'Settings backup created successfully'
1023 ], 200);
1024
1025 } catch (\Exception $e) {
1026 return new WP_Error(
1027 'backup_failed',
1028 'Settings backup failed: ' . $e->getMessage(),
1029 ['status' => 500]
1030 );
1031 }
1032 }
1033
1034 /**
1035 * Restore settings from backup
1036 *
1037 * @since 1.0.0
1038 *
1039 * @param WP_REST_Request $request Request object
1040 * @return WP_REST_Response|WP_Error Response object or error
1041 */
1042 public function restore_settings_backup(WP_REST_Request $request) {
1043 try {
1044 $backup_id = $request->get_param('backup_id');
1045 $categories = $request->get_param('categories') ?? null;
1046 $create_restore_point = $request->get_param('create_restore_point') ?? true;
1047
1048 // Validate backup ID
1049 if (empty($backup_id)) {
1050 return new WP_Error(
1051 'missing_backup_id',
1052 'Backup ID is required',
1053 ['status' => 400]
1054 );
1055 }
1056
1057 // Load backup data
1058 $backup_data = $this->load_settings_backup($backup_id);
1059
1060 if (!$backup_data) {
1061 return new WP_Error(
1062 'backup_not_found',
1063 'Backup not found or could not be loaded',
1064 ['status' => 404]
1065 );
1066 }
1067
1068 // Create restore point if requested
1069 $restore_point_id = null;
1070 if ($create_restore_point) {
1071 $restore_point_id = $this->create_restore_point();
1072 }
1073
1074 $restore_results = [];
1075
1076 // Determine categories to restore
1077 $categories_to_restore = $categories ?? array_keys($backup_data['settings']);
1078
1079 // Restore settings for each category
1080 foreach ($categories_to_restore as $category) {
1081 if (!isset($backup_data['settings'][$category])) {
1082 $restore_results[$category] = [
1083 'success' => false,
1084 'error' => 'Category not found in backup'
1085 ];
1086 continue;
1087 }
1088
1089 try {
1090 $category_settings = $backup_data['settings'][$category];
1091
1092 // Restore settings
1093 $restore_success = $this->settings_manager->update_settings($category_settings, $category);
1094
1095 // Also update through specific SEO manager if available
1096 if (isset($this->seo_managers[$category])) {
1097 $manager_update = $this->seo_managers[$category]->save_settings('site', null, $category_settings);
1098 $restore_success = $restore_success && $manager_update;
1099 }
1100
1101 $restore_results[$category] = [
1102 'success' => $restore_success,
1103 'settings_count' => count($category_settings)
1104 ];
1105
1106 } catch (\Exception $e) {
1107 $restore_results[$category] = [
1108 'success' => false,
1109 'error' => $e->getMessage()
1110 ];
1111 }
1112 }
1113
1114 // Update settings metadata
1115 $this->update_settings_metadata();
1116
1117 return new WP_REST_Response([
1118 'success' => true,
1119 'data' => [
1120 'backup_id' => $backup_id,
1121 'restore_point_id' => $restore_point_id,
1122 'restore_results' => $restore_results,
1123 'restored_categories' => count($restore_results),
1124 'backup_metadata' => $backup_data['metadata']
1125 ],
1126 'message' => 'Settings restored from backup successfully'
1127 ], 200);
1128
1129 } catch (\Exception $e) {
1130 return new WP_Error(
1131 'restore_failed',
1132 'Settings restore failed: ' . $e->getMessage(),
1133 ['status' => 500]
1134 );
1135 }
1136 }
1137
1138 /**
1139 * Detect settings conflicts
1140 *
1141 * @since 1.0.0
1142 *
1143 * @param WP_REST_Request $request Request object
1144 * @return WP_REST_Response Response object
1145 */
1146 public function detect_settings_conflicts(WP_REST_Request $request): WP_REST_Response {
1147 try {
1148 $categories = $request->get_param('categories') ?? array_keys($this->setting_categories);
1149
1150 // Get all settings for analysis
1151 $all_settings = [];
1152 foreach ($categories as $category) {
1153 if (isset($this->setting_categories[$category])) {
1154 $all_settings[$category] = $this->settings_manager->get_settings($category);
1155 }
1156 }
1157
1158 // Analyze conflicts
1159 $conflicts = $this->analyze_cross_category_conflicts($all_settings);
1160
1161 // Get conflict resolution suggestions
1162 $resolution_suggestions = $this->generate_conflict_resolution_suggestions($conflicts);
1163
1164 return new WP_REST_Response([
1165 'success' => true,
1166 'data' => [
1167 'conflicts' => $conflicts,
1168 'resolution_suggestions' => $resolution_suggestions,
1169 'analyzed_categories' => count($all_settings),
1170 'conflict_count' => count($conflicts),
1171 'analysis_timestamp' => current_time('mysql')
1172 ],
1173 'message' => 'Settings conflicts analysis completed'
1174 ], 200);
1175
1176 } catch (\Exception $e) {
1177 return new WP_REST_Response([
1178 'success' => false,
1179 'error' => 'Conflict detection failed: ' . $e->getMessage()
1180 ], 500);
1181 }
1182 }
1183
1184 /**
1185 * Resolve settings conflicts
1186 *
1187 * @since 1.0.0
1188 *
1189 * @param WP_REST_Request $request Request object
1190 * @return WP_REST_Response|WP_Error Response object or error
1191 */
1192 public function resolve_settings_conflicts(WP_REST_Request $request) {
1193 try {
1194 $resolutions = $request->get_param('resolutions');
1195
1196 // Validate resolutions
1197 if (empty($resolutions) || !is_array($resolutions)) {
1198 return new WP_Error(
1199 'invalid_resolutions',
1200 'Conflict resolutions must be provided as an array',
1201 ['status' => 400]
1202 );
1203 }
1204
1205 $resolution_results = [];
1206
1207 foreach ($resolutions as $resolution) {
1208 $conflict_id = $resolution['conflict_id'] ?? '';
1209 $resolution_action = $resolution['action'] ?? '';
1210 $resolution_data = $resolution['data'] ?? [];
1211
1212 try {
1213 $result = $this->apply_conflict_resolution($conflict_id, $resolution_action, $resolution_data);
1214 $resolution_results[$conflict_id] = $result;
1215 } catch (\Exception $e) {
1216 $resolution_results[$conflict_id] = [
1217 'success' => false,
1218 'error' => $e->getMessage()
1219 ];
1220 }
1221 }
1222
1223 return new WP_REST_Response([
1224 'success' => true,
1225 'data' => [
1226 'resolution_results' => $resolution_results,
1227 'resolved_conflicts' => count($resolution_results),
1228 'resolution_timestamp' => current_time('mysql')
1229 ],
1230 'message' => 'Settings conflicts resolution completed'
1231 ], 200);
1232
1233 } catch (\Exception $e) {
1234 return new WP_Error(
1235 'resolution_failed',
1236 'Conflict resolution failed: ' . $e->getMessage(),
1237 ['status' => 500]
1238 );
1239 }
1240 }
1241
1242 /**
1243 * Reset settings to defaults
1244 *
1245 * @since 1.0.0
1246 *
1247 * @param WP_REST_Request $request Request object
1248 * @return WP_REST_Response|WP_Error Response object or error
1249 */
1250 public function reset_settings(WP_REST_Request $request) {
1251 try {
1252 $categories = $request->get_param('categories') ?? array_keys($this->setting_categories);
1253 $create_backup = $request->get_param('create_backup') ?? true;
1254
1255 // Create backup before reset if requested
1256 $backup_id = null;
1257 if ($create_backup) {
1258 $backup_id = $this->create_pre_reset_backup($categories);
1259 }
1260
1261 $reset_results = [];
1262
1263 foreach ($categories as $category) {
1264 if (!isset($this->setting_categories[$category])) {
1265 continue;
1266 }
1267
1268 try {
1269 // Get default settings
1270 $default_settings = [];
1271 if (isset($this->seo_managers[$category])) {
1272 $default_settings = $this->seo_managers[$category]->get_default_settings($category);
1273 }
1274
1275 // Reset to defaults
1276 $reset_success = $this->settings_manager->update_settings($default_settings, $category);
1277
1278 // Also reset through specific SEO manager if available
1279 if (isset($this->seo_managers[$category])) {
1280 $manager_reset = $this->seo_managers[$category]->save_settings('site', null, $default_settings);
1281 $reset_success = $reset_success && $manager_reset;
1282 }
1283
1284 $reset_results[$category] = [
1285 'success' => $reset_success,
1286 'default_settings_count' => count($default_settings)
1287 ];
1288
1289 } catch (\Exception $e) {
1290 $reset_results[$category] = [
1291 'success' => false,
1292 'error' => $e->getMessage()
1293 ];
1294 }
1295 }
1296
1297 // Update settings metadata
1298 $this->update_settings_metadata();
1299
1300 return new WP_REST_Response([
1301 'success' => true,
1302 'data' => [
1303 'reset_results' => $reset_results,
1304 'backup_id' => $backup_id,
1305 'reset_categories' => count($reset_results),
1306 'reset_timestamp' => current_time('mysql')
1307 ],
1308 'message' => 'Settings reset to defaults completed'
1309 ], 200);
1310
1311 } catch (\Exception $e) {
1312 return new WP_Error(
1313 'reset_failed',
1314 'Settings reset failed: ' . $e->getMessage(),
1315 ['status' => 500]
1316 );
1317 }
1318 }
1319
1320 /**
1321 * Bulk operations for settings management
1322 *
1323 * @since 1.0.0
1324 *
1325 * @param WP_REST_Request $request Request object
1326 * @return WP_REST_Response|WP_Error Response object or error
1327 */
1328 public function bulk_operations(WP_REST_Request $request) {
1329 try {
1330 $operation = $request->get_param('operation');
1331 $items = $request->get_param('items') ?? [];
1332 $options = $request->get_param('options') ?? [];
1333
1334 // Validate input
1335 if (empty($operation) || empty($items)) {
1336 return new WP_Error(
1337 'missing_parameters',
1338 'Operation and items are required',
1339 ['status' => 400]
1340 );
1341 }
1342
1343 $results = [];
1344 $errors = [];
1345
1346 foreach ($items as $item) {
1347 try {
1348 switch ($operation) {
1349 case 'validate_settings':
1350 $result = $this->validate_category_settings_bulk($item);
1351 break;
1352 case 'update_settings':
1353 $result = $this->update_category_settings_bulk($item);
1354 break;
1355 case 'export_settings':
1356 $result = $this->export_category_settings_bulk($item);
1357 break;
1358 case 'reset_settings':
1359 $result = $this->reset_category_settings_bulk($item);
1360 break;
1361 default:
1362 throw new \Exception("Unsupported operation: {$operation}");
1363 }
1364
1365 $results[] = [
1366 'item' => $item,
1367 'success' => true,
1368 'data' => $result
1369 ];
1370
1371 } catch (\Exception $e) {
1372 $errors[] = [
1373 'item' => $item,
1374 'error' => $e->getMessage()
1375 ];
1376 }
1377 }
1378
1379 return new WP_REST_Response([
1380 'success' => empty($errors),
1381 'data' => [
1382 'results' => $results,
1383 'errors' => $errors,
1384 'total_processed' => count($items),
1385 'successful' => count($results),
1386 'failed' => count($errors)
1387 ],
1388 'message' => "Bulk {$operation} operation completed"
1389 ], 200);
1390
1391 } catch (\Exception $e) {
1392 return new WP_Error(
1393 'bulk_operation_failed',
1394 'Bulk operation failed: ' . $e->getMessage(),
1395 ['status' => 500]
1396 );
1397 }
1398 }
1399
1400 /**
1401 * Add performance indexes to database tables
1402 *
1403 * @since 1.0.0
1404 *
1405 * @param WP_REST_Request $request Request object
1406 * @return WP_REST_Response|WP_Error Response object
1407 */
1408 public function add_performance_indexes(WP_REST_Request $request): WP_REST_Response|WP_Error {
1409 try {
1410 // Import the Database_Schema class
1411 if (!class_exists('ThinkRank\\Database\\Database_Schema')) {
1412 require_once THINKRANK_PLUGIN_DIR . 'includes/database/class-database-schema.php';
1413 }
1414
1415 $schema = new \ThinkRank\Database\Database_Schema();
1416 $success = $schema->add_performance_indexes();
1417
1418 if ($success) {
1419 return new WP_REST_Response([
1420 'success' => true,
1421 'message' => 'Performance indexes added successfully',
1422 'data' => [
1423 'indexes_added' => true,
1424 'timestamp' => current_time('mysql')
1425 ]
1426 ], 200);
1427 } else {
1428 return new WP_REST_Response([
1429 'success' => false,
1430 'message' => 'Some performance indexes could not be added. Check error logs for details.',
1431 'data' => [
1432 'indexes_added' => false,
1433 'timestamp' => current_time('mysql')
1434 ]
1435 ], 200);
1436 }
1437
1438 } catch (\Exception $e) {
1439 return new WP_Error(
1440 'performance_indexes_failed',
1441 'Failed to add performance indexes: ' . $e->getMessage(),
1442 ['status' => 500]
1443 );
1444 }
1445 }
1446
1447 /**
1448 * Permission callbacks
1449 */
1450
1451 /**
1452 * Check permissions for reading settings data
1453 *
1454 * @since 1.0.0
1455 *
1456 * @return bool Permission status
1457 */
1458 public function check_read_permissions(): bool {
1459 return current_user_can('read');
1460 }
1461
1462 /**
1463 * Check permissions for managing settings
1464 *
1465 * @since 1.0.0
1466 *
1467 * @return bool Permission status
1468 */
1469 public function check_manage_permissions(): bool {
1470 return current_user_can('manage_options');
1471 }
1472
1473 /**
1474 * Helper methods
1475 */
1476
1477 /**
1478 * Get last settings update timestamp
1479 *
1480 * @since 1.0.0
1481 *
1482 * @return string|null Last update timestamp
1483 */
1484 private function get_last_settings_update(): ?string {
1485 $result = get_option('thinkrank_settings_last_updated');
1486 return $result !== false ? $result : null;
1487 }
1488
1489 /**
1490 * Get settings version
1491 *
1492 * @since 1.0.0
1493 *
1494 * @return string Settings version
1495 */
1496 private function get_settings_version(): string {
1497 return get_option('thinkrank_settings_version', '1.0.0');
1498 }
1499
1500 /**
1501 * Get schema version
1502 *
1503 * @since 1.0.0
1504 *
1505 * @return string Schema version
1506 */
1507 private function get_schema_version(): string {
1508 return get_option('thinkrank_schema_version', '1.0.0');
1509 }
1510
1511 /**
1512 * Get category last update timestamp
1513 *
1514 * @since 1.0.0
1515 *
1516 * @param string $category Category name
1517 * @return string|null Last update timestamp
1518 */
1519 private function get_category_last_update(string $category): ?string {
1520 $result = get_option("thinkrank_settings_{$category}_last_updated");
1521 return $result !== false ? $result : null;
1522 }
1523
1524 /**
1525 * Update settings metadata
1526 *
1527 * @since 1.0.0
1528 */
1529 private function update_settings_metadata(): void {
1530 update_option('thinkrank_settings_last_updated', current_time('mysql'));
1531
1532 // Increment version
1533 $current_version = $this->get_settings_version();
1534 $version_parts = explode('.', $current_version);
1535 $version_parts[2] = (int)$version_parts[2] + 1;
1536 $new_version = implode('.', $version_parts);
1537
1538 update_option('thinkrank_settings_version', $new_version);
1539 }
1540
1541 /**
1542 * Update category metadata
1543 *
1544 * @since 1.0.0
1545 *
1546 * @param string $category Category name
1547 */
1548 private function update_category_metadata(string $category): void {
1549 update_option("thinkrank_settings_{$category}_last_updated", current_time('mysql'));
1550 }
1551
1552 /**
1553 * Analyze cross-category conflicts
1554 *
1555 * @since 1.0.0
1556 *
1557 * @param array $settings Settings data
1558 * @return array Conflict analysis
1559 */
1560 private function analyze_cross_category_conflicts(array $settings): array {
1561 $conflicts = [];
1562
1563 // Example conflict detection logic
1564 // This would be enhanced with actual conflict detection algorithms
1565
1566 // Check for conflicting meta title settings
1567 $title_conflicts = $this->detect_title_conflicts($settings);
1568 if (!empty($title_conflicts)) {
1569 $conflicts = array_merge($conflicts, $title_conflicts);
1570 }
1571
1572 // Check for conflicting schema settings
1573 $schema_conflicts = $this->detect_schema_conflicts($settings);
1574 if (!empty($schema_conflicts)) {
1575 $conflicts = array_merge($conflicts, $schema_conflicts);
1576 }
1577
1578 return $conflicts;
1579 }
1580
1581 /**
1582 * Format export data
1583 *
1584 * @since 1.0.0
1585 *
1586 * @param array $export_data Export data
1587 * @param array $metadata Metadata
1588 * @param string $format Export format
1589 * @return string Formatted export data
1590 */
1591 private function format_export_data(array $export_data, array $metadata, string $format): string {
1592 $full_export = [
1593 'metadata' => $metadata,
1594 'settings' => $export_data
1595 ];
1596
1597 switch ($format) {
1598 case 'json':
1599 return wp_json_encode($full_export, JSON_PRETTY_PRINT);
1600 case 'yaml':
1601 // Would implement YAML formatting
1602 return wp_json_encode($full_export, JSON_PRETTY_PRINT);
1603 case 'xml':
1604 // Would implement XML formatting
1605 return wp_json_encode($full_export, JSON_PRETTY_PRINT);
1606 default:
1607 return wp_json_encode($full_export, JSON_PRETTY_PRINT);
1608 }
1609 }
1610
1611 /**
1612 * Parse import data
1613 *
1614 * @since 1.0.0
1615 *
1616 * @param string $import_data Import data
1617 * @param string $format Import format
1618 * @return array|false Parsed data or false on failure
1619 */
1620 private function parse_import_data(string $import_data, string $format) {
1621 switch ($format) {
1622 case 'json':
1623 $decoded = json_decode($import_data, true);
1624 return $decoded['settings'] ?? $decoded;
1625 case 'yaml':
1626 // Would implement YAML parsing
1627 $decoded = json_decode($import_data, true);
1628 return $decoded['settings'] ?? $decoded;
1629 case 'xml':
1630 // Would implement XML parsing
1631 $decoded = json_decode($import_data, true);
1632 return $decoded['settings'] ?? $decoded;
1633 default:
1634 return false;
1635 }
1636 }
1637
1638 /**
1639 * Save settings backup
1640 *
1641 * @since 1.0.0
1642 *
1643 * @param array $backup_data Backup data
1644 * @param array $backup_metadata Backup metadata
1645 * @return string|false Backup ID or false on failure
1646 */
1647 private function save_settings_backup(array $backup_data, array $backup_metadata) {
1648 $backup_id = uniqid('backup_', true);
1649
1650 $backup_record = [
1651 'backup_id' => $backup_id,
1652 'metadata' => $backup_metadata,
1653 'settings' => $backup_data
1654 ];
1655
1656 $saved = update_option("thinkrank_backup_{$backup_id}", $backup_record);
1657
1658 if ($saved) {
1659 // Add to backup index
1660 $backup_index = get_option('thinkrank_backup_index', []);
1661 $backup_index[$backup_id] = $backup_metadata;
1662 update_option('thinkrank_backup_index', $backup_index);
1663
1664 return $backup_id;
1665 }
1666
1667 return false;
1668 }
1669
1670 /**
1671 * Load settings backup
1672 *
1673 * @since 1.0.0
1674 *
1675 * @param string $backup_id Backup ID
1676 * @return array|false Backup data or false on failure
1677 */
1678 private function load_settings_backup(string $backup_id) {
1679 return get_option("thinkrank_backup_{$backup_id}", false);
1680 }
1681
1682 /**
1683 * Argument validation methods
1684 */
1685
1686 /**
1687 * Get arguments for global settings endpoints
1688 *
1689 * @since 1.0.0
1690 *
1691 * @return array Arguments array
1692 */
1693 private function get_global_settings_args(): array {
1694 return [
1695 'settings' => [
1696 'required' => true,
1697 'type' => 'object',
1698 'description' => 'Global settings to update across categories'
1699 ],
1700 'validate' => [
1701 'required' => false,
1702 'type' => 'boolean',
1703 'default' => true,
1704 'description' => 'Whether to validate settings before updating'
1705 ]
1706 ];
1707 }
1708
1709 /**
1710 * Get arguments for category settings endpoints
1711 *
1712 * @since 1.0.0
1713 *
1714 * @return array Arguments array
1715 */
1716 private function get_category_settings_args(): array {
1717 return [
1718 'settings' => [
1719 'required' => true,
1720 'type' => 'object',
1721 'description' => 'Category settings to update'
1722 ],
1723 'validate' => [
1724 'required' => false,
1725 'type' => 'boolean',
1726 'default' => true,
1727 'description' => 'Whether to validate settings before updating'
1728 ]
1729 ];
1730 }
1731
1732 /**
1733 * Get arguments for validation endpoint
1734 *
1735 * @since 1.0.0
1736 *
1737 * @return array Arguments array
1738 */
1739 private function get_validation_args(): array {
1740 return [
1741 'settings' => [
1742 'required' => true,
1743 'type' => 'object',
1744 'description' => 'Settings to validate'
1745 ],
1746 'categories' => [
1747 'required' => false,
1748 'type' => 'array',
1749 'items' => [
1750 'type' => 'string',
1751 'enum' => array_keys($this->setting_categories)
1752 ],
1753 'description' => 'Categories to validate'
1754 ]
1755 ];
1756 }
1757
1758 /**
1759 * Get arguments for export endpoint
1760 *
1761 * @since 1.0.0
1762 *
1763 * @return array Arguments array
1764 */
1765 private function get_export_args(): array {
1766 return [
1767 'categories' => [
1768 'required' => false,
1769 'type' => 'array',
1770 'items' => [
1771 'type' => 'string',
1772 'enum' => array_keys($this->setting_categories)
1773 ],
1774 'description' => 'Categories to export'
1775 ],
1776 'format' => [
1777 'required' => false,
1778 'type' => 'string',
1779 'enum' => ['json', 'yaml', 'xml'],
1780 'default' => 'json',
1781 'description' => 'Export format'
1782 ],
1783 'include_metadata' => [
1784 'required' => false,
1785 'type' => 'boolean',
1786 'default' => true,
1787 'description' => 'Whether to include metadata in export'
1788 ]
1789 ];
1790 }
1791
1792 /**
1793 * Get arguments for import endpoint
1794 *
1795 * @since 1.0.0
1796 *
1797 * @return array Arguments array
1798 */
1799 private function get_import_args(): array {
1800 return [
1801 'import_data' => [
1802 'required' => true,
1803 'type' => 'string',
1804 'description' => 'Settings data to import'
1805 ],
1806 'format' => [
1807 'required' => false,
1808 'type' => 'string',
1809 'enum' => ['json', 'yaml', 'xml'],
1810 'default' => 'json',
1811 'description' => 'Import format'
1812 ],
1813 'validate' => [
1814 'required' => false,
1815 'type' => 'boolean',
1816 'default' => true,
1817 'description' => 'Whether to validate before importing'
1818 ],
1819 'overwrite_existing' => [
1820 'required' => false,
1821 'type' => 'boolean',
1822 'default' => false,
1823 'description' => 'Whether to overwrite existing settings'
1824 ]
1825 ];
1826 }
1827
1828 /**
1829 * Get arguments for backup endpoint
1830 *
1831 * @since 1.0.0
1832 *
1833 * @return array Arguments array
1834 */
1835 private function get_backup_args(): array {
1836 return [
1837 'backup_name' => [
1838 'required' => false,
1839 'type' => 'string',
1840 'description' => 'Name for the backup'
1841 ],
1842 'categories' => [
1843 'required' => false,
1844 'type' => 'array',
1845 'items' => [
1846 'type' => 'string',
1847 'enum' => array_keys($this->setting_categories)
1848 ],
1849 'description' => 'Categories to backup'
1850 ],
1851 'description' => [
1852 'required' => false,
1853 'type' => 'string',
1854 'description' => 'Backup description'
1855 ]
1856 ];
1857 }
1858
1859 /**
1860 * Get arguments for restore endpoint
1861 *
1862 * @since 1.0.0
1863 *
1864 * @return array Arguments array
1865 */
1866 private function get_restore_args(): array {
1867 return [
1868 'backup_id' => [
1869 'required' => true,
1870 'type' => 'string',
1871 'description' => 'Backup ID to restore from'
1872 ],
1873 'categories' => [
1874 'required' => false,
1875 'type' => 'array',
1876 'items' => [
1877 'type' => 'string',
1878 'enum' => array_keys($this->setting_categories)
1879 ],
1880 'description' => 'Categories to restore'
1881 ],
1882 'create_restore_point' => [
1883 'required' => false,
1884 'type' => 'boolean',
1885 'default' => true,
1886 'description' => 'Whether to create restore point before restoring'
1887 ]
1888 ];
1889 }
1890
1891 /**
1892 * Get arguments for conflict resolution endpoint
1893 *
1894 * @since 1.0.0
1895 *
1896 * @return array Arguments array
1897 */
1898 private function get_conflict_resolution_args(): array {
1899 return [
1900 'resolutions' => [
1901 'required' => true,
1902 'type' => 'array',
1903 'items' => [
1904 'type' => 'object'
1905 ],
1906 'description' => 'Conflict resolutions to apply'
1907 ]
1908 ];
1909 }
1910
1911 /**
1912 * Get arguments for reset endpoint
1913 *
1914 * @since 1.0.0
1915 *
1916 * @return array Arguments array
1917 */
1918 private function get_reset_args(): array {
1919 return [
1920 'categories' => [
1921 'required' => false,
1922 'type' => 'array',
1923 'items' => [
1924 'type' => 'string',
1925 'enum' => array_keys($this->setting_categories)
1926 ],
1927 'description' => 'Categories to reset'
1928 ],
1929 'create_backup' => [
1930 'required' => false,
1931 'type' => 'boolean',
1932 'default' => true,
1933 'description' => 'Whether to create backup before reset'
1934 ]
1935 ];
1936 }
1937
1938 /**
1939 * Get arguments for bulk operations endpoint
1940 *
1941 * @since 1.0.0
1942 *
1943 * @return array Arguments array
1944 */
1945 private function get_bulk_operations_args(): array {
1946 return [
1947 'operation' => [
1948 'required' => true,
1949 'type' => 'string',
1950 'enum' => ['validate_settings', 'update_settings', 'export_settings', 'reset_settings'],
1951 'description' => 'Bulk operation type'
1952 ],
1953 'items' => [
1954 'required' => true,
1955 'type' => 'array',
1956 'items' => [
1957 'type' => 'object'
1958 ],
1959 'description' => 'Items to process in bulk'
1960 ],
1961 'options' => [
1962 'required' => false,
1963 'type' => 'object',
1964 'description' => 'Bulk operation options'
1965 ]
1966 ];
1967 }
1968
1969 /**
1970 * Placeholder implementations for methods referenced but not yet implemented
1971 * These would be enhanced with actual conflict detection and resolution algorithms
1972 */
1973
1974 private function detect_title_conflicts(array $settings): array {
1975 return []; // Would implement actual title conflict detection
1976 }
1977
1978 private function detect_schema_conflicts(array $settings): array {
1979 return []; // Would implement actual schema conflict detection
1980 }
1981
1982 private function generate_conflict_resolution_suggestions(array $conflicts): array {
1983 return []; // Would implement actual resolution suggestions
1984 }
1985
1986 private function apply_conflict_resolution(string $conflict_id, string $action, array $data): array {
1987 return ['success' => true, 'action' => $action]; // Would implement actual resolution
1988 }
1989
1990 private function create_restore_point(): string {
1991 return uniqid('restore_point_', true); // Would implement actual restore point creation
1992 }
1993
1994 private function create_pre_reset_backup(array $categories): string {
1995 return uniqid('pre_reset_backup_', true); // Would implement actual pre-reset backup
1996 }
1997
1998 private function validate_category_settings_bulk(array $item): array {
1999 return ['validation' => 'passed']; // Would implement bulk validation
2000 }
2001
2002 private function update_category_settings_bulk(array $item): array {
2003 return ['update' => 'successful']; // Would implement bulk update
2004 }
2005
2006 private function export_category_settings_bulk(array $item): array {
2007 return ['export' => 'completed']; // Would implement bulk export
2008 }
2009
2010 private function reset_category_settings_bulk(array $item): array {
2011 return ['reset' => 'completed']; // Would implement bulk reset
2012 }
2013 }
2014