' . esc_html__( 'Donate', '360-connect-hub' ) . ''; return $links; } public function menu() { add_menu_page( '360 CHAT', '360 CHAT', 'manage_options', '360wp', array( $this, 'settings_page' ), 'dashicons-format-chat', 58 ); add_submenu_page( '360wp', __( 'Configuración', '360-connect-hub' ), __( 'Configuración', '360-connect-hub' ), 'manage_options', '360wp', array( $this, 'settings_page' ) ); add_submenu_page( '360wp', __( 'Leads y estadísticas', '360-connect-hub' ), __( 'Leads y estadísticas', '360-connect-hub' ), 'manage_options', 'n360wp-leads', array( $this, 'leads_page' ) ); } public function settings() { register_setting( 'n360wp_settings', 'n360wp_title', array( 'sanitize_callback' => 'sanitize_text_field' ) ); register_setting( 'n360wp_settings', 'n360wp_status', array( 'sanitize_callback' => array( $this, 'sanitize_status' ) ) ); register_setting( 'n360wp_settings', 'n360wp_avatar', array( 'sanitize_callback' => 'esc_url_raw' ) ); register_setting( 'n360wp_settings', 'n360wp_avatar_id', array( 'sanitize_callback' => 'absint' ) ); register_setting( 'n360wp_settings', 'n360wp_header_color', array( 'sanitize_callback' => 'sanitize_hex_color' ) ); register_setting( 'n360wp_settings', 'n360wp_header_text_color', array( 'sanitize_callback' => 'sanitize_hex_color' ) ); register_setting( 'n360wp_settings', 'n360wp_text_color', array( 'sanitize_callback' => 'sanitize_hex_color' ) ); register_setting( 'n360wp_settings', 'n360wp_button_position', array( 'sanitize_callback' => array( $this, 'position' ) ) ); register_setting( 'n360wp_settings', 'n360wp_button_x', array( 'sanitize_callback' => array( $this, 'number' ) ) ); register_setting( 'n360wp_settings', 'n360wp_button_y', array( 'sanitize_callback' => array( $this, 'number' ) ) ); register_setting( 'n360wp_settings', 'n360wp_agents', array( 'sanitize_callback' => array( $this, 'agents' ) ) ); register_setting( 'n360wp_settings', 'n360wp_language', array( 'sanitize_callback' => array( $this, 'language' ) ) ); register_setting( 'n360wp_settings', 'n360wp_country_detection', array( 'sanitize_callback' => 'absint' ) ); register_setting( 'n360wp_settings', 'n360wp_button_icon', array( 'sanitize_callback' => 'esc_url_raw' ) ); register_setting( 'n360wp_settings', 'n360wp_button_icon_id', array( 'sanitize_callback' => 'absint' ) ); register_setting( 'n360wp_settings', 'n360wp_button_icon_preset', array( 'sanitize_callback' => array( $this, 'icon_preset' ) ) ); } public function assets( $hook ) { if ( false === strpos( $hook, '360wp' ) ) return; wp_enqueue_media(); wp_enqueue_style( 'n360wp-admin', NET360_360CH_URL . 'admin/css/admin.css', array(), NET360_360CH_VERSION ); wp_enqueue_script( 'n360wp-admin', NET360_360CH_URL . 'admin/js/admin.js', array( 'jquery' ), NET360_360CH_VERSION, true ); wp_localize_script( 'n360wp-admin', 'n360wpAdmin', array( 'defaultIcon' => NET360_360CH_URL . 'assets/icons/whatsapp-1.png', 'iconPresets' => array( 'whatsapp-1' => NET360_360CH_URL . 'assets/icons/whatsapp-1.png', 'whatsapp-2' => NET360_360CH_URL . 'assets/icons/whatsapp-2.png', 'social' => NET360_360CH_URL . 'assets/icons/social.png', 'whatsapp-3' => NET360_360CH_URL . 'assets/icons/whatsapp-3.png', 'whatsapp-4' => NET360_360CH_URL . 'assets/icons/whatsapp-4.png', 'whatsapp-5' => NET360_360CH_URL . 'assets/icons/whatsapp-5.png', ) ) ); } public function sanitize_status( $value ) { $allowed = array( 'Disponible', 'Ocupado', 'Ausente', 'No disponible', 'Fuera de horario', 'Solo mensajes' ); $value = sanitize_text_field( $value ); return in_array( $value, $allowed, true ) ? $value : 'Disponible'; } public function settings_page() { include NET360_360CH_DIR . 'admin/views/settings.php'; } public function leads_page() { include NET360_360CH_DIR . 'admin/views/leads.php'; } public function export_leads() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'No tienes permisos para exportar leads.', '360-connect-hub' ) ); } check_admin_referer( 'n360wp_export_leads', 'n360wp_export_nonce' ); $from = isset( $_POST['date_from'] ) ? sanitize_text_field( wp_unslash( $_POST['date_from'] ) ) : ''; $to = isset( $_POST['date_to'] ) ? sanitize_text_field( wp_unslash( $_POST['date_to'] ) ) : ''; $from = preg_match( '/^\d{4}-\d{2}-\d{2}$/', $from ) ? $from : ''; $to = preg_match( '/^\d{4}-\d{2}-\d{2}$/', $to ) ? $to : ''; if ( $from && $to && $from > $to ) { wp_die( esc_html__( 'El rango de fechas no es válido.', '360-connect-hub' ) ); } $rows = Net360_360CH_Leads::export_between( $from, $to ); $filename = '360-chat-leads-' . ( $from ?: 'inicio' ) . '-' . ( $to ?: 'hoy' ) . '.csv'; nocache_headers(); header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=' . sanitize_file_name( $filename ) ); header( 'Pragma: no-cache' ); header( 'Expires: 0' ); // UTF-8 BOM so Excel opens accented Spanish text correctly. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV download response; values are serialized by csv_line() for CSV format, not rendered as HTML. echo "\xEF\xBB\xBF"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV download response; csv_line() quotes and escapes CSV cells. echo $this->csv_line( array( 'ID', 'Nombre', 'Email', 'Agente', 'Mensaje', 'Página', 'País', 'Fecha' ) ); foreach ( $rows as $row ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV download response; csv_line() quotes and escapes CSV cells. echo $this->csv_line( array( $row->id, $row->name, $row->email, $row->agent, $row->message, $row->page, $row->country, $row->date ) ); } exit; } private function csv_line( $values ) { $cells = array(); foreach ( $values as $value ) { $value = (string) $value; $value = str_replace( '"', '""', $value ); $cells[] = '"' . $value . '"'; } return implode( ',', $cells ) . "\r\n"; } public function number( $value ) { return max( 0, min( 1000, absint( $value ) ) ); } public function position( $value ) { return in_array( $value, array( 'bottom-right','bottom-left','top-right','top-left' ), true ) ? $value : 'bottom-right'; } public function language( $value ) { return in_array( $value, array( 'site', 'en', 'es' ), true ) ? $value : 'site'; } public function icon_preset( $value ) { return in_array( $value, array( '', 'whatsapp-1', 'whatsapp-2', 'social', 'whatsapp-3', 'whatsapp-4', 'whatsapp-5', 'custom' ), true ) ? $value : ''; } public function agents( $value ) { $lines = preg_split( '/\r\n|\r|\n/', (string) $value ); $clean = array(); foreach ( $lines as $line ) { $parts = array_map( 'trim', explode( '|', $line, 2 ) ); if ( count( $parts ) !== 2 ) continue; $phone = preg_replace( '/[^0-9]/', '', $parts[0] ); $name = sanitize_text_field( $parts[1] ); if ( $phone && $name ) $clean[] = $phone . '|' . $name; } return implode( "\n", $clean ); } }