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');