diff --git a/wp-content/mu-plugins/gure-ametsa-core.php b/wp-content/mu-plugins/gure-ametsa-core.php
index 256fc2b..edaff52 100644
--- a/wp-content/mu-plugins/gure-ametsa-core.php
+++ b/wp-content/mu-plugins/gure-ametsa-core.php
@@ -12,3 +12,4 @@ require_once __DIR__ . '/gure-ametsa/i18n.php';
require_once __DIR__ . '/gure-ametsa/animals.php';
require_once __DIR__ . '/gure-ametsa/forms.php';
require_once __DIR__ . '/gure-ametsa/api.php';
+require_once __DIR__ . '/gure-ametsa/admin.php';
diff --git a/wp-content/mu-plugins/gure-ametsa/admin.php b/wp-content/mu-plugins/gure-ametsa/admin.php
new file mode 100644
index 0000000..db0f8f2
--- /dev/null
+++ b/wp-content/mu-plugins/gure-ametsa/admin.php
@@ -0,0 +1,201 @@
+ 'animal',
+ 'post_status' => array( 'publish', 'draft', 'pending' ),
+ 'numberposts' => -1,
+ 'lang' => function_exists( 'pll_default_language' ) ? pll_default_language() : '',
+ ) );
+ $days = max( 1, (int) ga_setting( 'adopcion', 'review_days', 30 ) );
+ $issues = array(
+ 'photo' => array( 'Sin foto principal', array() ),
+ 'eu' => array( 'Sin versión en euskera', array() ),
+ 'text' => array( 'Sin texto «Cómo es» (castellano o euskera)', array() ),
+ 'species' => array( 'Sin especie (perro o gato)', array() ),
+ 'process' => array( sprintf( 'En proceso desde hace más de %d días', $days ), array() ),
+ );
+ foreach ( $animals as $animal ) {
+ if ( 'adoptado' === ga_status( $animal->ID ) ) {
+ continue;
+ }
+ $eu = function_exists( 'pll_get_post' ) ? pll_get_post( $animal->ID, 'eu' ) : 0;
+ if ( ! has_post_thumbnail( $animal ) ) {
+ $issues['photo'][1][] = $animal;
+ }
+ if ( function_exists( 'pll_get_post' ) && ! $eu ) {
+ $issues['eu'][1][] = $animal;
+ }
+ if ( '' === ga_get( $animal->ID, 'caracter' ) || ( $eu && '' === ga_get( $eu, 'caracter' ) ) ) {
+ $issues['text'][1][] = $animal;
+ }
+ if ( ! has_term( '', 'especie', $animal ) ) {
+ $issues['species'][1][] = $animal;
+ }
+ $since = ga_get( $animal->ID, 'estado_desde' );
+ if ( 'proceso' === ga_status( $animal->ID ) && $since && strtotime( $since ) < strtotime( "-$days days" ) ) {
+ $issues['process'][1][] = $animal;
+ }
+ }
+ return array_filter( $issues, fn( $i ) => ! empty( $i[1] ) );
+}
+
+add_action( 'wp_dashboard_setup', function () {
+ if ( ! current_user_can( 'edit_posts' ) ) {
+ return;
+ }
+ wp_add_dashboard_widget( 'ga_review', 'Bilagun · Fichas por revisar', function () {
+ $issues = ga_review_issues();
+ if ( ! $issues ) {
+ echo '
✓ Todas las fichas están completas.
';
+ }
+ foreach ( $issues as [ $title, $animals ] ) {
+ printf( '%s (%d)
', esc_html( $title ), count( $animals ) );
+ foreach ( array_slice( $animals, 0, 8 ) as $animal ) {
+ printf( '- %s
', esc_url( get_edit_post_link( $animal ) ), esc_html( get_the_title( $animal ) ) );
+ }
+ if ( count( $animals ) > 8 ) {
+ echo '- …
';
+ }
+ echo '
';
+ }
+ $messages = (int) wp_count_posts( 'ga_mensaje' )->private;
+ printf(
+ 'Añadir animal Mensajes (%d)
',
+ esc_url( admin_url( 'post-new.php?post_type=animal' ) ),
+ esc_url( admin_url( 'edit.php?post_type=ga_mensaje' ) ),
+ $messages
+ );
+ } );
+ // Arriba del todo.
+ global $wp_meta_boxes;
+ $widget = $wp_meta_boxes['dashboard']['normal']['core']['ga_review'] ?? null;
+ if ( $widget ) {
+ unset( $wp_meta_boxes['dashboard']['normal']['core']['ga_review'] );
+ $wp_meta_boxes['dashboard']['normal']['high']['ga_review'] = $widget;
+ }
+} );
+
+/* -------------------------------------------------------------------------
+ * Listado de animales: acciones en bloque y destacar con un clic
+ * ---------------------------------------------------------------------- */
+
+function ga_bulk_actions() {
+ return array(
+ 'ga_disponible' => array( 'Estado: Disponible', 'estado', 'disponible' ),
+ 'ga_proceso' => array( 'Estado: En proceso', 'estado', 'proceso' ),
+ 'ga_adoptado' => array( 'Estado: Adoptado', 'estado', 'adoptado' ),
+ 'ga_destacar' => array( 'Destacar en portada', 'destacado', '1' ),
+ 'ga_no_destacar' => array( 'Quitar de destacados', 'destacado', '' ),
+ );
+}
+
+add_filter( 'bulk_actions-edit-animal', function ( $actions ) {
+ foreach ( ga_bulk_actions() as $key => [ $label ] ) {
+ $actions[ $key ] = $label;
+ }
+ return $actions;
+} );
+
+add_filter( 'handle_bulk_actions-edit-animal', function ( $redirect, $action, $ids ) {
+ $actions = ga_bulk_actions();
+ if ( ! isset( $actions[ $action ] ) ) {
+ return $redirect;
+ }
+ [ , $field, $value ] = $actions[ $action ];
+ $done = 0;
+ foreach ( $ids as $id ) {
+ if ( current_user_can( 'edit_post', $id ) ) {
+ ga_update_meta_all_languages( $id, "_ga_$field", $value );
+ ++$done;
+ }
+ }
+ return add_query_arg( 'ga_bulk', $done, $redirect );
+}, 10, 3 );
+
+add_action( 'admin_notices', function () {
+ if ( isset( $_GET['ga_bulk'] ) && 'edit-animal' === ( get_current_screen()->id ?? '' ) ) {
+ printf( '%d animales actualizados (también su versión en el otro idioma).
', absint( $_GET['ga_bulk'] ) );
+ }
+} );
+
+/* Estrella clicable en la columna «★» */
+add_action( 'manage_animal_posts_custom_column', function ( $col, $post_id ) {
+ if ( 'ga_destacado' !== $col || ! current_user_can( 'edit_post', $post_id ) ) {
+ return;
+ }
+ $on = (bool) ga_get( $post_id, 'destacado' );
+ $url = wp_nonce_url( admin_url( 'admin-post.php?action=ga_toggle_featured&post=' . $post_id ), 'ga_toggle_featured_' . $post_id );
+ printf(
+ '%s',
+ esc_url( $url ),
+ esc_attr( $on ? 'Quitar de destacados' : 'Destacar en portada' ),
+ $on ? '#bd8537' : '#c3c4c7',
+ $on ? '★' : '☆'
+ );
+}, 20, 2 );
+
+add_action( 'admin_post_ga_toggle_featured', function () {
+ $id = absint( $_GET['post'] ?? 0 );
+ check_admin_referer( 'ga_toggle_featured_' . $id );
+ if ( ! current_user_can( 'edit_post', $id ) ) {
+ wp_die( 'Sin permiso.' );
+ }
+ ga_update_meta_all_languages( $id, '_ga_destacado', ga_get( $id, 'destacado' ) ? '' : '1' );
+ wp_safe_redirect( wp_get_referer() ?: admin_url( 'edit.php?post_type=animal' ) );
+ exit;
+} );
+
+/* -------------------------------------------------------------------------
+ * Protección de datos: borrado automático de mensajes antiguos
+ * ---------------------------------------------------------------------- */
+
+add_action( 'init', function () {
+ if ( ! wp_next_scheduled( 'ga_purge_messages' ) ) {
+ wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'ga_purge_messages' );
+ }
+} );
+
+add_action( 'ga_purge_messages', 'ga_purge_messages' );
+
+/** Borra los mensajes del formulario más antiguos que el plazo configurado (0 = nunca). */
+function ga_purge_messages() {
+ $months = (int) ga_setting( 'adopcion', 'retention_months', 12 );
+ if ( $months <= 0 ) {
+ return 0;
+ }
+ $old = get_posts( array(
+ 'post_type' => 'ga_mensaje',
+ 'post_status' => 'any',
+ 'numberposts' => 200,
+ 'fields' => 'ids',
+ 'date_query' => array( array( 'before' => "$months months ago" ) ),
+ ) );
+ foreach ( $old as $id ) {
+ wp_delete_post( $id, true );
+ }
+ return count( $old );
+}
diff --git a/wp-content/mu-plugins/gure-ametsa/animals.php b/wp-content/mu-plugins/gure-ametsa/animals.php
index 1c9bed4..745a083 100644
--- a/wp-content/mu-plugins/gure-ametsa/animals.php
+++ b/wp-content/mu-plugins/gure-ametsa/animals.php
@@ -382,8 +382,6 @@ add_action( 'manage_animal_posts_custom_column', function ( $col, $post_id ) {
echo get_the_post_thumbnail( $post_id, array( 60, 60 ) );
} elseif ( 'ga_estado' === $col ) {
echo esc_html( ga_label( $post_id, 'estado' ) );
- } elseif ( 'ga_destacado' === $col ) {
- echo ga_get( $post_id, 'destacado' ) ? '★' : '';
}
}, 10, 2 );
diff --git a/wp-content/themes/gure-ametsa/inc/registry.php b/wp-content/themes/gure-ametsa/inc/registry.php
index 923e2b4..6f50185 100644
--- a/wp-content/themes/gure-ametsa/inc/registry.php
+++ b/wp-content/themes/gure-ametsa/inc/registry.php
@@ -236,6 +236,8 @@ function ga_customizer_registry() {
'process_note' => $t( 'Proceso · nota', 'Enviar la solicitud no confirma una adopción: es el primer paso para conocernos.', 'Eskaera bidaltzeak ez du adopzioa baieztatzen: elkar ezagutzeko lehen urratsa da.' ),
'share' => array( 'type' => 'checkbox', 'label' => 'Botones para compartir la ficha (WhatsApp, Facebook, copiar enlace)', 'default' => true ),
'form_email' => array( 'type' => 'email', 'label' => 'Correo que recibe las solicitudes y mensajes', 'default' => '', 'description' => 'Vacío = el de la variable GA_CONTACT_EMAIL del servidor.' ),
+ 'review_days' => array( 'type' => 'number', 'label' => 'Avisar en el Escritorio si un animal lleva «En proceso» más de (días)', 'default' => 30, 'min' => 1, 'max' => 365 ),
+ 'retention_months' => array( 'type' => 'number', 'label' => 'Borrar los mensajes del formulario pasados (meses, 0 = nunca)', 'default' => 12, 'min' => 0, 'max' => 120, 'description' => 'Protección de datos: no guardar datos personales más tiempo del necesario.' ),
),
),
'material' => array(