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

496 lines 20.4 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 $current_screen = get_current_screen();
257
258 if (!$current_screen || strpos($current_screen->id, $page_slug) === false) {
259 return;
260 }
261
262 // Get and display notices
263 $notices = get_transient('google_index_admin_notices');
264 if (!empty($notices)) {
265 foreach ($notices as $notice) {
266 $class = 'notice notice-' . esc_attr($notice['type']) . ' is-dismissible';
267 echo '<div class="' . $class . '">';
268 echo '<p><strong>Google Index:</strong> ' . esc_html($notice['message']) . '</p>';
269 echo '</div>';
270 }
271
272 // Clear notices after displaying
273 delete_transient('google_index_admin_notices');
274 }
275 }
276
277 /**
278 * AJAX handler for testing connection
279 */
280 public function ajax_test_connection()
281 {
282 // Check nonce and permissions
283 if (!wp_verify_nonce($_POST['nonce'] ?? '', 'metasync_google_index_direct_test') ||
284 !Metasync::current_user_has_plugin_access()) {
285 wp_die('Security check failed');
286 }
287
288 // Load Google Index functionality
289 if (!function_exists('google_index_direct')) {
290 if (file_exists(plugin_dir_path(__FILE__) . 'google-index-init.php')) {
291 require_once plugin_dir_path(__FILE__) . 'google-index-init.php';
292 } else {
293 error_log('MetaSync Google Index: google-index-init.php not found at ' . plugin_dir_path(__FILE__) . 'google-index-init.php');
294 return;
295 }
296 }
297
298 try {
299 // Test the connection
300 $google_index = google_index_direct();
301 $test_results = $google_index->test_connection();
302
303 wp_send_json_success([
304 'message' => 'Connection test completed',
305 'results' => $test_results
306 ]);
307
308 } catch (Exception $e) {
309 wp_send_json_error([
310 'message' => 'Test failed: ' . $e->getMessage()
311 ]);
312 }
313 }
314
315 /**
316 * Enqueue admin scripts and styles
317 */
318 public function enqueue_admin_scripts($hook)
319 {
320 // Only load on MetaSync settings pages
321 $page_slug = $this->get_metasync_page_slug();
322 if (strpos($hook, $page_slug) === false) {
323 return;
324 }
325
326 // Only load on general tab (default tab when none specified)
327 $current_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general';
328 if ($current_tab !== 'general') {
329 return;
330 }
331
332 // Add inline JavaScript for functionality
333 wp_add_inline_script('jquery', $this->get_admin_javascript());
334 }
335
336 /**
337 * Get JavaScript for admin functionality
338 *
339 * @return string JavaScript code
340 */
341 private function get_admin_javascript()
342 {
343 $nonce = wp_create_nonce('metasync_google_index_direct_test');
344
345 return "
346 jQuery(document).ready(function($) {
347 // Integration with MetaSync's unsaved changes detection
348 function integrateWithUnsavedChangesDetection() {
349 // Monitor textarea for changes (avoid recursion)
350 $('#google_index_service_account_json').on('input change paste keyup', function(e) {
351 console.log('Google Index: Textarea changed via', e.type);
352 // Trigger MetaSync's change detection (works on both Settings and Indexation Control pages)
353 $('#metaSyncGeneralSetting, #metaSyncSeoControlsForm').trigger('change');
354 });
355
356 // Handle file upload with auto-populate and change detection
357 $('#google_index_service_account_file').on('change', function(e) {
358 var file = e.target.files[0];
359 var textarea = $('#google_index_service_account_json');
360
361 if (file && file.type === 'application/json') {
362 var reader = new FileReader();
363 reader.onload = function(e) {
364 try {
365 var json = JSON.parse(e.target.result);
366 // Update textarea value
367 var jsonString = JSON.stringify(json, null, 2);
368 textarea.val(jsonString);
369
370 // Create and dispatch native events to ensure proper detection
371 setTimeout(function() {
372 // Create native events
373 var inputEvent = new Event('input', { bubbles: true, cancelable: true });
374 var changeEvent = new Event('change', { bubbles: true, cancelable: true });
375
376 // Dispatch events on the actual DOM element (not jQuery)
377 textarea[0].dispatchEvent(inputEvent);
378 textarea[0].dispatchEvent(changeEvent);
379
380 // Also trigger jQuery events as backup
381 textarea.trigger('input').trigger('change');
382
383 console.log('Google Index: File upload triggered change detection');
384 }, 100);
385 } catch (err) {
386 alert('Invalid JSON file selected.');
387 }
388 };
389 reader.readAsText(file);
390 } else {
391 // File upload indicates change even if not JSON (works on both Settings and Indexation Control pages)
392 $('#metaSyncGeneralSetting, #metaSyncSeoControlsForm').trigger('change');
393 }
394 });
395 }
396
397 // Initialize integration after a small delay to ensure MetaSync is ready
398 setTimeout(integrateWithUnsavedChangesDetection, 100);
399
400 // Handle test connection button
401 $('#google-index-test-connection').on('click', function(e) {
402 e.preventDefault();
403
404 var button = $(this);
405 var resultDiv = $('#google-index-test-results');
406
407 button.prop('disabled', true).text('🔄 Testing...');
408 resultDiv.html('<div class=\"notice notice-info inline\"><p>Testing connection...</p></div>');
409
410 $.ajax({
411 url: ajaxurl,
412 type: 'POST',
413 data: {
414 action: 'metasync_google_index_direct_test',
415 nonce: '{$nonce}'
416 },
417 success: function(response) {
418 if (response.success) {
419 var results = response.data.results;
420 var html = '<div class=\"notice notice-success inline\">';
421 html += '<p><strong>�
422 Connection Test Results:</strong></p>';
423 html += '<ul>';
424
425 if (results.token_test) {
426 html += '<li><strong>Token Generation:</strong> ' + (results.token_test.success ? '�
427 Success' : '❌ Failed') + '</li>';
428 html += '<li><strong>Token Cached:</strong> ' + (results.token_test.cached ? '�
429 Yes' : '⚪ No') + '</li>';
430 }
431
432 if (results.credentials_test) {
433 html += '<li><strong>Service Account:</strong> ' + results.credentials_test.client_email + '</li>';
434 html += '<li><strong>Project ID:</strong> ' + results.credentials_test.project_id + '</li>';
435 html += '<li><strong>Private Key:</strong> ' + (results.credentials_test.has_private_key ? '�
436 Present' : '❌ Missing') + '</li>';
437 }
438
439 if (results.homepage_test) {
440 if (results.homepage_test.success) {
441 html += '<li><strong>Homepage Status:</strong> �
442 Success</li>';
443 } else {
444 html += '<li><strong>Homepage Status:</strong> ⚠️ ' + results.homepage_test.error.message + '</li>';
445 if (results.homepage_test.note) {
446 html += '<li><strong>Note:</strong> ' + results.homepage_test.note + '</li>';
447 }
448 }
449 }
450
451 html += '</ul></div>';
452 resultDiv.html(html);
453 } else {
454 resultDiv.html('<div class=\"notice notice-error inline\"><p><strong>❌ Test Failed:</strong> ' + response.data.message + '</p></div>');
455 }
456 },
457 error: function() {
458 resultDiv.html('<div class=\"notice notice-error inline\"><p><strong>❌ Connection Error:</strong> Unable to perform test.</p></div>');
459 },
460 complete: function() {
461 button.prop('disabled', false).text('🧪 Test Connection');
462 }
463 });
464 });
465
466 // Handle clear configuration button
467 $('#google-index-clear-config').on('click', function(e) {
468 e.preventDefault();
469
470 if (!confirm('Are you sure you want to clear the service account configuration?')) {
471 return;
472 }
473
474 // Create hidden input to indicate clear configuration request
475 var input = $('<input>')
476 .attr('type', 'hidden')
477 .attr('name', 'google_index_clear_config')
478 .attr('value', '1');
479
480 // Append to form
481 $('#metaSyncGeneralSetting').append(input);
482
483 // Trigger the form's existing AJAX submission handler
484 $('#metaSyncGeneralSetting').trigger('submit');
485 });
486
487 });
488 ";
489 }
490 }
491
492 // Initialize admin functionality
493 if (is_admin()) {
494 new Google_Index_Admin();
495 }
496