Cuaderno Técnico: Plugin «Agenda Indaga» (v1.3.0), parte 3

Entrega 3: Los Shortcodes de Frontend (Formulario y Calendario Interactivo)

Valencia, 25/10/2026, G.B.
En las entregas anteriores vimos cómo levantar los cimientos del plugin en la base de datos y cómo estructurar el panel de administración en el backend. En este tercer bloque me centro en la parte visible para el usuario: los shortcodes que dan vida a la agenda en el frontend de la web.

Para conseguirlo, divido la funcionalidad en dos shortcodes independientes pero complementarios: indaga_agenda (entre corchetes) para gestionar el alta, edición y listado de citas, y calendario_agenda (entre corchetes) para renderizar el calendario mensual interactivo.

1. El Shortcode de Gestión y Formulario (indaga_agenda) (entre corchetes)

Este shortcode permite desplegar un formulario de alta y edición de eventos, acompañado de una tabla con los registros actuales de la instancia. Todo ello protegido mediante comprobaciones de sesión de usuario, validación de nonces y funciones de saneamiento de datos.

public function renderizar_shortcode_agenda( $atts ) {
    global $wpdb;
    $tabla = $wpdb->prefix . 'indaga_agenda';
    $atts = shortcode_atts( array( 'id' => 'default' ), $atts, 'indaga_agenda' );
    $grupo_id = sanitize_text_field( $atts['id'] );
    $user_id = get_current_user_id();
    $mensaje = '';

    // Procesamiento del formulario de guardado / edición
    if ( isset( $_POST['indaga_agenda_frontend_nonce'] ) && wp_verify_nonce( $_POST['indaga_agenda_frontend_nonce'], 'guardar_evento_frontend' ) ) {
        if ( !is_user_logged_in() ) {
            return '<p style="color:red;">Debes iniciar sesión para gestionar la agenda.</p>';
        }

        $evento_id = isset( $_POST['evento_id'] ) ? absint( $_POST['evento_id'] ) : 0;
        $titulo = sanitize_text_field( $_POST['titulo'] );
        $descripcion = sanitize_textarea_field( $_POST['descripcion'] );
        $fecha_inicio = sanitize_text_field( $_POST['fecha_inicio'] );
        $fecha_fin = sanitize_text_field( $_POST['fecha_fin'] );
        $categoria = sanitize_text_field( $_POST['categoria'] );

        if ( !empty( $titulo ) && !empty( $fecha_inicio ) ) {
            $datos = array(
                'user_id' => $user_id,
                'grupo_id' => $grupo_id,
                'titulo' => $titulo,
                'descripcion' => $descripcion,
                'fecha_inicio' => $fecha_inicio,
                'fecha_fin' => !empty( $fecha_fin ) ? $fecha_fin : $fecha_inicio,
                'categoria' => $categoria
            );

            if ( $evento_id > 0 ) {
                $wpdb->update( $tabla, $datos, array( 'id' => $evento_id ), array( '%d', '%s', '%s', '%s', '%s', '%s', '%s' ), array( '%d' ) );
                $mensaje = '<div style="background:#d4edda; color:#155724; padding:10px; margin-bottom:15px; border-radius:4px;">¡Evento actualizado con éxito!</div>';
            } else {
                $wpdb->insert( $tabla, $datos, array( '%d', '%s', '%s', '%s', '%s', '%s', '%s' ) );
                $mensaje = '<div style="background:#d4edda; color:#155724; padding:10px; margin-bottom:15px; border-radius:4px;">¡Evento creado con éxito!</div>';
            }
        }
    }
    // ... (código de renderizado de la vista y formulario)
}

Puntos clave del formulario:

  • Soporte multinstancia por atributo: A través del parámetro id="nombre_instancia" (por defecto default), delimito los registros que pertenecen a esa agenda concreta.
  • Doble funcionalidad (Alta / Edición): Si el sistema detecta un parámetro editar_id en la URL, carga automáticamente los datos del evento en los inputs para modificarlos sin duplicar registros.

2. El calendario interactivo (calendario_agenda) (entre corchetes)

Para mostrar los eventos en una vista de calendario mensual limpia y visual, transformo los registros de la base de datos a un array de JavaScript y construyo una cuadrícula dinámica.

