- Add adsense group to theme-settings.json schema (v1.2.0) - Add adsense_publisher_id and adsense_auto_ads fields - Add AdSense card UI in ThemeSettingsFormBuilder - Add field mappings in ThemeSettingsFieldMapper - Add renderAdSenseAutoAds() method in ThemeSettingsRenderer - Inject AdSense script in wp_head when configured 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
345 lines
9.9 KiB
PHP
345 lines
9.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Public\ThemeSettings\Infrastructure\Ui;
|
|
|
|
use ROITheme\Shared\Domain\Contracts\RendererInterface;
|
|
use ROITheme\Shared\Domain\Entities\Component;
|
|
|
|
/**
|
|
* ThemeSettingsRenderer
|
|
*
|
|
* Renderizador del componente Theme Settings.
|
|
* A diferencia de otros componentes, no renderiza HTML visual
|
|
* sino que genera codigo para inyectar en wp_head y wp_footer.
|
|
*
|
|
* NOTA: Este es un componente especial que NO requiere:
|
|
* - CSSGeneratorInterface (no genera CSS, solo inyecta CSS del usuario)
|
|
* - Grupo visibility (siempre esta activo, configuraciones globales)
|
|
* - Metodo getVisibilityClasses (no es un componente visual)
|
|
*
|
|
* Responsabilidades:
|
|
* - Generar script de Google Analytics
|
|
* - Generar script de Google AdSense Auto Ads
|
|
* - Generar CSS personalizado
|
|
* - Generar JavaScript para header
|
|
* - Generar JavaScript para footer
|
|
*
|
|
* @package ROITheme\Public\ThemeSettings\Infrastructure\Ui
|
|
*/
|
|
final class ThemeSettingsRenderer implements RendererInterface
|
|
{
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Para este componente, render() no se usa directamente.
|
|
* Se usan los metodos especificos: renderHeadContent() y renderFooterContent()
|
|
*/
|
|
public function render(Component $component): string
|
|
{
|
|
// Este componente no renderiza HTML visual
|
|
// Los contenidos se inyectan via hooks wp_head y wp_footer
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function supports(string $componentType): bool
|
|
{
|
|
return $componentType === 'theme-settings';
|
|
}
|
|
|
|
/**
|
|
* Genera contenido para wp_head
|
|
*
|
|
* Incluye:
|
|
* - Google Analytics script (si configurado)
|
|
* - Google AdSense Auto Ads script (si configurado)
|
|
* - Custom CSS (si configurado)
|
|
* - Custom JS Header (si configurado)
|
|
*
|
|
* @param array $data Datos del componente desde BD
|
|
* @return string Contenido para wp_head
|
|
*/
|
|
public function renderHeadContent(array $data): string
|
|
{
|
|
// Validar visibilidad general
|
|
if (!$this->isEnabled($data)) {
|
|
return '';
|
|
}
|
|
|
|
$output = '';
|
|
|
|
// Google Analytics
|
|
$gaOutput = $this->renderGoogleAnalytics($data);
|
|
if (!empty($gaOutput)) {
|
|
$output .= $gaOutput . "\n";
|
|
}
|
|
|
|
// Google AdSense Auto Ads
|
|
$adsenseOutput = $this->renderAdSenseAutoAds($data);
|
|
if (!empty($adsenseOutput)) {
|
|
$output .= $adsenseOutput . "\n";
|
|
}
|
|
|
|
// Custom CSS
|
|
$cssOutput = $this->renderCustomCSS($data);
|
|
if (!empty($cssOutput)) {
|
|
$output .= $cssOutput . "\n";
|
|
}
|
|
|
|
// Custom JS Header
|
|
$jsHeaderOutput = $this->renderCustomJSHeader($data);
|
|
if (!empty($jsHeaderOutput)) {
|
|
$output .= $jsHeaderOutput . "\n";
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Genera contenido para wp_footer
|
|
*
|
|
* Incluye:
|
|
* - Custom JS Footer (si configurado)
|
|
*
|
|
* @param array $data Datos del componente desde BD
|
|
* @return string Contenido para wp_footer
|
|
*/
|
|
public function renderFooterContent(array $data): string
|
|
{
|
|
// Validar visibilidad general
|
|
if (!$this->isEnabled($data)) {
|
|
return '';
|
|
}
|
|
|
|
return $this->renderCustomJSFooter($data);
|
|
}
|
|
|
|
/**
|
|
* Verifica si el componente esta habilitado
|
|
*
|
|
* NOTA: Theme Settings es un componente de configuracion global
|
|
* que siempre esta activo. No tiene grupo visibility.
|
|
* Si el usuario no quiere GA o CSS custom, simplemente deja
|
|
* los campos vacios.
|
|
*
|
|
* @param array $data Datos del componente (no usado)
|
|
* @return bool Siempre true
|
|
*/
|
|
private function isEnabled(array $data): bool
|
|
{
|
|
// Theme Settings siempre esta activo (configuraciones globales)
|
|
// Los campos individuales se validan en sus metodos respectivos
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Genera el script de Google Analytics
|
|
*
|
|
* @param array $data Datos del componente
|
|
* @return string Script de GA o vacio si no configurado
|
|
*/
|
|
private function renderGoogleAnalytics(array $data): string
|
|
{
|
|
$trackingId = trim($data['analytics']['ga_tracking_id'] ?? '');
|
|
|
|
if (empty($trackingId)) {
|
|
return '';
|
|
}
|
|
|
|
// Verificar si GA ya esta cargado por otro plugin
|
|
if ($this->isGoogleAnalyticsLoaded()) {
|
|
return '';
|
|
}
|
|
|
|
$anonymizeIp = ($data['analytics']['ga_anonymize_ip'] ?? true) === true;
|
|
|
|
// Detectar tipo de ID (GA4 vs Universal Analytics)
|
|
if (strpos($trackingId, 'G-') === 0) {
|
|
// Google Analytics 4
|
|
return $this->renderGA4Script($trackingId, $anonymizeIp);
|
|
} elseif (strpos($trackingId, 'UA-') === 0) {
|
|
// Universal Analytics (legacy)
|
|
return $this->renderUniversalAnalyticsScript($trackingId, $anonymizeIp);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Genera script de Google Analytics 4
|
|
*
|
|
* @param string $trackingId ID de GA4 (G-XXXXXXXXXX)
|
|
* @param bool $anonymizeIp Si anonimizar IP
|
|
* @return string Script HTML
|
|
*/
|
|
private function renderGA4Script(string $trackingId, bool $anonymizeIp): string
|
|
{
|
|
$config = $anonymizeIp ? "{ 'anonymize_ip': true }" : '{}';
|
|
|
|
return sprintf(
|
|
'<!-- Google Analytics 4 (ROI Theme) -->
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=%1$s"></script>
|
|
<script>
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag("js", new Date());
|
|
gtag("config", "%1$s", %2$s);
|
|
</script>',
|
|
esc_attr($trackingId),
|
|
$config
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Genera script de Universal Analytics (legacy)
|
|
*
|
|
* @param string $trackingId ID de UA (UA-XXXXXXXX-X)
|
|
* @param bool $anonymizeIp Si anonimizar IP
|
|
* @return string Script HTML
|
|
*/
|
|
private function renderUniversalAnalyticsScript(string $trackingId, bool $anonymizeIp): string
|
|
{
|
|
$anonymizeConfig = $anonymizeIp ? "ga('set', 'anonymizeIp', true);" : '';
|
|
|
|
return sprintf(
|
|
'<!-- Universal Analytics (ROI Theme) -->
|
|
<script>
|
|
(function(i,s,o,g,r,a,m){i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){
|
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
})(window,document,"script","https://www.google-analytics.com/analytics.js","ga");
|
|
ga("create", "%s", "auto");
|
|
%s
|
|
ga("send", "pageview");
|
|
</script>',
|
|
esc_attr($trackingId),
|
|
$anonymizeConfig
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Verifica si Google Analytics ya esta cargado
|
|
*
|
|
* @return bool True si ya esta cargado por otro plugin
|
|
*/
|
|
private function isGoogleAnalyticsLoaded(): bool
|
|
{
|
|
// Verificar plugins comunes de GA
|
|
if (function_exists('gtag')) {
|
|
return true;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* Genera el script de Google AdSense Auto Ads
|
|
*
|
|
* @param array $data Datos del componente
|
|
* @return string Script de AdSense o vacio si no configurado
|
|
*/
|
|
private function renderAdSenseAutoAds(array $data): string
|
|
{
|
|
$publisherId = trim($data['adsense']['adsense_publisher_id'] ?? '');
|
|
$autoAdsEnabled = ($data['adsense']['adsense_auto_ads'] ?? false) === true;
|
|
|
|
// Solo mostrar si tiene publisher ID y auto ads esta activado
|
|
if (empty($publisherId) || !$autoAdsEnabled) {
|
|
return '';
|
|
}
|
|
|
|
// Validar formato del publisher ID (ca-pub-XXXXXXXXXX)
|
|
if (!preg_match('/^ca-pub-\d+$/', $publisherId)) {
|
|
return '';
|
|
}
|
|
|
|
return sprintf(
|
|
'<!-- Google AdSense Auto Ads (ROI Theme) -->
|
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=%s" crossorigin="anonymous"></script>',
|
|
esc_attr($publisherId)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Genera el CSS personalizado
|
|
*
|
|
* @param array $data Datos del componente
|
|
* @return string Bloque style o vacio si no hay CSS
|
|
*/
|
|
private function renderCustomCSS(array $data): string
|
|
{
|
|
$css = trim($data['custom_code']['custom_css'] ?? '');
|
|
|
|
if (empty($css)) {
|
|
return '';
|
|
}
|
|
|
|
return sprintf(
|
|
'<!-- Custom CSS (ROI Theme) -->
|
|
<style id="roi-theme-custom-css">
|
|
%s
|
|
</style>',
|
|
$css // No escapar CSS - usuario avanzado responsable
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Genera el JavaScript personalizado para header
|
|
*
|
|
* @param array $data Datos del componente
|
|
* @return string Bloque script o vacio si no hay JS
|
|
*/
|
|
private function renderCustomJSHeader(array $data): string
|
|
{
|
|
$js = trim($data['custom_code']['custom_js_header'] ?? '');
|
|
|
|
if (empty($js)) {
|
|
return '';
|
|
}
|
|
|
|
return sprintf(
|
|
'<!-- Custom JS Header (ROI Theme) -->
|
|
<script id="roi-theme-custom-js-header">
|
|
%s
|
|
</script>',
|
|
$js // No escapar JS - usuario avanzado responsable
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Genera el JavaScript personalizado para footer
|
|
*
|
|
* @param array $data Datos del componente
|
|
* @return string Bloque script o vacio si no hay JS
|
|
*/
|
|
private function renderCustomJSFooter(array $data): string
|
|
{
|
|
$js = trim($data['custom_code']['custom_js_footer'] ?? '');
|
|
|
|
if (empty($js)) {
|
|
return '';
|
|
}
|
|
|
|
return sprintf(
|
|
'<!-- Custom JS Footer (ROI Theme) -->
|
|
<script id="roi-theme-custom-js-footer">
|
|
%s
|
|
</script>',
|
|
$js // No escapar JS - usuario avanzado responsable
|
|
);
|
|
}
|
|
}
|