state = new Consent_State(); $this->log = new Consent_Log(); $this->banner = new Banner( $this->state ); $this->blocker = new Script_Blocker(); $this->consent_mode = new Consent_Mode(); $this->rights = new Rights(); $this->transfers = new Transfers(); $this->rat = new Rat(); $this->diagnostics = new Diagnostics(); $this->forms = new Forms(); $this->provision = new Provision(); } /** Engancha todos los módulos. */ public function run(): void { // Las tablas de este plugin pueden crecer entre versiones: dbDelta vuelve a correr // cuando cambia la versión del esquema, no solo al activar. if ( get_option( Rights::SCHEMA_OPT ) !== Rights::SCHEMA_VERSION ) { Rights::install(); } if ( get_option( Consent_Log::SCHEMA_OPT ) !== Consent_Log::SCHEMA_VERSION ) { Consent_Log::install(); } // Las versiones anteriores guardaban todo con el prefijo corto “ccn_”, que no // cumple el mínimo de cuatro caracteres que pide el directorio: se traslada // una sola vez, conservando los valores (sobre todo la sal del registro). if ( '1' !== get_option( 'cumpleconsent_migrated' ) ) { self::migrate_legacy_names(); update_option( 'cumpleconsent_migrated', '1' ); } // La opción se registra en `init` (no en `admin_init`): las peticiones REST // no pasan por `admin_init` y la configuración tiene que estar disponible // para /wp/v2/settings, que es la vía de aprovisionamiento por contraseña // de aplicación. add_action( 'init', array( 'CumpleConsent\\Settings', 'register' ) ); $this->state->hook(); $this->consent_mode->hook(); $this->blocker->hook(); $this->banner->hook(); $this->rights->hook(); $this->transfers->hook(); $this->forms->hook(); $this->provision->hook(); add_action( CUMPLECONSENT_CRON_PURGE, array( $this->log, 'purge' ) ); add_action( 'admin_notices', array( $this, 'admin_notices' ) ); if ( is_admin() ) { $this->admin = new Admin(); $this->admin->hook(); } } /** Avisos de configuración pendiente (solo administradores). */ public function admin_notices(): void { if ( ! current_user_can( CUMPLECONSENT_CAP ) ) { return; } $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( $screen && false !== strpos( (string) $screen->id, 'cumpleconsent' ) ) { return; // En las pantallas del plugin ya se muestra todo el contexto. } if ( ! Settings::is_ready() ) { printf( '

CumpleConsent: %s %s

', esc_html__( 'falta completar la identidad del responsable y activar el aviso de privacidad.', 'cumpleconsent' ), esc_url( admin_url( 'admin.php?page=cumpleconsent-ajustes' ) ), esc_html__( 'Configurar ahora', 'cumpleconsent' ) ); return; } if ( '' === Policy::url() ) { printf( '

CumpleConsent: %s %s

', esc_html__( 'el aviso está activo pero no hay política de privacidad publicada ni enlazada (art. 14 ter).', 'cumpleconsent' ), esc_url( admin_url( 'admin.php?page=cumpleconsent-documentos' ) ), esc_html__( 'Generar la política', 'cumpleconsent' ) ); } } /** * Traslada lo guardado con el prefijo corto anterior. * * Cambiar el prefijo es obligatorio para el directorio, pero no puede costarle * datos a quien ya tenía el plugin instalado: opciones, tablas, el metadato de * la política y las tareas programadas se mueven conservando su contenido. */ public static function migrate_legacy_names(): void { global $wpdb; $opciones = array( 'ccn_settings' => 'cumpleconsent_settings', 'ccn_log_salt' => 'cumpleconsent_log_salt', 'ccn_requests_schema' => 'cumpleconsent_requests_schema', 'ccn_log_schema' => 'cumpleconsent_log_schema', ); foreach ( $opciones as $viejo => $nuevo ) { $valor = get_option( $viejo ); if ( false === $valor ) { continue; } if ( false === get_option( $nuevo ) ) { update_option( $nuevo, $valor ); } delete_option( $viejo ); } $tablas = array( 'ccn_requests' => 'cumpleconsent_requests', 'ccn_consent_log' => 'cumpleconsent_consent_log', ); foreach ( $tablas as $viejo => $nuevo ) { $t_vieja = $wpdb->prefix . $viejo; $t_nueva = $wpdb->prefix . $nuevo; $hay_vieja = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t_vieja ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery $hay_nueva = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t_nueva ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery if ( $hay_vieja && ! $hay_nueva ) { $wpdb->query( "RENAME TABLE `{$t_vieja}` TO `{$t_nueva}`" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, PluginCheck.Security.DirectDB.UnescapedDBParameter -- El nombre de la tabla se arma con $wpdb->prefix y un sufijo literal del plugin; no entra nada del visitante. } } $meta = $wpdb->prefix . 'postmeta'; $wpdb->query( "UPDATE `{$meta}` SET meta_key = '_cumpleconsent_generated' WHERE meta_key = '_ccn_generated'" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, PluginCheck.Security.DirectDB.UnescapedDBParameter -- La tabla se arma con $wpdb->prefix y las claves son literales; no entra nada del visitante. wp_clear_scheduled_hook( 'ccn_daily_maintenance' ); wp_clear_scheduled_hook( 'ccn_check_deadlines' ); } // ── Ciclo de vida ──────────────────────────────────────────────────────── /** Activación: tablas, opciones base y tareas programadas. */ public static function on_activate(): void { Consent_Log::install(); Rights::install(); Settings::log_salt(); $all = Settings::all(); if ( empty( $all['installed_at'] ) ) { Settings::save( array( 'installed_at' => current_time( 'mysql', true ), 'wp_version' => get_bloginfo( 'version' ), ) ); } if ( ! wp_next_scheduled( CUMPLECONSENT_CRON_PURGE ) ) { wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', CUMPLECONSENT_CRON_PURGE ); } if ( ! wp_next_scheduled( CUMPLECONSENT_CRON_DEADLINES ) ) { wp_schedule_event( time() + ( 2 * HOUR_IN_SECONDS ), 'daily', CUMPLECONSENT_CRON_DEADLINES ); } } /** Desactivación: se quitan las tareas y se conservan los datos. */ public static function on_deactivate(): void { wp_clear_scheduled_hook( CUMPLECONSENT_CRON_PURGE ); wp_clear_scheduled_hook( CUMPLECONSENT_CRON_DEADLINES ); } /** * Purga las cachés de página conocidas. * * Necesario porque el aviso y su versión viajan en el HTML: si el sitio * sirve una página cacheada con la versión anterior, el visitante no ve el * aviso nuevo. Se llama al guardar ajustes que cambian el aviso. * * @return string[] Cachés purgadas. */ public static function purge_caches(): array { $done = array(); if ( function_exists( 'rocket_clean_domain' ) ) { rocket_clean_domain(); $done[] = 'WP Rocket'; } if ( function_exists( 'wp_cache_clear_cache' ) ) { wp_cache_clear_cache(); $done[] = 'WP Super Cache'; } if ( function_exists( 'w3tc_flush_all' ) ) { w3tc_flush_all(); $done[] = 'W3 Total Cache'; } if ( function_exists( 'wpfc_clear_all_cache' ) ) { wpfc_clear_all_cache( true ); $done[] = 'WP Fastest Cache'; } if ( has_action( 'litespeed_purge_all' ) ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- litespeed_purge_all es el hook de LiteSpeed Cache: hay que invocarlo con su nombre exacto, y solo si alguien lo escucha (lo comprueba has_action). do_action( 'litespeed_purge_all' ); $done[] = 'LiteSpeed Cache'; } if ( function_exists( 'cache_enabler_clear_complete_cache' ) ) { cache_enabler_clear_complete_cache(); $done[] = 'Cache Enabler'; } if ( function_exists( 'wp_cache_flush' ) ) { wp_cache_flush(); $done[] = 'Caché de objetos'; } return $done; } }