PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.5.23
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.5.23
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.5.23, at includes/class-metasync-activator.php

354 lines 10.5 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 self::send_announce_ping();
49
50 // Set first activation flag for setup wizard
51 if (!get_option('metasync_first_activation_time')) {
52 update_option('metasync_first_activation_time', current_time('mysql'));
53 update_option('metasync_show_wizard', true);
54 }
55
56 flush_rewrite_rules();
57 }
58
59 /**
60 * Send pre-SSO announce ping to backend (zero-trust; backend rate-limits).
61 * POST /api/wp-plugin-announce/ with url + plugin_version; optional X-Plugin-Token for deduplication.
62 * Callable from activation and from init (rate-limited) when no API key yet.
63 *
64 * @since 2.5.x
65 */
66 public static function send_announce_ping()
67 {
68 $base = 'https://ca.searchatlas.com';
69 if (class_exists('Metasync_Endpoint_Manager')) {
70 $base = Metasync_Endpoint_Manager::get_endpoint('CA_API_DOMAIN');
71 } elseif (class_exists('Metasync')) {
72 $base = Metasync::CA_API_DOMAIN;
73 }
74 $url = rtrim($base, '/') . '/api/wp-plugin-announce/';
75
76 $body = wp_json_encode([
77 'url' => get_home_url(),
78 'plugin_version' => defined('METASYNC_VERSION') ? METASYNC_VERSION : 'unknown',
79 ]);
80
81 $options = get_option('metasync_options', []);
82 $plugin_auth_token = $options['general']['apikey'] ?? '';
83 $headers = [
84 'Content-Type' => 'application/json',
85 ];
86 if (!empty($plugin_auth_token)) {
87 $headers['X-Plugin-Token'] = $plugin_auth_token;
88 }
89
90 wp_remote_post($url, [
91 'body' => $body,
92 'headers' => $headers,
93 'timeout' => 10,
94 'blocking' => false,
95 ]);
96 }
97
98 /**
99 * Ensure Plugin Auth Token exists
100 * Generates a unique Plugin Auth Token during plugin activation
101 */
102 private static function ensure_plugin_auth_token()
103 {
104 $options = get_option('metasync_options', []);
105
106 if (empty($options['general']['apikey'])) {
107 // Generate unique Plugin Auth Token (alphanumeric only)
108 $plugin_auth_token = wp_generate_password(32, false, false);
109
110 // Initialize options structure if needed
111 if (!isset($options['general'])) {
112 $options['general'] = [];
113 }
114
115 // Store Plugin Auth Token
116 $options['general']['apikey'] = $plugin_auth_token;
117 update_option('metasync_options', $options);
118 }
119 }
120
121 /**
122 * Get the path to the whitelabel settings JSON file
123 *
124 * @return string|false Path to the file if it exists, false otherwise
125 * @since 2.5.0
126 */
127 public static function get_whitelabel_settings_file()
128 {
129 $plugin_dir = plugin_dir_path(dirname(__FILE__));
130
131 // Check for whitelabel-settings.json in plugin root
132 $json_file = $plugin_dir . 'whitelabel-settings.json';
133
134 // Also check in a common extracted zip location (if zip was extracted)
135 if (!file_exists($json_file)) {
136 $json_file = $plugin_dir . 'metasync/whitelabel-settings.json';
137 }
138
139 if (!file_exists($json_file)) {
140 return false;
141 }
142
143 return $json_file;
144 }
145
146 /**
147 * Check if whitelabel settings file has been updated and import if needed
148 * This method should be called on init to detect plugin uploads/updates
149 *
150 * @since 2.5.0
151 */
152 public static function check_whitelabel_settings_update()
153 {
154 $json_file = self::get_whitelabel_settings_file();
155
156 if ($json_file === false) {
157 return;
158 }
159
160 // Get current file modification time and content hash
161 $file_mtime = filemtime($json_file);
162 $file_hash = md5_file($json_file);
163
164 // Get stored file info
165 $stored_mtime = get_option('metasync_whitelabel_file_mtime', 0);
166 $stored_hash = get_option('metasync_whitelabel_file_hash', '');
167
168 // Check if file has changed (either modification time or content)
169 if ($file_mtime > $stored_mtime || $file_hash !== $stored_hash) {
170 // File has changed, import settings
171 self::import_whitelabel_settings();
172
173 // Store new file info
174 update_option('metasync_whitelabel_file_mtime', $file_mtime);
175 update_option('metasync_whitelabel_file_hash', $file_hash);
176 }
177 }
178
179 /**
180 * Import whitelabel settings from JSON file if available
181 * Checks for whitelabel-settings.json in the plugin directory or extracted zip
182 *
183 * This method is public to allow calling during both activation and plugin updates.
184 * @since 2.5.0
185 */
186 public static function import_whitelabel_settings()
187 {
188 $json_file = self::get_whitelabel_settings_file();
189
190 if ($json_file === false) {
191 return;
192 }
193
194 // Read JSON file
195 $json_content = file_get_contents($json_file);
196 if ($json_content === false) {
197 return;
198 }
199
200 // Decode JSON
201 $import_data = json_decode($json_content, true);
202 if ($import_data === null || json_last_error() !== JSON_ERROR_NONE) {
203 return;
204 }
205
206 // Validate import data structure
207 if (!isset($import_data['whitelabel_settings']) || !is_array($import_data['whitelabel_settings'])) {
208 return;
209 }
210
211 // Get current options
212 $options = get_option('metasync_options', array());
213
214 // Import whitelabel settings
215 if (isset($import_data['whitelabel_settings'])) {
216 $whitelabel_settings = $import_data['whitelabel_settings'];
217
218 // Initialize whitelabel array if needed
219 if (!isset($options['whitelabel'])) {
220 $options['whitelabel'] = array();
221 }
222
223 // Merge imported settings with existing (imported settings take precedence)
224 $options['whitelabel'] = array_merge($options['whitelabel'], $whitelabel_settings);
225
226 // Update timestamp
227 $options['whitelabel']['updated_at'] = time();
228 $options['whitelabel']['imported_at'] = current_time('mysql');
229 }
230
231 // Import general settings related to whitelabel
232 if (isset($import_data['general_settings']) && is_array($import_data['general_settings'])) {
233 if (!isset($options['general'])) {
234 $options['general'] = array();
235 }
236
237 // Merge general settings
238 foreach ($import_data['general_settings'] as $key => $value) {
239 $options['general'][$key] = $value;
240 }
241 }
242
243 // Save updated options
244 update_option('metasync_options', $options);
245
246 // Update the plugin file headers so whitelabel shows even when deactivated
247 self::update_plugin_file_headers($import_data);
248
249 // Optionally delete the JSON file after successful import (uncomment if desired)
250 // unlink($json_file);
251 }
252
253 /**
254 * Sync plugin file headers from the current saved options in the database.
255 * Call this after saving whitelabel settings via the admin UI to ensure
256 * the plugin file headers reflect the latest whitelabel values.
257 *
258 * @since 2.5.0
259 */
260 public static function sync_plugin_file_headers()
261 {
262 $options = get_option('metasync_options', array());
263 $general = $options['general'] ?? array();
264
265 // Build the import_data format expected by update_plugin_file_headers
266 $import_data = array(
267 'general_settings' => $general,
268 );
269
270 self::update_plugin_file_headers($import_data);
271 }
272
273 /**
274 * Update the main plugin file headers with whitelabel values
275 * WordPress reads plugin metadata directly from the file header comments,
276 * so modifying these ensures whitelabel shows even when the plugin is deactivated.
277 *
278 * @param array $import_data The imported whitelabel data
279 * @since 2.5.0
280 */
281 private static function update_plugin_file_headers($import_data)
282 {
283 $plugin_file = plugin_dir_path(dirname(__FILE__)) . 'metasync.php';
284
285 if (!file_exists($plugin_file) || !is_writable($plugin_file)) {
286 return;
287 }
288
289 $content = file_get_contents($plugin_file);
290 if ($content === false) {
291 return;
292 }
293
294 $general = $import_data['general_settings'] ?? array();
295
296 // Map of whitelabel setting keys to plugin header field names
297 // with default values to restore when whitelabel is cleared
298 $header_map = array(
299 'white_label_plugin_name' => array(
300 'header' => 'Plugin Name',
301 'default' => 'Search Atlas: The Premier AI SEO Plugin for Instant Optimization',
302 ),
303 'white_label_plugin_description' => array(
304 'header' => 'Description',
305 '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.',
306 ),
307 'white_label_plugin_author' => array(
308 'header' => 'Author',
309 'default' => 'Search Atlas',
310 ),
311 'white_label_plugin_author_uri' => array(
312 'header' => 'Author URI',
313 'default' => 'https://searchatlas.com',
314 ),
315 'white_label_plugin_uri' => array(
316 'header' => 'Plugin URI',
317 'default' => 'https://searchatlas.com/',
318 ),
319 );
320
321 $modified = false;
322
323 foreach ($header_map as $setting_key => $field_config) {
324 $header_field = $field_config['header'];
325
326 // Use whitelabel value if set, otherwise restore default
327 $new_value = !empty($general[$setting_key])
328 ? $general[$setting_key]
329 : $field_config['default'];
330
331 // Match the header line: " * Field Name: any value"
332 // Handles varying whitespace between field name and value
333 $pattern = '/^(\s*\*\s*' . preg_quote($header_field, '/') . ':\s*)(.+)$/m';
334
335 if (preg_match($pattern, $content, $matches)) {
336 // Only replace if the value actually differs from what's in the file
337 if (trim($matches[2]) !== trim($new_value)) {
338 // Escape both backslashes and $ signs for preg_replace replacement string
339 $escaped_value = str_replace(array('\\', '$'), array('\\\\', '\\$'), $new_value);
340 $content = preg_replace($pattern, '${1}' . $escaped_value, $content, 1);
341 $modified = true;
342 }
343 }
344 }
345
346 if ($modified) {
347 file_put_contents($plugin_file, $content);
348
349 // Clear WordPress plugin cache so it reads the updated headers
350 wp_cache_delete('plugins', 'plugins');
351 }
352 }
353 }
354