Panel AdSense reorganizado: - Diagrama visual mostrando ubicaciones de anuncios (POST-TOP, IN-CONTENT, POST-BOTTOM, RAIL) - Secciones colapsables por ubicación con badges de color - Slots con descripciones claras indicando uso (Auto, In-Article, Display, etc.) In-Content Ads mejorado: - Soporte para 1-8 anuncios dentro del contenido - Modo aleatorio (random) que varía posiciones en cada visita - Configuración de mínimo/máximo de ads - Párrafos mínimos entre anuncios configurable (2-6) - Primer ad siempre en posición fija configurada Archivos modificados: - Schema v1.2.0 con 4 nuevos campos (random_mode, min_ads, max_ads, min_paragraphs_between) - FormBuilder con diagrama visual y mejor organización - ContentAdInjector con lógica de posicionamiento random - Renderer con soporte para post-content-1 hasta post-content-8 - FieldMapper actualizado con nuevos campos 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
205 lines
6.5 KiB
PHP
205 lines
6.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace ROITheme\Public\AdsensePlacement\Infrastructure\Services;
|
|
|
|
use ROITheme\Public\AdsensePlacement\Infrastructure\Ui\AdsensePlacementRenderer;
|
|
|
|
/**
|
|
* Inyecta anuncios dentro del contenido del post
|
|
* via filtro the_content
|
|
*
|
|
* Soporta:
|
|
* - Modo aleatorio (random) con posiciones variables
|
|
* - Configuracion de 1-8 ads maximo
|
|
* - Espacio minimo entre anuncios
|
|
*/
|
|
final class ContentAdInjector
|
|
{
|
|
public function __construct(
|
|
private array $settings,
|
|
private AdsensePlacementRenderer $renderer
|
|
) {}
|
|
|
|
/**
|
|
* Filtra the_content para insertar anuncios
|
|
*/
|
|
public function inject(string $content): string
|
|
{
|
|
if (!($this->settings['behavior']['post_content_enabled'] ?? false)) {
|
|
return $content;
|
|
}
|
|
|
|
// Verificar longitud minima
|
|
$minLength = (int)($this->settings['forms']['min_content_length'] ?? 500);
|
|
if (strlen(strip_tags($content)) < $minLength) {
|
|
return $content;
|
|
}
|
|
|
|
// Obtener configuracion
|
|
$minAds = (int)($this->settings['behavior']['post_content_min_ads'] ?? 1);
|
|
$maxAds = (int)($this->settings['behavior']['post_content_max_ads'] ?? 3);
|
|
$afterParagraphs = (int)($this->settings['behavior']['post_content_after_paragraphs'] ?? 3);
|
|
$minBetween = (int)($this->settings['behavior']['post_content_min_paragraphs_between'] ?? 4);
|
|
$randomMode = ($this->settings['behavior']['post_content_random_mode'] ?? true) === true;
|
|
|
|
// Validar min <= max
|
|
if ($minAds > $maxAds) {
|
|
$minAds = $maxAds;
|
|
}
|
|
|
|
// Dividir contenido en parrafos
|
|
$paragraphs = $this->splitIntoParagraphs($content);
|
|
$totalParagraphs = count($paragraphs);
|
|
|
|
// Necesitamos al menos afterParagraphs + 1 parrafos
|
|
if ($totalParagraphs < $afterParagraphs + 1) {
|
|
return $content;
|
|
}
|
|
|
|
// Calcular posiciones de insercion
|
|
$adPositions = $this->calculateAdPositions(
|
|
$totalParagraphs,
|
|
$afterParagraphs,
|
|
$minBetween,
|
|
$minAds,
|
|
$maxAds,
|
|
$randomMode
|
|
);
|
|
|
|
if (empty($adPositions)) {
|
|
return $content;
|
|
}
|
|
|
|
// Reconstruir contenido con anuncios insertados
|
|
return $this->buildContentWithAds($paragraphs, $adPositions);
|
|
}
|
|
|
|
/**
|
|
* Divide el contenido en parrafos preservando el HTML
|
|
*/
|
|
private function splitIntoParagraphs(string $content): array
|
|
{
|
|
// Dividir por </p>, pero mantener el tag
|
|
$parts = preg_split('/(<\/p>)/i', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
|
|
|
|
$paragraphs = [];
|
|
$current = '';
|
|
|
|
foreach ($parts as $part) {
|
|
$current .= $part;
|
|
if (strtolower($part) === '</p>') {
|
|
$paragraphs[] = $current;
|
|
$current = '';
|
|
}
|
|
}
|
|
|
|
// Si hay contenido restante (sin cerrar), agregarlo
|
|
if (!empty(trim($current))) {
|
|
$paragraphs[] = $current;
|
|
}
|
|
|
|
return $paragraphs;
|
|
}
|
|
|
|
/**
|
|
* Calcula las posiciones donde insertar anuncios
|
|
*
|
|
* @return int[] Indices de parrafos despues de los cuales insertar ads
|
|
*/
|
|
private function calculateAdPositions(
|
|
int $totalParagraphs,
|
|
int $afterFirst,
|
|
int $minBetween,
|
|
int $minAds,
|
|
int $maxAds,
|
|
bool $randomMode
|
|
): array {
|
|
// Calcular posiciones disponibles respetando el espacio minimo
|
|
$availablePositions = [];
|
|
$lastPosition = $afterFirst; // Primera posicion fija
|
|
|
|
// La primera posicion siempre es despues del parrafo indicado
|
|
if ($afterFirst < $totalParagraphs) {
|
|
$availablePositions[] = $afterFirst;
|
|
}
|
|
|
|
// Calcular posiciones adicionales respetando minBetween
|
|
$nextPosition = $afterFirst + $minBetween;
|
|
while ($nextPosition < $totalParagraphs - 1) { // -1 para no insertar al final
|
|
$availablePositions[] = $nextPosition;
|
|
$nextPosition += $minBetween;
|
|
}
|
|
|
|
// Determinar cuantos ads insertar
|
|
$maxPossible = count($availablePositions);
|
|
if ($maxPossible === 0) {
|
|
return [];
|
|
}
|
|
|
|
// Limitar por maxAds y lo que el contenido permite
|
|
$actualMax = min($maxAds, $maxPossible);
|
|
$actualMin = min($minAds, $actualMax);
|
|
|
|
// Determinar cantidad final de ads
|
|
if ($randomMode) {
|
|
// En modo random, elegir cantidad aleatoria entre min y max
|
|
$numAds = rand($actualMin, $actualMax);
|
|
} else {
|
|
// En modo fijo, usar el maximo posible
|
|
$numAds = $actualMax;
|
|
}
|
|
|
|
if ($numAds === 0) {
|
|
return [];
|
|
}
|
|
|
|
// Seleccionar posiciones
|
|
if ($randomMode && $numAds < $maxPossible) {
|
|
// Modo random: elegir posiciones aleatorias
|
|
// Siempre incluir la primera posicion
|
|
$selectedPositions = [$availablePositions[0]];
|
|
|
|
if ($numAds > 1) {
|
|
// Elegir aleatoriamente del resto
|
|
$remainingPositions = array_slice($availablePositions, 1);
|
|
shuffle($remainingPositions);
|
|
$additionalPositions = array_slice($remainingPositions, 0, $numAds - 1);
|
|
$selectedPositions = array_merge($selectedPositions, $additionalPositions);
|
|
}
|
|
|
|
// Ordenar para insertar en orden correcto
|
|
sort($selectedPositions);
|
|
return $selectedPositions;
|
|
} else {
|
|
// Modo fijo o todas las posiciones necesarias
|
|
return array_slice($availablePositions, 0, $numAds);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reconstruye el contenido insertando anuncios en las posiciones indicadas
|
|
*/
|
|
private function buildContentWithAds(array $paragraphs, array $adPositions): string
|
|
{
|
|
$newContent = '';
|
|
$adsInserted = 0;
|
|
|
|
foreach ($paragraphs as $index => $paragraph) {
|
|
$newContent .= $paragraph;
|
|
|
|
// Verificar si debemos insertar un ad despues de este parrafo
|
|
// El indice es 0-based, las posiciones son 1-based (parrafo #3 = index 2)
|
|
$paragraphNumber = $index + 1;
|
|
|
|
if (in_array($paragraphNumber, $adPositions, true)) {
|
|
$adsInserted++;
|
|
$adHtml = $this->renderer->renderSlot($this->settings, 'post-content-' . $adsInserted);
|
|
$newContent .= $adHtml;
|
|
}
|
|
}
|
|
|
|
return $newContent;
|
|
}
|
|
}
|