From c1bbf78b5c6fd17ddf202bb65ddc09eb2ab40b4c Mon Sep 17 00:00:00 2001 From: Josep Piguillem Date: Fri, 25 Sep 2026 10:17:14 +0200 Subject: [PATCH] Panel: fichas por revisar, acciones en bloque, destacar con un clic y borrado de mensajes antiguos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Escritorio: animales sin foto, sin euskera, sin «Cómo es», sin especie o en proceso desde hace más de N días (configurable). - Listado: acciones en bloque de estado y destacado (en los dos idiomas) y estrella clicable. - Se guarda la fecha del último cambio de estado. - Borrado diario de mensajes del formulario más antiguos que N meses (12 por defecto). Co-Authored-By: Claude Opus 5.5 --- wp-content/mu-plugins/gure-ametsa-core.php | 1 + wp-content/mu-plugins/gure-ametsa/admin.php | 201 ++++++++++++++++++ wp-content/mu-plugins/gure-ametsa/animals.php | 2 - .../themes/gure-ametsa/inc/registry.php | 2 + 4 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 wp-content/mu-plugins/gure-ametsa/admin.php 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)

'; + } + $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(