public function renderizar_shortcode_calendario( $atts ) {
    global $wpdb;
    $tabla = $wpdb->prefix . 'indaga_agenda';
    $atts = shortcode_atts( array( 'id' => 'default' ), $atts, 'calendario_agenda' );
    $grupo_id = sanitize_text_field( $atts['id'] );

    $eventos = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $tabla WHERE grupo_id = %s ORDER BY fecha_inicio ASC", $grupo_id ) );
    
    $eventos_js = array();
    foreach ( $eventos as $ev ) {
        $eventos_js[] = array(
            'id' => $ev->id,
            'titulo' => $ev->titulo,
            'descripcion' => $ev->descripcion,
            'categoria' => $ev->categoria,
            'fecha_inicio' => $ev->fecha_inicio,
            'fecha_fin' => $ev->fecha_fin
        );
    }
    // ... (retorno del HTML y script de renderizado)
}

Características del motor visual en JavaScript:

  • Navegación fluida por meses: Botones de avance y retroceso (cambiarMes()) que recalculan los días de la semana y sitúan correctamente el primer día del mes.
  • Fichas modales emergentes: Al hacer clic sobre cualquier evento listado dentro de una casilla del calendario, se despliega una ventana flotante con los detalles completos (horarios, categoría y descripción).
  • Conexión directa con el alta: El botón superior «+ Añadir Evento» o hacer clic directamente sobre cualquier día del calendario desplaza la vista automáticamente hacia el formulario y precarga la fecha seleccionada.

Con esta tercera entrega queda completamente documentada la arquitectura del plugin Agenda Indaga (v1.3.0): desde los cimientos en la base de datos, pasando por el panel de administración multinstancia, hasta la interacción visual en el frontend.

Cuaderno Técnico: Plugin «Agenda Indaga» (v1.3.0), parte 2

Entrega 2: El Panel de Administración y la Gestión Multinstancia

En la primera entrega vimos los cimientos del plugin: la estructura principal con la clase IndagaAgenda, la seguridad inicial para bloquear accesos directos y la creación nativa de la tabla en la base de datos.

En este segundo bloque me centro en cómo está construido el panel de administración en el backend de WordPress (ubicado en Ajustes > Agenda Indaga), explicando el menú nativo, la estructura modular por pestañas y el control de las distintas instancias o grupos de eventos.

1. Registro del menú en el panel de administración

Para integrar la interfaz dentro del escritorio de WordPress sin cargar librerías adicionales, utilizo la API nativa de menús a través del gancho admin_menu:

public function registrar_menu_admin() {
    add_menu_page(
        'Agenda Indaga',              // Título de la página
        'Agenda Indaga',              // Título del menú
        'manage_options',             // Capacidad / Permiso requerido
        'indaga-agenda',              // Slug único del menú
        array( $this, 'renderizar_panel_admin' ), // Función de callback
        'dashicons-calendar-alt',     // Icono nativo de Dashicons
        6                             // Posición en la barra lateral
    );
}

Puntos clave:

  • manage_options: Garantizo que solo las cuentas con perfil de administración puedan acceder a este panel de control.
  • dashicons-calendar-alt: Aprovecho la librería de iconos nativa de WordPress (Dashicons), manteniendo el plugin sin dependencias externas.

2. Estructura modular por pestañas

Para que la experiencia de uso sea limpia e intuitiva, organizo el panel de administración en tres pestañas bien diferenciadas usando la navegación nativa de WordPress (nav-tab-wrapper):

  1. Gestión e Instancias: Control global de los eventos y de las instancias creadas.
  2. Instrucciones y Shortcodes: Guía rápida para saber cómo insertar los shortcodes.
  3. Ficha Técnica: Resumen de especificaciones del desarrollo.

El cambio entre pestañas se gestiona capturando el parámetro tab mediante la URL (p. ej., ?page=indaga-agenda&tab=instrucciones), sanitizándolo adecuadamente:

$tab = isset( $_GET['tab'] ) ? sanitize_text_field( $_GET['tab'] ) : 'gestion';
?>
<h2 class="nav-tab-wrapper">
    <a href="?page=indaga-agenda&tab=gestion" class="nav-tab <?php echo $tab === 'gestion' ? 'nav-tab-active' : ''; ?>">Gestión e Instancias</a>
    <a href="?page=indaga-agenda&tab=instrucciones" class="nav-tab <?php echo $tab === 'instrucciones' ? 'nav-tab-active' : ''; ?>">Instrucciones y Shortcodes</a>
    <a href="?page=indaga-agenda&tab=tecnica" class="nav-tab <?php echo $tab === 'tecnica' ? 'nav-tab-active' : ''; ?>">Ficha Técnica</a>
