';
@@ -154,37 +92,6 @@ final class ThemeSettingsFormBuilder
return $html;
}
- // Helper methods
- private function buildSwitch(string $id, string $label, string $icon, $value): string
- {
- $checked = $value === true || $value === '1' || $value === 1 ? 'checked' : '';
-
- $html = '
';
- $html .= ' ';
- $html .= ' ';
- $html .= ' ';
- $html .= ' ' . esc_html($label);
- $html .= ' ';
- $html .= '
';
-
- return $html;
- }
-
- private function buildTextInput(string $id, string $label, string $icon, mixed $value): string
- {
- $value = $this->normalizeStringValue($value);
-
- $html = '
';
- $html .= ' ';
- $html .= ' ';
- $html .= ' ' . esc_html($label);
- $html .= ' ';
- $html .= ' ';
- $html .= '
';
-
- return $html;
- }
-
private function buildTextareaCode(string $id, string $label, string $icon, mixed $value, string $helpText = ''): string
{
$value = $this->normalizeStringValue($value);
diff --git a/Inc/adsense-placement.php b/Inc/adsense-placement.php
index a7f78ca1..f99c47f7 100644
--- a/Inc/adsense-placement.php
+++ b/Inc/adsense-placement.php
@@ -1,10 +1,14 @@
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
+ */
+function roi_render_ga4_script(string $trackingId, bool $anonymizeIp): void
+{
+ $config = $anonymizeIp ? "{ 'anonymize_ip': true }" : '{}';
+
+ echo '' . "\n";
+ echo '' . "\n";
+ echo '' . "\n";
+}
+
+/**
+ * Renderiza script de Universal Analytics (legacy)
+ */
+function roi_render_universal_analytics_script(string $trackingId, bool $anonymizeIp): void
+{
+ $anonymizeConfig = $anonymizeIp ? 'ga("set", "anonymizeIp", true);' : '';
+
+ echo '' . "\n";
+ echo '' . "\n";
+}
diff --git a/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php b/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php
index 47189f4b..67d3783b 100644
--- a/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php
+++ b/Public/ThemeSettings/Infrastructure/Ui/ThemeSettingsRenderer.php
@@ -13,14 +13,10 @@ use ROITheme\Shared\Domain\Entities\Component;
* 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)
+ * NOTA: Este es un componente especial que solo maneja codigo personalizado.
+ * Analytics y AdSense se gestionan desde el componente adsense-placement.
*
* Responsabilidades:
- * - Generar script de Google Analytics
- * - Generar script de Google AdSense Auto Ads
* - Generar CSS personalizado
* - Generar JavaScript para header
* - Generar JavaScript para footer
@@ -54,8 +50,6 @@ final class ThemeSettingsRenderer implements RendererInterface
* 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)
*
@@ -64,25 +58,8 @@ final class ThemeSettingsRenderer implements RendererInterface
*/
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)) {
@@ -109,170 +86,9 @@ final class ThemeSettingsRenderer implements RendererInterface
*/
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(
- '
-
-',
- 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(
- '
-',
- 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(
- '
-',
- esc_attr($publisherId)
- );
- }
-
/**
* Genera el CSS personalizado
*
diff --git a/Schemas/adsense-placement.json b/Schemas/adsense-placement.json
index b47d0210..da1189d2 100644
--- a/Schemas/adsense-placement.json
+++ b/Schemas/adsense-placement.json
@@ -1,27 +1,20 @@
{
"component_name": "adsense-placement",
- "version": "1.0.0",
- "description": "Control manual de ubicacion de anuncios AdSense",
+ "version": "1.1.0",
+ "description": "Control de AdSense y Google Analytics",
"groups": {
"visibility": {
- "label": "Visibilidad",
+ "label": "Visibilidad AdSense",
"priority": 10,
"fields": {
"is_enabled": {
"type": "boolean",
- "label": "Activar Placement Manual",
+ "label": "Activar AdSense",
"default": false,
"editable": true,
"required": true,
"description": "Activa el control manual de ubicacion de anuncios"
},
- "disable_auto_ads": {
- "type": "boolean",
- "label": "Deshabilitar Auto Ads",
- "default": true,
- "editable": true,
- "description": "Desactiva Auto Ads de Google cuando el placement manual esta activo"
- },
"show_on_desktop": {
"type": "boolean",
"label": "Mostrar en escritorio",
@@ -38,6 +31,33 @@
}
}
},
+ "analytics": {
+ "label": "Google Analytics",
+ "priority": 15,
+ "fields": {
+ "analytics_enabled": {
+ "type": "boolean",
+ "label": "Activar Analytics",
+ "default": false,
+ "editable": true,
+ "description": "Activa Google Analytics en el sitio"
+ },
+ "ga_tracking_id": {
+ "type": "text",
+ "label": "Google Analytics ID",
+ "default": "",
+ "editable": true,
+ "description": "ID de seguimiento (G-XXXXXXXXXX o UA-XXXXXXXX-X)"
+ },
+ "ga_anonymize_ip": {
+ "type": "boolean",
+ "label": "Anonimizar IP (GDPR)",
+ "default": true,
+ "editable": true,
+ "description": "Recomendado para cumplir con GDPR/RGPD"
+ }
+ }
+ },
"content": {
"label": "Credenciales AdSense",
"priority": 20,
diff --git a/Schemas/theme-settings.json b/Schemas/theme-settings.json
index 2d9d25d9..dbb1da50 100644
--- a/Schemas/theme-settings.json
+++ b/Schemas/theme-settings.json
@@ -1,51 +1,11 @@
{
"component_name": "theme-settings",
- "version": "1.2.0",
- "description": "Configuraciones globales del tema: analytics, adsense y codigo personalizado",
+ "version": "1.3.0",
+ "description": "Configuraciones globales del tema: codigo personalizado",
"groups": {
- "analytics": {
- "label": "Analytics",
- "priority": 10,
- "fields": {
- "ga_tracking_id": {
- "type": "text",
- "label": "Google Analytics ID",
- "default": "",
- "editable": true,
- "description": "ID de seguimiento de Google Analytics (ej: G-XXXXXXXXXX o UA-XXXXXXXX-X)"
- },
- "ga_anonymize_ip": {
- "type": "boolean",
- "label": "Anonimizar IP",
- "default": true,
- "editable": true,
- "description": "Anonimiza las direcciones IP de los visitantes (recomendado para GDPR)"
- }
- }
- },
- "adsense": {
- "label": "Google AdSense",
- "priority": 20,
- "fields": {
- "adsense_publisher_id": {
- "type": "text",
- "label": "Publisher ID",
- "default": "",
- "editable": true,
- "description": "ID de publicador de AdSense (ej: ca-pub-1234567890123456)"
- },
- "adsense_auto_ads": {
- "type": "boolean",
- "label": "Activar Auto Ads",
- "default": false,
- "editable": true,
- "description": "Permite que Google coloque anuncios automaticamente en las mejores ubicaciones"
- }
- }
- },
"custom_code": {
"label": "Codigo Personalizado",
- "priority": 30,
+ "priority": 10,
"fields": {
"custom_css": {
"type": "textarea",