- renombrar openspec/ a _openspec/ (carpeta auxiliar) - mover specs de features a changes/ - crear specs base: arquitectura-limpia, estandares-codigo, nomenclatura - migrar _planificacion/ con design-system y roi-theme-template - agregar especificacion recaptcha anti-spam (proposal, tasks, spec) - corregir rutas y referencias en todas las specs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
9.8 KiB
Especificacion: Unificacion de Visibilidad AdSense para Compatibilidad con Cache
Purpose
Unificar la logica de visibilidad de AdSense para que TODA la evaluacion dependiente de usuario
(hide_for_logged_in) se realice en el cliente via JavaScript-First, eliminando llamadas a
is_user_logged_in() durante el render PHP.
Problema actual: El AdsensePlacementRenderer usa PageVisibilityHelper::shouldShow() que
internamente llama is_user_logged_in() en PHP. Esto causa que el HTML generado varie segun el
estado de autenticacion, rompiendo la compatibilidad con page cache.
Solucion: Cuando javascript_first_mode esta activo, el Renderer debe generar SIEMPRE el HTML
de los slots, delegando la decision de hide_for_logged_in al endpoint REST y al JavaScript del cliente.
Background
Arquitectura JavaScript-First existente (spec: adsense-javascript-first)
El sistema JavaScript-First ya implementa:
-
Endpoint REST
/wp-json/roi-theme/v1/adsense-placement/visibility- Evalua
is_user_logged_in()en tiempo real (no cacheado) - Retorna decision
show_ads: true/falsecon razones - Headers anti-cache (no-store, no-cache)
- Evalua
-
Cliente JavaScript
adsense-visibility.js- Consulta endpoint via AJAX con cookies (credentials: same-origin)
- Cachea decision en localStorage
- Aplica clases CSS para mostrar/ocultar slots
El gap actual
FLUJO ACTUAL (PROBLEMATICO):
Request → PHP Render → PageVisibilityHelper::shouldShow()
↓
is_user_logged_in() ← ROMPE CACHE
↓
HTML con/sin slots (depende de login)
↓
Page Cache ← HTML incorrecto para otros usuarios
FLUJO DESEADO (CON ESTA SPEC):
Request → PHP Render → [javascript_first_mode=true?]
↓ SI
SIEMPRE genera HTML con slots
↓
Page Cache ← HTML identico para todos
↓
JS consulta endpoint REST
↓
Muestra/oculta segun respuesta
Requirements
Requirement 1: Bypass de hide_for_logged_in en modo JavaScript-First
The AdsensePlacementRenderer MUST skip the is_user_logged_in() evaluation during PHP render
when javascript_first_mode is enabled.
Scenario: JavaScript-First activo, usuario anonimo
- GIVEN
javascript_first_modeesta habilitado en settings - AND
hide_for_logged_inesta habilitado - AND el visitante NO esta logueado
- WHEN se renderiza un slot de AdSense
- THEN el HTML del slot DEBE generarse (placeholders visibles)
- AND el JavaScript DEBE consultar el endpoint
- AND el endpoint retorna
show_ads: true - AND los anuncios se muestran
Scenario: JavaScript-First activo, usuario logueado
- GIVEN
javascript_first_modeesta habilitado - AND
hide_for_logged_inesta habilitado - AND el visitante ESTA logueado
- WHEN se renderiza un slot de AdSense
- THEN el HTML del slot DEBE generarse (en cache seria identico a anonimo)
- AND el JavaScript DEBE consultar el endpoint
- AND el endpoint retorna
show_ads: falsecon razonhide_for_logged_in - AND los anuncios se OCULTAN via CSS/JS
Scenario: JavaScript-First deshabilitado (modo legacy)
- GIVEN
javascript_first_modeesta DESHABILITADO - AND
hide_for_logged_inesta habilitado - AND el visitante ESTA logueado
- WHEN se renderiza un slot de AdSense
- THEN el HTML del slot NO se genera (comportamiento legacy)
- AND
is_user_logged_in()SE evalua en PHP - BECAUSE sin JS-First, el modo legacy es la unica opcion
Requirement 2: PageVisibilityHelper debe respetar modo JavaScript-First
The PageVisibilityHelper MUST provide a method that excludes user-dependent checks when JavaScript-First mode is active for a component.
Scenario: Nuevo metodo shouldShowForCache
- GIVEN un componente con
javascript_first_modehabilitado - WHEN se llama
PageVisibilityHelper::shouldShowForCache('adsense-placement') - THEN DEBE evaluar visibilidad por tipo de pagina (home, posts, pages, etc.)
- AND DEBE evaluar exclusiones por categoria, ID, URL pattern
- AND NO DEBE evaluar
hide_for_logged_in - BECAUSE esa evaluacion la hace el cliente
Scenario: Componente sin JavaScript-First usa metodo existente
- GIVEN un componente sin
javascript_first_mode - WHEN se llama
PageVisibilityHelper::shouldShow('otro-componente') - THEN DEBE usar flujo existente incluyendo
hide_for_logged_in - BECAUSE sin JS-First, PHP es la unica fuente de verdad
Requirement 3: WordPressComponentVisibilityRepository debe soportar modo bypass
The repository MUST support skipping is_user_logged_in() check when requested.
Scenario: isNotExcluded con bypass de login check
- GIVEN se llama
isNotExcluded('adsense-placement', skipLoginCheck: true) - WHEN el metodo evalua exclusiones
- THEN NO DEBE llamar
is_user_logged_in() - AND DEBE evaluar otras exclusiones normalmente
Requirement 4: El endpoint REST DEBE evaluar hide_for_logged_in
The REST endpoint /adsense-placement/visibility MUST evaluate hide_for_logged_in
and include it in the decision.
NOTA: Esto YA esta implementado en AdsenseVisibilityController.php. Solo documentamos
para claridad.
Scenario: Endpoint evalua usuario logueado
- GIVEN peticion al endpoint con cookies de sesion
- AND
hide_for_logged_inesta habilitado - AND el usuario ESTA logueado
- WHEN el endpoint procesa la peticion
- THEN retorna
show_ads: false - AND
reasonsincluyehide_for_logged_in
Requirement 5: El CSS de slots ocultos NO debe afectar el layout
When JavaScript hides ad slots, they MUST collapse completely without affecting page layout.
NOTA: Esto YA esta implementado con :has([data-ad-status='unfilled']). Documentamos para
verificar que se mantiene.
Implementation
Archivos a modificar
1. AdsensePlacementRenderer.php
// ANTES (linea 38-43):
public function renderSlot(array $settings, string $location): string
{
// 0. Verificar visibilidad por tipo de pagina y exclusiones
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
return '';
}
// ...
}
// DESPUES:
public function renderSlot(array $settings, string $location): string
{
// 0. Verificar visibilidad (respetando modo JS-First para cache)
$jsFirstMode = ($settings['behavior']['javascript_first_mode'] ?? false) === true;
if ($jsFirstMode) {
// En modo JS-First, usar evaluacion cache-friendly (sin is_user_logged_in)
if (!PageVisibilityHelper::shouldShowForCache('adsense-placement')) {
return '';
}
} else {
// Modo legacy: usar evaluacion completa
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
return '';
}
}
// ...
}
2. PageVisibilityHelper.php
// AGREGAR nuevo metodo:
/**
* Evalua visibilidad SIN checks dependientes de usuario
*
* Para uso cuando JavaScript manejara los checks de usuario.
* Evalua: tipos de pagina, exclusiones por categoria/ID/URL
* NO evalua: hide_for_logged_in
*
* @param string $componentName
* @return bool
*/
public static function shouldShowForCache(string $componentName): bool
{
$container = DIContainer::getInstance();
$useCase = $container->getEvaluateComponentVisibilityUseCase();
return $useCase->executeForCache($componentName);
}
3. EvaluateComponentVisibilityUseCase.php
// AGREGAR nuevo metodo:
/**
* Evalua visibilidad SIN checks dependientes de usuario
*/
public function executeForCache(string $componentName): bool
{
// Paso 1: Verificar visibilidad por tipo de pagina (sin cambios)
$visibleByPageType = $this->pageVisibilityUseCase->execute($componentName);
if (!$visibleByPageType) {
return false;
}
// Paso 2: Verificar exclusiones SIN hide_for_logged_in
$isExcluded = $this->exclusionsUseCase->executeForCache($componentName);
return !$isExcluded;
}
4. EvaluateExclusionsUseCase.php
// AGREGAR nuevo metodo:
/**
* Evalua exclusiones SIN hide_for_logged_in
*/
public function executeForCache(string $componentName): bool
{
// Evaluar exclusiones por categoria, ID, URL
// NO evaluar hide_for_logged_in
return $this->repository->isExcludedForCache($componentName);
}
5. WordPressComponentVisibilityRepository.php
// MODIFICAR isNotExcluded o agregar nuevo metodo:
/**
* Verifica exclusiones SIN evaluar hide_for_logged_in
*/
public function isNotExcludedForCache(string $componentName): bool
{
// OMITE: shouldHideForLoggedIn()
// MANTIENE: PageVisibilityHelper::shouldShow() para otras exclusiones
return PageVisibilityHelper::shouldShowByPageType($componentName);
}
Acceptance Criteria
-
Con
javascript_first_mode=trueyhide_for_logged_in=true:- Usuario anonimo: HTML generado, JS muestra ads
- Usuario logueado: HTML generado, JS oculta ads
- Page cache sirve mismo HTML a ambos
-
Con
javascript_first_mode=false:- Comportamiento legacy sin cambios
is_user_logged_in()se evalua en PHP
-
Otras exclusiones (categoria, ID, URL) funcionan igual en ambos modos
-
No hay regresion en visibilidad por tipo de pagina
-
Los slots ocultos por JS colapsan completamente (height: 0)
Migration Notes
- NO hay breaking changes:
shouldShow()mantiene comportamiento actual - Nuevo metodo opcional:
shouldShowForCache()para modo JS-First - Backward compatible: Si
javascript_first_mode=false, todo funciona igual
Version History
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2025-12-11 | Initial spec |