</h2>

3. Procesamiento seguro de acciones (eliminación con nonces)

Antes de pintar la vista o consultar los datos, verifico si se ha enviado alguna petición desde los formularios de administración (por ejemplo, para eliminar un evento suelto o una instancia completa).

La seguridad aquí descansa en dos pilares: comprobación de permisos y validación de Nonce para evitar ataques CSRF:

if ( isset( $_POST['indaga_agenda_nonce'] ) && wp_verify_nonce( $_POST['indaga_agenda_nonce'], 'indaga_agenda_admin_action' ) ) {
    
    // Eliminación de un evento individual
    if ( isset( $_POST['accion'] ) && $_POST['accion'] === 'eliminar_evento' && !empty( $_POST['evento_id'] ) ) {
        $evento_id = absint( $_POST['evento_id'] );
        $wpdb->delete( $tabla, array( 'id' => $evento_id ), array( '%d' ) );
        $mensaje = '<div class="updated notice"><p>Evento eliminado correctamente.</p></div>';
    
    // Eliminación de una instancia completa (y todos sus eventos asociados)
    } elseif ( isset( $_POST['accion'] ) && $_POST['accion'] === 'eliminar_instancia' && !empty( $_POST['grupo_id'] ) ) {
        $grupo_id = sanitize_text_field( $_POST['grupo_id'] );
        $wpdb->delete( $tabla, array( 'grupo_id' => $grupo_id ), array( '%s' ) );
        $mensaje = '<div class="updated notice"><p>Instancia completa y todos sus eventos asociados eliminados correctamente.</p></div>';
    }
}
  • wp_verify_nonce(): Garantiza que la petición se ha originado legítimamente dentro del panel y por un usuario autorizado.
  • absint() y sanitize_text_field(): Aseguran que los datos recibidos sean enteros positivos o cadenas limpias sin código malicioso.

4. Consulta agrupada para la gestión multinstancia

El valor diferencial de la versión 1.3.0 es el soporte multinstancia. Mediante una consulta SQL optimizada con agrupamiento (GROUP BY), obtengo la lista de todas las agendas o grupos existentes en la base de datos sin necesidad de tablas auxiliares:

$instancias = $wpdb->get_results( 
    "SELECT grupo_id, COUNT(*) as total_eventos, MIN(fecha_inicio) as primer_evento 
     FROM $tabla 
     GROUP BY grupo_id 
     ORDER BY grupo_id ASC" 
);

Con esto puedo renderizar una tabla clara que indica el nombre del grupo, cuántos eventos almacena y qué shortcode exacto debe copiarse para mostrar esa agenda concreta en el frontend:

<?php foreach ( $instancias as $inst ): ?>
    <tr>
        <td><strong><?php echo esc_html( $inst->grupo_id ); ?></strong></td>
        <td><?php echo esc_html( $inst->total_eventos ); ?> evento(s)</td>
        <td><code>

Acceso restringido: necesitas permisos de administrador para gestionar esta agenda. Iniciar sesión

</code></td> <td><code>

</code></td> <td> <form method="POST" style="display:inline;" onsubmit="return confirm('¿Estás seguro de eliminar esta instancia completa y todos sus eventos?');"> <?php wp_nonce_field( 'indaga_agenda_admin_action', 'indaga_agenda_nonce' ); ?> <input type="hidden" name="accion" value="eliminar_instancia"> <input type="hidden" name="grupo_id" value="<?php echo esc_attr( $inst->grupo_id ); ?>"> <button type="submit" class="button button-small button-link-delete">Eliminar Instancia</button> </form> </td> </tr> <?php endforeach; ?>

Con esto queda cubierto todo el funcionamiento del panel de administración y el control multinstancia desde el backend.

En la siguiente entrega abordaremos la tercera parte del cuaderno técnico: los Shortcodes de Frontend (indaga_agenda y calendario_agenda) (entre corchetes), analizando el formulario de alta de eventos y el calendario mensual interactivo programado en JavaScript nativo.

Hasta pronto!

GEO: cómo escribir para que una IA te cite

Valencia, 22/10/2026, G.B.
Llevo un tiempo viendo cómo cambia la forma de buscar información y, sinceramente, el SEO de toda la vida ya no es lo único que importa. Cada vez más gente le pregunta directamente a ChatGPT, Copilot, Deepseek, Gemini, Claude o Perplexity (por citar los más conocidos), y lo que recibe no es una lista de enlaces sino una respuesta ya redactada. De ahí sale el GEO, y aquí te cuento lo básico, de tú a tú.

