core = $core; // Registrar ajustes add_action('admin_menu', [$this, 'add_settings_page']); add_action('admin_init', [$this, 'register_settings']); // Añadir enlaces en la página de plugins add_filter('plugin_action_links_' . plugin_basename(ACCESSITRANS_PATH . 'accessitrans-aria.php'), [$this, 'add_action_links']); // Encolar scripts y estilos para la interfaz de administración add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']); // Registrar funciones AJAX add_action('wp_ajax_accessitrans_toggle_scan', [$this, 'toggle_scan_callback']); add_action('wp_ajax_accessitrans_diagnostics', [$this, 'diagnostics_callback']); add_action('wp_ajax_accessitrans_force_refresh', [$this, 'force_refresh_callback']); add_action('wp_ajax_accessitrans_check_health', [$this, 'check_health_callback']); } /** * Añade página de ajustes al menú de administración */ public function add_settings_page() { add_submenu_page( 'options-general.php', __('Settings for', 'accessitrans-aria') . ' AccessiTrans', __('AccessiTrans', 'accessitrans-aria'), 'manage_options', 'accessitrans-aria', [$this, 'settings_page'] ); } /** * Encola scripts y estilos para la interfaz de administración * * @param string $hook_suffix La página de administración actual */ public function enqueue_admin_assets($hook_suffix) { // Solo cargar en la página de configuración del plugin if ('settings_page_accessitrans-aria' !== $hook_suffix) { return; } // Encolar estilos CSS wp_enqueue_style( 'accessitrans-admin-styles', ACCESSITRANS_URL . 'assets/css/admin-styles.css', [], ACCESSITRANS_VERSION ); // Encolar scripts JS wp_enqueue_script( 'accessitrans-admin-scripts', ACCESSITRANS_URL . 'assets/js/admin-scripts.js', ['jquery'], ACCESSITRANS_VERSION, true // Cargar en el footer para mejor rendimiento ); // Pasar variables a JavaScript wp_localize_script( 'accessitrans-admin-scripts', 'accessitransAdmin', [ 'ajaxurl' => admin_url('admin-ajax.php'), 'nonces' => [ 'toggle_scan' => wp_create_nonce('accessitrans-toggle-scan'), 'diagnostics' => wp_create_nonce('accessitrans-diagnostics'), 'force_refresh' => wp_create_nonce('accessitrans-force-refresh'), 'check_health' => wp_create_nonce('accessitrans-check-health') ], 'strings' => [ 'saving' => __('Saving...', 'accessitrans-aria'), 'saveError' => __('Error saving settings.', 'accessitrans-aria'), 'processing' => __('Processing...', 'accessitrans-aria'), 'requestError' => __('Error processing request.', 'accessitrans-aria'), 'noString' => __('Please enter a string to verify.', 'accessitrans-aria'), 'analyzing' => __('Analyzing...', 'accessitrans-aria'), 'analyzingMessage' => __('Analyzing string...', 'accessitrans-aria'), 'errorNoString' => __('Error: No string has been entered to verify.', 'accessitrans-aria'), 'scanEnabledMessage' => __('Scanning enabled. Capture methods are available.', 'accessitrans-aria'), 'scanDisabledMessage' => __('Scanning disabled. Capture methods are disabled.', 'accessitrans-aria'), 'diagnosticResults' => __('Diagnostic results:', 'accessitrans-aria'), 'originalText' => __('Original text:', 'accessitrans-aria'), 'language' => __('Language:', 'accessitrans-aria'), 'languageCorrect' => __('✓ Correct language', 'accessitrans-aria'), 'languageNotPrimary' => __('✗ Not primary language', 'accessitrans-aria'), // Añadir todas las cadenas que faltan 'foundInWPML' => __('✓ Found in WPML', 'accessitrans-aria'), 'notFoundInWPML' => __('✗ Not found in WPML', 'accessitrans-aria'), 'registeredFormats' => __('Registered formats:', 'accessitrans-aria'), 'hasTranslations' => __('✓ Has translations', 'accessitrans-aria'), 'noTranslations' => __('✗ No translations', 'accessitrans-aria'), 'availableTranslations' => __('Available translations:', 'accessitrans-aria'), 'noCurrentLanguageTranslation' => __('✗ No translation for current language', 'accessitrans-aria'), 'recommendedActionTranslate' => __('Recommended action: Translate this string to the current language in WPML → String Translation.', 'accessitrans-aria'), 'translateAction' => __('Recommended action: Translate this string in WPML → String Translation.', 'accessitrans-aria'), 'notPrimaryLanguageWarning' => __('⚠️ You are browsing in a language that is not the primary one. Switch to the primary language to register strings.', 'accessitrans-aria'), 'recommendedActionNavigate' => __('Recommended action: Browse your site with capture methods enabled or edit the element in Elementor to register this string.', 'accessitrans-aria'), 'troubleshootingTips' => __('Troubleshooting tips:', 'accessitrans-aria'), 'tip1' => __('Make sure to browse in the site\'s primary language when capturing strings.', 'accessitrans-aria'), 'tip2' => __('Try using "Force Update" to clear all caches.', 'accessitrans-aria'), 'tip3' => __('If you\'ve changed the text in the original language, you\'ll need to translate it again in WPML.', 'accessitrans-aria'), 'technicalInfo' => __('Advanced technical information', 'accessitrans-aria'), 'analysisComplete' => __('Analysis completed. Results were found for the string', 'accessitrans-aria'), 'analysisError' => __('Error in analysis:', 'accessitrans-aria'), 'connectionError' => __('Error performing the analysis. Could not contact the server.', 'accessitrans-aria'), // Cadenas para verificación de salud 'verifying' => __('Verifying...', 'accessitrans-aria'), 'verifyingStatus' => __('Verifying system status...', 'accessitrans-aria'), 'systemStatus' => __('System status:', 'accessitrans-aria'), 'active' => __('Active', 'accessitrans-aria'), 'inactive' => __('Inactive', 'accessitrans-aria'), 'wpml' => __('WPML:', 'accessitrans-aria'), 'elementor' => __('Elementor:', 'accessitrans-aria'), 'registeredStrings' => __('Registered strings:', 'accessitrans-aria'), 'primaryLanguage' => __('Primary language:', 'accessitrans-aria'), 'currentLanguage' => __('Current language:', 'accessitrans-aria'), 'availableLanguages' => __('Available languages:', 'accessitrans-aria'), 'currentConfiguration' => __('Current configuration:', 'accessitrans-aria'), 'recommendations' => __('Recommendations:', 'accessitrans-aria'), 'noStringsRegistered' => __('• No registered strings. Browse your site with capture methods enabled.', 'accessitrans-aria'), 'stringsNoTranslations' => __('• There are registered strings but no translations. Visit WPML → String Translation to translate them.', 'accessitrans-aria'), 'navigatingNonPrimary' => __('• You are browsing in a language that is not the primary one. If you want to register new strings, switch to the primary language.', 'accessitrans-aria'), 'serverDate' => __('Server date:', 'accessitrans-aria'), 'pluginVersion' => __('Plugin version:', 'accessitrans-aria'), 'verificationComplete' => __('Verification completed. Found', 'accessitrans-aria'), 'registeredStringsText' => __('registered strings and', 'accessitrans-aria'), 'translationsText' => __('translations.', 'accessitrans-aria'), 'verificationError' => __('Error in verification:', 'accessitrans-aria'), 'connectionVerificationError' => __('Error performing the verification. Could not contact the server.', 'accessitrans-aria') ] ] ); } /** * Sanitiza las opciones del plugin * * @param array $input Las opciones a sanitizar * @return array Las opciones sanitizadas */ public function sanitize_plugin_options($input) { if (!is_array($input)) { return array(); } // Crear array para las opciones sanitizadas $sanitized_options = array(); // Lista de opciones esperadas tipo checkbox $checkbox_options = array( 'captura_total', 'captura_elementor', 'procesar_templates', 'procesar_elementos', 'modo_debug', 'solo_admin', 'captura_en_idioma_principal', 'permitir_escaneo' ); // Sanitizar checkbox options (true/false) foreach ($checkbox_options as $option) { $sanitized_options[$option] = isset($input[$option]) && filter_var($input[$option], FILTER_VALIDATE_BOOLEAN); } return $sanitized_options; } /** * Registra los ajustes del plugin */ public function register_settings() { // Registrar la opción principal del plugin register_setting( 'accessitrans_aria', // Option group 'accessitrans_aria_options', // Option name array( 'type' => 'array', 'sanitize_callback' => array($this, 'sanitize_plugin_options'), ) ); // Establecer valores por defecto si la opción no existe if (!get_option('accessitrans_aria_options')) { update_option('accessitrans_aria_options', array( 'captura_total' => true, 'captura_elementor' => true, 'procesar_templates' => true, 'procesar_elementos' => true, 'modo_debug' => false, 'solo_admin' => true, 'captura_en_idioma_principal' => true, 'permitir_escaneo' => true )); } // Esta sección no se usa directamente en el render, está configurada // solo para mantener compatibilidad con la API de WordPress // Nueva sección general para el interruptor principal add_settings_section( 'accessitrans_aria_general', __('General settings', 'accessitrans-aria'), array($this, 'section_general_callback'), 'accessitrans-aria' ); // Nuevo campo para el interruptor principal de escaneo add_settings_field( 'permitir_escaneo', __('Allow scanning for new strings', 'accessitrans-aria'), [$this, 'toggle_callback'], 'accessitrans-aria', 'accessitrans_aria_general', [ 'label_for' => 'permitir_escaneo', 'descripcion' => __('Activates the capture of new ARIA strings. Disable it after capturing all strings to improve performance.', 'accessitrans-aria') ] ); add_settings_section( 'accessitrans_aria_main', __('Capture methods settings', 'accessitrans-aria'), [$this, 'section_callback'], 'accessitrans-aria' ); add_settings_field( 'captura_total', __('Full HTML capture', 'accessitrans-aria'), [$this, 'checkbox_callback'], 'accessitrans-aria', 'accessitrans_aria_main', ['label_for' => 'captura_total', 'descripcion' => __('Captures all HTML of the page. Highly effective but may affect performance.', 'accessitrans-aria')] ); add_settings_field( 'captura_elementor', __('Elementor content filter', 'accessitrans-aria'), [$this, 'checkbox_callback'], 'accessitrans-aria', 'accessitrans_aria_main', ['label_for' => 'captura_elementor', 'descripcion' => __('Processes content generated by Elementor.', 'accessitrans-aria')] ); add_settings_field( 'procesar_templates', __('Process Elementor templates', 'accessitrans-aria'), [$this, 'checkbox_callback'], 'accessitrans-aria', 'accessitrans_aria_main', ['label_for' => 'procesar_templates', 'descripcion' => __('Processes Elementor template data.', 'accessitrans-aria')] ); add_settings_field( 'procesar_elementos', __('Process individual elements', 'accessitrans-aria'), [$this, 'checkbox_callback'], 'accessitrans-aria', 'accessitrans_aria_main', ['label_for' => 'procesar_elementos', 'descripcion' => __('Processes each Elementor widget and element individually.', 'accessitrans-aria')] ); add_settings_section( 'accessitrans_aria_advanced', __('Advanced settings', 'accessitrans-aria'), [$this, 'section_advanced_callback'], 'accessitrans-aria' ); add_settings_field( 'modo_debug', __('Debug mode', 'accessitrans-aria'), [$this, 'checkbox_callback'], 'accessitrans-aria', 'accessitrans_aria_advanced', ['label_for' => 'modo_debug', 'descripcion' => __('Enables detailed event logging. Stored in uploads/accessitrans-logs.', 'accessitrans-aria')] ); add_settings_field( 'solo_admin', __('Capture for admins only', 'accessitrans-aria'), [$this, 'checkbox_callback'], 'accessitrans-aria', 'accessitrans_aria_advanced', ['label_for' => 'solo_admin', 'descripcion' => __('Only processes full capture when an admin is logged in.', 'accessitrans-aria')] ); add_settings_field( 'captura_en_idioma_principal', __('Capture in main language only', 'accessitrans-aria'), [$this, 'checkbox_callback'], 'accessitrans-aria', 'accessitrans_aria_advanced', ['label_for' => 'captura_en_idioma_principal', 'descripcion' => __('Only captures strings when browsing in the default language. Prevents duplicates.', 'accessitrans-aria')] ); } /** * Callback para la acción AJAX de activar/desactivar escaneo */ public function toggle_scan_callback() { // Verificar nonce check_ajax_referer('accessitrans-toggle-scan', 'nonce'); // Verificar permisos if (!current_user_can('manage_options')) { wp_send_json_error(__('You don\'t have permission to perform this action.', 'accessitrans-aria')); return; } $enabled = isset($_POST['enabled']) ? absint($_POST['enabled']) === 1 : false; // Obtener opciones actuales $options = get_option('accessitrans_aria_options', []); // Actualizar la opción de escaneo $options['permitir_escaneo'] = $enabled; // Guardar opciones update_option('accessitrans_aria_options', $options); // Actualizar opciones en el objeto core $this->core->options = $options; // Mensaje para el usuario if ($enabled) { wp_send_json_success(__('String scanning enabled. Changes have been saved.', 'accessitrans-aria')); } else { wp_send_json_success(__('String scanning disabled. Changes have been saved.', 'accessitrans-aria')); } } /** * Callback para diagnóstico de cadenas */ public function diagnostics_callback() { // Verificar nonce check_ajax_referer('accessitrans-diagnostics', 'nonce'); // Verificar permisos if (!current_user_can('manage_options')) { wp_send_json_error(esc_html__('You don\'t have permission to perform this action.', 'accessitrans-aria')); return; } // Obtener la cadena a verificar - sanitizar y deseslashear $string = isset($_POST['string']) ? sanitize_text_field(wp_unslash($_POST['string'])) : ''; if (empty($string)) { wp_send_json_error(esc_html__('No string was provided for verification.', 'accessitrans-aria')); return; } // IMPORTANTE: Establecer temporalmente permitir_escaneo a false para evitar registros $original_scan_state = isset($this->core->options['permitir_escaneo']) ? $this->core->options['permitir_escaneo'] : false; $this->core->options['permitir_escaneo'] = false; // Obtener resultados de diagnóstico sin registrar nuevas cadenas $results = $this->core->diagnostics->diagnose_translation_improved($string); // Restaurar estado original $this->core->options['permitir_escaneo'] = $original_scan_state; wp_send_json_success($results); } /** * Callback para forzar actualización de traducciones */ public function force_refresh_callback() { // Verificar nonce check_ajax_referer('accessitrans-force-refresh', 'nonce'); // Verificar permisos if (!current_user_can('manage_options')) { wp_send_json_error(esc_html__('You don\'t have permission to perform this action.', 'accessitrans-aria')); return; } // IMPORTANTE: Establecer temporalmente permitir_escaneo a false $original_scan_state = isset($this->core->options['permitir_escaneo']) ? $this->core->options['permitir_escaneo'] : false; $this->core->options['permitir_escaneo'] = false; // Limpiar caché de traducciones delete_option('accessitrans_translation_cache'); if ($this->core->translator) { $this->core->translator->clear_cache(); } // Restaurar estado original $this->core->options['permitir_escaneo'] = $original_scan_state; wp_send_json_success(esc_html__('Translation cache successfully updated.', 'accessitrans-aria')); } /** * Callback para verificación de salud del sistema */ public function check_health_callback() { // Verificar nonce check_ajax_referer('accessitrans-check-health', 'nonce'); // Verificar permisos if (!current_user_can('manage_options')) { wp_send_json_error(esc_html__('You don\'t have permission to perform this action.', 'accessitrans-aria')); return; } // IMPORTANTE: Establecer temporalmente permitir_escaneo a false $original_scan_state = isset($this->core->options['permitir_escaneo']) ? $this->core->options['permitir_escaneo'] : false; $this->core->options['permitir_escaneo'] = false; // Obtener estado del sistema $health_data = $this->core->diagnostics->check_health(); // Restaurar estado original $this->core->options['permitir_escaneo'] = $original_scan_state; wp_send_json_success($health_data); } /** * Callback para la sección general */ public function section_general_callback() { echo '
' . esc_html__('General plugin settings.', 'accessitrans-aria') . '
'; } /** * Callback para la sección principal de ajustes */ public function section_callback() { echo '' . esc_html__('Configure ARIA attribute capture methods. You can activate multiple methods simultaneously for more robust detection.', 'accessitrans-aria') . '
'; } /** * Callback para la sección avanzada */ public function section_advanced_callback() { echo '' . esc_html__('Advanced settings for performance and debugging.', 'accessitrans-aria') . '
'; } /** * Callback para campos checkbox */ public function checkbox_callback($args) { $option_name = $args['label_for']; $descripcion = $args['descripcion']; $checked = isset($this->core->options[$option_name]) && $this->core->options[$option_name] ? 'checked' : ''; $disabled = ''; $disabled_attr = ''; // Desactivar los controles dependientes si el escaneo está desactivado if (in_array($option_name, ['captura_total', 'captura_elementor', 'procesar_templates', 'procesar_elementos']) && isset($this->core->options['permitir_escaneo']) && !$this->core->options['permitir_escaneo']) { $disabled = 'disabled'; $disabled_attr = 'disabled aria-disabled="true"'; } echo '' . esc_html($descripcion) . '
'; // Mensaje informativo destacado sobre la desactivación del escaneo echo '' . esc_html__('Settings saved successfully.', 'accessitrans-aria') . '