getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
if (empty($settings)) {
return '';
}
// Verificar visibilidad por usuario logueado (Plan 99.16)
if (!UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? [])) {
return '';
}
// 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();
return $renderer->renderSlot($settings, $location);
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI AdSense: ' . $e->getMessage());
}
return '';
}
}
/**
* Verifica si se deben ocultar anuncios para usuarios logueados
*
* @deprecated Plan 99.16: Usar UserVisibilityHelper::shouldShowForUser() en su lugar.
* Esta función se mantiene para compatibilidad hacia atrás.
*
* @param array $settings Configuración del componente
* @return bool true si se debe ocultar, false si se debe mostrar
*/
function roi_should_hide_for_logged_in(array $settings): bool
{
// Delegar a UserVisibilityHelper (Plan 99.16)
return !UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? []);
}
/**
* Verifica si el contenido actual esta excluido
*/
function roi_is_ad_excluded(array $settings): bool
{
$forms = $settings['forms'] ?? [];
// Excluir categorias
$excludeCats = array_filter(array_map('trim', explode(',', $forms['exclude_categories'] ?? '')));
if (!empty($excludeCats) && is_single()) {
$postCats = wp_get_post_categories(get_the_ID());
if (array_intersect($excludeCats, $postCats)) {
return true;
}
}
// Excluir tipos de post
$excludeTypes = array_filter(array_map('trim', explode(',', $forms['exclude_post_types'] ?? '')));
if (!empty($excludeTypes) && in_array(get_post_type(), $excludeTypes, true)) {
return true;
}
// Excluir posts especificos
$excludeIds = array_filter(array_map('trim', explode(',', $forms['exclude_post_ids'] ?? '')));
if (!empty($excludeIds) && in_array((string)get_the_ID(), $excludeIds, true)) {
return true;
}
return false;
}
/**
* Renderiza los Rail Ads (margenes laterales del viewport)
* Se llama desde wp_footer para inyectar al final del body
*
* NOTA DIP: El renderer se obtiene del DIContainer, NO se instancia directamente.
*/
function roi_render_rail_ads(): string
{
global $container;
if ($container === null) {
return '';
}
try {
$repository = $container->getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
if (empty($settings)) {
return '';
}
// Verificar visibilidad por usuario logueado (Plan 99.16)
if (!UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? [])) {
return '';
}
// 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();
return $renderer->renderRailAds($settings);
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI AdSense Rail Ads: ' . $e->getMessage());
}
return '';
}
}
/**
* Hook para inyectar Rail Ads en el footer
*/
add_action('wp_footer', function() {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo roi_render_rail_ads();
}, 50);
/**
* Carga el script principal de AdSense
*/
function roi_enqueue_adsense_script(): void
{
global $container;
if ($container === null || is_admin()) {
return;
}
try {
$repository = $container->getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
if (!($settings['visibility']['is_enabled'] ?? false)) {
return;
}
// Verificar visibilidad por usuario logueado (Plan 99.16)
if (!UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? [])) {
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;
}
$delayEnabled = ($settings['forms']['delay_enabled'] ?? true) === true;
if ($delayEnabled) {
echo '' . "\n";
} else {
echo '' . "\n";
}
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI AdSense: ' . $e->getMessage());
}
}
}
add_action('wp_head', 'roi_enqueue_adsense_script', 5);
/**
* Registra el filtro the_content para inyectar anuncios
*/
// Prioridad 150 para ejecutar DESPUÉS de restrict-content-pro (100)
add_filter('the_content', 'roi_inject_content_ads', 150);
function roi_inject_content_ads(string $content): string
{
global $container;
// Solo en posts individuales
if (!is_single() || !in_the_loop() || !is_main_query()) {
return $content;
}
if ($container === null) {
return $content;
}
try {
$repository = $container->getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
if (empty($settings) || !($settings['visibility']['is_enabled'] ?? false)) {
return $content;
}
// Verificar visibilidad por usuario logueado (Plan 99.16)
if (!UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? [])) {
return $content;
}
// 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)
$postTopHtml = '';
if ($settings['behavior']['post_top_enabled'] ?? false) {
$postTopHtml = $renderer->renderSlot($settings, 'post-top');
}
// Inyectar anuncio al final (post-bottom)
$postBottomHtml = '';
if ($settings['behavior']['post_bottom_enabled'] ?? false) {
$postBottomHtml = $renderer->renderSlot($settings, 'post-bottom');
}
// Inyectar anuncios dentro del contenido
$injector = new \ROITheme\Public\AdsensePlacement\Infrastructure\Services\ContentAdInjector(
$settings,
$renderer
);
$content = $injector->inject($content);
return $postTopHtml . $content . $postBottomHtml;
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI AdSense Content ERROR: ' . $e->getMessage());
}
return $content;
}
}
/**
* Carga el script de Google Analytics (GA4 o Universal Analytics)
*/
function roi_enqueue_analytics_script(): void
{
global $container;
if ($container === null || is_admin()) {
return;
}
try {
$repository = $container->getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
// Verificar si Analytics esta habilitado
if (!($settings['analytics']['analytics_enabled'] ?? false)) {
return;
}
$trackingId = trim($settings['analytics']['ga_tracking_id'] ?? '');
if (empty($trackingId)) {
return;
}
// Verificar si GA ya esta cargado por otro plugin
if (roi_is_analytics_loaded()) {
return;
}
$anonymizeIp = ($settings['analytics']['ga_anonymize_ip'] ?? true) === true;
// Detectar tipo de ID (GA4 vs Universal Analytics)
if (strpos($trackingId, 'G-') === 0) {
roi_render_ga4_script($trackingId, $anonymizeIp);
} elseif (strpos($trackingId, 'UA-') === 0) {
roi_render_universal_analytics_script($trackingId, $anonymizeIp);
}
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI Analytics: ' . $e->getMessage());
}
}
}
add_action('wp_head', 'roi_enqueue_analytics_script', 1);
/**
* Verifica si Google Analytics ya esta cargado por otro plugin
*/
function roi_is_analytics_loaded(): bool
{
// Verificar si MonsterInsights esta activo
if (class_exists('MonsterInsights_Lite') || class_exists('MonsterInsights')) {
return true;
}
// Verificar si Site Kit de Google esta activo
if (class_exists('Google\Site_Kit\Plugin')) {
return true;
}
return false;
}
/**
* Renderiza script de Google Analytics 4 (DIFERIDO)
*
* Para mejorar Core Web Vitals (TBT), GA4 se carga:
* 1. Después de 3 segundos de timeout, O
* 2. Al primer scroll/click/touch del usuario
*
* Esto reduce ~59 KiB de JavaScript bloqueante.
*/
function roi_render_ga4_script(string $trackingId, bool $anonymizeIp): void
{
$config = $anonymizeIp ? "{ 'anonymize_ip': true }" : '{}';
$escapedTrackingId = esc_attr($trackingId);
$escapedConfig = esc_js($trackingId);
echo '' . "\n";
echo '' . "\n";
}
/**
* Renderiza script de Universal Analytics (legacy - DIFERIDO)
*
* Carga diferida para mejorar Core Web Vitals.
*/
function roi_render_universal_analytics_script(string $trackingId, bool $anonymizeIp): void
{
$anonymizeConfig = $anonymizeIp ? 'ga("set","anonymizeIp",true);' : '';
$escapedTrackingId = esc_js($trackingId);
echo '' . "\n";
echo '' . "\n";
}
/**
* Renderiza Anchor Ads (anuncios fijos top/bottom)
*
* NOTA DIP: El renderer se obtiene del DIContainer, NO se instancia directamente.
*/
function roi_render_anchor_ads(): string
{
global $container;
if ($container === null) {
return '';
}
try {
$repository = $container->getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
if (empty($settings)) {
return '';
}
// Verificar visibilidad por usuario logueado (Plan 99.16)
if (!UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? [])) {
return '';
}
// 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();
return $renderer->renderAnchorAds($settings);
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI Anchor Ads: ' . $e->getMessage());
}
return '';
}
}
/**
* Renderiza Vignette Ad (pantalla completa)
*
* NOTA DIP: El renderer se obtiene del DIContainer, NO se instancia directamente.
*/
function roi_render_vignette_ad(): string
{
global $container;
if ($container === null) {
return '';
}
try {
$repository = $container->getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
if (empty($settings)) {
return '';
}
// Verificar visibilidad por usuario logueado (Plan 99.16)
if (!UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? [])) {
return '';
}
// 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();
return $renderer->renderVignetteAd($settings);
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI Vignette Ad: ' . $e->getMessage());
}
return '';
}
}
/**
* Hook para inyectar Anchor Ads en el footer
*/
add_action('wp_footer', function() {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo roi_render_anchor_ads();
}, 51);
/**
* Hook para inyectar Vignette Ad en el footer
*/
add_action('wp_footer', function() {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo roi_render_vignette_ad();
}, 52);
/**
* Encola el script JavaScript para Anchor y Vignette Ads
*/
function roi_enqueue_anchor_vignette_scripts(): void
{
global $container;
if ($container === null || is_admin()) {
return;
}
try {
$repository = $container->getComponentSettingsRepository();
$settings = $repository->getComponentSettings('adsense-placement');
if (!($settings['visibility']['is_enabled'] ?? false)) {
return;
}
// Verificar si Anchor o Vignette estan habilitados
$anchorEnabled = $settings['anchor_ads']['anchor_enabled'] ?? false;
$vignetteEnabled = $settings['vignette_ads']['vignette_enabled'] ?? false;
if (!$anchorEnabled && !$vignetteEnabled) {
return;
}
// Verificar visibilidad por usuario logueado (Plan 99.16)
if (!UserVisibilityHelper::shouldShowForUser($settings['visibility'] ?? [])) {
return;
}
// Verificar exclusiones modernas (Plan 99.11: _exclusions, _page_visibility)
if (!PageVisibilityHelper::shouldShow('adsense-placement')) {
return;
}
// Encolar script
wp_enqueue_script(
'roi-anchor-vignette',
get_template_directory_uri() . '/Public/AdsensePlacement/Infrastructure/Ui/Assets/anchor-vignette.js',
[],
'1.0.0',
true // En footer
);
} catch (\Throwable $e) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('ROI Anchor/Vignette Scripts: ' . $e->getMessage());
}
}
}
add_action('wp_enqueue_scripts', 'roi_enqueue_anchor_vignette_scripts');