Qué es el GEO

GEO son las siglas de Generative Engine Optimization, o optimización para motores generativos. Si el SEO busca que tu página aparezca bien colocada en Google, el GEO busca que una inteligencia artificial elija tu contenido y lo cite cuando redacte su respuesta. No es algo que sustituya al SEO, es más bien su evolución natural, y muchas de las buenas prácticas de uno sirven para el otro.

Lo que mejor funciona al escribir

El trabajo de referencia en esto es un estudio de investigadores de varias universidades (el de Princeton es el más citado) que probó distintas técnicas de redacción para ver cuáles aumentaban la visibilidad de una web en las respuestas generativas. Sin entrar en el detalle académico, las que mejor resultado dieron son tres:

  • Citas de autoridad: incluir declaraciones o referencias de expertos reconocidos del sector, con nombre y apellidos. A los modelos les gusta apoyar sus respuestas en voces con peso.
  • Datos y estadísticas: números concretos, porcentajes y fuentes verificables, bien integrados en el texto. Un «mucha gente» no le sirve a nadie, un «el 62 % según tal informe» sí.
  • Lenguaje claro y directo: frases que un modelo pueda resumir sin esfuerzo. Fuera el relleno y la jerga innecesaria, que además también se agradece como lector.

Por cierto, rellenar el texto de palabras clave, la vieja táctica del SEO, no ayuda en este terreno. Si acaso, resta.

El E-E-A-T, ahora más en serio

Si conoces el SEO, el E-E-A-T te sonará. Son cuatro letras que resumen lo que debería transmitir una web:

  • Experience (experiencia): que se note que has vivido de primera mano lo que cuentas.
  • Expertise (pericia): que sabes de lo que hablas.
  • Authoritativeness (autoridad): que otros te reconocen como referencia.
  • Trustworthiness (confianza): que eres transparente y se puede comprobar lo que dices.

Los motores generativos tienen un problema conocido, las «alucinaciones», es decir, inventarse datos con total seguridad. Por eso tienden a apoyarse en fuentes que dan señales claras de fiabilidad. En la práctica, esto significa que firmar tus textos, tener una página de autor decente y dejar claro quién está detrás de la web te juega a favor, y que los contenidos anónimos o generados en masa sin revisión lo tienen bastante más difícil para ser citados.

Preguntas largas, respuestas directas

Aquí está, para mí, el cambio más interesante. Antes buscábamos cosas como «mejor portátil Valencia». Ahora la gente escribe algo así: «necesito un portátil para estudiar diseño gráfico en la universidad, ¿cuál me recomiendas si tengo 800 euros de presupuesto?». Es una consulta completa, con contexto, y espera una respuesta del mismo nivel.

Para adaptarse, lo más práctico es esto:

  • Plantear los apartados como preguntas reales en los encabezados (H2 y H3).
  • Responder de forma concisa en las primeras líneas de cada sección y desarrollar después.
  • Pensar en cómo preguntaría alguien de verdad, no en cómo buscaría un robot.

Cómo lo voy a aplicar en Indaga.net

Sin complicarme demasiado: autoría clara en los artículos, datos con su fuente cuando los haya, párrafos que vayan al grano y títulos que se parezcan a lo que alguien le preguntaría a un asistente. Tampoco hace falta reescribir todo el blog de golpe, pero sí tenerlo en mente al escribir lo nuevo.

Quedan temas pendientes, como cómo medir si todo esto funciona (los clics clásicos ya no cuentan toda la historia) y ejemplos prácticos de cómo convertir un artículo antiguo a formato GEO. Lo dejo para otro día.

Buenas noches!

Gestión de instrucciones y envíos: plugin Indaga Instrucciones

Valencia, 22/10/2026, G.B.
Os presento un nuevo plugin para la gestión de instrucciones y envíos por email. Se trata de Indaga Instrucciones y sirve para enviar instrucciones, como por ejemplo el acceso y uso a una intranet, a los usuarios que registramos directamente desde el panel de control del plugin. Además, permite mantener varias plantillas distintas, cada una con su propio contenido y sus propios destinatarios adicionales, y elegir la que corresponda en cada alta.

Descripción del Plugin

