PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.19
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.19
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
metasync / google-index / class-google-index-admin.php

class-google-index-admin.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.19, at google-index/class-google-index-admin.php

502 lines 20.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Google Index - Admin Integration
5 *
6 * Integrates Google Index settings into MetaSync admin general settings
7 *
8 * @package GoogleIndexDirect
9 * @version 1.0.0
10 */
11
12 // Prevent direct access
13 if (!defined('ABSPATH')) {
14 exit;
15 }
16
17 class Google_Index_Admin
18 {
19 /**
20 * Section ID for Google Index settings
21 */
22 private const SECTION_GOOGLE_INDEX = 'google_index_direct_settings';
23
24 /**
25 * Initialize admin functionality
26 */
27 public function __construct()
28 {
29 // Hook into MetaSync admin initialization
30 add_action('admin_init', array($this, 'add_settings_to_metasync'), 20);
31 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
32
33 // Hook into MetaSync's AJAX settings processing
34 add_action('wp_ajax_meta_sync_save_settings', array($this, 'process_google_index_settings'), 5);
35 add_action('wp_ajax_meta_sync_save_seo_controls', array($this, 'process_google_index_settings'), 5);
36
37 // Display admin notices after redirect
38 add_action('admin_notices', array($this, 'display_admin_notices'));
39
40 // AJAX handlers
41 add_action('wp_ajax_metasync_google_index_direct_test', array($this, 'ajax_test_connection'));
42 }
43
44
45 /**
46 * Add Google Index settings to MetaSync admin
47 */
48 public function add_settings_to_metasync()
49 {
50 // Only add if MetaSync Admin class exists
51 if (!class_exists('Metasync_Admin')) {
52 return;
53 }
54
55 // Get MetaSync page slug
56 $page_slug = $this->get_metasync_page_slug();
57 if (!$page_slug) {
58 return;
59 }
60
61 // Add settings section for Google Index
62 add_settings_section(
63 self::SECTION_GOOGLE_INDEX,
64 '', // Empty title - we'll use dashboard card styling
65 function(){}, // Empty callback
66 $page_slug . '_general'
67 );
68
69 // Add the main settings field
70 add_settings_field(
71 'google_index_direct_config',
72 'Google Index API',
73 array($this, 'render_settings_field'),
74 $page_slug . '_general',
75 self::SECTION_GOOGLE_INDEX
76 );
77 }
78
79 /**
80 * Get MetaSync page slug
81 */
82 private function get_metasync_page_slug()
83 {
84 if (class_exists('Metasync_Admin') && property_exists('Metasync_Admin', 'page_slug')) {
85 return Metasync_Admin::$page_slug;
86 }
87 return 'searchatlas'; // fallback
88 }
89
90 /**
91 * Render the Google Index settings field
92 */
93 public function render_settings_field()
94 {
95 // Load Google Index functionality
96 if (!function_exists('google_index_direct')) {
97 if (file_exists(plugin_dir_path(__FILE__) . 'google-index-init.php')) {
98 require_once plugin_dir_path(__FILE__) . 'google-index-init.php';
99 } else {
100 error_log('MetaSync Google Index: google-index-init.php not found at ' . plugin_dir_path(__FILE__) . 'google-index-init.php');
101 return;
102 }
103 }
104
105 // Get current service account info (safe - doesn't expose private key)
106 $google_index = google_index_direct();
107 $service_info = $google_index->get_service_account_info();
108 $is_configured = !isset($service_info['error']);
109
110 $saved_json_display = $is_configured ? $google_index->get_redacted_config_json() : '';
111
112 // Include the settings field view
113 include plugin_dir_path(__FILE__) . '../views/metasync-google-index-api-settings.php';
114 }
115
116 /**
117 * Process Google Index settings during MetaSync AJAX save
118 * This runs early in the AJAX processing chain (priority 5)
119 */
120 public function process_google_index_settings()
121 {
122 // Only process if our fields are present in the request
123 if (!isset($_POST['google_index_service_account_json']) &&
124 !isset($_POST['google_index_clear_config']) &&
125 !isset($_FILES['google_index_service_account_file'])) {
126 return; // No Google Index data to process
127 }
128
129 // Load Google Index functionality
130 if (!function_exists('google_index_save_service_account')) {
131 if (file_exists(plugin_dir_path(__FILE__) . 'google-index-init.php')) {
132 require_once plugin_dir_path(__FILE__) . 'google-index-init.php';
133 } else {
134 error_log('MetaSync Google Index: google-index-init.php not found at ' . plugin_dir_path(__FILE__) . 'google-index-init.php');
135 return;
136 }
137 }
138
139 $service_account_json = '';
140
141 // Get JSON from textarea
142 if (isset($_POST['google_index_service_account_json'])) {
143 $service_account_json = sanitize_textarea_field(wp_unslash($_POST['google_index_service_account_json']));
144 }
145
146 // Override with file upload if provided
147 if (isset($_FILES['google_index_service_account_file']) &&
148 !empty($_FILES['google_index_service_account_file']['tmp_name']) &&
149 file_exists($_FILES['google_index_service_account_file']['tmp_name'])) {
150
151 $uploaded_json = file_get_contents($_FILES['google_index_service_account_file']['tmp_name']);
152 if ($uploaded_json !== false) {
153 $service_account_json = $uploaded_json; // Removed unnecessary wp_unslash
154 }
155 }
156
157 // Handle clear configuration
158 if (isset($_POST['google_index_clear_config'])) {
159 delete_option('google_index_service_account');
160 // Success messages shown after redirect (to prevent interfering with MetaSync flow)
161 $this->add_settings_notice('Service account configuration cleared successfully!', 'success');
162 }
163 // Process service account JSON if provided
164 elseif (!empty($service_account_json) && trim($service_account_json) !== '') {
165
166 // Skip if it contains redacted private key (user didn't paste new JSON)
167 if (strpos($service_account_json, '-----REDACTED-----') !== false) {
168 return; // Don't process redacted display text
169 }
170
171 // Decode and validate JSON
172 $service_account_data = json_decode($service_account_json, true);
173
174 if (json_last_error() !== JSON_ERROR_NONE) {
175 // JSON parsing error - send immediate error response
176 $json_error_message = 'Invalid JSON format';
177 switch (json_last_error()) {
178 case JSON_ERROR_SYNTAX:
179 $json_error_message .= ' - Syntax error in JSON';
180 break;
181 case JSON_ERROR_UTF8:
182 $json_error_message .= ' - Invalid UTF-8 encoding';
183 break;
184 default:
185 $json_error_message .= ' - ' . json_last_error_msg();
186 break;
187 }
188
189 wp_send_json_error([
190 'errors' => ['Google Index: ' . $json_error_message . '. Please check your service account JSON.']
191 ]);
192 }
193
194 if (!is_array($service_account_data)) {
195 wp_send_json_error([
196 'errors' => ['Google Index: JSON must be an object/array. Please check your service account JSON format.']
197 ]);
198 }
199
200 // Validate required fields
201 $required_fields = ['type', 'project_id', 'private_key_id', 'private_key', 'client_email', 'client_id', 'auth_uri', 'token_uri'];
202 $missing_fields = [];
203
204 foreach ($required_fields as $field) {
205 if (!isset($service_account_data[$field]) || empty($service_account_data[$field])) {
206 $missing_fields[] = $field;
207 }
208 }
209
210 if (!empty($missing_fields)) {
211 wp_send_json_error([
212 'errors' => ['Google Index: Missing required fields - ' . implode(', ', $missing_fields) . '. Please ensure you have a complete service account JSON.']
213 ]);
214 }
215
216 // Save using Google Index function
217 $result = google_index_save_service_account($service_account_data);
218
219 if ($result) {
220 $this->add_settings_notice('Google Index service account configured successfully!', 'success');
221 } else {
222 // Send immediate error response using MetaSync's expected format
223 wp_send_json_error([
224 'errors' => ['Google Index: Failed to save service account configuration. Please try again or check your JSON format.']
225 ]);
226 }
227 }
228 }
229
230 /**
231 * Add a settings notice to be displayed after AJAX save
232 *
233 * @param string $message Notice message
234 * @param string $type Notice type: 'success', 'error', 'warning', 'info'
235 */
236 private function add_settings_notice($message, $type = 'info')
237 {
238 $notices = get_transient('google_index_admin_notices') ?: [];
239 $notices[] = [
240 'message' => $message,
241 'type' => $type,
242 'time' => time()
243 ];
244
245 // Store notices for 30 seconds (enough time for page redirect)
246 set_transient('google_index_admin_notices', $notices, 30);
247 }
248
249 /**
250 * Display admin notices for Google Index settings
251 */
252 public function display_admin_notices()
253 {
254 // Only show notices on MetaSync settings pages
255 $page_slug = $this->get_metasync_page_slug();
256 if ( ! function_exists( 'get_current_screen' ) ) {
257 return;
258 }
259 $current_screen = get_current_screen();
260
261 if (!$current_screen || strpos($current_screen->id, $page_slug) === false) {
262 return;
263 }
264
265 // Get and display notices
266 $notices = get_transient('google_index_admin_notices');
267 if (!empty($notices)) {
268 foreach ($notices as $notice) {
269 $class = 'notice notice-' . esc_attr($notice['type']) . ' is-dismissible';
270 echo '<div class="' . $class . '">';
271 echo '<p><strong>Google Index:</strong> ' . esc_html($notice['message']) . '</p>';
272 echo '</div>';
273 }
274
275 // Clear notices after displaying
276 delete_transient('google_index_admin_notices');
277 }
278 }
279
280 /**
281 * AJAX handler for testing connection
282 */
283 public function ajax_test_connection()
284 {
285 // Check nonce and permissions
286 if (!wp_verify_nonce($_POST['nonce'] ?? '', 'metasync_google_index_direct_test') ||
287 !Metasync::current_user_has_plugin_access()) {
288 wp_die('Security check failed');
289 }
290
291 // Load Google Index functionality
292 if (!function_exists('google_index_direct')) {
293 if (file_exists(plugin_dir_path(__FILE__) . 'google-index-init.php')) {
294 require_once plugin_dir_path(__FILE__) . 'google-index-init.php';
295 } else {
296 error_log('MetaSync Google Index: google-index-init.php not found at ' . plugin_dir_path(__FILE__) . 'google-index-init.php');
297 return;
298 }
299 }
300
301 try {
302 // Test the connection
303 $google_index = google_index_direct();
304 $test_results = $google_index->test_connection();
305
306 wp_send_json_success([
307 'message' => 'Connection test completed',
308 'results' => $test_results
309 ]);
310
311 } catch (Exception $e) {
312 wp_send_json_error([
313 'message' => 'Test failed: ' . $e->getMessage()
314 ]);
315 }
316 }
317
318 /**
319 * Enqueue admin scripts and styles
320 */
321 public function enqueue_admin_scripts($hook)
322 {
323 // Only load on MetaSync settings pages
324 $page_slug = $this->get_metasync_page_slug();
325 if (strpos($hook, $page_slug) === false) {
326 return;
327 }
328
329 // Only load on general tab (default tab when none specified)
330 $current_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general';
331 if ($current_tab !== 'general') {
332 return;
333 }
334
335 // Add inline JavaScript for functionality
336 wp_add_inline_script('jquery', $this->get_admin_javascript());
337 }
338
339 /**
340 * Get JavaScript for admin functionality
341 *
342 * @return string JavaScript code
343 */
344 private function get_admin_javascript()
345 {
346 $nonce = wp_create_nonce('metasync_google_index_direct_test');
347
348 return "
349 jQuery(document).ready(function($) {
350 // Integration with MetaSync's unsaved changes detection
351 function integrateWithUnsavedChangesDetection() {
352 // Monitor textarea for changes (avoid recursion)
353 $('#google_index_service_account_json').on('input change paste keyup', function(e) {
354 console.log('Google Index: Textarea changed via', e.type);
355 // Trigger MetaSync's change detection (works on both Settings and Indexation Control pages)
356 $('#metaSyncGeneralSetting, #metaSyncSeoControlsForm').trigger('change');
357 });
358
359 // Handle file upload with auto-populate and change detection
360 $('#google_index_service_account_file').on('change', function(e) {
361 var file = e.target.files[0];
362 var textarea = $('#google_index_service_account_json');
363
364 if (file && file.name.toLowerCase().endsWith('.json')) {
365 var reader = new FileReader();
366 reader.onload = function(e) {
367 try {
368 var json = JSON.parse(e.target.result);
369 // Update textarea value
370 var jsonString = JSON.stringify(json, null, 2);
371 textarea.val(jsonString);
372
373 // Create and dispatch native events to ensure proper detection
374 setTimeout(function() {
375 // Create native events
376 var inputEvent = new Event('input', { bubbles: true, cancelable: true });
377 var changeEvent = new Event('change', { bubbles: true, cancelable: true });
378
379 // Dispatch events on the actual DOM element (not jQuery)
380 textarea[0].dispatchEvent(inputEvent);
381 textarea[0].dispatchEvent(changeEvent);
382
383 // Also trigger jQuery events as backup
384 textarea.trigger('input').trigger('change');
385
386 console.log('Google Index: File upload triggered change detection');
387 }, 100);
388 } catch (err) {
389 alert('Invalid JSON file selected.');
390 }
391 };
392 reader.readAsText(file);
393 } else {
394 // File upload indicates change even if not JSON (works on both Settings and Indexation Control pages)
395 $('#metaSyncGeneralSetting, #metaSyncSeoControlsForm').trigger('change');
396 }
397 });
398 }
399
400 // Initialize integration after a small delay to ensure MetaSync is ready
401 setTimeout(integrateWithUnsavedChangesDetection, 100);
402
403 // Handle test connection button
404 $('#google-index-test-connection').on('click', function(e) {
405 e.preventDefault();
406
407 var button = $(this);
408 var resultDiv = $('#google-index-test-results');
409
410 button.prop('disabled', true).text('🔄 Testing...');
411 resultDiv.html('<div class=\"notice notice-info inline\"><p>Testing connection...</p></div>');
412
413 $.ajax({
414 url: ajaxurl,
415 type: 'POST',
416 data: {
417 action: 'metasync_google_index_direct_test',
418 nonce: '{$nonce}'
419 },
420 success: function(response) {
421 if (response.success) {
422 var results = response.data.results;
423 var html = '<div class=\"notice notice-success inline\">';
424 html += '<p><strong>�
425 Connection Test Results:</strong></p>';
426 html += '<ul>';
427
428 if (results.token_test) {
429 html += '<li><strong>Token Generation:</strong> ' + (results.token_test.success ? '�
430 Success' : '❌ Failed') + '</li>';
431 html += '<li><strong>Token Cached:</strong> ' + (results.token_test.cached ? '�
432 Yes' : '⚪ No') + '</li>';
433 }
434
435 if (results.credentials_test) {
436 html += '<li><strong>Service Account:</strong> ' + results.credentials_test.client_email + '</li>';
437 html += '<li><strong>Project ID:</strong> ' + results.credentials_test.project_id + '</li>';
438 html += '<li><strong>Private Key:</strong> ' + (results.credentials_test.has_private_key ? '�
439 Present' : '❌ Missing') + '</li>';
440 }
441
442 if (results.homepage_test) {
443 if (results.homepage_test.success) {
444 html += '<li><strong>Homepage Status:</strong> �
445 Success</li>';
446 } else {
447 html += '<li><strong>Homepage Status:</strong> ⚠️ ' + results.homepage_test.error.message + '</li>';
448 if (results.homepage_test.note) {
449 html += '<li><strong>Note:</strong> ' + results.homepage_test.note + '</li>';
450 }
451 }
452 }
453
454 html += '</ul></div>';
455 resultDiv.html(html);
456 } else {
457 resultDiv.html('<div class=\"notice notice-error inline\"><p><strong>❌ Test Failed:</strong> ' + response.data.message + '</p></div>');
458 }
459 },
460 error: function() {
461 resultDiv.html('<div class=\"notice notice-error inline\"><p><strong>❌ Connection Error:</strong> Unable to perform test.</p></div>');
462 },
463 complete: function() {
464 button.prop('disabled', false).text('🧪 Test Connection');
465 }
466 });
467 });
468
469 // Handle clear configuration button
470 $('#google-index-clear-config').on('click', function(e) {
471 e.preventDefault();
472
473 if (!confirm('Are you sure you want to clear the service account configuration?')) {
474 return;
475 }
476
477 // Create hidden input to indicate clear configuration request
478 var input = $('<input>')
479 .attr('type', 'hidden')
480 .attr('name', 'google_index_clear_config')
481 .attr('value', '1');
482
483 // Append to the correct form (Indexation Control vs General Settings) and submit via AJAX
484 var clrForm = $('#metaSyncSeoControlsForm').length ? $('#metaSyncSeoControlsForm') : $('#metaSyncGeneralSetting');
485 var clrAction = $('#metaSyncSeoControlsForm').length ? 'meta_sync_save_seo_controls' : 'meta_sync_save_settings';
486 clrForm.append(input);
487 var clrData = clrForm.serialize() + '&action=' + clrAction;
488
489 // Submit the clear request directly via AJAX, then reload
490 $.ajax({ url: ajaxurl || metaSync.ajax_url, type: 'POST', data: clrData, complete: function() { window.location.reload(); } });
491 });
492
493 });
494 ";
495 }
496 }
497
498 // Initialize admin functionality
499 if (is_admin()) {
500 new Google_Index_Admin();
501 }
502