fix(wrappers): eliminar wrappers vacíos y corregir exclusiones AdSense (Plan 99.15)
## Problema - Componentes deshabilitados/excluidos dejaban wrappers HTML vacíos (navbar 32px, sidebar col-lg-3 294px) - AdSense ignoraba exclusiones por URL pattern en grupo _exclusions ## Solución Plan 99.15 (Clean Architecture) ### Domain Layer - WrapperVisibilityCheckerInterface: contrato para verificar visibilidad ### Application Layer - CheckWrapperVisibilityUseCase: orquesta verificaciones de visibilidad ### Infrastructure Layer - WordPressComponentVisibilityRepository: consulta BD + PageVisibilityHelper - WrapperVisibilityService: facade estático para templates - BodyClassHooksRegistrar: agrega clases CSS failsafe al body ### Templates modificados - header.php: renderizado condicional de <nav> wrapper - page.php/single.php: lógica dinámica col-lg-9/col-lg-12 según sidebar ### CSS Failsafe - css-global-utilities.css: reglas body.roi-hide-* como respaldo ## Fix AdSense (Inc/adsense-placement.php) - Agregado PageVisibilityHelper::shouldShow() a todas las funciones: roi_render_ad_slot, roi_render_rail_ads, roi_enqueue_adsense_script, roi_inject_content_ads, roi_render_anchor_ads, roi_render_vignette_ad, roi_enqueue_anchor_vignette_scripts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -88,3 +88,43 @@
|
||||
.transition-none {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
COMPONENT VISIBILITY FAILSAFE (Plan 99.15)
|
||||
|
||||
CSS failsafe: Oculta wrappers de componentes
|
||||
cuando body tiene clases roi-hide-*
|
||||
|
||||
Estas clases se agregan via BodyClassHooksRegistrar
|
||||
cuando los componentes están deshabilitados/excluidos.
|
||||
======================================== */
|
||||
|
||||
/* Navbar hidden */
|
||||
body.roi-hide-navbar .navbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Table of Contents hidden */
|
||||
body.roi-hide-toc .roi-toc-container {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* CTA Sidebar hidden */
|
||||
body.roi-hide-cta-sidebar .roi-cta-box {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Generic sidebar hidden */
|
||||
body.roi-hide-sidebar .sidebar-sticky {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* When ALL sidebar components are hidden, expand main column */
|
||||
body.roi-sidebar-empty .col-lg-9 {
|
||||
flex: 0 0 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
body.roi-sidebar-empty .col-lg-3 {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use ROITheme\Shared\Infrastructure\Services\PageVisibilityHelper;
|
||||
|
||||
/**
|
||||
* Renderiza un slot de anuncio en una ubicacion
|
||||
*
|
||||
@@ -52,11 +54,16 @@ function roi_render_ad_slot(string $location): string
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones
|
||||
// Verificar exclusiones legacy (forms group)
|
||||
if (roi_is_ad_excluded($settings)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
|
||||
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Obtener renderer desde DIContainer (DIP compliant)
|
||||
$renderer = $container->getAdsensePlacementRenderer();
|
||||
|
||||
@@ -143,11 +150,16 @@ function roi_render_rail_ads(): string
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones
|
||||
// Verificar exclusiones legacy (forms group)
|
||||
if (roi_is_ad_excluded($settings)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
|
||||
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Obtener renderer desde DIContainer (DIP compliant)
|
||||
$renderer = $container->getAdsensePlacementRenderer();
|
||||
|
||||
@@ -193,6 +205,11 @@ function roi_enqueue_adsense_script(): void
|
||||
return;
|
||||
}
|
||||
|
||||
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
|
||||
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$publisherId = $settings['content']['publisher_id'] ?? '';
|
||||
if (empty($publisherId)) {
|
||||
return;
|
||||
@@ -246,11 +263,16 @@ function roi_inject_content_ads(string $content): string
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Verificar exclusiones
|
||||
// Verificar exclusiones legacy (forms group)
|
||||
if (roi_is_ad_excluded($settings)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
|
||||
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$renderer = $container->getAdsensePlacementRenderer();
|
||||
|
||||
// Inyectar anuncio al inicio (post-top)
|
||||
@@ -446,11 +468,16 @@ function roi_render_anchor_ads(): string
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones
|
||||
// Verificar exclusiones legacy (forms group)
|
||||
if (roi_is_ad_excluded($settings)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
|
||||
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Obtener renderer desde DIContainer (DIP compliant)
|
||||
$renderer = $container->getAdsensePlacementRenderer();
|
||||
|
||||
@@ -490,11 +517,16 @@ function roi_render_vignette_ad(): string
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones
|
||||
// Verificar exclusiones legacy (forms group)
|
||||
if (roi_is_ad_excluded($settings)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
|
||||
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Obtener renderer desde DIContainer (DIP compliant)
|
||||
$renderer = $container->getAdsensePlacementRenderer();
|
||||
|
||||
@@ -556,6 +588,11 @@ function roi_enqueue_anchor_vignette_scripts(): void
|
||||
return;
|
||||
}
|
||||
|
||||
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
|
||||
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Encolar script
|
||||
wp_enqueue_script(
|
||||
'roi-anchor-vignette',
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Application\UseCases;
|
||||
|
||||
use ROITheme\Shared\Domain\Contracts\WrapperVisibilityCheckerInterface;
|
||||
|
||||
/**
|
||||
* UseCase: Verificar si un wrapper de componente debe renderizarse
|
||||
*
|
||||
* Responsabilidad: Orquestar la lógica de verificación de visibilidad
|
||||
* combinando múltiples criterios:
|
||||
* 1. Componente habilitado (is_enabled)
|
||||
* 2. Visible en dispositivo actual (show_on_mobile/desktop)
|
||||
* 3. No excluido por reglas (categoría, post ID, URL, page visibility)
|
||||
*
|
||||
* @package ROITheme\Shared\Application\UseCases
|
||||
* @see Plan 99.15 - Fix Empty Layout Wrappers
|
||||
*/
|
||||
final class CheckWrapperVisibilityUseCase
|
||||
{
|
||||
public function __construct(
|
||||
private readonly WrapperVisibilityCheckerInterface $visibilityChecker
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Ejecuta la verificación de visibilidad del wrapper
|
||||
*
|
||||
* @param string $componentName Nombre del componente (kebab-case)
|
||||
* @param bool $isMobile True si es dispositivo móvil
|
||||
* @return bool True si el wrapper debe renderizarse
|
||||
*/
|
||||
public function execute(string $componentName, bool $isMobile): bool
|
||||
{
|
||||
// Criterio 1: Debe estar habilitado
|
||||
if (!$this->visibilityChecker->isEnabled($componentName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Criterio 2: Debe ser visible en el dispositivo actual
|
||||
if (!$this->visibilityChecker->isVisibleOnDevice($componentName, $isMobile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Criterio 3: No debe estar excluido
|
||||
if (!$this->visibilityChecker->isNotExcluded($componentName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Domain\Contracts;
|
||||
|
||||
/**
|
||||
* Interface para verificar visibilidad de wrappers de componentes
|
||||
*
|
||||
* Responsabilidad: Definir contrato para determinar si un wrapper
|
||||
* de componente debe renderizarse basándose en:
|
||||
* - Estado habilitado/deshabilitado
|
||||
* - Visibilidad por dispositivo
|
||||
* - Reglas de exclusión (categoría, post ID, URL pattern, page visibility)
|
||||
*
|
||||
* @package ROITheme\Shared\Domain\Contracts
|
||||
* @see Plan 99.15 - Fix Empty Layout Wrappers
|
||||
*/
|
||||
interface WrapperVisibilityCheckerInterface
|
||||
{
|
||||
/**
|
||||
* Verifica si el componente está habilitado globalmente
|
||||
*
|
||||
* @param string $componentName Nombre del componente (kebab-case)
|
||||
* @return bool True si is_enabled = true en BD
|
||||
*/
|
||||
public function isEnabled(string $componentName): bool;
|
||||
|
||||
/**
|
||||
* Verifica si el componente es visible en el dispositivo actual
|
||||
*
|
||||
* @param string $componentName Nombre del componente (kebab-case)
|
||||
* @param bool $isMobile True si es dispositivo móvil
|
||||
* @return bool True si show_on_mobile/show_on_desktop según corresponda
|
||||
*/
|
||||
public function isVisibleOnDevice(string $componentName, bool $isMobile): bool;
|
||||
|
||||
/**
|
||||
* Verifica si el componente NO está excluido para la página actual
|
||||
*
|
||||
* Evalúa todas las reglas de exclusión:
|
||||
* - Exclusión por categoría
|
||||
* - Exclusión por post ID
|
||||
* - Exclusión por URL pattern
|
||||
* - Page visibility (home, posts, pages, archives, search)
|
||||
*
|
||||
* @param string $componentName Nombre del componente (kebab-case)
|
||||
* @return bool True si el componente NO está excluido
|
||||
*/
|
||||
public function isNotExcluded(string $componentName): bool;
|
||||
}
|
||||
@@ -37,6 +37,11 @@ use ROITheme\Shared\Infrastructure\Services\WordPressPageContextProvider;
|
||||
use ROITheme\Shared\Infrastructure\Services\WordPressServerRequestProvider;
|
||||
use ROITheme\Shared\Application\UseCases\EvaluateExclusions\EvaluateExclusionsUseCase;
|
||||
use ROITheme\Shared\Application\UseCases\EvaluateComponentVisibility\EvaluateComponentVisibilityUseCase;
|
||||
// Wrapper Visibility System (Plan 99.15)
|
||||
use ROITheme\Shared\Domain\Contracts\WrapperVisibilityCheckerInterface;
|
||||
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentVisibilityRepository;
|
||||
use ROITheme\Shared\Application\UseCases\CheckWrapperVisibilityUseCase;
|
||||
use ROITheme\Shared\Infrastructure\Wordpress\BodyClassHooksRegistrar;
|
||||
|
||||
/**
|
||||
* DIContainer - Contenedor de Inyección de Dependencias
|
||||
@@ -443,4 +448,49 @@ final class DIContainer
|
||||
}
|
||||
return $this->instances['evaluateComponentVisibilityUseCase'];
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// Wrapper Visibility System (Plan 99.15)
|
||||
// ===============================
|
||||
|
||||
/**
|
||||
* Obtiene el repositorio de visibilidad de wrappers
|
||||
*
|
||||
* Implementa WrapperVisibilityCheckerInterface
|
||||
*/
|
||||
public function getWrapperVisibilityChecker(): WrapperVisibilityCheckerInterface
|
||||
{
|
||||
if (!isset($this->instances['wrapperVisibilityChecker'])) {
|
||||
$this->instances['wrapperVisibilityChecker'] = new WordPressComponentVisibilityRepository($this->wpdb);
|
||||
}
|
||||
return $this->instances['wrapperVisibilityChecker'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene el caso de uso para verificar visibilidad de wrappers
|
||||
*
|
||||
* Usado por WrapperVisibilityService para templates
|
||||
*/
|
||||
public function getCheckWrapperVisibilityUseCase(): CheckWrapperVisibilityUseCase
|
||||
{
|
||||
if (!isset($this->instances['checkWrapperVisibilityUseCase'])) {
|
||||
$this->instances['checkWrapperVisibilityUseCase'] = new CheckWrapperVisibilityUseCase(
|
||||
$this->getWrapperVisibilityChecker()
|
||||
);
|
||||
}
|
||||
return $this->instances['checkWrapperVisibilityUseCase'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene el registrador de hooks para body_class
|
||||
*
|
||||
* CSS failsafe: Agrega clases cuando componentes están ocultos
|
||||
*/
|
||||
public function getBodyClassHooksRegistrar(): BodyClassHooksRegistrar
|
||||
{
|
||||
if (!isset($this->instances['bodyClassHooksRegistrar'])) {
|
||||
$this->instances['bodyClassHooksRegistrar'] = new BodyClassHooksRegistrar();
|
||||
}
|
||||
return $this->instances['bodyClassHooksRegistrar'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Infrastructure\Persistence\WordPress;
|
||||
|
||||
use ROITheme\Shared\Domain\Contracts\WrapperVisibilityCheckerInterface;
|
||||
use ROITheme\Shared\Infrastructure\Services\PageVisibilityHelper;
|
||||
|
||||
/**
|
||||
* Implementación de WrapperVisibilityCheckerInterface para WordPress
|
||||
*
|
||||
* Responsabilidad: Consultar BD y evaluar visibilidad de wrappers de componentes
|
||||
*
|
||||
* - Consulta tabla wp_roi_theme_component_settings para is_enabled, show_on_mobile, show_on_desktop
|
||||
* - Delega evaluación de exclusiones a PageVisibilityHelper (DRY)
|
||||
*
|
||||
* @package ROITheme\Shared\Infrastructure\Persistence\WordPress
|
||||
* @see Plan 99.15 - Fix Empty Layout Wrappers
|
||||
*/
|
||||
final class WordPressComponentVisibilityRepository implements WrapperVisibilityCheckerInterface
|
||||
{
|
||||
private string $tableName;
|
||||
|
||||
public function __construct(
|
||||
private \wpdb $wpdb
|
||||
) {
|
||||
$this->tableName = $this->wpdb->prefix . 'roi_theme_component_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isEnabled(string $componentName): bool
|
||||
{
|
||||
$value = $this->getVisibilityAttribute($componentName, 'is_enabled');
|
||||
|
||||
// Si no existe el registro, asumir habilitado por defecto
|
||||
if ($value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->toBool($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isVisibleOnDevice(string $componentName, bool $isMobile): bool
|
||||
{
|
||||
$attribute = $isMobile ? 'show_on_mobile' : 'show_on_desktop';
|
||||
$value = $this->getVisibilityAttribute($componentName, $attribute);
|
||||
|
||||
// Si no existe el registro, asumir visible por defecto
|
||||
if ($value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->toBool($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Delega a PageVisibilityHelper que ya implementa:
|
||||
* - Visibilidad por tipo de página (home, posts, pages, archives, search)
|
||||
* - Exclusiones por categoría, post ID, URL pattern
|
||||
*/
|
||||
public function isNotExcluded(string $componentName): bool
|
||||
{
|
||||
return PageVisibilityHelper::shouldShow($componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene un atributo del grupo visibility desde la BD
|
||||
*
|
||||
* @param string $componentName
|
||||
* @param string $attributeName
|
||||
* @return string|null
|
||||
*/
|
||||
private function getVisibilityAttribute(string $componentName, string $attributeName): ?string
|
||||
{
|
||||
$sql = $this->wpdb->prepare(
|
||||
"SELECT attribute_value
|
||||
FROM {$this->tableName}
|
||||
WHERE component_name = %s
|
||||
AND group_name = %s
|
||||
AND attribute_name = %s
|
||||
LIMIT 1",
|
||||
$componentName,
|
||||
'visibility',
|
||||
$attributeName
|
||||
);
|
||||
|
||||
$result = $this->wpdb->get_var($sql);
|
||||
|
||||
return $result !== null ? (string) $result : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convierte string a boolean
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
private function toBool(string $value): bool
|
||||
{
|
||||
return $value === '1' || strtolower($value) === 'true';
|
||||
}
|
||||
}
|
||||
100
Shared/Infrastructure/Services/WrapperVisibilityService.php
Normal file
100
Shared/Infrastructure/Services/WrapperVisibilityService.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Infrastructure\Services;
|
||||
|
||||
use ROITheme\Shared\Application\UseCases\CheckWrapperVisibilityUseCase;
|
||||
use ROITheme\Shared\Infrastructure\Di\DIContainer;
|
||||
|
||||
/**
|
||||
* Servicio facade para verificar visibilidad de wrappers desde templates
|
||||
*
|
||||
* Responsabilidad: Proveer acceso simplificado (singleton/static) al
|
||||
* CheckWrapperVisibilityUseCase para uso en templates PHP.
|
||||
*
|
||||
* USO EN TEMPLATES:
|
||||
* ```php
|
||||
* if (WrapperVisibilityService::shouldRenderWrapper('navbar')) {
|
||||
* // Renderizar wrapper y componente
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @package ROITheme\Shared\Infrastructure\Services
|
||||
* @see Plan 99.15 - Fix Empty Layout Wrappers
|
||||
*/
|
||||
final class WrapperVisibilityService
|
||||
{
|
||||
private static ?CheckWrapperVisibilityUseCase $useCase = null;
|
||||
|
||||
/**
|
||||
* Verifica si el wrapper de un componente debe renderizarse
|
||||
*
|
||||
* @param string $componentName Nombre del componente (kebab-case)
|
||||
* @return bool True si el wrapper debe renderizarse
|
||||
*/
|
||||
public static function shouldRenderWrapper(string $componentName): bool
|
||||
{
|
||||
$useCase = self::getUseCase();
|
||||
$isMobile = self::detectMobile();
|
||||
|
||||
return $useCase->execute($componentName, $isMobile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica visibilidad para múltiples componentes
|
||||
*
|
||||
* Útil para determinar si renderizar un contenedor que agrupa varios componentes
|
||||
*
|
||||
* @param array<string> $componentNames Lista de nombres de componentes
|
||||
* @return bool True si AL MENOS UNO de los componentes debe mostrarse
|
||||
*/
|
||||
public static function shouldRenderAnyWrapper(array $componentNames): bool
|
||||
{
|
||||
foreach ($componentNames as $componentName) {
|
||||
if (self::shouldRenderWrapper($componentName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene o crea el UseCase
|
||||
*
|
||||
* @return CheckWrapperVisibilityUseCase
|
||||
*/
|
||||
private static function getUseCase(): CheckWrapperVisibilityUseCase
|
||||
{
|
||||
if (self::$useCase === null) {
|
||||
$container = DIContainer::getInstance();
|
||||
self::$useCase = $container->getCheckWrapperVisibilityUseCase();
|
||||
}
|
||||
|
||||
return self::$useCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detecta si el dispositivo actual es móvil
|
||||
*
|
||||
* Usa wp_is_mobile() de WordPress
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function detectMobile(): bool
|
||||
{
|
||||
if (function_exists('wp_is_mobile')) {
|
||||
return wp_is_mobile();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia la instancia del UseCase (útil para tests)
|
||||
*/
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$useCase = null;
|
||||
}
|
||||
}
|
||||
96
Shared/Infrastructure/Wordpress/BodyClassHooksRegistrar.php
Normal file
96
Shared/Infrastructure/Wordpress/BodyClassHooksRegistrar.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Infrastructure\Wordpress;
|
||||
|
||||
use ROITheme\Shared\Infrastructure\Services\WrapperVisibilityService;
|
||||
|
||||
/**
|
||||
* Registra hook body_class para agregar clases CSS de componentes ocultos
|
||||
*
|
||||
* RESPONSABILIDAD:
|
||||
* - Registrar hook body_class
|
||||
* - Agregar clases CSS cuando componentes están ocultos
|
||||
*
|
||||
* FLUJO:
|
||||
* 1. body_class filter → addHiddenComponentClasses()
|
||||
* - Verifica visibilidad de componentes clave (navbar, sidebar components)
|
||||
* - Agrega clases: roi-hide-navbar, roi-hide-sidebar, etc.
|
||||
*
|
||||
* PROPÓSITO:
|
||||
* Failsafe CSS: Si los templates no pueden ocultar wrappers completamente,
|
||||
* estas clases permiten ocultarlos via CSS.
|
||||
*
|
||||
* PATRÓN:
|
||||
* - SRP: Solo registra hooks, delega lógica a WrapperVisibilityService
|
||||
*
|
||||
* @package ROITheme\Shared\Infrastructure\Wordpress
|
||||
* @see Plan 99.15 - Fix Empty Layout Wrappers
|
||||
*/
|
||||
final class BodyClassHooksRegistrar
|
||||
{
|
||||
/**
|
||||
* Componentes que afectan el layout principal
|
||||
*/
|
||||
private const LAYOUT_COMPONENTS = [
|
||||
'navbar' => 'roi-hide-navbar',
|
||||
'table-of-contents' => 'roi-hide-toc',
|
||||
'cta-box-sidebar' => 'roi-hide-cta-sidebar',
|
||||
'sidebar' => 'roi-hide-sidebar',
|
||||
];
|
||||
|
||||
/**
|
||||
* Componentes de sidebar que determinan si mostrar columna lateral
|
||||
*/
|
||||
private const SIDEBAR_COMPONENTS = [
|
||||
'table-of-contents',
|
||||
'cta-box-sidebar',
|
||||
];
|
||||
|
||||
/**
|
||||
* Registrar hooks de WordPress
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
add_filter('body_class', [$this, 'addHiddenComponentClasses']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback para body_class - agrega clases para componentes ocultos
|
||||
*
|
||||
* @param array<string> $classes Clases existentes
|
||||
* @return array<string> Clases modificadas
|
||||
*/
|
||||
public function addHiddenComponentClasses(array $classes): array
|
||||
{
|
||||
// Agregar clase por cada componente oculto
|
||||
foreach (self::LAYOUT_COMPONENTS as $componentName => $cssClass) {
|
||||
if (!WrapperVisibilityService::shouldRenderWrapper($componentName)) {
|
||||
$classes[] = $cssClass;
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar si TODOS los componentes de sidebar están ocultos
|
||||
if ($this->allSidebarComponentsHidden()) {
|
||||
$classes[] = 'roi-sidebar-empty';
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica si todos los componentes de sidebar están ocultos
|
||||
*
|
||||
* @return bool True si ningún componente de sidebar debe mostrarse
|
||||
*/
|
||||
private function allSidebarComponentsHidden(): bool
|
||||
{
|
||||
foreach (self::SIDEBAR_COMPONENTS as $componentName) {
|
||||
if (WrapperVisibilityService::shouldRenderWrapper($componentName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -370,6 +370,11 @@ add_action('after_setup_theme', function() {
|
||||
$criticalCSSService = roi_get_critical_css_service();
|
||||
$hooksRegistrar = new \ROITheme\Shared\Infrastructure\Wordpress\CriticalCSSHooksRegistrar($criticalCSSService);
|
||||
$hooksRegistrar->register();
|
||||
|
||||
// 3. Body Class Hooks (Plan 99.15) - CSS failsafe para componentes ocultos
|
||||
$container = \ROITheme\Shared\Infrastructure\Di\DIContainer::getInstance();
|
||||
$bodyClassHooksRegistrar = $container->getBodyClassHooksRegistrar();
|
||||
$bodyClassHooksRegistrar->register();
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
@@ -378,6 +383,47 @@ add_action('after_setup_theme', function() {
|
||||
// NO hardcodear CSS aquí - viola la arquitectura Clean Architecture.
|
||||
// =============================================================================
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTION: roi_should_render_wrapper() - Plan 99.15
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Verifica si el wrapper de un componente debe renderizarse
|
||||
*
|
||||
* Evalúa:
|
||||
* - is_enabled
|
||||
* - show_on_mobile / show_on_desktop
|
||||
* - Exclusiones (categoría, post ID, URL pattern, page visibility)
|
||||
*
|
||||
* USO EN TEMPLATES:
|
||||
* ```php
|
||||
* if (roi_should_render_wrapper('navbar')) {
|
||||
* echo '<nav class="navbar">';
|
||||
* echo roi_render_component('navbar');
|
||||
* echo '</nav>';
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param string $componentName Nombre del componente (kebab-case)
|
||||
* @return bool True si el wrapper debe renderizarse
|
||||
* @see Plan 99.15 - Fix Empty Layout Wrappers
|
||||
*/
|
||||
function roi_should_render_wrapper(string $componentName): bool {
|
||||
return \ROITheme\Shared\Infrastructure\Services\WrapperVisibilityService::shouldRenderWrapper($componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica si AL MENOS UN componente de una lista debe renderizarse
|
||||
*
|
||||
* Útil para determinar si mostrar columna sidebar
|
||||
*
|
||||
* @param array<string> $componentNames Lista de nombres de componentes
|
||||
* @return bool True si al menos uno debe mostrarse
|
||||
*/
|
||||
function roi_should_render_any_wrapper(array $componentNames): bool {
|
||||
return \ROITheme\Shared\Infrastructure\Services\WrapperVisibilityService::shouldRenderAnyWrapper($componentNames);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTION: roi_get_adsense_search_config()
|
||||
// =============================================================================
|
||||
|
||||
@@ -35,6 +35,10 @@ if (function_exists('roi_render_component')) {
|
||||
?>
|
||||
|
||||
<!-- Navbar (Template líneas 264-320) -->
|
||||
<?php
|
||||
// Plan 99.15: Solo renderizar wrapper si navbar debe mostrarse
|
||||
if (function_exists('roi_should_render_wrapper') && roi_should_render_wrapper('navbar')):
|
||||
?>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark py-3" role="navigation" aria-label="<?php esc_attr_e('Primary Navigation', 'roi-theme'); ?>">
|
||||
<div class="container">
|
||||
|
||||
@@ -95,3 +99,4 @@ if (function_exists('roi_render_component')) {
|
||||
|
||||
</div><!-- .container -->
|
||||
</nav><!-- .navbar -->
|
||||
<?php endif; // roi_should_render_wrapper('navbar') ?>
|
||||
16
page.php
16
page.php
@@ -26,11 +26,19 @@ if (function_exists('roi_render_component')) {
|
||||
?>
|
||||
|
||||
<!-- Main Content Grid -->
|
||||
<?php
|
||||
// Plan 99.15: Determinar si mostrar sidebar basándose en visibilidad de componentes
|
||||
$sidebar_components = ['table-of-contents', 'cta-box-sidebar'];
|
||||
$show_sidebar = function_exists('roi_should_render_any_wrapper')
|
||||
? roi_should_render_any_wrapper($sidebar_components)
|
||||
: true;
|
||||
$main_col_class = $show_sidebar ? 'col-lg-9' : 'col-lg-12';
|
||||
?>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
<!-- Main Content Column (col-lg-9) -->
|
||||
<div class="col-lg-9">
|
||||
<!-- Main Content Column -->
|
||||
<div class="<?php echo esc_attr($main_col_class); ?>">
|
||||
|
||||
<!-- Featured Image - Componente dinámico -->
|
||||
<?php
|
||||
@@ -79,8 +87,9 @@ if (function_exists('roi_render_component')) {
|
||||
}
|
||||
?>
|
||||
|
||||
</div><!-- .col-lg-9 -->
|
||||
</div><!-- .<?php echo esc_attr($main_col_class); ?> -->
|
||||
|
||||
<?php if ($show_sidebar): ?>
|
||||
<!-- Sidebar Column (col-lg-3) -->
|
||||
<div class="col-lg-3">
|
||||
<div class="sidebar-sticky">
|
||||
@@ -99,6 +108,7 @@ if (function_exists('roi_render_component')) {
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div><!-- .row -->
|
||||
</div><!-- .container -->
|
||||
|
||||
16
single.php
16
single.php
@@ -24,11 +24,19 @@ if (function_exists('roi_render_component')) {
|
||||
?>
|
||||
|
||||
<!-- Main Content Grid (Template líneas 169-1020) -->
|
||||
<?php
|
||||
// Plan 99.15: Determinar si mostrar sidebar basándose en visibilidad de componentes
|
||||
$sidebar_components = ['table-of-contents', 'cta-box-sidebar'];
|
||||
$show_sidebar = function_exists('roi_should_render_any_wrapper')
|
||||
? roi_should_render_any_wrapper($sidebar_components)
|
||||
: true;
|
||||
$main_col_class = $show_sidebar ? 'col-lg-9' : 'col-lg-12';
|
||||
?>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
<!-- Main Content Column (col-lg-9) -->
|
||||
<div class="col-lg-9">
|
||||
<!-- Main Content Column -->
|
||||
<div class="<?php echo esc_attr($main_col_class); ?>">
|
||||
|
||||
<!-- Featured Image - Componente dinámico -->
|
||||
<?php
|
||||
@@ -77,8 +85,9 @@ if (function_exists('roi_render_component')) {
|
||||
}
|
||||
?>
|
||||
|
||||
</div><!-- .col-lg-9 -->
|
||||
</div><!-- .<?php echo esc_attr($main_col_class); ?> -->
|
||||
|
||||
<?php if ($show_sidebar): ?>
|
||||
<!-- Sidebar Column (col-lg-3) -->
|
||||
<div class="col-lg-3">
|
||||
<div class="sidebar-sticky">
|
||||
@@ -97,6 +106,7 @@ if (function_exists('roi_render_component')) {
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div><!-- .row -->
|
||||
</div><!-- .container -->
|
||||
|
||||
Reference in New Issue
Block a user