Este desarrollo nativo para Indaga.net automatiza el alta segura de direcciones de correo electrónico, enviando una confirmación individual al usuario y a los destinatarios adicionales de la plantilla elegida, con un documento de instrucciones en PDF, formateado estrictamente en tamaño A4, adjunto.

Permite mantener varias plantillas independientes, de modo que guardar una nunca sobrescribe las demás, y restringir el uso tanto a administradores como a usuarios específicos mediante sus IDs de WordPress, ofreciendo versatilidad tanto en el panel de control como en páginas públicas mediante shortcode.

Guía de Uso

  1. Panel de Administración: Accede a Indaga Instrucciones desde el menú lateral de WordPress para dar de alta correos.
  2. Plantillas: En la pestaña Plantillas puedes crear, editar, duplicar y eliminar tantas plantillas como necesites. Cada una lleva sus propios emails adicionales de confirmación y admite etiquetas dinámicas como {email}, {fecha} o {sitio} para inyectar información de forma automática. Siempre hay una plantilla predeterminada, que no se puede eliminar.
  3. Alta de un email: Al dar de alta una dirección eliges la plantilla. Se usan su contenido y sus destinatarios adicionales, y el registro recuerda qué plantilla se utilizó.
  4. Reenviar PDF: Desde la tabla de registros recientes dispones de un botón rápido con verificación de seguridad para reenviar el documento de manera individual al usuario de esa fila, con la plantilla de su alta y sin avisar de nuevo a los destinatarios adicionales.
  5. Shortcode para Frontend: Inserta el shortcode (entre corchetes) indaga_instrucciones en cualquier página o entrada para que los usuarios con permisos debidamente logueados puedan realizar altas directamente desde la web pública, eligiendo plantilla. La gestión de las plantillas se hace desde el panel de administración.

Ficha Técnica

  • Versión del Plugin: 1.1.0
  • Autor: Guillermo Beltrán Pilato (Indaga.net)
  • Dependencias: 100% Nativo (Cero dependencias externas, librerías de terceros o CDNs. El PDF se genera en PHP puro).
  • Base de datos: Dos tablas propias, una de registros y otra de plantillas, con actualización incremental mediante dbDelta. Al actualizar desde versiones anteriores, la plantilla y los emails existentes se migran sin pérdidas a la primera plantilla.
  • Seguridad: Sanitización completa de variables (sanitize_email, sanitize_text_field, wp_unslash), nonces de verificación dinámicos por elemento (reenvío, duplicado y borrado) contra ataques CSRF, control de capacidades y consultas preparadas con $wpdb.
  • Shortcode: (entre corchetes) indaga_instrucciones

Descarga del plugin: Descargar Indaga Instrucciones v1.1.0

¿Qué es el CTR en analítica web y por qué es una métrica clave?

Valencia, 22/10/2026, G.B.
En el ámbito de la analítica web, la gestión de contenidos y el desarrollo digital, el CTR (Click-Through Rate, o tasa de clics) es una de las métricas fundamentales para medir el rendimiento de un sitio y la interacción de sus usuarios.

Esta métrica calcula el porcentaje de usuarios que hacen clic en un enlace específico en relación con el número total de impresiones (las veces que dicho enlace se muestra en pantalla). Un CTR elevado refleja que los títulos, las metadescripciones y las llamadas a la acción (CTA) resultan atrayentes y relevantes para la audiencia.

CTR = (Clics / Impresiones) × 100

Glosario de métricas esenciales en analítica web y marketing digital

Para evaluar con precisión el rendimiento de cualquier plataforma digital, es conveniente dominar las siguientes siglas y conceptos del sector:

SiglaNombre en inglésSignificado / Descripción
CTRClick-Through RatePorcentaje de clics obtenidos por un enlace respecto al total de visualizaciones o impresiones.
CPCCost Per ClickCoste por clic. Modelo publicitario donde se paga únicamente cuando un usuario hace clic en un enlace o anuncio.
CPACost Per AcquisitionCoste por adquisición. Mide la inversión necesaria para alcanzar una conversión final (registro, compra, descarga).
CPLCost Per LeadCoste por cliente potencial. Indica el coste derivado de captar los datos de contacto de un usuario interesado.
CPMCost Per MilleCoste por mil impresiones. Modelo de tarificación basado en cada millar de visualizaciones.
ROIReturn on InvestmentRetorno de la inversión. Evalúa el beneficio económico generado frente al presupuesto invertido.
ROASReturn on Ad SpendRetorno de la inversión publicitaria. Mide los ingresos específicos obtenidos por cada unidad monetaria dedicada a campañas publicitarias.
SEOSearch Engine OptimizationOptimización para motores de búsqueda. Conjunto de técnicas para mejorar la visibilidad orgánica en buscadores.
SEMSearch Engine MarketingMarketing en motores de búsqueda enfocado a estrategias de pago y enlaces patrocinados.
KPIKey Performance IndicatorIndicador clave de rendimiento. Métrica cuantificable empleada para medir el éxito frente a objetivos definidos.

