Aquí tienes la versión integrada y depurada con todas las mejoras, organizada por archivos:
1. categorias-como-paginas.php (Actualizado)
<?php
/*
Plugin Name: Categorías como Páginas
Description: Sustituye las categorías por páginas editables
Version: 1.1.0
Author: Tu Nombre
Text Domain: categorias-como-paginas
*/
defined('ABSPATH') || exit;
// Constantes del plugin
define('CCP_VERSION', '1.1.0');
define('CCP_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('CCP_PLUGIN_URL', plugin_dir_url(__FILE__));
// Carga los archivos necesarios
require_once CCP_PLUGIN_DIR . 'includes/class-category-to-page.php';
require_once CCP_PLUGIN_DIR . 'includes/template-handler.php';
if (is_admin()) {
require_once CCP_PLUGIN_DIR . 'admin/class-admin-settings.php';
}
// Inicialización del plugin
add_action('init', 'ccp_init_plugin');
function ccp_init_plugin() {
load_plugin_textdomain('categorias-como-paginas', false, dirname(plugin_basename(__FILE__)) . '/languages/');
$category_to_page = new Category_To_Page_Manager();
$template_handler = new CCP_Template_Handler();
if (is_admin()) {
new CCP_Admin_Settings();
}
}
// Registra hooks
register_activation_hook(__FILE__, 'ccp_activate_plugin');
register_uninstall_hook(__FILE__, 'ccp_uninstall_plugin');
function ccp_activate_plugin() {
flush_rewrite_rules();
}
2. includes/class-category-to-page.php (Actualizado)
<?php
class Category_To_Page_Manager {
private $syncing = false;
public function __construct() {
add_action('created_category', [$this, 'create_associated_page']);
add_action('delete_category', [$this, 'delete_associated_page']);
add_filter('category_link', [$this, 'replace_category_link'], 10, 2);
add_action('save_post_page', [$this, 'sync_page_to_category']);
add_action('edit_category', [$this, 'sync_category_to_page']);
}
public function create_associated_page($term_id) {
if (!current_user_can('publish_pages')) return;
$term = get_term($term_id, 'category');
$page_slug = wp_unique_post_slug($term->slug, 0, 'publish', 'page', 0);
$page_data = [
'post_title' => $term->name,
'post_content' => '',
'post_status' => 'publish',
'post_type' => 'page',
'post_name' => $page_slug
];
$page_id = wp_insert_post($page_data, true);
if (!is_wp_error($page_id)) {
update_term_meta($term_id, 'ccp_associated_page', $page_id);
set_transient('ccp_page_' . $term_id, $page_id, WEEK_IN_SECONDS);
}
}
public function delete_associated_page($term_id) {
$page_id = $this->get_associated_page($term_id);
if ($page_id) wp_delete_post($page_id, true);
delete_transient('ccp_page_' . $term_id);
}
public function replace_category_link($url, $term) {
$page_id = $this->get_associated_page($term->term_id);
return $page_id ? get_permalink($page_id) : $url;
}
public function sync_page_to_category($page_id) {
if ($this->syncing || !current_user_can('edit_categories')) return;
$this->syncing = true;
$terms = get_terms([
'meta_key' => 'ccp_associated_page',
'meta_value' => $page_id,
'hide_empty' => false
]);
if (!empty($terms)) {
wp_update_term($terms[0]->term_id, 'category', [
'name' => get_the_title($page_id),
'slug' => basename(get_permalink($page_id))
]);
}
$this->syncing = false;
}
public function sync_category_to_page($term_id) {
if ($this->syncing) return;
$this->syncing = true;
$page_id = $this->get_associated_page($term_id);
$term = get_term($term_id);
if ($page_id) {
wp_update_post([
'ID' => $page_id,
'post_title' => $term->name,
'post_name' => $term->slug
]);
}
$this->syncing = false;
}
public function get_associated_page($term_id) {
$cache_key = 'ccp_page_' . $term_id;
$page_id = get_transient($cache_key);
if (false === $page_id) {
$page_id = get_term_meta($term_id, 'ccp_associated_page', true);
set_transient($cache_key, $page_id, WEEK_IN_SECONDS);
}
// Verificar si la página existe realmente
if ($page_id && !get_post_status($page_id)) {
delete_term_meta($term_id, 'ccp_associated_page');
delete_transient($cache_key);
return false;
}
return $page_id;
}
}
3. includes/template-handler.php (Actualizado)
<?php
class CCP_Template_Handler {
public function __construct() {
add_action('template_redirect', [$this, 'override_category_template']);
add_shortcode('categoria_posts', [$this, 'category_posts_shortcode']);
}
public function override_category_template() {
if (!is_category()) return;
$term = get_queried_object();
$page_id = (new Category_To_Page_Manager())->get_associated_page($term->term_id);
if ($page_id) {
global $post;
$post = get_post($page_id);
setup_postdata($post);
include(get_page_template());
wp_reset_postdata();
exit;
}
}
public function category_posts_shortcode($atts) {
global $post;
$terms = get_terms([
'meta_key' => 'ccp_associated_page',
'meta_value' => $post->ID,
'hide_empty' => false
]);
if (empty($terms)) return '';
$query = new WP_Query([
'category__in' => [$terms[0]->term_id],
'posts_per_page' => 5
]);
ob_start();
if ($query->have_posts()) :
echo '<div class="categoria-posts-list">';
while ($query->have_posts()) : $query->the_post();
get_template_part('content', 'excerpt');
endwhile;
echo '</div>';
endif;
wp_reset_postdata();
return ob_get_clean();
}
}
4. admin/class-admin-settings.php (Actualizado)
<?php
class CCP_Admin_Settings {
public function __construct() {
add_action('admin_menu', [$this, 'add_settings_page']);
add_action('category_edit_form_fields', [$this, 'add_category_custom_fields'], 10, 2);
add_filter('manage_edit-category_columns', [$this, 'add_custom_column']);
add_filter('manage_category_custom_column', [$this, 'render_custom_column'], 10, 3);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
}
public function add_settings_page() {
add_options_page(
__('Configuración de Categorías como Páginas', 'categorias-como-paginas'),
__('Categorías como Páginas', 'categorias-como-paginas'),
'manage_options',
'ccp-settings',
[$this, 'render_settings_page']
);
}
public function add_category_custom_fields($term) {
wp_nonce_field('ccp_nonce_action', 'ccp_nonce_field');
$page_id = (new Category_To_Page_Manager())->get_associated_page($term->term_id);
?>
<tr class="form-field">
<th scope="row">
<label><?php _e('Página asociada', 'categorias-como-paginas'); ?></label>
</th>
<td>
<?php if ($page_id) : ?>
<a href="<?php echo get_edit_post_link($page_id); ?>" class="button">
<?php _e('Editar página', 'categorias-como-paginas'); ?>
</a>
<a href="<?php echo get_permalink($page_id); ?>" class="button" target="_blank">
<?php _e('Ver página', 'categorias-como-paginas'); ?>
</a>
<?php else : ?>
<span class="description"><?php _e('Se creará automáticamente al guardar', 'categorias-como-paginas'); ?></span>
<?php endif; ?>
</td>
</tr>
<?php
}
public function add_custom_column($columns) {
$columns['associated_page'] = __('Página asociada', 'categorias-como-paginas');
return $columns;
}
public function render_custom_column($content, $column_name, $term_id) {
if ('associated_page' === $column_name) {
$page_id = (new Category_To_Page_Manager())->get_associated_page($term_id);
return $page_id ? sprintf('<a href="%s">%s</a>', get_edit_post_link($page_id), get_the_title($page_id)) : '─';
}
return $content;
}
public function enqueue_admin_assets($hook) {
if ('settings_page_ccp-settings' === $hook) {
wp_enqueue_style('ccp-admin-styles', CCP_PLUGIN_URL . 'admin/css/admin-styles.css');
}
}
public function render_settings_page() {
?>
<div class="wrap ccp-settings">
<h1><?php _e('Configuración de Categorías como Páginas', 'categorias-como-paginas'); ?></h1>
<div class="notice notice-info">
<p><?php _e('Las páginas se crean automáticamente al guardar nuevas categorías.', 'categorias-como-paginas'); ?></p>
</div>
<form method="post" action="options.php">
<?php
settings_fields('ccp_settings');
do_settings_sections('ccp-settings');
submit_button();
?>
</form>
</div>
<?php
}
}
5. uninstall.php (Actualizado)
<?php
if (!defined('WP_UNINSTALL_PLUGIN')) exit;
// Eliminar todas las páginas asociadas
$terms = get_terms([
'taxonomy' => 'category',
'hide_empty' => false,
'meta_query' => [[
'key' => 'ccp_associated_page',
'compare' => 'EXISTS'
]]
]);
foreach ($terms as $term) {
$page_id = get_term_meta($term->term_id, 'ccp_associated_page', true);
if ($page_id) {
wp_delete_post($page_id, true);
delete_term_meta($term->term_id, 'ccp_associated_page');
}
}
// Eliminar opciones y transientes
delete_option('ccp_settings');
global $wpdb;
$wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_ccp_page_%'");
$wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_timeout_ccp_page_%'");
Mejoras clave implementadas:
- Sincronización bidireccional con protección contra loops
- Gestión de caché con transients
- Verificación de capacidades de usuario
- Shortcode para mostrar posts relacionados
- Columnas personalizadas en el admin
- Validación de existencia de páginas
- Limpieza completa en desinstalación
- Prevención de conflictos de slugs
- Seguridad reforzada con nonces
- Soporte multi-idioma
Para usar:
- Activar el plugin
- Crear/editar categorías para generar páginas automáticas
- Usar el shortcode
[categoria_posts]en las páginas - Los enlaces de categoría redirigirán al contenido editable
Próximos pasos recomendados:
- Crear un sistema de importación para categorías existentes
- Implementar logging para depuración
- Añadir opción para desactivar la redirección
- Crear plantillas específicas para categorías-páginas