PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.3
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.3
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 2.5.23 2.5.24 All 137 releases
metasync / metasync.php

metasync.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.3, at metasync.php

763 lines 27.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is read by WordPress to generate the plugin information in the plugin
5 * admin area. This file also includes all of the dependencies used by the plugin,
6 * registers the activation and deactivation functions, and defines a function
7 * that starts the plugin.
8 *
9 * @package Search Atlas SEO
10 * @copyright Copyright (C) 2021-2025, Search Atlas Group - support@searchatlas.com
11 * @link https://searchatlas.com/
12 * @since 1.0.0
13 *
14 * @wordpress-plugin
15 * Plugin Name: Search Atlas: The Premier AI SEO Plugin for Instant Optimization
16 * Plugin URI: https://searchatlas.com/
17 * Description: Search Atlas SEO is an intuitive WordPress Plugin that transforms the most complicated, most labor-intensive SEO tasks into streamlined, straightforward processes. With a few clicks, the meta-bulk update feature automates the re-optimization of meta tags using AI to increase clicks. Stay up-to-date with the freshest Google Search data for your entire site or targeted URLs within the Meta Sync plug-in page.
18 * Version: 2.6.3
19 * Author: Search Atlas
20 * Author URI: https://searchatlas.com
21 * License: GPL v3
22 * License URI: https://www.gnu.org/licenses/gpl-3.0.txt
23 * Text Domain: metasync
24 */
25
26 // If this file is called directly, abort.
27 if (!defined('WPINC')) {
28 die;
29 }
30
31 // Composer classmap autoloader — loads all plugin classes on demand.
32 require_once __DIR__ . '/vendor/autoload.php';
33
34 /**
35 * Currently plugin version.
36 * Start at version 1.0.0 and use SemVer - https://semver.org
37 * Rename this for your plugin and update it as you release new versions.
38 */
39 $metasync_version = '2.6.3';
40 define('METASYNC_VERSION', preg_match('/^\d+\.\d+/', $metasync_version) ? $metasync_version : '9.9.9');
41 /**
42 * Define the current required php version
43 * This will be used to validate whether the user can install the plugin or not
44 */
45 define('METASYNC_MIN_PHP', '8.2');
46
47 /**
48 * Define the current required php version
49 * This will be used to validate whether the user can install the plugin or not
50 */
51 define('METASYNC_MIN_WP', '5.2');
52
53 /**
54 * Telemetry Configuration Constants
55 * These replace the old database options for better security and consistency
56 */
57 define('METASYNC_SENTRY_PROJECT_ID', '4509950439849985');
58 define('METASYNC_SENTRY_ENVIRONMENT', 'production');
59 define('METASYNC_SENTRY_RELEASE', METASYNC_VERSION);
60 define('METASYNC_SENTRY_SAMPLE_RATE', 1.0);
61
62 /**
63 * GA4 Analytics Configuration
64 * Measurement ID for Google Analytics 4 event tracking (format: G-XXXXXXXX)
65 *
66 * IMPORTANT: This constant is REQUIRED for GA4 tracking to function.
67 * If not defined or empty, all GA4 analytics tracking will be disabled.
68 *
69 * GA4_API_SECRET is required for server-side Measurement Protocol events
70 * (Content Genius, OTTO optimization). Generate it in GA4:
71 * Admin → Data Streams → (stream) → Measurement Protocol → Create
72 */
73 define('METASYNC_GA4_MEASUREMENT_ID', 'G-SBLWW1EMTJ');
74 define('METASYNC_GA4_API_SECRET', 'nMGs22mxQ3qVUy-aInqfZA');
75
76 /**
77 * Define whether to show the plugin status in WordPress admin top navigation bar
78 * Set to false to hide the status indicator
79 */
80 define('METASYNC_SHOW_ADMIN_BAR_STATUS', true);
81
82 /**
83 * Sanitize POST/GET/REQUEST data recursively
84 *
85 * @param array $data Data to sanitize
86 * @return array Sanitized data
87 */
88 if (!function_exists('metasync_sanitize_input_array')) {
89 function metasync_sanitize_input_array($data) {
90 if (!is_array($data)) {
91 return sanitize_text_field($data);
92 }
93
94 $sanitized = [];
95 foreach ($data as $key => $value) {
96 if (is_array($value)) {
97 $sanitized[$key] = metasync_sanitize_input_array($value);
98 } else {
99 // Check if it's a URL
100 if (filter_var($value, FILTER_VALIDATE_URL)) {
101 $sanitized[$key] = esc_url_raw($value);
102 } else {
103 $sanitized[$key] = sanitize_text_field($value);
104 }
105 }
106 }
107 return $sanitized;
108 }
109 }
110
111 // Phase 2 — these files have file-level side effects (add_action outside class body)
112 // and must remain as explicit require_once until refactored.
113 require_once plugin_dir_path( __FILE__ ) . 'includes/class-metasync-api-backoff-rest.php';
114 require_once plugin_dir_path( __FILE__ ) . 'includes/class-metasync-error-logger.php';
115
116 /**
117 * Include the Otto Pixel Php Code
118 */
119 require_once plugin_dir_path( __FILE__ ) . '/otto/otto_pixel.php';
120
121
122 /**
123 * Initialize OTTO Persistence Settings (registers REST API endpoints)
124 */
125 add_action('init', function() {
126 Metasync_Otto_Persistence_Settings::init();
127 }, 5);
128
129 /**
130 * The code that runs during plugin activation.
131 * This action is documented in includes/class-metasync-activator.php
132 */
133 function activate_metasync()
134 {
135 # Include WordPress plugin functions to access plugin metadata
136 if (!function_exists('get_plugin_data')) {
137 require_once ABSPATH . 'wp-admin/includes/plugin.php';
138 }
139
140 # Get plugin data
141 $plugin_data = get_plugin_data(__FILE__);
142
143 # Get the plugin name
144 $plugin_name = $plugin_data['Name'];
145
146 # Get the current WordPress
147 global $wp_version;
148
149 # Get the current php version
150 $php_version = PHP_VERSION;
151
152 # Check for incompatible WordPress version
153 if (version_compare($wp_version, METASYNC_MIN_WP, '<')) {
154
155 #show error
156 wp_die(
157
158 #craft the message
159 sprintf(
160 '%s requires WordPress version %s or later. You are currently using version %s. Please update WordPress to activate this plugin.',
161 esc_html($plugin_name),
162 METASYNC_MIN_WP,
163 esc_html($wp_version)
164 ),
165
166 #the plugin title as page title
167 esc_html($plugin_name).'Plugin Activation Error',
168
169 #include the back link
170 ['back_link' => true]
171 );
172 }
173
174 # Check for incompatible PHP version
175 if (version_compare($php_version, METASYNC_MIN_PHP, '<')) {
176
177 #show error message
178 wp_die(
179
180 #craft the message
181 sprintf(
182 '%s requires PHP version %s or later. You are currently using version %s. Please update PHP to activate this plugin.',
183 esc_html($plugin_name),
184 METASYNC_MIN_PHP,
185 esc_html($php_version)
186 ),
187
188 #the plugin title as page title
189 esc_html($plugin_name).'Plugin Activation Error',
190
191 #include the back link
192 ['back_link' => true]
193 );
194 }
195
196 Metasync_Activator::activate();
197 // class name is changed at class-db-migrations.php
198 MetaSync_DBMigration::activation();
199
200 // Set initial version
201 update_option('metasync_version', METASYNC_VERSION);
202
203 // Clear all cache plugins to ensure fresh start
204 Metasync_Cache_Purge::purge_all('plugin_activation');
205 }
206
207 // Log-sync removed - error monitoring now handled by Sentry
208 // require_once plugin_dir_path(__FILE__) . 'log-sync/log-sync.php';
209
210 /**
211 * Initialize telemetry system for error monitoring and Sentry integration
212 */
213 require_once plugin_dir_path(__FILE__) . 'telemetry/telemetry-init.php';
214
215 /**
216 * The code that runs during plugin deactivation.
217 * This action is documented in includes/class-metasync-deactivator.php
218 */
219 function deactivate_metasync()
220 {
221 Metasync_Deactivator::deactivate();
222 // class name is changed at class-db-migrations.php
223 MetaSync_DBMigration::deactivation();
224
225 // Unschedule the sync log cleanup cron to avoid orphaned events.
226 $timestamp = wp_next_scheduled('metasync_sync_log_daily_cleanup');
227 if ($timestamp) {
228 wp_unschedule_event($timestamp, 'metasync_sync_log_daily_cleanup');
229 }
230
231 // Clear news/video sitemap caches
232 delete_transient('metasync_vsm_' . md5('news-sitemap.xml'));
233 delete_transient('metasync_vsm_' . md5('video-sitemap.xml'));
234 delete_option('metasync_sitemap_virtual_index');
235 }
236
237 register_activation_hook(__FILE__, 'activate_metasync');
238 register_deactivation_hook(__FILE__, 'deactivate_metasync');
239
240 /**
241 * Check for plugin updates and run migrations if needed
242 */
243 function check_metasync_updates()
244 {
245 static $checked = false;
246 if ($checked) return;
247 $checked = true;
248
249 $current_version = get_option('metasync_version', '0.0.0');
250 $plugin_version = METASYNC_VERSION;
251
252 // If versions don't match, run migration
253 if (version_compare($current_version, $plugin_version, '<')) {
254 // Import whitelabel settings only if the JSON file is new or changed
255 // (prevents overwriting admin UI changes on every version check)
256 Metasync_Activator::check_whitelabel_settings_update();
257
258 // Run version-specific migrations first
259 MetaSync_DBMigration::run_version_migrations($current_version, $plugin_version);
260
261 // Migration for v2.7.0+: Remove AI Agent, switch to plugin auth token, make MCP always-on
262 if (version_compare($current_version, '2.7.0', '<')) {
263 // Remove old MCP API key option
264 delete_option('metasync_mcp_api_key');
265
266 // Remove MCP enabled/disabled toggle option (MCP is now always enabled)
267 delete_option('metasync_mcp_enabled');
268
269 // Remove AI Agent settings (AI Agent has been removed)
270 delete_option('metasync_ai_agent_mcp_config');
271 delete_option('metasync_ai_agent_ai_config');
272 delete_option('metasync_ai_agent_enabled');
273
274 // Ensure plugin auth token exists
275 $options = get_option('metasync_options', []);
276 if (empty($options['general']['apikey'])) {
277 $options['general']['apikey'] = wp_generate_password(32, false, false);
278 update_option('metasync_options', $options);
279 }
280 }
281
282 // Run full migration to ensure all tables are up to date
283 MetaSync_DBMigration::run_migrations();
284
285 // Update stored version
286 update_option('metasync_version', $plugin_version);
287
288 // Clear all cache plugins after update
289 Metasync_Cache_Purge::purge_all('plugin_update');
290
291 // Log the update
292 //error_log("MetaSync: Plugin updated from {$current_version} to {$plugin_version}. Database migration completed.");
293 }
294 }
295
296 // Hook into WordPress init to check for updates
297 add_action('init', 'check_metasync_updates', 1);
298
299 /**
300 * Handle whitelabel settings import after plugin is updated via WordPress admin
301 * This hook fires when plugins are installed/updated through the WordPress updater
302 *
303 * @param WP_Upgrader $upgrader WP_Upgrader instance
304 * @param array $hook_extra Extra arguments passed to hooked filters
305 */
306 function metasync_handle_plugin_upgrade($upgrader, $hook_extra)
307 {
308 // Only process plugin updates/installs
309 if (!isset($hook_extra['type']) || $hook_extra['type'] !== 'plugin') {
310 return;
311 }
312
313 // Only process install and update actions
314 if (!isset($hook_extra['action']) || !in_array($hook_extra['action'], ['install', 'update'], true)) {
315 return;
316 }
317
318 $this_plugin = plugin_basename(__FILE__);
319 $this_plugin_slug = dirname($this_plugin); // Get 'metasync' from 'metasync/metasync.php'
320 $should_import = false;
321
322 // Handle bulk updates
323 if (isset($hook_extra['bulk']) && $hook_extra['bulk'] === true && isset($hook_extra['plugins'])) {
324 foreach ($hook_extra['plugins'] as $plugin) {
325 // Match by exact path OR by plugin slug/directory
326 if ($plugin === $this_plugin || dirname($plugin) === $this_plugin_slug) {
327 $should_import = true;
328 break;
329 }
330 }
331 }
332
333 // Handle single plugin update/install
334 if (isset($hook_extra['plugin'])) {
335 $plugin = $hook_extra['plugin'];
336 // Match by exact path OR by plugin slug/directory
337 if ($plugin === $this_plugin || dirname($plugin) === $this_plugin_slug) {
338 $should_import = true;
339 }
340 }
341
342 // SPECIAL CASE: When uploading plugin via "Add New > Upload Plugin",
343 // WordPress doesn't set the 'plugin' parameter during 'install' action.
344 // Check if we can get the plugin info from the upgrader result or whitelabel file exists.
345 if (!$should_import && $hook_extra['action'] === 'install') {
346 // Check upgrader result for destination
347 if (isset($upgrader->result) && isset($upgrader->result['destination'])) {
348 $destination = $upgrader->result['destination'];
349 // Check if destination contains our plugin slug
350 if (strpos($destination, $this_plugin_slug) !== false) {
351 $should_import = true;
352 }
353 }
354
355 // Fallback: Check if whitelabel file exists in our plugin directory
356 // This means our plugin was just installed/updated with whitelabel settings
357 if (!$should_import) {
358 $whitelabel_file = Metasync_Activator::get_whitelabel_settings_file();
359 if ($whitelabel_file !== false) {
360 $should_import = true;
361 }
362 }
363 }
364
365 if ($should_import) {
366 Metasync_Activator::check_whitelabel_settings_update();
367 }
368 }
369
370 // Hook into WordPress upgrader to detect plugin updates
371 add_action('upgrader_process_complete', 'metasync_handle_plugin_upgrade', 10, 2);
372
373 /**
374 * Fallback: Check for whitelabel file changes on admin pages
375 * This handles edge cases where upgrader_process_complete doesn't fire
376 * (e.g., FTP uploads, manual file replacements)
377 * Only checks once per admin session to minimize performance impact
378 */
379 // function metasync_check_whitelabel_on_admin()
380 // {
381 // // Only check once per admin session to avoid overhead
382 // static $checked = false;
383 // if ($checked) {
384 // return;
385 // }
386 // $checked = true;
387
388 // require_once plugin_dir_path(__FILE__) . 'includes/class-metasync-activator.php';
389 // Metasync_Activator::check_whitelabel_settings_update();
390 // }
391
392 // Check for whitelabel changes on admin pages (fallback for edge cases)
393 // add_action('admin_init', 'metasync_check_whitelabel_on_admin', 1);
394
395 // Include Media Optimization Module
396 require_once plugin_dir_path(__FILE__) . 'media-optimization/media-optimization-loader.php';
397
398 // Include Code Minification & Delivery Module
399 require_once plugin_dir_path(__FILE__) . 'code-minification/code-minification-loader.php';
400
401 // Include Zapier Connector
402 require_once plugin_dir_path(__FILE__) . 'zapier/zapier-loader.php';
403
404 function run_metasync()
405 {
406 $plugin = new Metasync();
407 $plugin->run();
408 }
409 run_metasync();
410
411 /**
412 * Initialize WordPress MCP Server
413 *
414 * Creates and configures the MCP server instance,
415 * registers all available MCP tools for WordPress operations.
416 * Hooked to 'init' for proper WordPress lifecycle integration.
417 */
418 function metasync_init_mcp_server() {
419 // Initialize MCP server
420 global $metasync_mcp_server;
421
422 try {
423 $metasync_mcp_server = new Metasync_MCP_Server();
424
425 // Attach MCP sync logger so all write tool calls are recorded in Sync History.
426 new Metasync_MCP_Sync_Logger();
427
428 // SEO Inventory: shared builder + standalone REST endpoint (WP-135)
429 new Metasync_REST_SEO_Inventory();
430 } catch (Exception $e) {
431 error_log('MCP Server Initialization Error: ' . $e->getMessage());
432 return;
433 }
434
435 // Helper: register a single tool, logging failures without aborting subsequent registrations
436 $safe_register = function($tool) use ($metasync_mcp_server) {
437 try {
438 $metasync_mcp_server->register_tool($tool);
439 } catch (Exception $e) {
440 error_log('MetaSync MCP: Failed to register tool ' . get_class($tool) . ': ' . $e->getMessage());
441 }
442 };
443
444 // Register MCP Tools (Total: 92 existing + 8 new = 100 tools total!)
445 // NEW in v2.8.0: +4 Taxonomy Meta tools, +4 Bulk Alt Text tools
446
447 // Post Meta Operations (4 tools)
448 $safe_register(new MCP_Tool_Update_Post_Meta());
449 $safe_register(new MCP_Tool_Get_Post_Meta());
450 $safe_register(new MCP_Tool_Get_SEO_Meta());
451 $safe_register(new MCP_Tool_Get_Hreflang_Links());
452
453 // Post Operations (4 tools)
454 $safe_register(new MCP_Tool_Get_Post());
455 $safe_register(new MCP_Tool_Get_Post_By_URL());
456 $safe_register(new MCP_Tool_List_Posts());
457 $safe_register(new MCP_Tool_Update_Post());
458 $safe_register(new MCP_Tool_Get_Post_Types());
459
460 // SEO Analysis (3 tools)
461 $safe_register(new MCP_Tool_Analyze_SEO());
462 $safe_register(new MCP_Tool_Check_Indexability());
463 $safe_register(new MCP_Tool_SEO_Health_Report());
464
465 // Search Operations (3 tools)
466 $safe_register(new MCP_Tool_Search_Posts());
467 $safe_register(new MCP_Tool_Search_By_Keyword());
468 $safe_register(new MCP_Tool_Find_Missing_Meta());
469
470 // Redirect Management (4 tools)
471 $safe_register(new MCP_Tool_Create_Redirect());
472 $safe_register(new MCP_Tool_List_Redirects());
473 $safe_register(new MCP_Tool_Delete_Redirect());
474 $safe_register(new MCP_Tool_Update_Redirect());
475
476 // 404 Error Monitoring (5 tools)
477 $safe_register(new MCP_Tool_List_404_Errors());
478 $safe_register(new MCP_Tool_Get_404_Stats());
479 $safe_register(new MCP_Tool_Delete_404_Error());
480 $safe_register(new MCP_Tool_Clear_404_Errors());
481 $safe_register(new MCP_Tool_Create_Redirect_From_404());
482
483 // Robots.txt & Sitemap Management (11 tools - 7 existing + 4 new)
484 $safe_register(new MCP_Tool_Get_Robots_Txt());
485 $safe_register(new MCP_Tool_Update_Robots_Txt());
486 $safe_register(new MCP_Tool_Get_Sitemap_Status());
487 $safe_register(new MCP_Tool_Regenerate_Sitemap());
488 $safe_register(new MCP_Tool_Exclude_From_Sitemap());
489 $safe_register(new MCP_Tool_Add_Robots_Rule());
490 $safe_register(new MCP_Tool_Remove_Robots_Rule());
491 $safe_register(new MCP_Tool_Parse_Robots_Txt());
492 $safe_register(new MCP_Tool_Validate_Robots_Txt());
493 $safe_register(new MCP_Tool_Get_News_Sitemap());
494 $safe_register(new MCP_Tool_Get_Video_Sitemap());
495
496 // Plugin Settings Management (4 tools)
497 $safe_register(new MCP_Tool_Get_Plugin_Settings());
498 $safe_register(new MCP_Tool_Update_Plugin_Settings());
499 $safe_register(new MCP_Tool_List_Plugin_Settings_Schema());
500 $safe_register(new MCP_Tool_Get_MCP_Settings());
501
502 // Schema Markup Management (5 tools)
503 $safe_register(new MCP_Tool_Get_Schema_Markup());
504 $safe_register(new MCP_Tool_Update_Schema_Markup());
505 $safe_register(new MCP_Tool_Add_Schema_Type());
506 $safe_register(new MCP_Tool_Remove_Schema_Type());
507 $safe_register(new MCP_Tool_Validate_Schema());
508
509 // Google Instant Index (6 tools)
510 $safe_register(new MCP_Tool_Instant_Index_Update());
511 $safe_register(new MCP_Tool_Instant_Index_Delete());
512 $safe_register(new MCP_Tool_Instant_Index_Status());
513 $safe_register(new MCP_Tool_Instant_Index_Bulk_Update());
514 $safe_register(new MCP_Tool_Get_Instant_Index_Settings());
515 $safe_register(new MCP_Tool_Update_Instant_Index_Settings());
516
517 // Custom HTML Pages (5 tools)
518 $safe_register(new MCP_Tool_Create_Custom_Page());
519 $safe_register(new MCP_Tool_Get_Custom_Page());
520 $safe_register(new MCP_Tool_List_Custom_Pages());
521 $safe_register(new MCP_Tool_Update_Custom_Page());
522 $safe_register(new MCP_Tool_Delete_Custom_Page());
523
524 // HTML to Builder Converter (3 tools)
525 $safe_register(new MCP_Tool_Convert_HTML_To_Builder());
526 $safe_register(new MCP_Tool_Create_Builder_Page_From_HTML());
527 $safe_register(new MCP_Tool_Convert_Custom_Page_To_Builder());
528
529 // Code Snippets (6 tools)
530 $safe_register(new MCP_Tool_Get_Header_Snippet());
531 $safe_register(new MCP_Tool_Update_Header_Snippet());
532 $safe_register(new MCP_Tool_Get_Footer_Snippet());
533 $safe_register(new MCP_Tool_Update_Footer_Snippet());
534 $safe_register(new MCP_Tool_Get_Post_Snippets());
535 $safe_register(new MCP_Tool_Update_Post_Snippets());
536
537 // Categories & Taxonomies (15 tools)
538 $safe_register(new MCP_Tool_List_Categories());
539 $safe_register(new MCP_Tool_Get_Category());
540 $safe_register(new MCP_Tool_Create_Category());
541 $safe_register(new MCP_Tool_Update_Category());
542 $safe_register(new MCP_Tool_Delete_Category());
543 $safe_register(new MCP_Tool_Get_Post_Categories());
544 $safe_register(new MCP_Tool_Set_Post_Categories());
545
546 // Tags (8 tools)
547 $safe_register(new MCP_Tool_List_Tags());
548 $safe_register(new MCP_Tool_Get_Tag());
549 $safe_register(new MCP_Tool_Create_Tag());
550 $safe_register(new MCP_Tool_Update_Tag());
551 $safe_register(new MCP_Tool_Delete_Tag());
552 $safe_register(new MCP_Tool_Get_Post_Tags());
553 $safe_register(new MCP_Tool_Set_Post_Tags());
554
555 // Featured Images & Media (6 tools)
556 $safe_register(new MCP_Tool_Get_Featured_Image());
557 $safe_register(new MCP_Tool_Set_Featured_Image());
558 $safe_register(new MCP_Tool_Upload_Featured_Image());
559 $safe_register(new MCP_Tool_Remove_Featured_Image());
560 $safe_register(new MCP_Tool_List_Media());
561 $safe_register(new MCP_Tool_Get_Media_Details());
562
563 // Post CRUD Operations (1 tool - delete/restore disabled for safety)
564 $safe_register(new MCP_Tool_Create_Post());
565 // $safe_register(new MCP_Tool_Delete_Post()); // DISABLED - safety
566 // $safe_register(new MCP_Tool_Restore_Post()); // DISABLED - safety
567
568 // Bulk Operations (3 tools - bulk delete disabled for safety)
569 $safe_register(new MCP_Tool_Bulk_Update_Meta());
570 $safe_register(new MCP_Tool_Bulk_Set_Categories());
571 $safe_register(new MCP_Tool_Bulk_Change_Status());
572 // $safe_register(new MCP_Tool_Bulk_Delete_Posts()); // DISABLED - safety
573
574 // WordPress Core SEO Settings (10 tools)
575 $safe_register(new MCP_Tool_Get_Site_Info());
576 $safe_register(new MCP_Tool_Update_Site_Info());
577 $safe_register(new MCP_Tool_Get_Permalink_Structure());
578 $safe_register(new MCP_Tool_Update_Permalink_Structure());
579 $safe_register(new MCP_Tool_Get_Reading_Settings());
580 $safe_register(new MCP_Tool_Update_Reading_Settings());
581 $safe_register(new MCP_Tool_Get_Search_Visibility());
582 $safe_register(new MCP_Tool_Update_Search_Visibility());
583 $safe_register(new MCP_Tool_Get_Date_Format());
584 $safe_register(new MCP_Tool_Get_Discussion_Settings());
585
586 // Taxonomy Meta Operations (4 tools - NEW in v2.8.0)
587 $safe_register(new MCP_Tool_Get_Term_Meta());
588 $safe_register(new MCP_Tool_Update_Term_Meta());
589 $safe_register(new MCP_Tool_Bulk_Update_Term_Meta());
590 $safe_register(new MCP_Tool_List_Terms_With_Meta());
591
592 // Bulk Alt Text Operations (4 tools - NEW in v2.8.0)
593 $safe_register(new MCP_Tool_Audit_Alt_Text());
594 $safe_register(new MCP_Tool_Bulk_Update_Alt_Text());
595 $safe_register(new MCP_Tool_Generate_Alt_Text());
596 $safe_register(new MCP_Tool_Validate_Alt_Text());
597
598 // OTTO Persistence Settings (2 tools)
599 if (class_exists('MCP_Tool_Get_Otto_Persistence_Settings')) {
600 $safe_register(new MCP_Tool_Get_Otto_Persistence_Settings());
601 }
602 if (class_exists('MCP_Tool_Update_Otto_Persistence_Settings')) {
603 $safe_register(new MCP_Tool_Update_Otto_Persistence_Settings());
604 }
605
606 // OTTO Pipeline Tools (3 tools)
607 if (class_exists('MCP_Tool_Trigger_Otto_Optimization')) {
608 $safe_register(new MCP_Tool_Trigger_Otto_Optimization());
609 }
610 if (class_exists('MCP_Tool_Get_Otto_Status')) {
611 $safe_register(new MCP_Tool_Get_Otto_Status());
612 }
613 if (class_exists('MCP_Tool_Verify_SEO_Output')) {
614 $safe_register(new MCP_Tool_Verify_SEO_Output());
615 }
616
617 // System Diagnostics & Plugin Info (4 tools)
618 $safe_register(new MCP_Tool_System_Diagnostics());
619 $safe_register(new MCP_Tool_List_All_Plugins());
620 $safe_register(new MCP_Tool_Get_Cron_Jobs());
621 $safe_register(new MCP_Tool_Get_WP_Option());
622
623 // SEO Inventory (1 tool — WP-135)
624 $safe_register(new MCP_Tool_List_Posts_SEO_Inventory());
625
626 // Read-Only Database Access (3 tools)
627 $safe_register(new MCP_Tool_DB_Tables());
628 $safe_register(new MCP_Tool_DB_Describe());
629 $safe_register(new MCP_Tool_DB_Select());
630
631 // Breadcrumb Tools (1 tool)
632 $safe_register(new MCP_Tool_Get_Breadcrumb_Path());
633
634 // Cache Purge (2 tools)
635 $safe_register(new MCP_Tool_Cache_Purge_All());
636 $safe_register(new MCP_Tool_Cache_Purge_URL());
637
638 // LLMs.txt Tools (5 tools)
639 $safe_register(new MCP_Tool_Get_LLMs_Txt());
640 $safe_register(new MCP_Tool_Regenerate_LLMs_Txt());
641 $safe_register(new MCP_Tool_Get_LLMs_Txt_Settings());
642 $safe_register(new MCP_Tool_Update_LLMs_Txt_Settings());
643 $safe_register(new MCP_Tool_Get_Post_Markdown());
644
645 // SEO Plugin Audit (4 tools — WP-202)
646 $safe_register(new MCP_Tool_Read_SEO_Plugin_Data());
647 $safe_register(new MCP_Tool_SEO_Plugin_Diff());
648 if (class_exists('Metasync_Plugin_Sync')) {
649 $safe_register(new MCP_Tool_Sync_To_Active_Plugins());
650 }
651 $safe_register(new MCP_Tool_Detect_SEO_Conflicts());
652
653 // Allow other plugins/themes to register tools
654 do_action('metasync_mcp_register_tools', $metasync_mcp_server);
655 }
656 add_action('init', 'metasync_init_mcp_server', 5);
657
658 /**
659 * Schedule a daily cron event to auto-purge Sync History records older than 90 days.
660 */
661 function metasync_schedule_sync_log_cleanup() {
662 if (!wp_next_scheduled('metasync_sync_log_daily_cleanup')) {
663 wp_schedule_event(time(), 'daily', 'metasync_sync_log_daily_cleanup');
664 }
665 }
666 add_action('wp', 'metasync_schedule_sync_log_cleanup');
667
668 /**
669 * Cron callback: delete Sync History records older than 90 days.
670 */
671 function metasync_sync_log_cleanup_handler() {
672 $sync_db = new Metasync_Sync_History_Database();
673 $sync_db->delete_older_than_days(90);
674 }
675 add_action('metasync_sync_log_daily_cleanup', 'metasync_sync_log_cleanup_handler');
676
677 /**
678 * Output DYO initialization flag to the frontend
679 * Makes window.__SA_DYO_INITIALIZED__ = true available in the DOM
680 * This indicates the Search Atlas plugin is active and initialized
681 */
682 function metasync_output_dyo_init_flag() {
683 echo '<script>window.__SA_DYO_INITIALIZED__=true;</script>' . "\n";
684 }
685 add_action('wp_head', 'metasync_output_dyo_init_flag', 1);
686
687 /**
688 * Initialize GA4 Analytics for Admin Area
689 * Hooked to admin_init for proper WordPress lifecycle integration
690 * Only loads after WordPress, plugins, and themes are fully loaded
691 */
692 function metasync_init_analytics() {
693 // Only initialize in admin area (excludes AJAX and REST API requests)
694 if (is_admin() && !wp_doing_ajax() && !defined('REST_REQUEST')) {
695 Metasync_GA4::get_instance();
696 }
697 }
698 add_action('admin_init', 'metasync_init_analytics', 10);
699
700 /**
701 * Initialize API Backoff System
702 * Hooked to init for proper WordPress lifecycle integration
703 * Monitors API responses and manages exponential backoff for rate limiting
704 * @since 2.7.1
705 */
706 function metasync_init_api_backoff() {
707 // Initialize backoff manager (registers HTTP response filters)
708 Metasync_API_Backoff_Manager::get_instance();
709
710 // Initialize admin notices (only in admin area)
711 if (is_admin()) {
712 Metasync_API_Backoff_Notices::get_instance();
713 }
714 }
715 add_action('init', 'metasync_init_api_backoff', 5);
716
717 /**
718 * Initialize Review Notice
719 * Shows a dismissible notice asking users to rate the plugin after a usage period
720 * @since 2.8.0
721 */
722 function metasync_init_review_notice() {
723 if (is_admin()) {
724 Metasync_Review_Notice::get_instance();
725 }
726 }
727 add_action('init', 'metasync_init_review_notice');
728
729 /**
730 * Global convenience function to get active JWT token
731 * Can be called from anywhere in WordPress (themes, other plugins, etc.)
732 *
733 * @param bool $force_refresh Force generation of new token even if cached one exists
734 * @return string|false JWT token on success, false on failure
735 */
736 if (!function_exists('metasync_get_jwt_token')) {
737 function metasync_get_jwt_token($force_refresh = false)
738 {
739 return Metasync::get_jwt_token($force_refresh);
740 }
741 }
742
743
744 /**
745 * Initialize Debug Mode Manager
746 * Hooked to 'init' for proper WordPress lifecycle integration
747 */
748 function metasync_init_debug_mode_manager() {
749 // Initialize the Debug Mode Manager singleton
750 Metasync_Debug_Mode_Manager::get_instance();
751 }
752 add_action('init', 'metasync_init_debug_mode_manager', 10);
753
754 /**
755 * Oxygen Builder Compatibility
756 * Auto re-signs [oxygen] dynamic-data shortcodes when their HMAC signatures
757 * are invalid (e.g. after design-set import or site migration).
758 * Runs once on admin_init; skips entirely when Oxygen is inactive.
759 */
760 if (is_admin()) {
761 add_action('admin_init', ['Metasync_Oxygen_Compat', 'maybe_resign_shortcodes'], 20);
762 }
763