isFile() && $file->getFilename() === $filename ) { return $file->getPathname(); } } } catch ( UnexpectedValueException $exception ) { return ''; } return ''; } public function __construct() { add_action('admin_init', array($this, 'register_settings')); add_action('admin_post_ablycuic_upload_zip', array($this, 'handle_zip_upload')); add_action('admin_post_ablycuic_delete_collection', array($this, 'handle_delete_collection')); add_action('admin_menu', array($this, 'add_admin_menu')); // Enqueue custom fonts and FA on frontend and admin add_action('wp_enqueue_scripts', array($this, 'enqueue_custom_fonts'), 20); add_action('admin_enqueue_scripts', array($this, 'enqueue_custom_fonts'), 20); // Dynamically inject picker trigger into mapped ACF fields $this->setup_mapped_fields(); } public function register_settings() { register_setting('ablycuic_settings_group', 'ablycuic_fa_cdn', array( 'sanitize_callback' => array($this, 'sanitize_cdn_urls'), )); register_setting('ablycuic_settings_group', 'ablycuic_custom_data', array( 'sanitize_callback' => array($this, 'sanitize_custom_data'), )); // Stores the merged custom icons array register_setting('ablycuic_settings_group', 'ablycuic_mapped_fields', array( 'sanitize_callback' => array($this, 'sanitize_mapped_fields'), )); } public function sanitize_cdn_urls($value) { $urls = preg_split('/\r\n|\r|\n/', (string) $value); $urls = array_filter(array_map('trim', $urls)); $urls = array_filter(array_map('esc_url_raw', $urls), function ($url) { return (bool) preg_match('#^https?://#i', $url); }); return implode("\n", $urls); } public function sanitize_mapped_fields($value) { $fields = array_filter(array_map('trim', explode(',', (string) $value))); $fields = array_map('sanitize_key', $fields); return implode(', ', array_filter($fields)); } /** * Keep the internal custom icon data as an array when Settings API saves it. * * @param mixed $value Setting value. * @return array */ public function sanitize_custom_data($value) { if ( ! is_array($value) ) { return array(); } $clean = array(); $clean['icons'] = array(); foreach ( (array) ($value['icons'] ?? array()) as $class => $label ) { $class = sanitize_html_class($class); if ( $class ) { $clean['icons'][$class] = sanitize_text_field($label); } } foreach ( array('css_files', 'font_files') as $list_key ) { $clean[$list_key] = array(); foreach ( (array) ($value[$list_key] ?? array()) as $file ) { $file = sanitize_file_name($file); if ( $file ) { $clean[$list_key][] = $file; } } } $clean['collections'] = array(); foreach ( (array) ($value['collections'] ?? array()) as $collection ) { if ( ! is_array($collection) ) { continue; } $item = array( 'id' => sanitize_text_field($collection['id'] ?? ''), 'name' => sanitize_file_name($collection['name'] ?? ''), 'imported_at' => absint($collection['imported_at'] ?? 0), 'css_file' => sanitize_file_name($collection['css_file'] ?? ''), 'css_files' => array(), 'font_files' => array(), 'icons' => array(), ); foreach ( (array) ($collection['css_files'] ?? array()) as $file ) { $file = sanitize_file_name($file); if ( $file ) $item['css_files'][] = $file; } foreach ( (array) ($collection['font_files'] ?? array()) as $file ) { $file = sanitize_file_name($file); if ( $file ) $item['font_files'][] = $file; } foreach ( (array) ($collection['icons'] ?? array()) as $class ) { $class = sanitize_html_class($class); if ( $class ) $item['icons'][] = $class; } if ( $item['id'] ) $clean['collections'][] = $item; } return $clean; } public function add_admin_menu() { add_menu_page( __( 'Custom Icons', 'ably-custom-icon-field-for-acf' ), 'Custom Icons', 'manage_options', 'ablycuic-settings', array($this, 'render_admin_page'), 'dashicons-art', 80 ); } public function setup_mapped_fields() { $mapped = get_option('ablycuic_mapped_fields'); if ( empty($mapped) ) { return; } $fields = array_map('trim', explode(',', $mapped)); foreach ( $fields as $field_name ) { if ( ! empty($field_name) ) { add_action('acf/render_field/name=' . $field_name, array($this, 'render_mapped_field_picker')); } } } public function enqueue_picker_assets() { wp_enqueue_style( 'ably-custom-icon-field-for-acf-picker', ABLYCUIC_URL . 'assets/css/icon-picker.css', array(), ABLYCUIC_VERSION ); wp_enqueue_script( 'ably-custom-icon-field-for-acf-picker', ABLYCUIC_URL . 'assets/js/icon-picker.js', array( 'jquery' ), ABLYCUIC_VERSION, true ); wp_localize_script( 'ably-custom-icon-field-for-acf-picker', 'ablyCuic', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'ablycuic_nonce' ), )); } public function render_mapped_field_picker($field) { $this->enqueue_picker_assets(); $value = isset($field['value']) ? sanitize_text_field($field['value']) : ''; ?>
'application/zip')); if ( empty($file_type['type']) || ! is_uploaded_file($zip_file) ) { wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=invalid_zip')); exit; } if ( ! class_exists('ZipArchive') ) { wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=zip_unavailable')); exit; } $archive = new ZipArchive(); if ( true !== $archive->open($zip_file) || $archive->numFiles > 1000 ) { wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=invalid_zip')); exit; } $uncompressed_size = 0; $allowed_entries = array(); for ( $index = 0; $index < $archive->numFiles; $index++ ) { $entry_name = $archive->getNameIndex($index); $entry_path = str_replace('\\', '/', (string) $entry_name); $entry_base = basename($entry_path); $entry_stat = $archive->statIndex($index); $uncompressed_size += isset($entry_stat['size']) ? (int) $entry_stat['size'] : 0; if ( strpos($entry_path, '../') !== false || 0 === strpos($entry_path, '/') ) { $archive->close(); wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=unsafe_zip')); exit; } if ( false !== strpos($entry_path, "\0") ) { $archive->close(); wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=unsafe_zip')); exit; } if ( $uncompressed_size > 20 * 1024 * 1024 ) { $archive->close(); wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=unsafe_zip')); exit; } // Extract only files required by an IcoMoon export. Other entries // remain inside the uploaded archive and are never written here. $entry_extension = strtolower( (string) pathinfo($entry_base, PATHINFO_EXTENSION) ); $is_font_entry = preg_match('#(?:^|/)fonts/[^/]+$#i', $entry_path) && in_array( $entry_extension, array( 'eot', 'svg', 'ttf', 'woff', 'woff2' ), true ); if ( 'selection.json' === strtolower($entry_base) || 'style.css' === strtolower($entry_base) || $is_font_entry ) { $allowed_entries[] = $entry_name; } } require_once ABSPATH . 'wp-admin/includes/file.php'; WP_Filesystem(); global $wp_filesystem; $unzip_dir = $this->get_upload_dir() . '/temp_' . time(); $result = $archive->extractTo( $unzip_dir, $allowed_entries ); $archive->close(); if ( true !== $result ) { wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=unzip_failed')); exit; } // Parse selection.json $selection_file = $this->find_import_file( $unzip_dir, 'selection.json' ); if ( '' === $selection_file ) { $wp_filesystem->delete($unzip_dir, true); wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=no_selection')); exit; } $json_data = file_get_contents($selection_file); $font_data = json_decode((string) $json_data, true); if ( ! is_array($font_data) || empty($font_data['icons']) || ! is_array($font_data['icons']) ) { $wp_filesystem->delete($unzip_dir, true); wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&error=invalid_selection')); exit; } // Extract icons logic will go here $new_icons = array(); if (isset($font_data['icons'])) { $prefix = isset($font_data['preferences']['fontPref']['prefix']) ? $font_data['preferences']['fontPref']['prefix'] : 'icon-'; $font_family = isset($font_data['preferences']['fontPref']['metadata']['fontFamily']) ? $font_data['preferences']['fontPref']['metadata']['fontFamily'] : 'icomoon'; // Generate a unique identifier for this font family version $time_id = time(); $collection_id = wp_generate_uuid4(); $custom_font_family = 'custom_font_' . $time_id; $font_files = array(); foreach ($font_data['icons'] as $icon) { $name = isset($icon['properties']['name']) ? sanitize_title($icon['properties']['name']) : ''; if ( '' === $name ) { continue; } $class_name = $prefix . $name; $label = ucwords(str_replace(array('-', '_'), ' ', $name)); $new_icons[$class_name] = $label; } // Move font files $font_dir = $this->get_upload_dir(); $fonts_source = dirname( $selection_file ) . '/fonts'; if ( is_dir($fonts_source) ) { $files = scandir($fonts_source); foreach ($files as $file) { $allowed_font_extensions = array('eot', 'svg', 'ttf', 'woff', 'woff2'); if ($file !== '.' && $file !== '..' && is_file($fonts_source . '/' . $file)) { $ext = pathinfo($file, PATHINFO_EXTENSION); if ( ! in_array(strtolower($ext), $allowed_font_extensions, true) ) { continue; } // Rename file to prevent caching $new_filename = 'font_' . $time_id . '.' . $ext; if ( $wp_filesystem->copy($fonts_source . '/' . $file, $font_dir . '/' . $new_filename, true) ) { $font_files[] = $new_filename; } } } } // Build custom CSS $css_content = "@font-face {\n"; $css_content .= " font-family: '" . $custom_font_family . "';\n"; $css_content .= " src: url('font_" . $time_id . ".woff') format('woff'),\n"; $css_content .= " url('font_" . $time_id . ".ttf') format('truetype');\n"; $css_content .= " font-weight: normal; font-style: normal; font-display: block;\n"; $css_content .= "}\n\n"; $css_content .= "[class^=\"$prefix\"], [class*=\" $prefix\"] {\n"; $css_content .= " font-family: '" . $custom_font_family . "' !important;\n"; $css_content .= " speak: never; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1;\n"; $css_content .= " -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;\n"; $css_content .= "}\n\n"; // Extract CSS rules from style.css if present in Icomoon zip $style_css = $this->find_import_file( $unzip_dir, 'style.css' ); if ( file_exists($style_css) ) { $style_content = file_get_contents($style_css); // Extract before/after definitions, including multicolor rules. // IcoMoon may place color declarations after content, so a simple // content-only regex would silently omit those icons. preg_match_all('/\.([A-Za-z0-9_-]+):(before|after)\s*\{([^}]*)\}/i', $style_content, $matches, PREG_SET_ORDER); $font_classes = array(); foreach ( $matches as $match ) { $class_name = $match[1]; $pseudo = $match[2]; $declarations = $match[3]; if ( ! preg_match('/content\s*:\s*["\']([^"\']+)["\']/i', $declarations, $content_match) ) { continue; } if ( ! isset($font_classes[$class_name]) ) { // Scope the font family to each icon class so multiple ZIP // fonts using the same prefix can coexist. $css_content .= "." . $class_name . " { font-family: '" . $custom_font_family . "' !important; }\n"; $font_classes[$class_name] = true; } $css_content .= "." . $class_name . ":" . $pseudo . " { content: \"" . $content_match[1] . "\";"; if ( preg_match('/color\s*:\s*([^;}]*)/i', $declarations, $color_match) ) { $css_content .= " color: " . trim($color_match[1]) . ";"; } $css_content .= " }\n"; } } $wp_filesystem->put_contents( $font_dir . '/custom_icons_' . $time_id . '.css', $css_content, FS_CHMOD_FILE ); // Save new icons to WP Options, resolving duplicates $existing_data = get_option('ablycuic_custom_data', array()); $merged_icons = isset($existing_data['icons']) ? $existing_data['icons'] : array(); // Keep every imported stylesheet so multiple ZIP collections work together. $css_file = 'custom_icons_' . $time_id . '.css'; $css_files = isset($existing_data['css_files']) && is_array($existing_data['css_files']) ? $existing_data['css_files'] : array(); // Migrate the previous single-file format when present. if ( ! empty($existing_data['css_file']) && ! in_array($existing_data['css_file'], $css_files, true) ) { $css_files[] = $existing_data['css_file']; } if ( ! in_array($css_file, $css_files, true) ) { $css_files[] = $css_file; } $existing_data['css_files'] = $css_files; $collections = isset($existing_data['collections']) && is_array($existing_data['collections']) ? $existing_data['collections'] : array(); $collections[] = array( 'id' => $collection_id, 'name' => $zip_name ? $zip_name : 'Colección importada', 'imported_at'=> current_time('timestamp'), 'css_file' => $css_file, 'font_files' => $font_files, 'icons' => array_keys($new_icons), ); $existing_data['collections'] = $collections; foreach ($new_icons as $class => $label) { // If the exact class already exists, we overwrite safely because it just updates the label/unicode $merged_icons[$class] = $label; } $existing_data['icons'] = $merged_icons; update_option('ablycuic_custom_data', $existing_data); } $wp_filesystem->delete($unzip_dir, true); wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&success=1')); exit; } public function handle_delete_collection() { if ( ! current_user_can('manage_options') ) { wp_die('No tienes permisos.'); } check_admin_referer('ablycuic_delete_collection_action', 'ablycuic_delete_nonce'); $collection_id = isset($_POST['collection_id']) ? sanitize_text_field(wp_unslash($_POST['collection_id'])) : ''; $custom_data = get_option('ablycuic_custom_data', array()); $collections = isset($custom_data['collections']) && is_array($custom_data['collections']) ? $custom_data['collections'] : array(); if ( 'legacy-imported-icons' === $collection_id && empty($collections) ) { $legacy_fonts = array(); foreach ( (array) glob($this->get_upload_dir() . '/font_*') as $legacy_font ) { $legacy_fonts[] = basename($legacy_font); } $collections[] = array( 'id' => 'legacy-imported-icons', 'icons' => array_keys((array) ($custom_data['icons'] ?? array())), 'css_files' => array_values(array_filter(array_map('sanitize_file_name', (array) $custom_data['css_files']))), 'font_files' => $legacy_fonts, ); } $remaining = array(); $removed_icons = array(); $removed_css = array(); $deleted_collection = false; $font_dir = $this->get_upload_dir(); foreach ( $collections as $collection ) { if ( ! is_array($collection) || empty($collection['id']) || $collection['id'] !== $collection_id ) { $remaining[] = $collection; continue; } $deleted_collection = true; $removed_icons = isset($collection['icons']) && is_array($collection['icons']) ? $collection['icons'] : array(); $collection_css = isset($collection['css_files']) && is_array($collection['css_files']) ? $collection['css_files'] : array(isset($collection['css_file']) ? $collection['css_file'] : ''); foreach ( $collection_css as $css_file ) { $removed_css[] = sanitize_file_name($css_file); } foreach ( (array) ($collection['font_files'] ?? array()) as $font_file ) { $font_file = sanitize_file_name($font_file); if ( $font_file && file_exists($font_dir . '/' . $font_file) ) { wp_delete_file($font_dir . '/' . $font_file); } } } $remaining_css = array(); foreach ( $remaining as $other_collection ) { $other_css = isset($other_collection['css_files']) && is_array($other_collection['css_files']) ? $other_collection['css_files'] : array(isset($other_collection['css_file']) ? $other_collection['css_file'] : ''); foreach ( $other_css as $css_file ) { $css_file = sanitize_file_name($css_file); if ( $css_file ) { $remaining_css[$css_file] = true; } } } foreach ( array_unique($removed_css) as $css_file ) { if ( isset($remaining_css[$css_file]) ) { continue; } if ( $css_file && file_exists($font_dir . '/' . $css_file) ) { wp_delete_file($font_dir . '/' . $css_file); } } $remaining_icons = array(); foreach ( $remaining as $other_collection ) { foreach ( (array) ($other_collection['icons'] ?? array()) as $icon_class ) { $remaining_icons[$icon_class] = true; } } if ( ! empty($removed_icons) && isset($custom_data['icons']) && is_array($custom_data['icons']) ) { foreach ( $removed_icons as $icon_class ) { if ( ! isset($remaining_icons[$icon_class]) ) { unset($custom_data['icons'][$icon_class]); } } } if ( $deleted_collection ) { $custom_data['collections'] = $remaining; $custom_data['css_files'] = array_values(array_filter(array_map('sanitize_file_name', array_keys($remaining_css)))); update_option('ablycuic_custom_data', $custom_data); } wp_safe_redirect(admin_url('admin.php?page=ablycuic-settings&deleted=1')); exit; } public function render_settings_page() { $show_success = sanitize_key( (string) filter_input( INPUT_GET, 'success', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ); $show_deleted = sanitize_key( (string) filter_input( INPUT_GET, 'deleted', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ); $show_error = sanitize_key( (string) filter_input( INPUT_GET, 'error', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ); ?>Iconos actualizados correctamente.
Colección eliminada correctamente.
Error al procesar el archivo. Revisa que sea un .zip válido de IcoMoon.
Sube el archivo .zip exportado de tu biblioteca de IcoMoon.io. Los iconos nuevos se sumarán a la colección actual.
get_upload_dir() . '/font_*') as $legacy_font ) { $legacy_fonts[] = basename($legacy_font); } $collections[] = array( 'id' => 'legacy-imported-icons', 'name' => 'Colección importada anteriormente', 'icons' => array_keys((array) ($custom_data['icons'] ?? array())), 'css_files' => array_values(array_filter(array_map('sanitize_file_name', (array) $custom_data['css_files']))), 'font_files' => $legacy_fonts, ); } if ( ! empty($collections) ) : ?>Diseñados y optimizados exclusivamente para empresas del sector viajes. Aumenta tus conversiones, automatiza tus reservas y brinda información en tiempo real con nuestras soluciones especializadas para WordPress.
Sistema integral de reservas que gestiona itinerarios, pasarelas de pago, adicionales de equipo (rentals) y comprobantes de compra dinámicos en una sola interfaz.
Plugin premium para mostrar la disponibilidad oficial de espacios en el Camino Inca (4 Días y 2 Días) mediante un calendario moderno, veloz y multilenguaje.
Colección de iconos personalizados e integración nativa con ACF. Sube tus fuentes ZIP de IcoMoon y mapea tus campos dinámicamente.
Haz clic en el icono para copiar el código HTML: <i class="..."></i>.
Nota: Si agregaste CDNs externos, sus iconos funcionarán en toda la
web pero no aparecerán gráficamente en esta grilla automática a menos que los incluyas en un ZIP de
IcoMoon.
No hay iconos registrados.
$data ) : $label_text = is_array($data) ? $data['label'] : $data; ?>