- proposal.md: define problem and expected changes - design.md: 9 technical decisions with rationale - spec.md: complete GIVEN/WHEN/THEN scenarios - tasks.md: implementation tasks with dependencies Features specified: - 7 ad insertion locations (H2, H3, p, img, lists, blockquotes, tables) - 5 density modes (legacy, conservative, balanced, aggressive, custom) - 2 selection strategies (position vs priority) - Deterministic probability with daily seed - Backward compatibility with legacy fields 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
27 KiB
Especificacion: AdSense Placement - In-Content Ads Avanzados
Definiciones Tecnicas
Definicion: Elemento de Bloque Contable
Un "elemento" para efectos de espaciado y conteo se define como el tag de cierre de los siguientes elementos de bloque principal:
| Tag | Cuenta como elemento | Notas |
|---|---|---|
</p> |
SI | Parrafo |
</h2> |
SI | Encabezado nivel 2 |
</h3> |
SI | Encabezado nivel 3 |
</figure> |
SI | Contenedor de imagen con caption |
</ul> |
SI | Lista desordenada (el contenedor, no los <li>) |
</ol> |
SI | Lista ordenada (el contenedor, no los <li>) |
</table> |
SI | Tabla (el contenedor, no <tr> ni <td>) |
</blockquote> |
SI | Cita en bloque |
<img> |
SOLO si no esta dentro de <figure> |
Imagen standalone |
</li> |
NO | Items de lista no cuentan individualmente |
</tr>, </td> |
NO | Elementos internos de tabla no cuentan |
</div> |
NO | Divs genericos no cuentan |
Definicion: Prioridades de Ubicacion (Corregidas)
| Tipo de Ubicacion | Prioridad | Justificacion |
|---|---|---|
| Despues de H2 | 10 | Ruptura tematica mayor, alta visibilidad |
| Despues de parrafos | 8 | Ubicacion tradicional, probada |
| Despues de H3 | 7 | Ruptura tematica menor |
| Despues de imagenes/figure | 6 | Pausa visual natural |
| Despues de listas | 5 | Fin de enumeracion |
| Despues de blockquotes | 4 | Fin de cita |
| Despues de tablas | 3 | Fin de datos tabulares |
ADDED Requirements
Requirement: Algoritmo de Insercion de Anuncios
El sistema DEBE seguir un algoritmo determinista para insertar anuncios in-content.
Scenario: Algoritmo completo de insercion
- GIVEN contenido HTML con multiples elementos de bloque
- WHEN se procesa el contenido para insertar ads
- THEN el sistema DEBE ejecutar los siguientes pasos en orden:
PASO 0: PRECONDICION - VALIDAR LONGITUD MINIMA
- Obtener min_content_length del grupo forms (default: 500)
- Si strlen(strip_tags($content)) < min_content_length:
- Retornar contenido sin modificar
- NO ejecutar pasos siguientes
- Esta validacion aplica a TODOS los modos (legacy y nuevo)
PASO 1: ESCANEO
- Parsear contenido usando regex: preg_split('/(<\/(?:p|h[2-3]|figure|ul|ol|table|blockquote)>)/i', ...)
- Para cada tag de cierre, registrar: {posicion, tipo, indice_elemento}
- Detectar <img> standalone usando logica de dos pasos:
1. Encontrar todos los <img> con: preg_match_all('/<img[^>]*>/i', $content, $imgs, PREG_OFFSET_CAPTURE)
2. Para cada <img> encontrado:
- Buscar si existe <figure> abierto antes sin cerrar
- Si NO hay <figure> abierto: registrar como ubicacion elegible tipo "image"
- Si SI hay <figure> abierto: ignorar (se contara con </figure>)
PASO 2: FILTRADO POR CONFIGURACION
- Eliminar ubicaciones cuyo tipo tenga enabled=false
- Ejemplo: si incontent_after_h3_enabled=false, eliminar todas las ubicaciones </h3>
PASO 3: APLICAR PROBABILIDAD DETERMINISTICA
- Calcular seed: crc32(post_id . date('Y-m-d'))
- Inicializar: mt_srand(seed)
- Para cada ubicacion restante:
- Si mt_rand(1, 100) > probabilidad_del_tipo: eliminar ubicacion
- Esto garantiza consistencia durante el mismo dia para cache
PASO 4-5: FILTRADO Y SELECCION (segun incontent_priority_mode)
SI incontent_priority_mode == "position" (default):
PASO 4: FILTRAR POR ESPACIADO (orden DOM)
- Ordenar ubicaciones por posicion en DOM
- Iterar secuencialmente:
- Si distancia a ubicacion anterior < min_spacing: eliminar ubicacion
- La distancia se mide en cantidad de elementos de bloque entre ambas
PASO 5: APLICAR LIMITE
- Si cantidad de ubicaciones > max_total_ads:
- Ordenar por prioridad descendente (H2=10 primero)
- Tomar las primeras max_total_ads ubicaciones
- Reordenar por posicion en DOM
SI incontent_priority_mode == "priority":
PASO 4: ORDENAR POR PRIORIDAD
- Ordenar ubicaciones por prioridad DESC (H2=10 primero, luego p=8, etc.)
PASO 5: FILTRAR POR ESPACIADO (orden de prioridad)
- Iterar en orden de prioridad (no DOM):
- Si la ubicacion viola min_spacing con alguna ya seleccionada: eliminar
- Si ya tenemos max_total_ads: parar
- Reordenar resultado final por posicion DOM
PASO 6: INSERCION
- Para cada ubicacion final, insertar HTML del ad despues del tag de cierre
- El ad usa el formato configurado (in-article, auto, etc.)
Scenario: Seed deterministico para cache
- GIVEN un post con ID 12345 visitado multiples veces el mismo dia
- WHEN se calculan las posiciones de ads
- THEN las posiciones DEBEN ser identicas en todas las visitas del mismo dia
- AND al dia siguiente las posiciones pueden cambiar (nuevo seed)
Scenario: Validacion de longitud minima de contenido
- GIVEN
min_content_length= 500 (campo del grupoforms) - AND el contenido tiene 300 caracteres (sin tags HTML)
- WHEN se invoca el algoritmo de insercion
- THEN NO se ejecuta ningun paso del algoritmo
- AND se retorna el contenido original sin modificar
- AND esta validacion aplica tanto a modo "legacy" como a modos nuevos
Scenario: Resolucion de conflictos de posicion
- GIVEN un H2 seguido inmediatamente por un parrafo
- WHEN ambos tipos estan habilitados
- AND el espaciado minimo es 1
- THEN solo se inserta ad en la ubicacion de mayor prioridad (H2)
- AND el parrafo se cuenta para el espaciado pero no recibe ad
Requirement: Ubicaciones de insercion por tipo de elemento
El sistema DEBE permitir insertar anuncios despues de diferentes tipos de elementos HTML estructurales, cada uno con configuracion independiente.
Scenario: Insercion despues de encabezados H2
- WHEN el campo
incontent_after_h2_enabledes true - AND el contenido tiene elementos
</h2> - THEN el sistema DEBE registrar cada
</h2>como ubicacion elegible - AND la probabilidad de insercion es controlada por
incontent_after_h2_probability
Scenario: Insercion despues de parrafos
- WHEN el campo
incontent_after_paragraphs_enabledes true - AND el contenido tiene elementos
</p> - THEN el sistema DEBE registrar cada
</p>como ubicacion elegible - AND la probabilidad de insercion es controlada por
incontent_after_paragraphs_probability - AND esta es la ubicacion "tradicional" del sistema legacy
Scenario: Insercion despues de encabezados H3
- WHEN el campo
incontent_after_h3_enabledes true - AND el contenido tiene elementos
</h3> - THEN el sistema DEBE registrar cada
</h3>como ubicacion elegible - AND la probabilidad de insercion es controlada por
incontent_after_h3_probability
Scenario: Insercion despues de imagenes
- WHEN el campo
incontent_after_images_enabledes true - AND el contenido tiene elementos
</figure>o<img>standalone - THEN el sistema DEBE registrar como ubicacion elegible
- AND si
<img>esta dentro de<figure>, solo cuenta</figure>(no duplicar) - AND la probabilidad es controlada por
incontent_after_images_probability
Scenario: Insercion despues de listas
- WHEN el campo
incontent_after_lists_enabledes true - AND el contenido tiene elementos
</ul>o</ol> - THEN el sistema DEBE registrar cada cierre de lista como ubicacion elegible
- AND NO se insertara ad si la lista tiene menos de 3
<li>directos - AND la probabilidad es controlada por
incontent_after_lists_probability
Scenario: Conteo de items en listas anidadas
- GIVEN el siguiente contenido:
<ul>
<li>Item 1</li>
<li>Item 2
<ul>
<li>Sub-item A</li>
<li>Sub-item B</li>
</ul>
</li>
<li>Item 3</li>
</ul>
- WHEN se evalua si la lista externa es elegible para ad
- THEN solo se cuentan los
<li>directos (hijos inmediatos): 3 - AND los
<li>de la lista anidada NO se cuentan para la lista padre - AND la lista anidada se evalua por separado (tiene 2 items, no elegible)
- NOTA IMPLEMENTACION: Usar
substr_count($list_content, '<li')para contar items. Limitacion conocida: listas anidadas contaran todos los<li>. Esto es aceptable para v1 ya que listas anidadas son infrecuentes en el contenido del sitio.
Scenario: Todos los tipos deshabilitados
- GIVEN todos los campos
*_enabledson false (H2, H3, paragraphs, images, lists, blockquotes, tables) - WHEN se ejecuta el algoritmo de insercion
- THEN PASO 2 elimina todas las ubicaciones candidatas
- AND se retorna el contenido sin modificar
- AND no se insertan anuncios
Scenario: Insercion despues de blockquotes
- WHEN el campo
incontent_after_blockquotes_enabledes true - AND el contenido tiene elementos
</blockquote> - THEN el sistema DEBE registrar cada
</blockquote>como ubicacion elegible - AND la probabilidad es controlada por
incontent_after_blockquotes_probability
Scenario: Insercion despues de tablas
- WHEN el campo
incontent_after_tables_enabledes true - AND el contenido tiene elementos
</table> - THEN el sistema DEBE registrar cada
</table>como ubicacion elegible - AND la probabilidad es controlada por
incontent_after_tables_probability
Requirement: Modos de densidad de anuncios
El sistema DEBE ofrecer modos predefinidos que controlan la cantidad y espaciado de anuncios.
Scenario: Modo legacy (backward compatibility)
- WHEN
incontent_modees "legacy" - THEN el sistema NO usa el grupo
incontent_advancedpara el algoritmo - AND usa los campos del grupo
behavior(post_content_*) - AND ejecuta la logica de insercion anterior (solo despues de parrafos)
- AND los campos de
incontent_advancedse muestran deshabilitados en el UI - AND se muestra banner: "Usando configuracion legacy. Migra al nuevo sistema para mas opciones."
Scenario: Modo conservador
- WHEN
incontent_modees "conservative" - THEN el maximo de ads in-content es 5
- AND el espaciado minimo entre ads es 5 elementos
- AND solo se activan ubicaciones despues de H2 y parrafos por defecto
Scenario: Modo balanceado
- WHEN
incontent_modees "balanced" - THEN el maximo de ads in-content es 8
- AND el espaciado minimo entre ads es 3 elementos
- AND se activan H2, H3, imagenes y parrafos por defecto
Scenario: Modo agresivo
- WHEN
incontent_modees "aggressive" - THEN el maximo de ads in-content es 15
- AND el espaciado minimo entre ads es 2 elementos
- AND se activan todas las ubicaciones por defecto
Scenario: Modo personalizado
- WHEN
incontent_modees "custom" - THEN el usuario puede configurar cada campo individualmente
- AND los valores de max/spacing no se sobreescriben al cambiar de modo
Scenario: Modificacion de campos en modo preset (auto-switch a custom)
- GIVEN
incontent_modees "balanced" (o cualquier preset excepto "custom" y "legacy") - WHEN el usuario modifica manualmente cualquiera de estos campos:
incontent_max_total_adsincontent_min_spacing- Cualquier campo
*_enabledo*_probability
- THEN el sistema DEBE cambiar automaticamente
incontent_modea "custom" - AND mostrar mensaje informativo: "Modo cambiado a Personalizado"
- AND los valores modificados se preservan
Scenario: Cambio de modo preset sobreescribe valores
- GIVEN
incontent_modees "custom" con valores personalizados - WHEN el usuario cambia
incontent_modea "balanced" - THEN los valores de max_total_ads, min_spacing y flags enabled se sobreescriben con los del preset
- AND se muestra confirmacion antes de aplicar el cambio
Requirement: Espaciado minimo entre anuncios
El sistema DEBE mantener un espaciado minimo entre cualquier par de anuncios in-content.
Scenario: Calculo de espaciado
- GIVEN dos ubicaciones candidatas para ads
- WHEN se evalua el espaciado entre ellas
- THEN el espaciado se calcula contando elementos de bloque entre ambas posiciones
- AND solo se cuentan los elementos definidos en "Elemento de Bloque Contable"
Scenario: Ejemplo de calculo de espaciado
- GIVEN el siguiente contenido:
<h2>Titulo</h2> <!-- Ubicacion A (posible ad) -->
<p>Parrafo 1</p> <!-- Elemento 1 -->
<p>Parrafo 2</p> <!-- Elemento 2 -->
<figure><img></figure> <!-- Elemento 3 -->
<p>Parrafo 3</p> <!-- Ubicacion B (posible ad) - Elemento 4 -->
- WHEN min_spacing es 3
- THEN la distancia entre A y B es 4 elementos
- AND ambas ubicaciones pueden tener ads (4 >= 3)
Scenario: Espaciado insuficiente
- GIVEN min_spacing es 5
- AND solo hay 3 elementos entre dos ubicaciones candidatas
- THEN la segunda ubicacion se elimina de las candidatas
- AND se conserva la de mayor prioridad
Requirement: Estrategia de seleccion configurable
El sistema DEBE permitir elegir como resolver conflictos cuando dos ubicaciones estan muy cerca.
Scenario: Modo position (default)
- WHEN
incontent_priority_modees "position" - AND hay un H2 (prioridad 10) en posicion 3 y un parrafo (prioridad 8) en posicion 1
- AND min_spacing es 3
- THEN el parrafo en posicion 1 se selecciona primero (por orden DOM)
- AND el H2 en posicion 3 se elimina por violar espaciado (distancia 2 < 3)
- AND el resultado favorece distribucion uniforme
Scenario: Modo priority
- WHEN
incontent_priority_modees "priority" - AND hay un H2 (prioridad 10) en posicion 3 y un parrafo (prioridad 8) en posicion 1
- AND min_spacing es 3
- THEN el H2 se selecciona primero (por mayor prioridad)
- AND el parrafo se elimina por violar espaciado con el H2
- AND el resultado maximiza ubicaciones de alto valor
Requirement: Probabilidad deterministica por ubicacion
El sistema DEBE soportar probabilidad configurable que sea consistente durante el dia.
Scenario: Implementacion de probabilidad deterministica
- GIVEN un post_id y una fecha
- WHEN se calcula si insertar ad en una ubicacion
- THEN el seed es
crc32(post_id . 'YYYY-MM-DD') - AND se usa
mt_srand(seed)antes de evaluar probabilidades - AND cada ubicacion consume un
mt_rand(1, 100)en orden de aparicion
Scenario: Valores de probabilidad disponibles
- WHEN el usuario configura probabilidad para cualquier tipo
- THEN los valores disponibles son: 25, 50, 75, 100
- AND el valor se interpreta como porcentaje
MODIFIED Requirements
Requirement: Estrategia de campos legacy
Los campos existentes del grupo behavior relacionados con in-content ads DEBEN coexistir con el nuevo sistema mediante deprecacion suave.
Scenario: Campos legacy a deprecar
- GIVEN los siguientes campos existentes:
post_content_enabled(behavior)post_content_max_ads(behavior)post_content_after_paragraphs(behavior)post_content_min_paragraphs_between(behavior)post_content_random_mode(behavior)post_content_format(behavior)
- WHEN el sistema tiene ambos grupos de campos
- THEN el grupo
incontent_advancedtiene precedencia siincontent_mode!= "legacy" - AND si
incontent_mode== "legacy", se usan los campos del grupobehavior
Scenario: Migracion automatica en UI
- WHEN el usuario visita el panel de AdSense por primera vez despues de la actualizacion
- AND tiene configuracion legacy activa (
post_content_enabled= true) - THEN se muestra un banner informativo sobre el nuevo sistema
- AND se ofrece boton "Migrar a nuevo sistema" que copia valores equivalentes
Scenario: Mapeo de campos legacy a nuevos
| Campo Legacy | Campo Nuevo | Logica de Mapeo |
|---|---|---|
| post_content_max_ads | incontent_max_total_ads | Copia directa (ampliar opciones) |
| post_content_min_paragraphs_between | incontent_min_spacing | Copia directa |
| post_content_random_mode | N/A | Si true, todas las probabilidades = 75% |
| post_content_after_paragraphs | N/A | Se usa para primer ad, resto aleatorio |
Requirement: UI de In-Content Ads reorganizada
La interfaz de administracion DEBE organizarse en subsecciones claras usando elementos HTML semanticos.
Scenario: Estructura HTML de la seccion
- WHEN se renderiza el card de In-Content Ads Avanzado
- THEN DEBE seguir esta estructura:
<div class="card shadow-sm mb-3" style="border-left: 4px solid #0d6efd;">
<div class="card-body">
<h5 class="fw-bold mb-3">
<i class="bi bi-body-text me-2"></i>
In-Content Ads Avanzado
<span class="badge bg-success ms-2">Nuevo</span>
</h5>
<!-- Indicador de densidad -->
<div id="densityIndicator" class="alert alert-info small mb-3">
<i class="bi bi-speedometer2 me-1"></i>
Densidad estimada: <strong>Media</strong>
<span class="badge bg-warning">~6 ads</span>
</div>
<!-- Selector de modo -->
<div class="mb-4">
<label class="form-label fw-semibold">Modo de densidad</label>
<select class="form-select" id="incontentMode">
<option value="legacy">Legacy (usar config anterior)</option>
<option value="conservative">Conservador (max 5, espaciado 5)</option>
<option value="balanced" selected>Balanceado (max 8, espaciado 3)</option>
<option value="aggressive">Agresivo (max 15, espaciado 2)</option>
<option value="custom">Personalizado</option>
</select>
</div>
<!-- Subseccion: Ubicaciones por elemento -->
<details class="mb-3 border rounded" open>
<summary class="p-3 bg-light fw-bold cursor-pointer">
<i class="bi bi-geo-alt me-1"></i>
Ubicaciones por Elemento
</summary>
<div class="p-3">
<!-- Toggle + probabilidad para cada tipo -->
<div class="row g-3">
<div class="col-md-6">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" id="afterH2Enabled" checked>
<label class="form-check-label">Despues de H2</label>
</div>
</div>
<div class="col-md-6">
<select class="form-select form-select-sm" id="afterH2Prob">
<option value="100" selected>100%</option>
<option value="75">75%</option>
<option value="50">50%</option>
<option value="25">25%</option>
</select>
</div>
<!-- Repetir para H3, images, lists, blockquotes, tables -->
</div>
</div>
</details>
<!-- Subseccion: Limites y espaciado -->
<details class="mb-3 border rounded">
<summary class="p-3 bg-light fw-bold cursor-pointer">
<i class="bi bi-sliders me-1"></i>
Limites y Espaciado
</summary>
<div class="p-3">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Maximo total de ads</label>
<select class="form-select" id="maxTotalAds">
<!-- Opciones 1-15 -->
</select>
</div>
<div class="col-md-6">
<label class="form-label">Espaciado minimo (elementos)</label>
<select class="form-select" id="minSpacing">
<option value="2">2 elementos</option>
<option value="3" selected>3 elementos</option>
<option value="4">4 elementos</option>
<option value="5">5 elementos</option>
<option value="6">6 elementos</option>
</select>
</div>
<div class="col-md-12 mt-3">
<label class="form-label">Estrategia de seleccion</label>
<select class="form-select" id="priorityMode">
<option value="position" selected>Por posicion (distribucion uniforme)</option>
<option value="priority">Por prioridad (maximizar H2/H3)</option>
</select>
<small class="text-muted">Como resolver conflictos cuando dos ubicaciones estan muy cerca</small>
</div>
</div>
</div>
</details>
<!-- Warning para densidad alta -->
<div id="highDensityWarning" class="alert alert-warning small d-none">
<i class="bi bi-exclamation-triangle me-1"></i>
<strong>Atencion:</strong> Densidad alta puede afectar UX y violar politicas de AdSense.
</div>
</div>
</div>
Scenario: Indicador de densidad dinamico
- WHEN el usuario modifica cualquier campo de in-content ads
- THEN el indicador de densidad se actualiza en tiempo real via JavaScript
- AND muestra estimacion basada en: max_ads * promedio_probabilidades / 100
- AND colores: verde (<5), amarillo (5-10), rojo (>10)
Schema JSON - Campos Completos
Grupo: incontent_advanced (priority 69)
{
"incontent_advanced": {
"label": "In-Content Ads Avanzado",
"priority": 69,
"fields": {
"incontent_mode": {
"type": "select",
"label": "Modo de densidad",
"default": "legacy",
"editable": true,
"options": {
"legacy": "Legacy (config anterior)",
"conservative": "Conservador",
"balanced": "Balanceado",
"aggressive": "Agresivo",
"custom": "Personalizado"
},
"description": "Presets que ajustan limites y ubicaciones automaticamente. Default 'legacy' para backward compatibility."
},
"incontent_after_h2_enabled": {
"type": "boolean",
"label": "Despues de H2",
"default": true,
"editable": true,
"description": "Insertar anuncios despues de encabezados H2"
},
"incontent_after_h2_probability": {
"type": "select",
"label": "Probabilidad H2",
"default": "100",
"editable": true,
"options": ["25", "50", "75", "100"],
"description": "Porcentaje de probabilidad de insercion"
},
"incontent_after_h3_enabled": {
"type": "boolean",
"label": "Despues de H3",
"default": true,
"editable": true,
"description": "Insertar anuncios despues de encabezados H3"
},
"incontent_after_h3_probability": {
"type": "select",
"label": "Probabilidad H3",
"default": "50",
"editable": true,
"options": ["25", "50", "75", "100"]
},
"incontent_after_paragraphs_enabled": {
"type": "boolean",
"label": "Despues de parrafos",
"default": true,
"editable": true,
"description": "Insertar anuncios despues de parrafos (ubicacion tradicional)"
},
"incontent_after_paragraphs_probability": {
"type": "select",
"label": "Probabilidad parrafos",
"default": "75",
"editable": true,
"options": ["25", "50", "75", "100"],
"description": "Porcentaje de probabilidad de insercion despues de parrafos"
},
"incontent_after_images_enabled": {
"type": "boolean",
"label": "Despues de imagenes",
"default": true,
"editable": true,
"description": "Insertar anuncios despues de figure o img standalone"
},
"incontent_after_images_probability": {
"type": "select",
"label": "Probabilidad imagenes",
"default": "75",
"editable": true,
"options": ["25", "50", "75", "100"]
},
"incontent_after_lists_enabled": {
"type": "boolean",
"label": "Despues de listas",
"default": false,
"editable": true,
"description": "Insertar anuncios despues de ul/ol (minimo 3 items)"
},
"incontent_after_lists_probability": {
"type": "select",
"label": "Probabilidad listas",
"default": "50",
"editable": true,
"options": ["25", "50", "75", "100"]
},
"incontent_after_blockquotes_enabled": {
"type": "boolean",
"label": "Despues de blockquotes",
"default": false,
"editable": true,
"description": "Insertar anuncios despues de citas en bloque"
},
"incontent_after_blockquotes_probability": {
"type": "select",
"label": "Probabilidad blockquotes",
"default": "50",
"editable": true,
"options": ["25", "50", "75", "100"]
},
"incontent_after_tables_enabled": {
"type": "boolean",
"label": "Despues de tablas",
"default": false,
"editable": true,
"description": "Insertar anuncios despues de tablas"
},
"incontent_after_tables_probability": {
"type": "select",
"label": "Probabilidad tablas",
"default": "50",
"editable": true,
"options": ["25", "50", "75", "100"]
},
"incontent_max_total_ads": {
"type": "select",
"label": "Maximo total de ads",
"default": "8",
"editable": true,
"options": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"],
"description": "Cantidad maxima de anuncios in-content por post"
},
"incontent_min_spacing": {
"type": "select",
"label": "Espaciado minimo",
"default": "3",
"editable": true,
"options": {
"2": "2 elementos",
"3": "3 elementos",
"4": "4 elementos",
"5": "5 elementos",
"6": "6 elementos"
},
"description": "Minimo de elementos de bloque entre anuncios"
},
"incontent_format": {
"type": "select",
"label": "Formato de ads",
"default": "in-article",
"editable": true,
"options": {
"in-article": "In-Article (fluid)",
"auto": "Auto (responsive)"
},
"description": "Formato de anuncio para todas las ubicaciones in-content"
},
"incontent_priority_mode": {
"type": "select",
"label": "Estrategia de seleccion",
"default": "position",
"editable": true,
"options": {
"position": "Por posicion (distribucion uniforme)",
"priority": "Por prioridad (maximizar H2/H3)"
},
"description": "Como resolver conflictos cuando dos ubicaciones estan muy cerca"
}
}
}
}
Implementacion Tecnica
Opcion de Parsing Recomendada: Regex Multiple
// Regex para detectar todos los elementos de bloque contables (H4 no soportado)
$pattern = '/(<\/(?:p|h[2-3]|figure|ul|ol|table|blockquote)>)/i';
$parts = preg_split($pattern, $content, -1, PREG_SPLIT_DELIM_CAPTURE);
// Para detectar <img> standalone (no dentro de figure)
// Procesar en segundo paso, verificando contexto
Justificacion:
- Mas rapido que DOMDocument
- No modifica el HTML original
- Suficiente para el caso de uso (no necesitamos validar anidamiento complejo)
- El contexto de
<img>dentro de<figure>se resuelve verificando si hay<figure>abierto sin cerrar
Diagrama de Dependencias de Tasks
1.1 Schema: grupo incontent_advanced ─┐
1.2 Schema: campos individuales ──────┼──> 1.3 Sync BD ──┬──> 2.x FormBuilder
│ │
│ └──> 3.x Renderer
│ │
│ v
│ 4.x Validacion
│ │
└──────────────────────> 5.x Docs