Files
Josep Piguillem 4acc8b62d1 Indicaciones del cliente: adopción solo Perros/Gatos, fichas nuevas y todo configurable
- Adopción sin filtros ni etiquetas: acceso a Perros y Gatos, tarjetas de foto y nombre.
- Ficha nueva: galería, vídeo, fecha de nacimiento o edad aproximada, carácter,
  necesidades, convivencia y hogar que buscamos (textos por idioma, datos compartidos).
- Estados disponible / en proceso / adoptado y animales destacados.
- Botón «Quiero conocerlo/la» con formulario breve (animal incluido) y confirmación.
- Botones para compartir cada ficha (WhatsApp, Facebook, copiar enlace).
- Portada corta según la propuesta del cliente; donaciones, cifras, etc. ocultas.
- Páginas Acoge y Preguntas frecuentes, menú con submenús, sin hotel.
- Aviso de cita previa y lista de ayuda material editables desde el panel.
- Plugin núcleo dividido en módulos (i18n, animales, formularios, APIs).
- Migración 5 idempotente: respeta las páginas editadas por el cliente.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 22:16:24 +02:00

193 lines
6.6 KiB
PHP

<?php
/**
* Personalizador: colores, tipografías, cabecera, contacto, portada y pie.
*
* Todas las opciones se definen en ga_customizer_registry(). Los textos marcados
* como 'i18n' se guardan dos veces: en castellano (clave) y en euskera (clave + '_eu').
*/
defined( 'ABSPATH' ) || exit;
require_once __DIR__ . '/registry.php';
/** Color efectivo: el de la paleta elegida o, si es «Personalizada», el elegido a mano. */
function ga_color( $key ) {
$palette = ga_opt( 'colors', 'palette' );
$palettes = ga_palettes();
if ( isset( $palettes[ $palette ][ $key ] ) ) {
return $palettes[ $palette ][ $key ];
}
return ga_opt( 'colors', $key );
}
/** Clave del theme_mod para un campo (y un idioma). */
function ga_mod_key( $section, $field, $lang = 'es' ) {
return "ga_{$section}_{$field}" . ( 'eu' === $lang ? '_eu' : '' );
}
/** Valor por defecto de un campo. */
function ga_field_default( $field, $lang = 'es' ) {
if ( ! empty( $field['i18n'] ) ) {
return $field[ $lang ] ?? '';
}
return $field['default'] ?? '';
}
/**
* Valor de una opción. Los textos 'i18n' se devuelven en el idioma actual.
*/
function ga_opt( $section, $field ) {
$registry = ga_customizer_registry();
$def = $registry[ $section ]['fields'][ $field ] ?? null;
if ( ! $def ) {
return '';
}
$lang = ! empty( $def['i18n'] ) ? ga_lang() : 'es';
$key = ga_mod_key( $section, $field, $lang );
return get_theme_mod( $key, ga_field_default( $def, $lang ) );
}
/** Imprime un texto del personalizador escapado. */
function ga_opt_e( $section, $field ) {
echo esc_html( ga_opt( $section, $field ) );
}
/** Secciones de portada visibles, ordenadas. */
function ga_home_sections() {
$sections = array();
foreach ( ga_customizer_registry() as $id => $section ) {
if ( ! empty( $section['section'] ) && ga_opt( $id, 'show' ) ) {
$sections[ $id ] = (int) ga_opt( $id, 'order' );
}
}
asort( $sections );
return array_keys( $sections );
}
/* Los datos de contacto y redes del personalizador sustituyen a los predeterminados. */
add_filter( 'ga_info', function ( $info ) {
foreach ( array( 'direccion', 'maps', 'telefono', 'email', 'horario', 'cita' ) as $key ) {
$info[ $key ] = ga_opt( 'contact', $key );
}
foreach ( array( 'instagram', 'facebook', 'tiktok', 'youtube', 'whatsapp' ) as $key ) {
$info[ $key ] = ga_opt( 'social', $key );
}
return $info;
} );
/* ---------- Registro en el personalizador ---------- */
add_action( 'customize_register', function ( WP_Customize_Manager $c ) {
$c->add_panel( 'ga_design', array( 'title' => 'Diseño (Gure Ametsa)', 'priority' => 25 ) );
$c->add_panel( 'ga_info', array( 'title' => 'Contacto y redes', 'priority' => 26 ) );
$c->add_panel( 'ga_adopt', array( 'title' => 'Adopción y colabora', 'priority' => 26 ) );
$c->add_panel( 'ga_home', array(
'title' => 'Portada',
'priority' => 27,
'description' => 'Muestra, oculta y reordena las secciones de la portada. Los textos se escriben en castellano y en euskera.',
) );
$sanitizers = array(
'text' => 'sanitize_text_field',
'textarea' => 'sanitize_textarea_field',
'url' => 'esc_url_raw',
'email' => 'sanitize_email',
'color' => 'sanitize_hex_color',
'checkbox' => fn( $v ) => (bool) $v,
'number' => 'absint',
'range' => 'absint',
'image' => 'absint',
'video' => 'absint',
);
foreach ( ga_customizer_registry() as $sid => $section ) {
$section_id = "ga_section_$sid";
$c->add_section( $section_id, array(
'title' => $section['title'],
'panel' => $section['panel'],
'description' => $section['description'] ?? '',
) );
foreach ( $section['fields'] as $fid => $field ) {
$langs = ! empty( $field['i18n'] ) ? array( 'es', 'eu' ) : array( 'es' );
foreach ( $langs as $lang ) {
$key = ga_mod_key( $sid, $fid, $lang );
$sanitize = $sanitizers[ $field['type'] ] ?? null;
if ( 'select' === $field['type'] ) {
$choices = $field['choices'];
$default = $field['default'];
$sanitize = fn( $v ) => array_key_exists( $v, $choices ) ? $v : $default;
}
$c->add_setting( $key, array(
'default' => ga_field_default( $field, $lang ),
'sanitize_callback' => $sanitize,
) );
$label = $field['label'] . ( ! empty( $field['i18n'] ) ? ( 'eu' === $lang ? ' · EU' : ' · ES' ) : '' );
$args = array(
'label' => $label,
'section' => $section_id,
'description' => $field['description'] ?? '',
'priority' => empty( $field['i18n'] ) ? 50 : ( 'eu' === $lang ? 200 : 100 ),
);
switch ( $field['type'] ) {
case 'color':
$c->add_control( new WP_Customize_Color_Control( $c, $key, $args ) );
break;
case 'image':
case 'video':
$c->add_control( new WP_Customize_Media_Control( $c, $key, $args + array( 'mime_type' => $field['type'] ) ) );
break;
case 'range':
case 'number':
$args['type'] = $field['type'];
$args['input_attrs'] = array_filter( array(
'min' => $field['min'] ?? null,
'max' => $field['max'] ?? null,
'step' => $field['step'] ?? 1,
), fn( $v ) => null !== $v );
if ( 'range' === $field['type'] ) {
$args['description'] = trim( $args['description'] . ' Por defecto: ' . $field['default'] . '.' );
}
$c->add_control( $key, $args );
break;
case 'select':
$c->add_control( $key, $args + array( 'type' => 'select', 'choices' => $field['choices'] ) );
break;
default:
$c->add_control( $key, $args + array( 'type' => $field['type'] ) );
}
}
}
// Separador visual entre castellano y euskera.
$has_i18n = (bool) array_filter( $section['fields'], fn( $f ) => ! empty( $f['i18n'] ) );
if ( $has_i18n ) {
foreach ( array( 'es' => array( 99, 'Castellano' ), 'eu' => array( 199, 'Euskara' ) ) as $lang => [ $priority, $title ] ) {
$c->add_setting( "ga_sep_{$sid}_{$lang}", array( 'sanitize_callback' => '__return_empty_string' ) );
$c->add_control( new GA_Customize_Heading_Control( $c, "ga_sep_{$sid}_{$lang}", array(
'label' => $title,
'section' => $section_id,
'priority' => $priority,
) ) );
}
}
}
} );
add_action( 'customize_register', function () {
if ( class_exists( 'GA_Customize_Heading_Control' ) ) {
return;
}
/** Encabezado sin campo, para separar idiomas. */
class GA_Customize_Heading_Control extends WP_Customize_Control {
public $type = 'ga-heading';
public function render_content() {
printf(
'<h3 style="margin:18px 0 4px;padding:6px 10px;background:#1e5b3e;color:#fff;border-radius:4px;font-size:13px;letter-spacing:.06em;text-transform:uppercase">%s</h3>',
esc_html( $this->label )
);
}
}
}, 1 );