Plugin Resúmenes Indaga

Ya lo explicaré. aquí va el shortcode. Las  cinco últimas entradas publicadas en Indaga.net.

Últimas entradas

Alerta mundial de ciberseguridad: dos fallos críticos en PHP obligan a aislar sitios y aplicaciones web (solucionado)

seguridad

Valencia, 21/10/2026
Fuente: Puntocomunica.com

La comunidad tecnológica internacional se ha visto afectada por una alerta de máxima gravedad tras detectarse dos vulnerabilidades críticas en PHP (registradas bajo los códigos CVE-2026-109618 y CVE-2026-109619). Estos fallos permitían la ejecución remota de código sin necesidad de autenticación, poniendo en riesgo una parte masiva de internet y de plataformas digitales globales como WordPress, Symfony o MediaWiki.

Ante la magnitud de la amenaza, agencias de ciberseguridad y administraciones de todo el mundo ordenaron de manera preventiva el aislamiento y la desconexión temporal de numerosos servidores y aplicaciones web en red para frenar posibles ataques informáticos.

Paralelamente, el equipo de desarrollo de PHP ha publicado las actualizaciones y parches de seguridad necesarios para corregir por completo ambas brechas. Una vez aplicadas estas actualizaciones oficiales en los servidores, los servicios afectados han podido restablecer su conectividad y operar con normalidad de forma segura.

Versiones corregidas

Rama de PHPVersión segura
PHP 8.58.5.12
PHP 8.48.4.27
PHP 8.38.3.36
PHP 8.28.2.35

Las correcciones cubren PHP 8.2 y versiones posteriores; los sistemas con PHP 8.1 o anterior requieren un parche retroportado del proveedor del sistema operativo o una actualización a una versión compatible.

+ info acerca de la vulnerabilidad crítica en: https://www.muycomputer.com/2026/10/21/php2shell-el-fallo-de-php-que-ha-puesto-en-jaque-a-internet/

Cuaderno Técnico: Plugin «Agenda Indaga» (v1.3.0), parte I

Valencia, 21/10/2026, G.B.
Empiezo una serie de cuadernos técnicos para explicar paso a paso los plugins de Indaga.net. Es una forma práctica de documentar el proceso, compartir el aprendizaje y, de paso, ver de un vistazo qué se puede mejorar o dónde falló algo en su día. Aquí va la primera parte del plugin Agenda Indaga.

Bloque 1: Cabecera, Seguridad y Estructura de la Clase Principal

Todo buen plugin de WordPress empieza con una cabecera limpia y un pequeño control de seguridad para evitar que nadie acceda al archivo directamente desde el navegador.

1. Cabecera del Plugin e Identificación

En las primeras líneas definimos los metadatos que lee WordPress en el listado de plugins. Destaca el uso del dominio de texto (indaga-agenda) y la especificación de autoría de Indaga.net:

<?php
/**
* Plugin Name: Agenda Indaga
* Plugin URI: https://indaga.net
* Description: Plugin nativo de agenda y eventos con soporte multinstancia avanzada...
* Version: 1.3.0
* Author: Guillermo Beltrán Pilato (Indaga.net)
* Author URI: https://indaga.net
* License: GPLv2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: indaga-agenda
*/

2. Bloqueo de Acceso Directo

La seguridad en WordPress empieza por proteger los archivos individuales frente a peticiones HTTP directas que no pasen por el núcleo del CMS. Si la constante global ABSPATH no está definida, abortamos la ejecución de inmediato:

if ( ! defined( 'ABSPATH' ) ) {
exit; // Seguridad: Evitar acceso directo
}

3. Patrón Singleton (IndagaAgenda)

Para evitar duplicidad de instancias y mantener el código ordenado y eficiente en memoria, implementamos el patrón Singleton. Esto garantiza que la clase solo se cargue una vez:

class IndagaAgenda {
private static $instance = null;

public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}

