PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.5
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.5
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / includes / class-metasync-activator.php

class-metasync-activator.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.5, at includes/class-metasync-activator.php

388 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Fired during plugin activation
5 *
6 * @link https://searchatlas.com
7 * @since 1.0.0
8 *
9 * @package Metasync
10 * @subpackage Metasync/includes
11 */
12
13 /**
14 * Fired during plugin activation.
15 *
16 * This class defines all code necessary to run during the plugin's activation.
17 *
18 * @since 1.0.0
19 * @package Metasync
20 * @subpackage Metasync/includes
21 * @author Engineering Team <support@searchatlas.com>
22 */
23 class Metasync_Activator
24 {
25
26 /**
27 * Short Description. (use period)
28 *
29 * Long Description.
30 *
31 * @since 1.0.0
32 */
33 public static function activate()
34 {
35 // WordPress core sitemap functionality is required
36 // if (wp_sitemaps_get_server()->sitemaps_enabled() == false) {
37 // add_filter('wp_sitemaps_enabled', '__return_true');
38 // }
39
40 // Generate Plugin Auth Token on first activation
41 self::ensure_plugin_auth_token();
42
43 // Import whitelabel settings only if the JSON file is new or changed
44 // (prevents overwriting admin UI changes on every deactivate/activate cycle)
45 self::check_whitelabel_settings_update();
46
47 // Pre-SSO announce: tell backend plugin is installed (PR4 - heartbeat reliability)
48 update_option('metasync_announce_attempt_count', 0);
49 self::send_announce_ping();
50 update_option('metasync_announce_attempt_count', 1);
51
52 // Schedule cron for announce pings 2-5 (every 10 minutes)
53 if (!wp_next_scheduled('metasync_announce_cron')) {
54 wp_schedule_event(time() + 10 * MINUTE_IN_SECONDS, 'metasync_every_10_minutes', 'metasync_announce_cron');
55 }
56
57 // Set first activation flag for setup wizard
58 if (!get_option('metasync_first_activation_time')) {
59 update_option('metasync_first_activation_time', current_time('mysql'));
60 update_option('metasync_show_wizard', true);
61 }
62
63 flush_rewrite_rules();
64 }
65
66 /**
67 * Send pre-SSO announce ping to backend (zero-trust; backend rate-limits).
68 * POST /api/wp-plugin-announce/ with url + plugin_version; optional X-Plugin-Token for deduplication.
69 * Callable from activation and from init (rate-limited) when no API key yet.
70 *
71 * @since 2.5.x
72 */
73 public static function send_announce_ping()
74 {
75 $base = 'https://ca.searchatlas.com';
76 if (class_exists('Metasync_Endpoint_Manager')) {
77 $base = Metasync_Endpoint_Manager::get_endpoint('CA_API_DOMAIN');
78 } elseif (class_exists('Metasync')) {
79 $base = Metasync::CA_API_DOMAIN;
80 }
81 $url = rtrim($base, '/') . '/api/wp-plugin-announce/';
82
83 $body = wp_json_encode([
84 'url' => get_home_url(),
85 'plugin_version' => defined('METASYNC_VERSION') ? METASYNC_VERSION : 'unknown',
86 ]);
87
88 $options = get_option('metasync_options', []);
89 $plugin_auth_token = $options['general']['apikey'] ?? '';
90 $headers = [
91 'Content-Type' => 'application/json',
92 ];
93 if (!empty($plugin_auth_token)) {
94 $headers['X-Plugin-Token'] = $plugin_auth_token;
95 }
96
97 wp_remote_post($url, [
98 'body' => $body,
99 'headers' => $headers,
100 'timeout' => 10,
101 'blocking' => false,
102 ]);
103 }
104
105 /**
106 * Ensure Plugin Auth Token exists
107 * Generates a unique Plugin Auth Token during plugin activation
108 */
109 private static function ensure_plugin_auth_token()
110 {
111 $options = get_option('metasync_options', []);
112
113 if (empty($options['general']['apikey'])) {
114 // Generate unique Plugin Auth Token (alphanumeric only)
115 $plugin_auth_token = wp_generate_password(32, false, false);
116
117 // Initialize options structure if needed
118 if (!isset($options['general'])) {
119 $options['general'] = [];
120 }
121
122 // Store Plugin Auth Token
123 $options['general']['apikey'] = $plugin_auth_token;
124 update_option('metasync_options', $options);
125 }
126 }
127
128 /**
129 * Get the path to the whitelabel settings JSON file
130 *
131 * @return string|false Path to the file if it exists, false otherwise
132 * @since 2.5.0
133 */
134 public static function get_whitelabel_settings_file()
135 {
136 $plugin_dir = plugin_dir_path(dirname(__FILE__));
137
138 // Check for whitelabel-settings.json in plugin root
139 $json_file = $plugin_dir . 'whitelabel-settings.json';
140
141 // Also check in a common extracted zip location (if zip was extracted)
142 if (!file_exists($json_file)) {
143 $json_file = $plugin_dir . 'metasync/whitelabel-settings.json';
144 }
145
146 if (!file_exists($json_file)) {
147 return false;
148 }
149
150 return $json_file;
151 }
152
153 /**
154 * Check if whitelabel settings file has been updated and import if needed
155 * This method should be called on init to detect plugin uploads/updates
156 *
157 * @since 2.5.0
158 */
159 public static function check_whitelabel_settings_update()
160 {
161 $json_file = self::get_whitelabel_settings_file();
162
163 if ($json_file === false) {
164 return;
165 }
166
167 // Get current file modification time and content hash
168 $file_mtime = filemtime($json_file);
169 $file_hash = md5_file($json_file);
170
171 // Get stored file info
172 $stored_mtime = get_option('metasync_whitelabel_file_mtime', 0);
173 $stored_hash = get_option('metasync_whitelabel_file_hash', '');
174
175 // Check if file has changed (either modification time or content)
176 if ($file_mtime > $stored_mtime || $file_hash !== $stored_hash) {
177 // File has changed, import settings
178 self::import_whitelabel_settings();
179
180 // Store new file info
181 update_option('metasync_whitelabel_file_mtime', $file_mtime);
182 update_option('metasync_whitelabel_file_hash', $file_hash);
183 }
184 }
185
186 /**
187 * Import whitelabel settings from JSON file if available
188 * Checks for whitelabel-settings.json in the plugin directory or extracted zip
189 *
190 * This method is public to allow calling during both activation and plugin updates.
191 * @since 2.5.0
192 */
193 public static function import_whitelabel_settings()
194 {
195 $json_file = self::get_whitelabel_settings_file();
196
197 if ($json_file === false) {
198 return;
199 }
200
201 // Read JSON file
202 $json_content = file_get_contents($json_file);
203 if ($json_content === false) {
204 return;
205 }
206
207 // Decode JSON
208 $import_data = json_decode($json_content, true);
209 if ($import_data === null || json_last_error() !== JSON_ERROR_NONE) {
210 return;
211 }
212
213 // Validate import data structure
214 if (!isset($import_data['whitelabel_settings']) || !is_array($import_data['whitelabel_settings'])) {
215 return;
216 }
217
218 // Get current options
219 $options = get_option('metasync_options', array());
220
221 // Import whitelabel settings
222 if (isset($import_data['whitelabel_settings'])) {
223 $whitelabel_settings = $import_data['whitelabel_settings'];
224
225 // Initialize whitelabel array if needed
226 if (!isset($options['whitelabel'])) {
227 $options['whitelabel'] = array();
228 }
229
230 // Merge imported settings with existing (imported settings take precedence)
231 $options['whitelabel'] = array_merge($options['whitelabel'], $whitelabel_settings);
232
233 // Update timestamp
234 $options['whitelabel']['updated_at'] = time();
235 $options['whitelabel']['imported_at'] = current_time('mysql');
236 }
237
238 // Import general settings related to whitelabel
239 if (isset($import_data['general_settings']) && is_array($import_data['general_settings'])) {
240 if (!isset($options['general'])) {
241 $options['general'] = array();
242 }
243
244 // Merge general settings
245 foreach ($import_data['general_settings'] as $key => $value) {
246 $options['general'][$key] = $value;
247 }
248 }
249
250 // Restore bundled icon: if the icon value is a __bundled_icon__{ext} marker,
251 // copy the bundled file from the plugin directory to uploads and update the URL.
252 $icon_value = $options['general']['white_label_plugin_menu_icon'] ?? '';
253 if (!empty($icon_value) && strpos($icon_value, '__bundled_icon__') === 0) {
254 $ext = substr($icon_value, strlen('__bundled_icon__'));
255 $ext = preg_replace('/[^a-z0-9]/', '', strtolower($ext)); // sanitize
256 $bundled_file = plugin_dir_path(dirname(__FILE__)) . 'whitelabel-icon.' . $ext;
257
258 if (file_exists($bundled_file) && in_array($ext, ['png', 'svg'], true)) {
259 $upload_dir = wp_upload_dir();
260 $dest_dir = $upload_dir['basedir'] . '/metasync';
261 if (!file_exists($dest_dir)) {
262 wp_mkdir_p($dest_dir);
263 }
264 $dest_file = $dest_dir . '/whitelabel-icon.' . $ext;
265 if (copy($bundled_file, $dest_file)) {
266 $options['general']['white_label_plugin_menu_icon'] = $upload_dir['baseurl'] . '/metasync/whitelabel-icon.' . $ext;
267 } else {
268 // Could not copy — clear the broken marker so default icon shows
269 $options['general']['white_label_plugin_menu_icon'] = '';
270 }
271 } else {
272 // Bundled file missing or unsupported extension — clear the marker
273 $options['general']['white_label_plugin_menu_icon'] = '';
274 }
275 }
276
277 // Save updated options
278 update_option('metasync_options', $options);
279
280 // Update the plugin file headers so whitelabel shows even when deactivated
281 self::update_plugin_file_headers($import_data);
282
283 // Optionally delete the JSON file after successful import (uncomment if desired)
284 // unlink($json_file);
285 }
286
287 /**
288 * Sync plugin file headers from the current saved options in the database.
289 * Call this after saving whitelabel settings via the admin UI to ensure
290 * the plugin file headers reflect the latest whitelabel values.
291 *
292 * @since 2.5.0
293 */
294 public static function sync_plugin_file_headers()
295 {
296 $options = get_option('metasync_options', array());
297 $general = $options['general'] ?? array();
298
299 // Build the import_data format expected by update_plugin_file_headers
300 $import_data = array(
301 'general_settings' => $general,
302 );
303
304 self::update_plugin_file_headers($import_data);
305 }
306
307 /**
308 * Update the main plugin file headers with whitelabel values
309 * WordPress reads plugin metadata directly from the file header comments,
310 * so modifying these ensures whitelabel shows even when the plugin is deactivated.
311 *
312 * @param array $import_data The imported whitelabel data
313 * @since 2.5.0
314 */
315 private static function update_plugin_file_headers($import_data)
316 {
317 $plugin_file = plugin_dir_path(dirname(__FILE__)) . 'metasync.php';
318
319 if (!file_exists($plugin_file) || !is_writable($plugin_file)) {
320 return;
321 }
322
323 $content = file_get_contents($plugin_file);
324 if ($content === false) {
325 return;
326 }
327
328 $general = $import_data['general_settings'] ?? array();
329
330 // Map of whitelabel setting keys to plugin header field names
331 // with default values to restore when whitelabel is cleared
332 $header_map = array(
333 'white_label_plugin_name' => array(
334 'header' => 'Plugin Name',
335 'default' => 'Search Atlas: The Premier AI SEO Plugin for Instant Optimization',
336 ),
337 'white_label_plugin_description' => array(
338 'header' => 'Description',
339 'default' => '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.',
340 ),
341 'white_label_plugin_author' => array(
342 'header' => 'Author',
343 'default' => 'Search Atlas',
344 ),
345 'white_label_plugin_author_uri' => array(
346 'header' => 'Author URI',
347 'default' => 'https://searchatlas.com',
348 ),
349 'white_label_plugin_uri' => array(
350 'header' => 'Plugin URI',
351 'default' => 'https://searchatlas.com/',
352 ),
353 );
354
355 $modified = false;
356
357 foreach ($header_map as $setting_key => $field_config) {
358 $header_field = $field_config['header'];
359
360 // Use whitelabel value if set, otherwise restore default
361 $new_value = !empty($general[$setting_key])
362 ? $general[$setting_key]
363 : $field_config['default'];
364
365 // Match the header line: " * Field Name: any value"
366 // Handles varying whitespace between field name and value
367 $pattern = '/^(\s*\*\s*' . preg_quote($header_field, '/') . ':\s*)(.+)$/m';
368
369 if (preg_match($pattern, $content, $matches)) {
370 // Only replace if the value actually differs from what's in the file
371 if (trim($matches[2]) !== trim($new_value)) {
372 // Escape both backslashes and $ signs for preg_replace replacement string
373 $escaped_value = str_replace(array('\\', '$'), array('\\\\', '\\$'), $new_value);
374 $content = preg_replace($pattern, '${1}' . $escaped_value, $content, 1);
375 $modified = true;
376 }
377 }
378 }
379
380 if ($modified) {
381 file_put_contents($plugin_file, $content);
382
383 // Clear WordPress plugin cache so it reads the updated headers
384 wp_cache_delete('plugins', 'plugins');
385 }
386 }
387 }
388