feat(visibility): sistema de visibilidad por tipo de página

- Añadir PageVisibility use case y repositorio
- Implementar PageTypeDetector para detectar home/single/page/archive
- Actualizar FieldMappers con soporte show_on_[page_type]
- Extender FormBuilders con UI de visibilidad por página
- Refactorizar Renderers para evaluar visibilidad dinámica
- Limpiar schemas removiendo campos de visibilidad legacy
- Añadir MigrationCommand para migrar configuraciones existentes
- Implementar adsense-loader.js para carga lazy de ads
- Actualizar front-page.php con nueva estructura
- Extender DIContainer con nuevos servicios

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-12-03 09:16:34 -06:00
parent 7fb5eda108
commit 8735962f52
66 changed files with 2614 additions and 573 deletions

View File

@@ -380,3 +380,127 @@ add_action('after_setup_theme', function() {
// desde la base de datos a través de sus respectivos Renderers.
// NO hardcodear CSS aquí - viola la arquitectura Clean Architecture.
// =============================================================================
// =============================================================================
// HELPER FUNCTION: roi_get_adsense_search_config()
// =============================================================================
/**
* Obtiene la configuracion de AdSense para resultados de busqueda
*
* Esta funcion es la API publica que el plugin roi-apu-search consume.
* El plugin NO debe acceder directamente a la tabla del tema.
*
* OPTIMIZACION: Una sola query carga todos los settings del componente.
*
* @return array Configuracion para JavaScript
*/
function roi_get_adsense_search_config(): array {
global $wpdb;
// =========================================================================
// CARGAR TODOS LOS SETTINGS EN UNA SOLA QUERY
// =========================================================================
$table = $wpdb->prefix . 'roi_theme_component_settings';
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT group_name, attribute_name, attribute_value
FROM {$table}
WHERE component_name = %s",
'adsense-placement'
));
if (empty($rows)) {
return ['enabled' => false];
}
// Organizar en array asociativo por grupo/atributo
$settings = [];
foreach ($rows as $row) {
if (!isset($settings[$row->group_name])) {
$settings[$row->group_name] = [];
}
// Decodificar valor
$value = $row->attribute_value;
if ($value === '1') $value = true;
elseif ($value === '0') $value = false;
else {
$decoded = json_decode($value, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$value = $decoded;
}
}
$settings[$row->group_name][$row->attribute_name] = $value;
}
// Helper para obtener valor con default
$get = function(string $group, string $attr, $default = null) use ($settings) {
return $settings[$group][$attr] ?? $default;
};
// =========================================================================
// VALIDAR CONDICIONES GLOBALES
// =========================================================================
// AdSense global deshabilitado
if ($get('visibility', 'is_enabled', false) !== true) {
return ['enabled' => false];
}
// Ads en busqueda deshabilitados
if ($get('search_results', 'search_ads_enabled', false) !== true) {
return ['enabled' => false];
}
// Publisher ID vacio
$publisherId = $get('content', 'publisher_id', '');
if (empty($publisherId)) {
return ['enabled' => false];
}
// =========================================================================
// VALIDAR EXCLUSIONES (igual que el resto del sistema)
// =========================================================================
// Ocultar para usuarios logueados
if ($get('visibility', 'hide_for_logged_in', false) === true && is_user_logged_in()) {
return ['enabled' => false];
}
// Visibilidad por dispositivo
$isMobile = wp_is_mobile();
if ($isMobile && $get('visibility', 'show_on_mobile', true) !== true) {
return ['enabled' => false];
}
if (!$isMobile && $get('visibility', 'show_on_desktop', true) !== true) {
return ['enabled' => false];
}
// =========================================================================
// CONSTRUIR CONFIGURACION
// =========================================================================
return [
'enabled' => true,
'publisherId' => $publisherId,
'slots' => [
'auto' => $get('content', 'slot_auto', ''),
'inArticle' => $get('content', 'slot_inarticle', ''),
'autorelaxed' => $get('content', 'slot_autorelaxed', ''),
'display' => $get('content', 'slot_display', ''),
],
'topAd' => [
'enabled' => $get('search_results', 'search_top_ad_enabled', true) === true,
'format' => $get('search_results', 'search_top_ad_format', 'auto'),
],
'betweenAds' => [
'enabled' => $get('search_results', 'search_between_enabled', true) === true,
'max' => min(3, max(1, (int) $get('search_results', 'search_between_max', '1'))),
'format' => $get('search_results', 'search_between_format', 'in-article'),
'position' => $get('search_results', 'search_between_position', 'random'),
'every' => (int) $get('search_results', 'search_between_every', '5'),
],
'delay' => [
'enabled' => $get('forms', 'delay_enabled', true) === true,
'timeout' => (int) $get('forms', 'delay_timeout', '5000'),
],
];
}