post_type !== 'post') { return $permalink; } if (has_category('bloques-en-transicion', $post) || has_category('bloques', $post)) { return home_url('/bloques-en-transicion/noticias/' . $post->post_name . '/'); } return $permalink; } /** * Construir tax_query correcta según el tipo de contenido. * * Para noticias: siempre incluye la condición base (category = bloques-en-transicion) * y la combina con AND con los filtros de iniciativa/linea_trabajo. * Para CPTs: solo filtra por iniciativa/linea_trabajo. * * @param string $type Tipo de contenido (noticias, actuaciones, recursos, eventos) * @param string $iniciativa Slug de iniciativa para filtrar (vacío = sin filtro) * @param string $linea Slug de línea de trabajo para filtrar (vacío = sin filtro) * @return array tax_query lista para WP_Query */ private static function build_tax_query($type, $iniciativa = '', $linea = '') { $tax_query = []; // Para noticias: siempre requerir la categoría base if ($type === 'noticias') { $tax_query[] = [ 'taxonomy' => 'category', 'field' => 'slug', 'terms' => ['bloques-en-transicion', 'bloques'], 'include_children' => true, ]; } // Filtro por iniciativa if (!empty($iniciativa)) { $tax_query[] = [ 'taxonomy' => 'iniciativa', 'field' => 'slug', 'terms' => is_array($iniciativa) ? $iniciativa : explode(',', $iniciativa), ]; } // Filtro por línea de trabajo if (!empty($linea)) { $tax_query[] = [ 'taxonomy' => 'linea_trabajo', 'field' => 'slug', 'terms' => is_array($linea) ? $linea : explode(',', $linea), ]; } // Si hay más de una condición, usar AND if (count($tax_query) > 1) { $tax_query['relation'] = 'AND'; } return $tax_query; } /** * Mapeo de tipos */ private static function get_type_config($type) { $configs = [ 'actuaciones' => [ 'post_type' => 'actuacion', 'template' => 'actuaciones', 'label_singular' => __('actuación', 'bloques-transicion'), 'label_plural' => __('actuaciones', 'bloques-transicion'), ], 'recursos' => [ 'post_type' => 'recurso_bloques', 'template' => 'recursos', 'label_singular' => __('recurso', 'bloques-transicion'), 'label_plural' => __('recursos', 'bloques-transicion'), ], 'eventos' => [ 'post_type' => 'evento_bloques', 'template' => 'eventos', 'label_singular' => __('evento', 'bloques-transicion'), 'label_plural' => __('eventos', 'bloques-transicion'), ], 'noticias' => [ 'post_type' => 'post', 'template' => 'noticias', 'label_singular' => __('noticia', 'bloques-transicion'), 'label_plural' => __('noticias', 'bloques-transicion'), ], ]; return $configs[$type] ?? $configs['actuaciones']; } /** * Shortcode principal: [bloques-listado] * * Atributos: * - type: actuaciones|recursos|eventos|noticias (requerido) * - widget: grid|list (default: grid) * - columns: 1|2|3|4 (default: 3) * - limit: número de items a mostrar (default: -1 = todos) * - pagination: true|false - botón "Ver más" (default: false) * - filter: true|false - filtros tipo botonera (default: false) * - search: true|false - campo de búsqueda (default: false) * - iniciativa: slug de iniciativa para filtrar * - linea: slug de línea de trabajo para filtrar * - orderby: date|title|menu_order (default: date) * - order: ASC|DESC (default: DESC) * - upcoming: solo eventos futuros (default: true para eventos) * - class: clase CSS adicional */ public static function render_listado($atts) { $atts = shortcode_atts([ 'type' => 'recursos', 'widget' => 'grid', 'columns' => 3, 'limit' => -1, 'pagination' => 'false', 'filter' => 'false', 'search' => 'false', 'iniciativa' => '', 'linea' => '', 'orderby' => 'date', 'order' => 'DESC', 'upcoming' => 'true', 'class' => '', ], $atts, 'bloques-listado'); $type_config = self::get_type_config($atts['type']); $post_type = $type_config['post_type']; $show_filter = filter_var($atts['filter'], FILTER_VALIDATE_BOOLEAN); $show_search = filter_var($atts['search'], FILTER_VALIDATE_BOOLEAN); $show_pagination = filter_var($atts['pagination'], FILTER_VALIDATE_BOOLEAN); $upcoming = filter_var($atts['upcoming'], FILTER_VALIDATE_BOOLEAN); $limit = intval($atts['limit']); // Query args $args = [ 'post_type' => $post_type, 'posts_per_page' => $limit > 0 ? $limit : -1, 'orderby' => $atts['orderby'], 'order' => $atts['order'], 'post_status' => 'publish', ]; // Ordenar eventos por fecha de inicio if ($post_type === 'evento_bloques') { $args['meta_key'] = 'fecha_inicio'; $args['orderby'] = 'meta_value'; $args['order'] = 'ASC'; // Solo eventos futuros if ($upcoming) { $args['meta_query'] = [ [ 'key' => 'fecha_inicio', 'value' => date('Ymd'), 'compare' => '>=', 'type' => 'DATE', ], ]; } } // Construir tax_query (para noticias incluye automáticamente la categoría base) $tax_query = self::build_tax_query($atts['type'], $atts['iniciativa'], $atts['linea']); if (!empty($tax_query)) { $args['tax_query'] = $tax_query; } $query = new WP_Query($args); $total_posts = $query->found_posts; // Generar ID único para el contenedor $container_id = 'bloques-listado-' . wp_rand(1000, 9999); ob_start(); ?>
'iniciativa', 'hide_empty' => true, ]); $lineas = get_terms([ 'taxonomy' => 'linea_trabajo', 'hide_empty' => true, ]); ob_start(); ?>description); ?>
' . __('No se encontraron resultados.', 'bloques-transicion') . '
'; } $html = ob_get_clean(); wp_send_json_success([ 'html' => $html, 'count' => $query->found_posts, 'label' => $query->found_posts === 1 ? $type_config['label_singular'] : $type_config['label_plural'], ]); } /** * Handler AJAX para cargar más */ public static function ajax_load_more() { check_ajax_referer('bloques_nonce', 'nonce'); $type = sanitize_text_field($_POST['type'] ?? 'recursos'); $offset = intval($_POST['offset'] ?? 0); $limit = intval($_POST['limit'] ?? 9); $widget = sanitize_text_field($_POST['widget'] ?? 'grid'); $iniciativa = sanitize_text_field($_POST['iniciativa'] ?? ''); $linea = sanitize_text_field($_POST['linea_trabajo'] ?? ''); $upcoming = sanitize_text_field($_POST['upcoming'] ?? 'true'); $type_config = self::get_type_config($type); $post_type = $type_config['post_type']; $args = [ 'post_type' => $post_type, 'posts_per_page' => $limit, 'offset' => $offset, 'post_status' => 'publish', ]; // Eventos if ($post_type === 'evento_bloques') { $args['meta_key'] = 'fecha_inicio'; $args['orderby'] = 'meta_value'; $args['order'] = 'ASC'; if (filter_var($upcoming, FILTER_VALIDATE_BOOLEAN)) { $args['meta_query'] = [ [ 'key' => 'fecha_inicio', 'value' => date('Ymd'), 'compare' => '>=', 'type' => 'DATE', ], ]; } } // Construir tax_query $args['tax_query'] = self::build_tax_query($type, $iniciativa, $linea); $query = new WP_Query($args); ob_start(); if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); echo self::render_item(get_post(), $type, $widget); } wp_reset_postdata(); } $html = ob_get_clean(); // Calcular si hay más $total_query = new WP_Query(array_merge($args, ['posts_per_page' => -1, 'offset' => 0, 'fields' => 'ids'])); $has_more = ($offset + $limit) < $total_query->found_posts; wp_send_json_success([ 'html' => $html, 'has_more' => $has_more, 'new_offset' => $offset + $query->post_count, ]); } }