PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.25
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.25
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.25, at google-index/class-google-index-admin.php

539 lines 22.0 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 if (!$this->authorize_settings_request()) {
130 return;
131 }
132
133 // Clearing does not need to load or inspect credential contents.
134 if (isset($_POST['google_index_clear_config'])) {
135 delete_option('google_index_service_account');
136 $this->add_settings_notice('Service account configuration cleared successfully!', 'success');
137 return;
138 }
139
140 // Load Google Index functionality
141 if (!function_exists('google_index_save_service_account')) {
142 if (file_exists(plugin_dir_path(__FILE__) . 'google-index-init.php')) {
143 require_once plugin_dir_path(__FILE__) . 'google-index-init.php';
144 } else {
145 error_log('MetaSync Google Index: google-index-init.php not found at ' . plugin_dir_path(__FILE__) . 'google-index-init.php');
146 return;
147 }
148 }
149
150 $service_account_json = '';
151
152 // Get JSON from textarea
153 if (isset($_POST['google_index_service_account_json'])) {
154 $service_account_json = sanitize_textarea_field(wp_unslash($_POST['google_index_service_account_json']));
155 }
156
157 // Override with file upload if provided
158 if (isset($_FILES['google_index_service_account_file']) &&
159 !empty($_FILES['google_index_service_account_file']['tmp_name']) &&
160 file_exists($_FILES['google_index_service_account_file']['tmp_name'])) {
161
162 $uploaded_json = file_get_contents($_FILES['google_index_service_account_file']['tmp_name']);
163 if ($uploaded_json !== false) {
164 $service_account_json = $uploaded_json; // Removed unnecessary wp_unslash
165 }
166 }
167
168 // Process service account JSON if provided
169 if (!empty($service_account_json) && trim($service_account_json) !== '') {
170
171 // Skip if it contains redacted private key (user didn't paste new JSON)
172 if (strpos($service_account_json, '-----REDACTED-----') !== false) {
173 return; // Don't process redacted display text
174 }
175
176 // Decode and validate JSON
177 $service_account_data = json_decode($service_account_json, true);
178
179 if (json_last_error() !== JSON_ERROR_NONE) {
180 // JSON parsing error - send immediate error response
181 $json_error_message = 'Invalid JSON format';
182 switch (json_last_error()) {
183 case JSON_ERROR_SYNTAX:
184 $json_error_message .= ' - Syntax error in JSON';
185 break;
186 case JSON_ERROR_UTF8:
187 $json_error_message .= ' - Invalid UTF-8 encoding';
188 break;
189 default:
190 $json_error_message .= ' - ' . json_last_error_msg();
191 break;
192 }
193
194 wp_send_json_error([
195 'errors' => ['Google Index: ' . $json_error_message . '. Please check your service account JSON.']
196 ]);
197 }
198
199 if (!is_array($service_account_data)) {
200 wp_send_json_error([
201 'errors' => ['Google Index: JSON must be an object/array. Please check your service account JSON format.']
202 ]);
203 }
204
205 // Validate required fields
206 $required_fields = ['type', 'project_id', 'private_key_id', 'private_key', 'client_email', 'client_id', 'auth_uri', 'token_uri'];
207 $missing_fields = [];
208
209 foreach ($required_fields as $field) {
210 if (!isset($service_account_data[$field]) || empty($service_account_data[$field])) {
211 $missing_fields[] = $field;
212 }
213 }
214
215 if (!empty($missing_fields)) {
216 wp_send_json_error([
217 'errors' => ['Google Index: Missing required fields - ' . implode(', ', $missing_fields) . '. Please ensure you have a complete service account JSON.']
218 ]);
219 }
220
221 // Save using Google Index function
222 $result = google_index_save_service_account($service_account_data);
223
224 if ($result) {
225 $this->add_settings_notice('Google Index service account configured successfully!', 'success');
226 } else {
227 // Send immediate error response using MetaSync's expected format
228 wp_send_json_error([
229 'errors' => ['Google Index: Failed to save service account configuration. Please try again or check your JSON format.']
230 ]);
231 }
232 }
233 }
234
235 /**
236 * Add a settings notice to be displayed after AJAX save
237 *
238 * @param string $message Notice message
239 * @param string $type Notice type: 'success', 'error', 'warning', 'info'
240 */
241 private function add_settings_notice($message, $type = 'info')
242 {
243 $notices = get_transient('google_index_admin_notices') ?: [];
244 $notices[] = [
245 'message' => $message,
246 'type' => $type,
247 'time' => time()
248 ];
249
250 // Store notices for 30 seconds (enough time for page redirect)
251 set_transient('google_index_admin_notices', $notices, 30);
252 }
253
254 /**
255 * Display admin notices for Google Index settings
256 */
257 public function display_admin_notices()
258 {
259 // Only show notices on MetaSync settings pages
260 $page_slug = $this->get_metasync_page_slug();
261 if ( ! function_exists( 'get_current_screen' ) ) {
262 return;
263 }
264 $current_screen = get_current_screen();
265
266 if (!$current_screen || strpos($current_screen->id, $page_slug) === false) {
267 return;
268 }
269
270 // Get and display notices
271 $notices = get_transient('google_index_admin_notices');
272 if (!empty($notices)) {
273 foreach ($notices as $notice) {
274 $class = 'notice notice-' . esc_attr($notice['type']) . ' is-dismissible';
275 echo '<div class="' . $class . '">';
276 echo '<p><strong>Google Index:</strong> ' . esc_html($notice['message']) . '</p>';
277 echo '</div>';
278 }
279
280 // Clear notices after displaying
281 delete_transient('google_index_admin_notices');
282 }
283 }
284
285 /**
286 * AJAX handler for testing connection
287 */
288 public function ajax_test_connection()
289 {
290 // Check nonce and permissions
291 if (!current_user_can('manage_options') ||
292 !wp_verify_nonce(wp_unslash($_POST['nonce'] ?? ''), 'metasync_google_index_direct_test')) {
293 wp_die('Security check failed');
294 }
295
296 // Load Google Index functionality
297 if (!function_exists('google_index_direct')) {
298 if (file_exists(plugin_dir_path(__FILE__) . 'google-index-init.php')) {
299 require_once plugin_dir_path(__FILE__) . 'google-index-init.php';
300 } else {
301 error_log('MetaSync Google Index: google-index-init.php not found at ' . plugin_dir_path(__FILE__) . 'google-index-init.php');
302 return;
303 }
304 }
305
306 try {
307 // Test the connection
308 $google_index = google_index_direct();
309 $test_results = $google_index->test_connection();
310
311 wp_send_json_success([
312 'message' => 'Connection test completed',
313 'results' => $test_results
314 ]);
315
316 } catch (Exception $e) {
317 wp_send_json_error([
318 'message' => 'Test failed: ' . $e->getMessage()
319 ]);
320 }
321 }
322
323 /**
324 * Authorize the shared MetaSync settings AJAX action before secret handling.
325 */
326 private function authorize_settings_request()
327 {
328 if (!current_user_can('manage_options')) {
329 wp_send_json_error(array('message' => 'Insufficient permissions.'), 403);
330 }
331
332 $action = isset($_POST['action']) ? sanitize_key(wp_unslash($_POST['action'])) : '';
333 $nonce_field = '';
334 $nonce_action = '';
335
336 if ($action === 'meta_sync_save_settings') {
337 $nonce_field = 'meta_sync_nonce';
338 $nonce_action = 'meta_sync_general_setting_nonce';
339 } elseif ($action === 'meta_sync_save_seo_controls') {
340 $nonce_field = 'meta_sync_seo_controls_nonce';
341 $nonce_action = 'meta_sync_seo_controls_nonce';
342 }
343
344 if (
345 $nonce_field === ''
346 || !isset($_POST[$nonce_field])
347 || !wp_verify_nonce(wp_unslash($_POST[$nonce_field]), $nonce_action)
348 ) {
349 wp_send_json_error(array('message' => 'Invalid nonce'), 403);
350 }
351
352 return true;
353 }
354
355 /**
356 * Enqueue admin scripts and styles
357 */
358 public function enqueue_admin_scripts($hook)
359 {
360 // Only load on MetaSync settings pages
361 $page_slug = $this->get_metasync_page_slug();
362 if (strpos($hook, $page_slug) === false) {
363 return;
364 }
365
366 // Only load on general tab (default tab when none specified)
367 $current_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general';
368 if ($current_tab !== 'general') {
369 return;
370 }
371
372 // Add inline JavaScript for functionality
373 wp_add_inline_script('jquery', $this->get_admin_javascript());
374 }
375
376 /**
377 * Get JavaScript for admin functionality
378 *
379 * @return string JavaScript code
380 */
381 private function get_admin_javascript()
382 {
383 $nonce = wp_create_nonce('metasync_google_index_direct_test');
384
385 return "
386 jQuery(document).ready(function($) {
387 // Integration with MetaSync's unsaved changes detection
388 function integrateWithUnsavedChangesDetection() {
389 // Monitor textarea for changes (avoid recursion)
390 $('#google_index_service_account_json').on('input change paste keyup', function(e) {
391 console.log('Google Index: Textarea changed via', e.type);
392 // Trigger MetaSync's change detection (works on both Settings and Indexation Control pages)
393 $('#metaSyncGeneralSetting, #metaSyncSeoControlsForm').trigger('change');
394 });
395
396 // Handle file upload with auto-populate and change detection
397 $('#google_index_service_account_file').on('change', function(e) {
398 var file = e.target.files[0];
399 var textarea = $('#google_index_service_account_json');
400
401 if (file && file.name.toLowerCase().endsWith('.json')) {
402 var reader = new FileReader();
403 reader.onload = function(e) {
404 try {
405 var json = JSON.parse(e.target.result);
406 // Update textarea value
407 var jsonString = JSON.stringify(json, null, 2);
408 textarea.val(jsonString);
409
410 // Create and dispatch native events to ensure proper detection
411 setTimeout(function() {
412 // Create native events
413 var inputEvent = new Event('input', { bubbles: true, cancelable: true });
414 var changeEvent = new Event('change', { bubbles: true, cancelable: true });
415
416 // Dispatch events on the actual DOM element (not jQuery)
417 textarea[0].dispatchEvent(inputEvent);
418 textarea[0].dispatchEvent(changeEvent);
419
420 // Also trigger jQuery events as backup
421 textarea.trigger('input').trigger('change');
422
423 console.log('Google Index: File upload triggered change detection');
424 }, 100);
425 } catch (err) {
426 alert('Invalid JSON file selected.');
427 }
428 };
429 reader.readAsText(file);
430 } else {
431 // File upload indicates change even if not JSON (works on both Settings and Indexation Control pages)
432 $('#metaSyncGeneralSetting, #metaSyncSeoControlsForm').trigger('change');
433 }
434 });
435 }
436
437 // Initialize integration after a small delay to ensure MetaSync is ready
438 setTimeout(integrateWithUnsavedChangesDetection, 100);
439
440 // Handle test connection button
441 $('#google-index-test-connection').on('click', function(e) {
442 e.preventDefault();
443
444 var button = $(this);
445 var resultDiv = $('#google-index-test-results');
446
447 button.prop('disabled', true).text('🔄 Testing...');
448 resultDiv.html('<div class=\"notice notice-info inline\"><p>Testing connection...</p></div>');
449
450 $.ajax({
451 url: ajaxurl,
452 type: 'POST',
453 data: {
454 action: 'metasync_google_index_direct_test',
455 nonce: '{$nonce}'
456 },
457 success: function(response) {
458 if (response.success) {
459 var results = response.data.results;
460 var html = '<div class=\"notice notice-success inline\">';
461 html += '<p><strong>�
462 Connection Test Results:</strong></p>';
463 html += '<ul>';
464
465 if (results.token_test) {
466 html += '<li><strong>Token Generation:</strong> ' + (results.token_test.success ? '�
467 Success' : '❌ Failed') + '</li>';
468 html += '<li><strong>Token Cached:</strong> ' + (results.token_test.cached ? '�
469 Yes' : '⚪ No') + '</li>';
470 }
471
472 if (results.credentials_test) {
473 html += '<li><strong>Service Account:</strong> ' + results.credentials_test.client_email + '</li>';
474 html += '<li><strong>Project ID:</strong> ' + results.credentials_test.project_id + '</li>';
475 html += '<li><strong>Private Key:</strong> ' + (results.credentials_test.has_private_key ? '�
476 Present' : '❌ Missing') + '</li>';
477 }
478
479 if (results.homepage_test) {
480 if (results.homepage_test.success) {
481 html += '<li><strong>Homepage Status:</strong> �
482 Success</li>';
483 } else {
484 html += '<li><strong>Homepage Status:</strong> ⚠️ ' + results.homepage_test.error.message + '</li>';
485 if (results.homepage_test.note) {
486 html += '<li><strong>Note:</strong> ' + results.homepage_test.note + '</li>';
487 }
488 }
489 }
490
491 html += '</ul></div>';
492 resultDiv.html(html);
493 } else {
494 resultDiv.html('<div class=\"notice notice-error inline\"><p><strong>❌ Test Failed:</strong> ' + response.data.message + '</p></div>');
495 }
496 },
497 error: function() {
498 resultDiv.html('<div class=\"notice notice-error inline\"><p><strong>❌ Connection Error:</strong> Unable to perform test.</p></div>');
499 },
500 complete: function() {
501 button.prop('disabled', false).text('🧪 Test Connection');
502 }
503 });
504 });
505
506 // Handle clear configuration button
507 $('#google-index-clear-config').on('click', function(e) {
508 e.preventDefault();
509
510 if (!confirm('Are you sure you want to clear the service account configuration?')) {
511 return;
512 }
513
514 // Create hidden input to indicate clear configuration request
515 var input = $('<input>')
516 .attr('type', 'hidden')
517 .attr('name', 'google_index_clear_config')
518 .attr('value', '1');
519
520 // Append to the correct form (Indexation Control vs General Settings) and submit via AJAX
521 var clrForm = $('#metaSyncSeoControlsForm').length ? $('#metaSyncSeoControlsForm') : $('#metaSyncGeneralSetting');
522 var clrAction = $('#metaSyncSeoControlsForm').length ? 'meta_sync_save_seo_controls' : 'meta_sync_save_settings';
523 clrForm.append(input);
524 var clrData = clrForm.serialize() + '&action=' + clrAction;
525
526 // Submit the clear request directly via AJAX, then reload
527 $.ajax({ url: ajaxurl || metaSync.ajax_url, type: 'POST', data: clrData, complete: function() { window.location.reload(); } });
528 });
529
530 });
531 ";
532 }
533 }
534
535 // Initialize admin functionality
536 if (is_admin()) {
537 new Google_Index_Admin();
538 }
539