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>
This commit is contained in:
2026-09-24 22:16:24 +02:00
parent 532bc620de
commit 4acc8b62d1
41 changed files with 1985 additions and 1533 deletions

View File

@@ -0,0 +1,178 @@
<?php
/**
* Formularios (contacto general y «Quiero conocerlo/la»), ayuda material y cita previa.
*/
defined( 'ABSPATH' ) || exit;
/** Motivos del formulario general. */
function ga_contact_reasons() {
return array(
'adopcion' => ga__( 'Adopción', 'Adopzioa' ),
'acogida' => ga__( 'Casa de acogida', 'Harrera-etxea' ),
'voluntario' => ga__( 'Voluntariado', 'Boluntariotza' ),
'material' => ga__( 'Ayuda material', 'Laguntza materiala' ),
'otro' => ga__( 'Otro', 'Beste bat' ),
);
}
/** Texto de confirmación tras enviar. */
function ga_confirmation_text( $is_animal ) {
return $is_animal
? ga_setting( 'adopcion', 'confirm', ga__( 'Hemos recibido tu solicitud. Nos pondremos en contacto contigo para enviarte nuestro cuestionario y conoceros un poquito mejor.', 'Zure eskaera jaso dugu. Zurekin harremanetan jarriko gara gure galdetegia bidaltzeko eta elkar hobeto ezagutzeko.' ) )
: ga_setting( 'adopcion', 'confirm_contact', ga__( '¡Gracias! Hemos recibido tu mensaje y te responderemos lo antes posible.', 'Eskerrik asko! Zure mezua jaso dugu eta ahalik eta lasterren erantzungo dizugu.' ) );
}
/**
* [ga_contacto] formulario general.
* [ga_contacto animal="123"] formulario breve «Quiero conocerlo/la» (el animal va incluido).
*/
add_shortcode( 'ga_contacto', function ( $atts ) {
$atts = shortcode_atts( array( 'animal' => 0 ), $atts, 'ga_contacto' );
$animal_id = absint( $atts['animal'] ) ?: absint( $_GET['adoptar'] ?? 0 );
$animal_id = $animal_id && 'animal' === get_post_type( $animal_id ) ? $animal_id : 0;
$is_animal = absint( $atts['animal'] ) > 0; // Formulario breve dentro de la ficha.
$status = sanitize_key( $_GET['enviado'] ?? '' );
$preselect = $animal_id ? 'adopcion' : sanitize_key( $_GET['motivo'] ?? '' );
$privacy = ga_page_url( 'politica-de-privacidad' );
$anchor = $is_animal ? 'conocer' : 'formulario';
ob_start();
if ( 'ok' === $status ) {
echo '<div class="ga-notice ga-notice--ok" role="status">' . esc_html( ga_confirmation_text( (bool) $animal_id ) ) . '</div>';
if ( $is_animal ) {
return ob_get_clean();
}
} elseif ( 'error' === $status ) {
echo '<div class="ga-notice ga-notice--error" role="alert">'
. esc_html( ga__( 'No hemos podido enviar el mensaje. Revisa los campos obligatorios o escríbenos a ', 'Ezin izan dugu mezua bidali. Egiaztatu derrigorrezko eremuak edo idatzi iezaguzu helbide honetara: ' ) . ga_info( 'email' ) ) . '</div>';
}
?>
<form class="ga-form" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<input type="hidden" name="action" value="ga_contacto">
<input type="hidden" name="idioma" value="<?php echo esc_attr( ga_lang() ); ?>">
<input type="hidden" name="redirect" value="<?php echo esc_url( get_permalink() . '#' . $anchor ); ?>">
<?php if ( $animal_id ) : ?>
<input type="hidden" name="animal_id" value="<?php echo (int) $animal_id; ?>">
<?php endif; ?>
<?php wp_nonce_field( 'ga_contacto', 'ga_nonce' ); ?>
<div class="ga-hp" aria-hidden="true"><label>No rellenar <input type="text" name="web" tabindex="-1" autocomplete="off"></label></div>
<div class="ga-form__row">
<label><?php ga_e( 'Nombre', 'Izena' ); ?> *<input type="text" name="nombre" required autocomplete="name"></label>
<label><?php ga_e( 'Correo electrónico', 'Posta elektronikoa' ); ?> *<input type="email" name="email" required autocomplete="email"></label>
</div>
<div class="ga-form__row">
<label><?php ga_e( 'Teléfono', 'Telefonoa' ); ?><?php echo $is_animal ? ' *' : ''; ?><input type="tel" name="telefono" autocomplete="tel"<?php echo $is_animal ? ' required' : ''; ?>></label>
<?php if ( ! $is_animal ) : ?>
<label><?php ga_e( 'Motivo', 'Arrazoia' ); ?>
<select name="motivo">
<?php foreach ( ga_contact_reasons() as $value => $label ) : ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $preselect, $value ); ?>><?php echo esc_html( $label ); ?></option>
<?php endforeach; ?>
</select>
</label>
<?php endif; ?>
</div>
<?php if ( $animal_id && ! $is_animal ) : ?>
<p class="ga-form__animal"><?php echo esc_html( ga__( 'Animal: ', 'Animalia: ' ) . get_the_title( $animal_id ) ); ?></p>
<?php endif; ?>
<label><?php ga_e( 'Mensaje', 'Mezua' ); ?><?php echo $is_animal ? ' (' . esc_html( ga__( 'opcional', 'aukerakoa' ) ) . ')' : ' *'; ?>
<textarea name="mensaje" rows="<?php echo $is_animal ? 4 : 6; ?>"<?php echo $is_animal ? '' : ' required'; ?>></textarea>
</label>
<label class="ga-form__check">
<input type="checkbox" name="privacidad" value="1" required>
<span>
<?php if ( 'eu' === ga_lang() ) : ?>
<a href="<?php echo esc_url( $privacy ); ?>" target="_blank">Pribatutasun-politika</a> irakurri eta onartzen dut. *
<?php else : ?>
He leído y acepto la <a href="<?php echo esc_url( $privacy ); ?>" target="_blank">política de privacidad</a>. *
<?php endif; ?>
</span>
</label>
<button type="submit" class="ga-btn"><?php echo esc_html( $is_animal ? ga__( 'Enviar solicitud', 'Eskaera bidali' ) : ga__( 'Enviar mensaje', 'Mezua bidali' ) ); ?></button>
</form>
<?php
return ob_get_clean();
} );
add_action( 'admin_post_nopriv_ga_contacto', 'ga_handle_contact' );
add_action( 'admin_post_ga_contacto', 'ga_handle_contact' );
function ga_handle_contact() {
$redirect = wp_validate_redirect( esc_url_raw( wp_unslash( $_POST['redirect'] ?? '' ) ), home_url( '/' ) );
$go = function ( $status ) use ( $redirect ) {
$parts = explode( '#', $redirect, 2 );
wp_safe_redirect( add_query_arg( 'enviado', $status, $parts[0] ) . ( isset( $parts[1] ) ? '#' . $parts[1] : '' ) );
exit;
};
if ( ! isset( $_POST['ga_nonce'] ) || ! wp_verify_nonce( $_POST['ga_nonce'], 'ga_contacto' ) ) {
$go( 'error' );
}
if ( ! empty( $_POST['web'] ) ) { // Honeypot: los bots rellenan el campo oculto.
$go( 'ok' );
}
$animal_id = absint( $_POST['animal_id'] ?? 0 );
$animal = $animal_id && 'animal' === get_post_type( $animal_id ) ? get_the_title( $animal_id ) : '';
$reasons = ga_contact_reasons();
$reason = $animal ? 'adopcion' : sanitize_key( $_POST['motivo'] ?? '' );
$data = array(
'nombre' => sanitize_text_field( wp_unslash( $_POST['nombre'] ?? '' ) ),
'email' => sanitize_email( wp_unslash( $_POST['email'] ?? '' ) ),
'telefono' => sanitize_text_field( wp_unslash( $_POST['telefono'] ?? '' ) ),
'motivo' => $reasons[ $reason ] ?? $reasons['otro'],
'mensaje' => sanitize_textarea_field( wp_unslash( $_POST['mensaje'] ?? '' ) ),
'idioma' => 'eu' === ( $_POST['idioma'] ?? '' ) ? 'Euskera' : 'Castellano',
);
$message_required = ! $animal;
if ( ! $data['nombre'] || ! is_email( $data['email'] ) || ( $message_required && ! $data['mensaje'] ) || empty( $_POST['privacidad'] ) ) {
$go( 'error' );
}
$subject = $animal
? sprintf( '[Web] Quiero conocer a %s – %s', $animal, $data['nombre'] )
: sprintf( '[Web] %s – %s', $data['motivo'], $data['nombre'] );
$body = sprintf(
"Nombre: %s\nEmail: %s\nTeléfono: %s\nMotivo: %s\nAnimal: %s\nIdioma: %s\n\n%s",
$data['nombre'], $data['email'], $data['telefono'] ?: '-', $data['motivo'],
$animal ? $animal . ' — ' . get_permalink( $animal_id ) : '-',
$data['idioma'], $data['mensaje'] ?: '(sin mensaje)'
);
// Se guarda siempre en el panel, aunque falle el correo.
wp_insert_post( array(
'post_type' => 'ga_mensaje',
'post_status' => 'private',
'post_title' => $subject,
'post_content' => $body,
) );
wp_mail( ga_contact_email(), $subject, $body, array( 'Reply-To: ' . $data['nombre'] . ' <' . $data['email'] . '>' ) );
$go( 'ok' );
}
/* [ga_necesidades] lista de ayuda material (se edita en Personalizar → Colabora) */
add_shortcode( 'ga_necesidades', function () {
$items = array_filter( array_map( 'trim', explode( "\n", (string) ga_setting( 'material', 'list', '' ) ) ) );
$note = ga_setting( 'material', 'note', '' );
ob_start();
if ( $items ) {
echo '<ul class="ga-needs-list">';
foreach ( $items as $item ) {
echo '<li>' . esc_html( $item ) . '</li>';
}
echo '</ul>';
}
if ( $note ) {
echo '<p class="ga-needs-note">' . esc_html( $note ) . '</p>';
}
return ob_get_clean();
} );
/* [ga_cita] aviso de cita previa */
add_shortcode( 'ga_cita', function () {
$text = ga_info( 'cita' );
return $text ? '<p class="ga-cita">' . esc_html( $text ) . '</p>' : '';
} );