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;
}
/**
* 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'),
'tax_query' => [
'relation' => 'OR',
[
'taxonomy' => 'category',
'field' => 'slug',
'terms' => ['bloques-en-transicion', 'bloques'],
],
],
],
];
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 según el tipo
if ($atts['type'] === 'noticias') {
// Para noticias: siempre filtrar por categoría bloques-en-transicion
$args['tax_query'] = [
[
'taxonomy' => 'category',
'field' => 'slug',
'terms' => ['bloques-en-transicion', 'bloques'],
'include_children' => true,
],
];
} else {
// Para CPTs: filtros por taxonomía custom
$tax_query = [];
if (!empty($atts['iniciativa'])) {
$tax_query[] = [
'taxonomy' => 'iniciativa',
'field' => 'slug',
'terms' => explode(',', $atts['iniciativa']),
];
}
if (!empty($atts['linea'])) {
$tax_query[] = [
'taxonomy' => 'linea_trabajo',
'field' => 'slug',
'terms' => explode(',', $atts['linea']),
];
}
if (count($tax_query) > 1) {
$tax_query['relation'] = 'AND';
}
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();
?>
'category',
'child_of' => $bloques_cat->term_id,
'hide_empty' => true,
]);
}
if (!empty($child_cats) && !is_wp_error($child_cats)): ?>
'iniciativa',
'hide_empty' => true,
]);
$lineas = get_terms([
'taxonomy' => 'linea_trabajo',
'hide_empty' => true,
]);
?>
$post->ID,
'title' => get_the_title($post),
'excerpt' => get_the_excerpt($post),
'content' => apply_filters('the_content', $post->post_content),
'permalink' => get_permalink($post),
'thumbnail' => get_the_post_thumbnail_url($post, 'medium_large'),
'thumbnail_alt' => get_post_meta(get_post_thumbnail_id($post), '_wp_attachment_image_alt', true),
'iniciativas' => wp_get_post_terms($post->ID, 'iniciativa'),
'lineas' => wp_get_post_terms($post->ID, 'linea_trabajo'),
'widget' => $widget,
'type' => $type,
'date' => get_the_date('j M Y', $post),
];
// Datos específicos por tipo
switch ($type) {
case 'actuaciones':
$data['es_piloto'] = get_field('es_piloto', $post->ID);
$data['direccion'] = get_field('direccion', $post->ID);
$data['localidad'] = get_field('localidad', $post->ID);
$data['latitud'] = get_field('latitud', $post->ID);
$data['longitud'] = get_field('longitud', $post->ID);
break;
case 'recursos':
$data['archivo'] = get_field('archivo', $post->ID);
$data['tipo_recurso'] = get_field('tipo_recurso', $post->ID);
$data['url_externa'] = get_field('url_externa', $post->ID);
break;
case 'eventos':
$data['fecha_inicio'] = get_field('fecha_inicio', $post->ID);
$data['hora_inicio'] = get_field('hora_inicio', $post->ID);
$data['fecha_fin'] = get_field('fecha_fin', $post->ID);
$data['hora_fin'] = get_field('hora_fin', $post->ID);
$data['lugar'] = get_field('lugar', $post->ID);
$data['direccion'] = get_field('direccion', $post->ID);
$data['url_online'] = get_field('url_online', $post->ID);
$data['url_inscripcion'] = get_field('url_inscripcion', $post->ID);
$data['tipos_evento'] = wp_get_post_terms($post->ID, 'tipo_evento');
break;
case 'noticias':
// Obtener categorías del post (excluyendo bloques-en-transicion)
$categories = get_the_category($post->ID);
$display_category = null;
$category_color = '#F97316';
foreach ($categories as $cat) {
if (!in_array($cat->slug, ['bloques-en-transicion', 'bloques'])) {
$display_category = $cat;
// Intentar obtener color de iniciativa si existe con ese slug
$iniciativa = get_term_by('slug', $cat->slug, 'iniciativa');
if ($iniciativa) {
$color = get_field('color', $iniciativa);
if ($color) {
$category_color = $color;
}
}
break;
}
}
if (!$display_category && !empty($categories)) {
$display_category = $categories[0];
}
$data['category'] = $display_category;
$data['category_color'] = $category_color;
break;
}
return $data;
}
/**
* Shortcode para grid simple: [bloques-grid]
*/
public static function render_grid($atts) {
$atts['widget'] = 'grid';
return self::render_listado($atts);
}
/**
* Shortcode para recursos: [bloques-recursos]
*/
public static function render_recursos($atts) {
$atts = shortcode_atts([
'widget' => 'grid',
'columns' => 3,
'limit' => -1,
'pagination' => 'false',
'filter' => 'true',
'search' => 'true',
'class' => '',
], $atts, 'bloques-recursos');
$atts['type'] = 'recursos';
return self::render_listado($atts);
}
/**
* Shortcode para noticias: [bloques-noticias]
*/
public static function render_noticias($atts) {
$atts = shortcode_atts([
'widget' => 'grid',
'columns' => 3,
'limit' => -1,
'pagination' => 'false',
'filter' => 'false',
'class' => '',
], $atts, 'bloques-noticias');
$atts['type'] = 'noticias';
return self::render_listado($atts);
}
/**
* Shortcode específico para eventos: [bloques-eventos]
*/
public static function render_eventos($atts) {
$atts = shortcode_atts([
'widget' => 'list',
'columns' => 1,
'limit' => 5,
'upcoming' => 'true',
'pagination' => 'false',
'filter' => 'false',
'class' => '',
], $atts, 'bloques-eventos');
$atts['type'] = 'eventos';
return self::render_listado($atts);
}
/**
* Shortcode para Novedades con header: [bloques-novedades]
*/
public static function render_novedades($atts) {
$atts = shortcode_atts([
'limit' => 3,
'columns' => 1,
'title' => __('Novedades', 'bloques-transicion'),
'subtitle' => __('Últimas noticias del proyecto', 'bloques-transicion'),
'show_header' => 'true',
'link_text' => __('Ver todas las novedades', 'bloques-transicion'),
'link_url' => '/bloques-en-transicion/noticias/',
'class' => '',
], $atts, 'bloques-novedades');
$show_header = filter_var($atts['show_header'], FILTER_VALIDATE_BOOLEAN);
ob_start();
?>
'noticias',
'widget' => 'compact',
'columns' => $atts['columns'],
'limit' => $atts['limit'],
'class' => 'bloques-novedades-list',
]);
?>
4,
'title' => __('Agenda', 'bloques-transicion'),
'subtitle' => __('Próximas actividades', 'bloques-transicion'),
'show_header' => 'true',
'link_text' => __('Ver calendario completo', 'bloques-transicion'),
'link_url' => '/bloques-en-transicion/eventos/',
'upcoming' => 'true',
'class' => '',
], $atts, 'bloques-agenda');
$show_header = filter_var($atts['show_header'], FILTER_VALIDATE_BOOLEAN);
ob_start();
?>
'eventos',
'widget' => 'agenda',
'columns' => 1,
'limit' => $atts['limit'],
'upcoming' => $atts['upcoming'],
'class' => 'bloques-agenda-list',
]);
?>
'cards',
'columns' => 4,
'show_description' => 'true',
'class' => '',
], $atts, 'bloques-iniciativas');
$iniciativas = get_terms([
'taxonomy' => 'iniciativa',
'hide_empty' => false,
]);
$show_desc = filter_var($atts['show_description'], FILTER_VALIDATE_BOOLEAN);
ob_start();
?>
description): ?>
description); ?>
'icons',
'columns' => 6,
'show_description' => 'true',
'class' => '',
], $atts, 'bloques-lineas');
$lineas = get_terms([
'taxonomy' => 'linea_trabajo',
'hide_empty' => false,
]);
$show_desc = filter_var($atts['show_description'], FILTER_VALIDATE_BOOLEAN);
ob_start();
?>
description): ?>
description); ?>
$post_type,
'posts_per_page' => -1,
'post_status' => 'publish',
];
// Búsqueda
if (!empty($search)) {
$args['s'] = $search;
}
// 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 según el tipo
if ($type === 'noticias') {
// Para noticias: filtrar por categorías de WordPress
$base_cat_query = [
'taxonomy' => 'category',
'field' => 'slug',
'terms' => ['bloques-en-transicion', 'bloques'],
'include_children' => true,
];
if (!empty($category)) {
// Filtro activo: base AND categoría seleccionada
$args['tax_query'] = [
'relation' => 'AND',
$base_cat_query,
[
'taxonomy' => 'category',
'field' => 'slug',
'terms' => $category,
'include_children' => true,
],
];
} else {
// Sin filtro: solo la condición base
$args['tax_query'] = [$base_cat_query];
}
} else {
// Para CPTs: filtrar por iniciativa y/o línea de trabajo
$tax_query = [];
if (!empty($iniciativa)) {
$tax_query[] = [
'taxonomy' => 'iniciativa',
'field' => 'slug',
'terms' => $iniciativa,
];
}
if (!empty($linea)) {
$tax_query[] = [
'taxonomy' => 'linea_trabajo',
'field' => 'slug',
'terms' => $linea,
];
}
if (count($tax_query) > 1) {
$tax_query['relation'] = 'AND';
}
if (!empty($tax_query)) {
$args['tax_query'] = $tax_query;
}
}
$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();
} else {
echo '' . __('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'] ?? '');
$category = sanitize_text_field($_POST['category'] ?? '');
$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 según el tipo
if ($type === 'noticias') {
$base_cat_query = [
'taxonomy' => 'category',
'field' => 'slug',
'terms' => ['bloques-en-transicion', 'bloques'],
'include_children' => true,
];
if (!empty($category)) {
$args['tax_query'] = [
'relation' => 'AND',
$base_cat_query,
[
'taxonomy' => 'category',
'field' => 'slug',
'terms' => $category,
'include_children' => true,
],
];
} else {
$args['tax_query'] = [$base_cat_query];
}
} else {
$tax_query = [];
if (!empty($iniciativa)) {
$tax_query[] = [
'taxonomy' => 'iniciativa',
'field' => 'slug',
'terms' => $iniciativa,
];
}
if (!empty($linea)) {
$tax_query[] = [
'taxonomy' => 'linea_trabajo',
'field' => 'slug',
'terms' => $linea,
];
}
if (count($tax_query) > 1) {
$tax_query['relation'] = 'AND';
}
if (!empty($tax_query)) {
$args['tax_query'] = $tax_query;
}
}
$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,
]);
}
}