$this->get_tracking_plugin_id(), 'from' => sanitize_key( $from ), ], $url ); } /** * Adds the "Donate" link in the Plugins list row. */ public function add_plugin_action_links( array $links ): array { $donate_url = $this->tracked_url( 'https://joseabdiel.com/apoyar/', 'plugins_row' ); $donate = sprintf( '%s', esc_url( $donate_url ), esc_html__( 'Donate', 'abdiel-global-variables' ) ); array_unshift( $links, $donate ); return $links; } public function __construct() { add_action( 'init', [ $this, 'maybe_migrate_legacy_option' ] ); add_action( 'init', [ $this, 'register_dynamic_shortcodes' ] ); add_action( 'admin_menu', [ $this, 'register_menu' ] ); add_action( 'admin_init', [ $this, 'handle_post' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'admin_assets' ] ); add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), [ $this, 'add_plugin_action_links' ] ); } /** * Migrates from the VERY old key (nr_links...) to your current standard (vg_abdiel...) */ public function maybe_migrate_legacy_option() { $current = get_option( self::OPTION_KEY, null ); $old = get_option( self::LEGACY_OPTION_KEY, null ); if ( ! empty( $old ) && ( empty( $current ) || ! is_array( $current ) ) ) { update_option( self::OPTION_KEY, $old ); } } /** * Admin menu icon: inline SVG (data URI). */ private function get_menu_icon_data_uri() { $svg_path = plugin_dir_path( __FILE__ ) . 'assets/icon.svg'; $svg = ''; if ( file_exists( $svg_path ) && is_readable( $svg_path ) ) { $svg = file_get_contents( $svg_path ); } if ( ! $svg ) { return 'dashicons-admin-generic'; } // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode return 'data:image/svg+xml;base64,' . base64_encode( $svg ); } public function register_menu() { $icon = $this->get_menu_icon_data_uri(); add_menu_page( __( 'Global Variables', 'abdiel-global-variables' ), __( 'Global Variables', 'abdiel-global-variables' ), 'manage_options', self::MENU_SLUG, [ $this, 'render_main_page' ], $icon, 58 ); add_submenu_page( self::MENU_SLUG, __( 'Contact', 'abdiel-global-variables' ), __( 'Contact', 'abdiel-global-variables' ), 'manage_options', self::CONTACT_SLUG, [ $this, 'render_contact_page' ] ); } public function handle_post() { if ( ! current_user_can( 'manage_options' ) ) { return; } $nonce = isset( $_POST[ self::NONCE_NAME ] ) ? sanitize_text_field( wp_unslash( $_POST[ self::NONCE_NAME ] ) ) : ''; if ( ! $nonce || ! wp_verify_nonce( $nonce, self::NONCE_ACTION ) ) { return; } // EXPORT JSON if ( isset( $_POST['vg_abdiel_export_items'] ) ) { $items = get_option( self::OPTION_KEY, [] ); if ( ! is_array( $items ) ) { $items = []; } $json = wp_json_encode( $items, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); if ( $json === false ) { add_settings_error( self::SETTINGS_GROUP, 'export_error', __( 'Could not generate the JSON file.', 'abdiel-global-variables' ), 'error' ); return; } $domain = wp_parse_url( home_url(), PHP_URL_HOST ); if ( empty( $domain ) ) { $domain = 'global-variables'; } $filename = $domain . '-abdiel-global-variables.json'; nocache_headers(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="' . $filename . '"' ); header( 'Content-Length: ' . strlen( $json ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $json; exit; } // IMPORT JSON if ( isset( $_POST['vg_abdiel_import_items'] ) ) { $tmp_name = isset( $_FILES['vg_abdiel_import_file']['tmp_name'] ) ? sanitize_text_field( wp_unslash( $_FILES['vg_abdiel_import_file']['tmp_name'] ) ) : ''; if ( empty( $tmp_name ) || ! is_uploaded_file( $tmp_name ) ) { add_settings_error( self::SETTINGS_GROUP, 'import_no_file', __( 'No file was selected for import.', 'abdiel-global-variables' ), 'error' ); return; } $raw = file_get_contents( $tmp_name ); if ( $raw === false ) { add_settings_error( self::SETTINGS_GROUP, 'import_read_error', __( 'Could not read the uploaded file.', 'abdiel-global-variables' ), 'error' ); return; } $data = json_decode( $raw, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { add_settings_error( self::SETTINGS_GROUP, 'import_json_error', __( 'The JSON file is not valid.', 'abdiel-global-variables' ), 'error' ); return; } if ( isset( $data['items'] ) && is_array( $data['items'] ) ) { $data = $data['items']; } if ( ! is_array( $data ) ) { add_settings_error( self::SETTINGS_GROUP, 'import_format_error', __( 'The file format is not what we expected.', 'abdiel-global-variables' ), 'error' ); return; } $items = get_option( self::OPTION_KEY, [] ); if ( ! is_array( $items ) ) { $items = []; } $existing_scs = []; foreach ( $items as $it ) { if ( ! empty( $it['shortcode'] ) ) { $existing_scs[] = $it['shortcode']; } } $added = 0; $skipped = 0; foreach ( $data as $row ) { if ( ! is_array( $row ) ) { $skipped++; continue; } $label = isset( $row['label'] ) ? sanitize_text_field( $row['label'] ) : ''; // CORRECCIÓN SANITIZACIÓN: Usamos wp_kses_post para permitir HTML seguro/enlaces $value = isset( $row['value'] ) ? wp_kses_post( $row['value'] ) : ''; $sc = isset( $row['shortcode'] ) ? sanitize_key( $row['shortcode'] ) : ''; if ( ! $label || ! $value || ! $sc ) { $skipped++; continue; } if ( in_array( $sc, $existing_scs, true ) ) { $skipped++; continue; } $items[] = [ 'label' => $label, 'value' => $value, 'shortcode' => $sc, ]; $existing_scs[] = $sc; $added++; } update_option( self::OPTION_KEY, $items ); if ( $added > 0 ) { $msg = sprintf( /* translators: 1: items added, 2: items skipped */ __( 'Import completed: %1$d added, %2$d skipped (invalid format or duplicate shortcode).', 'abdiel-global-variables' ), $added, $skipped ); add_settings_error( self::SETTINGS_GROUP, 'import_ok', $msg, 'updated' ); } else { add_settings_error( self::SETTINGS_GROUP, 'import_empty', __( 'No items were added from the file.', 'abdiel-global-variables' ), 'error' ); } return; } // ADD NEW if ( isset( $_POST['vg_abdiel_add_item'] ) ) { $label = isset( $_POST['vg_abdiel_label'] ) ? sanitize_text_field( wp_unslash( $_POST['vg_abdiel_label'] ) ) : ''; // CORRECCIÓN SANITIZACIÓN: wp_kses_post para permitir HTML/Símbolos $value = isset( $_POST['vg_abdiel_value'] ) ? wp_kses_post( wp_unslash( $_POST['vg_abdiel_value'] ) ) : ''; $sc = isset( $_POST['vg_abdiel_shortcode'] ) ? sanitize_key( wp_unslash( $_POST['vg_abdiel_shortcode'] ) ) : ''; if ( $label && $value && $sc ) { $items = get_option( self::OPTION_KEY, [] ); if ( ! is_array( $items ) ) { $items = []; } $exists = array_filter( $items, function( $it ) use ( $sc ) { return isset( $it['shortcode'] ) && $it['shortcode'] === $sc; } ); if ( $exists ) { add_settings_error( self::SETTINGS_GROUP, 'sc_exists', __( 'That shortcode already exists.', 'abdiel-global-variables' ), 'error' ); } else { $items[] = [ 'label' => $label, 'value' => $value, 'shortcode' => $sc, ]; update_option( self::OPTION_KEY, $items ); add_settings_error( self::SETTINGS_GROUP, 'sc_added', __( 'Shortcode added.', 'abdiel-global-variables' ), 'updated' ); } } else { add_settings_error( self::SETTINGS_GROUP, 'missing', __( 'Please complete all fields.', 'abdiel-global-variables' ), 'error' ); } } // UPDATE (edit) if ( isset( $_POST['vg_abdiel_update_item'] ) ) { $idx = isset( $_POST['vg_abdiel_edit_index'] ) ? absint( wp_unslash( $_POST['vg_abdiel_edit_index'] ) ) : -1; $label = isset( $_POST['vg_abdiel_label'] ) ? sanitize_text_field( wp_unslash( $_POST['vg_abdiel_label'] ) ) : ''; // CORRECCIÓN SANITIZACIÓN: wp_kses_post $value = isset( $_POST['vg_abdiel_value'] ) ? wp_kses_post( wp_unslash( $_POST['vg_abdiel_value'] ) ) : ''; $sc = isset( $_POST['vg_abdiel_shortcode'] ) ? sanitize_key( wp_unslash( $_POST['vg_abdiel_shortcode'] ) ) : ''; $items = get_option( self::OPTION_KEY, [] ); if ( is_array( $items ) && isset( $items[ $idx ] ) && $label && $value && $sc ) { foreach ( $items as $i => $it ) { if ( $i !== $idx && isset( $it['shortcode'] ) && $it['shortcode'] === $sc ) { add_settings_error( self::SETTINGS_GROUP, 'sc_exists', __( 'That shortcode already exists.', 'abdiel-global-variables' ), 'error' ); return; } } $items[ $idx ] = [ 'label' => $label, 'value' => $value, 'shortcode' => $sc, ]; update_option( self::OPTION_KEY, array_values( $items ) ); add_settings_error( self::SETTINGS_GROUP, 'sc_updated', __( 'Shortcode updated.', 'abdiel-global-variables' ), 'updated' ); wp_safe_redirect( remove_query_arg( 'edit' ) ); exit; } } // DELETE if ( isset( $_POST['vg_abdiel_delete_index'] ) ) { $idx = absint( wp_unslash( $_POST['vg_abdiel_delete_index'] ) ); $items = get_option( self::OPTION_KEY, [] ); if ( is_array( $items ) && isset( $items[ $idx ] ) ) { unset( $items[ $idx ] ); update_option( self::OPTION_KEY, array_values( $items ) ); add_settings_error( self::SETTINGS_GROUP, 'sc_deleted', __( 'Shortcode deleted.', 'abdiel-global-variables' ), 'updated' ); } } } /** * Dynamically register each saved shortcode as a tag. */ public function register_dynamic_shortcodes() { $items = get_option( self::OPTION_KEY, [] ); if ( ! is_array( $items ) ) { return; } foreach ( $items as $item ) { if ( empty( $item['shortcode'] ) || ! isset( $item['value'] ) ) { continue; } $tag = sanitize_key( $item['shortcode'] ); if ( ! $tag || shortcode_exists( $tag ) ) { continue; } add_shortcode( $tag, function( $atts = [] ) use ( $item ) { $atts = shortcode_atts( [ 'value' => $item['value'] ], $atts, $item['shortcode'] ); // ESCAPE LATE: // Usamos wp_kses_post si quieres que se rendericen enlaces/HTML seguro en el frontend. // O esc_html si quieres que sea texto plano estricto. // Dado tu requerimiento de "no romper el texto/símbolos", esc_html es lo más seguro por defecto, // pero si usas HTML en tus variables, cambia esto a wp_kses_post($atts['value']). // Por ahora lo dejo en esc_html para máxima seguridad y aprobación fácil. return esc_html( (string) $atts['value'] ); }); } } /** * Admin styles + JS */ public function admin_assets( $hook ) { // Apply to both pages of this plugin if ( strpos( $hook, self::MENU_SLUG ) === false ) { return; } // He cambiado las clases CSS a vg-abdiel-xxx para mantener la uniformidad total. $css = ' .vg-abdiel-card{background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:16px;margin-top:16px} .vg-abdiel-card-banner{background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:0px;margin-top:16px} .vg-abdiel-flex{display:flex;gap:12px;flex-wrap:wrap} .vg-abdiel-flex .field{flex:1 1 280px} .vg-abdiel-table{width:100%;border-collapse:collapse;margin-top:10px} .vg-abdiel-table th,.vg-abdiel-table td{padding:8px;border-bottom:1px solid #eee;text-align:left;vertical-align:top} .vg-abdiel-small{font-size:12px;color:#666;margin:6px 0 0} .vg-abdiel-badge{display:inline-block;background:#eefbf4;color:#0a6e3f;border:1px solid #b6f5d2;border-radius:999px;padding:3px 8px;font-size:12px;cursor:pointer} .vg-abdiel-code{background:#111;color:#0f0;padding:2px 6px;border-radius:4px} .vg-abdiel-foot{margin-top:24px;color:#666} .vg-abdiel-btn-neon{ background:#00FF66; color:#111; border:none; box-shadow:0 0 10px #00FF66; font-weight:700; padding:8px 14px; border-radius:6px; text-decoration:none; display:inline-block; cursor:pointer; } .vg-abdiel-btn-neon:hover{ box-shadow:0 0 16px #00FF66; transform:translateY(-1px); } /* Force neon green SVG menu icon */ #toplevel_page_' . self::MENU_SLUG . ' .wp-menu-image svg{fill:#00FF66 !important;} #toplevel_page_' . self::MENU_SLUG . ':hover .wp-menu-image svg, #toplevel_page_' . self::MENU_SLUG . '.wp-has-current-submenu .wp-menu-image svg{fill:#00FF66 !important;} '; wp_register_style( 'vg-abdiel-admin', false, [], VG_ABDIEL_VERSION ); wp_enqueue_style( 'vg-abdiel-admin' ); wp_add_inline_style( 'vg-abdiel-admin', $css ); $copied = esc_js( __( 'Copied!', 'abdiel-global-variables' ) ); $js = " (function(){ function toast(el, txt){ var old = el.textContent; el.textContent = txt; setTimeout(function(){ el.textContent = old; }, 1200); } document.addEventListener('click', function(e){ var t = e.target.closest('.vg-abdiel-badge[data-sc]'); if(!t) return; e.preventDefault(); var sc = '[' + t.getAttribute('data-sc') + ']'; if (navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(sc).then(function(){ toast(t, '{$copied}'); }); } else { var ta = document.createElement('textarea'); ta.value = sc; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); toast(t, '{$copied}'); } catch(err){} document.body.removeChild(ta); } }, false); })(); "; wp_register_script( 'vg-abdiel-admin', false, [], VG_ABDIEL_VERSION, true ); wp_enqueue_script( 'vg-abdiel-admin' ); wp_add_inline_script( 'vg-abdiel-admin', $js ); } public function render_main_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } settings_errors( self::SETTINGS_GROUP ); $items = get_option( self::OPTION_KEY, [] ); if ( ! is_array( $items ) ) { $items = []; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $edit_idx = isset( $_GET['edit'] ) ? absint( wp_unslash( $_GET['edit'] ) ) : -1; $editing = ( $edit_idx >= 0 && isset( $items[ $edit_idx ] ) ); $edit_item = $editing ? $items[ $edit_idx ] : [ 'label' => '', 'value' => '', 'shortcode' => '' ]; ?>

support_whatsapp [support_whatsapp]




[a-z0-9_].

$it ) : ?>
[]

— Joseabdiel.com