private function __construct() {
// Aquí engancharemos nuestros hooks de activación, menús y shortcodes
register_activation_hook( __FILE__, array( $this, 'activar_plugin' ) );
add_action( 'admin_menu', array( $this, 'registrar_menu_admin' ) );
add_shortcode( 'indaga_agenda', array( $this, 'renderizar_shortcode_agenda' ) );
add_shortcode( 'calendario_agenda', array( $this, 'renderizar_shortcode_calendario' ) );
}
}

// Inicializamos el plugin
IndagaAgenda::get_instance();

Bloque 2: Creación Nativa de la Base de Datos (activar_plugin)

Una de las premisas clave de los plugins en Indaga.net es cero dependencias externas: toda la información reside en nuestras propias tablas optimizadas dentro de la base de datos de WordPress.

Cuando el usuario activa el plugin por primera vez, se dispara el gancho (register_activation_hook) que ejecuta el siguiente método:

public function activar_plugin() {
global $wpdb;
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );

$tabla = $wpdb->prefix . 'indaga_agenda';
$charset_collate = $wpdb->get_charset_collate();

$sql = "CREATE TABLE $tabla (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
grupo_id varchar(50) DEFAULT 'default' NOT NULL,
titulo varchar(255) NOT NULL,
descripcion text NOT NULL,
fecha_inicio datetime NOT NULL,
fecha_fin datetime NOT NULL,
categoria varchar(50) DEFAULT 'general' NOT NULL,
observaciones text DEFAULT '',
PRIMARY KEY (id),
KEY user_id (user_id),
KEY grupo_id (grupo_id),
KEY fecha_inicio (fecha_inicio)
) $charset_collate;";

dbDelta( $sql );
update_option( 'indaga_agenda_version', '1.3.0' );
}

¿Qué ocurre paso a paso en este método?

  1. global $wpdb;: Cargamos la clase global de WordPress para interactuar de forma segura con la base de datos.

  2. $wpdb->prefix: Añadimos el prefijo personalizado de las tablas de tu instalación de WordPress (por defecto solía ser wp_, resultando en wp_indaga_agenda), garantizando compatibilidad con cualquier entorno.

  3. dbDelta( $sql );: Es la función nativa recomendada de WordPress. Compara si la tabla ya existe o si necesita actualizar su esquema sin destruir los datos ya almacenados.

  4. Campos clave (grupo_id): Este campo es el corazón del soporte multinstancia avanzada, ya que nos permite filtrar y agrupar múltiples agendas independientes dentro de una misma tabla.

Continuaré…

Solución: Falso bloqueo de Nextcloud en Windows tras cambiar la contraseña de usuario

¿El cliente de Nextcloud en Windows dejó de sincronizar carpetas locales tras cambiar la contraseña del sistema? Explicación y solución

Resumen: Cuando cambias la contraseña de una cuenta de usuario en Windows 11 (especialmente si se hace de forma administrativa o externa  como por ejemplo, para permitir una firma digital temporal)es muy común que el cliente de Nextcloud pierda la capacidad de sincronizar la carpeta local predeterminada, funcionando únicamente mediante el modo de «archivos virtuales». Vamos a ver el motivo técnico detrás de este comportamiento y cómo solucionarlo paso a paso.

El problema: Nextcloud falla al elegir la carpeta local

Tras desinstalar y reinstalar completamente el cliente de escritorio (incluso eliminando datos de %AppData% / %LocalAppData%), la aplicación rechaza o falla al configurar la sincronización en una carpeta local existente, obligando al usuario a activar el modo de Archivos Virtuales.

¿Por qué ocurre esto?

  1. Desvinculación de la clave maestra DPAPI (Data Protection API): Windows utiliza la contraseña de la sesión para proteger y desencriptar la Master Key de DPAPI del usuario. Si la contraseña de Windows se modifica de manera administrada o forzada (sin pasar por el cambio de clave estándar desde la propia sesión iniciada), se rompe la cadena de claves cifradas locales.

  2. Bloqueo de permisos NTFS y propiedad en la carpeta local: Las carpetas previamente creadas (como C:\Users\usuario\Nextcloud) quedan asociadas al estado de seguridad/permisos previo al cambio. Al no coincidir la validación de seguridad de Windows, la aplicación pierde el control total de lectura/escritura necesario para mantener una sincronización bidireccional física.

  3. Por qué la sincronización de archivos virtuales sí funciona: Los archivos virtuales (Virtual Files) aprovechan la API cfapi (Files-on-Demand) del propio sistema operativo para crear accesos directos bajo demanda, sin necesidad de realizar escrituras directas sobre la estructura física tradicional de carpetas.

Solución recomendada

Si te encuentras con este escenario, no es necesario reinstalar el cliente múltiples veces. La solución pasa por restablecer los permisos o cambiar el directorio destino (si tienes suficiente espacio en disco, claro…).:

  • Opción 1: Crear un directorio local totalmente nuevo Al configurar la cuenta en Nextcloud Desktop, selecciona una nueva ruta local limpia (ej. C:\Users\usuario\Nextcloud_Sync). Al crearse bajo el nuevo contexto de seguridad, la sincronización local completa funcionará inmediatamente.

  • Opción 2: Reparar la propiedad de la carpeta existente

    1. Haz clic derecho sobre la carpeta original de Nextcloud > Propiedades > Seguridad > Opciones avanzadas.

    2. En Propietario, haz clic en Cambiar y vuelve a asignar al usuario actual de Windows.

    3. Marca la casilla «Reemplazar propietario en subcontenedores y objetos».

    4. En la lista de permisos, asigna Control total al usuario y marca «Reemplazar todas las entradas de permisos de objetos secundarios…».

    5. Aplica los cambios y vuelve a intentar la vinculación en el cliente de Nextcloud.

Copilot en modo IA frente a alternativas como Gemini o Claude: rendimiento real contra expectativas

Valencia, 20/10/2026, G.B.
Llevo un tiempo realizando pruebas con distintos modelos de Inteligencia Artificial para las tareas del día a día (desde redacción de borrador técnico hasta investigación y filtrado de datos). Mi impresión es que, en la práctica diaria y sin rodeos, Copilot suele decepcionar bastante en comparación con lo que te da un modelo puro de IA. Microsoft Copilot suele quedarse un paso por detrás en comparación con lo que ofrecen otros modelos o interfaces cuando trabajas en modo IA puro.

No es una manía personal ni una crítica sin fundamento (su integración con el paquete Office o Teams resulta útil en dinámicas corporativas), pero en cuanto le pides razonamiento analítico, búsquedas finas sobre entes específicos o una gestión de contexto amplia, las limitaciones saltan a la vista.

¿Por qué Copilot flojea ante alternativas como Gemini o Claude?

Al contrastar la herramienta con el uso continuo de modelos como Gemini (especialmente al trabajar con Cuadernos o fuentes de información masivas) o Claude, emergen varios factores claros:

  • Capas de restricción y filtros agresivos: Al estar concebido para entornos de empresa, Copilot opera bajo una capa de abstracción muy rígida. Los system prompts internos y los filtros de seguridad reducen la fluidez y hacen que las respuestas sean a menudo planos, conservadores o robóticos.
  • Errores en búsquedas de personas y entes específicos: En consultas generales sobre perfiles profesionales o marcas personales, el modelo patina con frecuencia. Suele confundir identidades, mezclar datos de personas con nombres similares o dar por buenas asociaciones toscas sin realizar un filtrado lógico real.
  • Vulnerabilidad al SEO de baja calidad: Al apoyarse directamente en el índice web tradicional, en las respuestas se cuela fácilmente ruido comercial, granjas de enlaces o directorios automatizados, en lugar de priorizar las fuentes de verdadera autoridad.
  • Falta de profundidad y contraste de contexto: Mientras que entornos como Gemini destacan por su capacidad multimodal y la ingesta masiva de fuentes heterogéneas (archivos de audio, vídeo o documentación de Google Drive con trazabilidad directa), Copilot prioriza resolver con rapidez resúmenes ejecutivos, quedándose en la superficie en cuanto se exige rigor técnico.

Estar «donde vive el dato» frente a la agudeza del modelo

La mayor baza de Copilot reside simplemente en estar presente en la misma ventana donde tienes el documento o el correo abierto. Pero al aislarlo de ese entorno y evaluarlo estrictamente como motor de respuesta, de investigación o de redacción avanzada, la diferencia de rendimiento resulta bastante evidente, a mi modo de ver.

Para consultas finas, gestión de documentaciones complejas o análisis de información sobre proyectos y personas, la combinación de modelos con mejor capacidad de síntesis (como Gemini en sus entornos de trabajo contextuales) sigue ofreciendo una experiencia muy superior, por supuesto, también en mi opinión.