['position' => 'before_content'], 2 => ['position' => 'after_content'], 3 => ['position' => 'before_content'], 4 => ['position' => 'after_content'], 5 => ['position' => 'incontent', 'insertionRule' => 'before_n'], 6 => ['position' => 'incontent', 'insertionRule' => 'after_n'], 7 => ['position' => 'before_excerpt'], 8 => ['position' => 'after_excerpt'], 9 => ['position' => 'between_posts'], 10 => ['position' => 'before_comments'], 11 => ['position' => 'between_comments'], 12 => ['position' => 'after_comments'], 13 => ['position' => 'footer'], 14 => ['position' => 'header'], 15 => ['position' => 'selector', 'selectorPosition' => 'before'], 16 => ['position' => 'selector', 'selectorPosition' => 'after'], 17 => ['position' => 'selector', 'selectorPosition' => 'append'], ]; const AI_ALIGN = [1 => 'left', 2 => 'right', 3 => 'center', 4 => 'float-left', 5 => 'float-right']; const AI_DEVICES = [ 0 => 'desktop', 1 => 'tablet,mobile', 2 => 'tablet', 3 => 'mobile', 4 => 'desktop,tablet', 5 => 'desktop,mobile', 6 => '', ]; const AI_PAGES = [ 'display_on_posts' => ['post', '1'], 'display_on_pages' => ['page', '0'], 'display_on_homepage' => ['home', '0'], 'display_on_category_pages' => ['category', '0'], 'display_on_search_pages' => ['search', '0'], 'display_on_archive_pages' => ['archive', '0'], ]; const AA_PLACES = [ 'post_top' => ['position' => 'before_content'], 'post_bottom' => ['position' => 'after_content'], 'post_content' => ['position' => 'incontent'], 'sidebar_widget' => ['position' => 'sidebar'], 'footer' => ['position' => 'footer'], ]; public static function detect() { $found = [ self::AD_INSERTER => (bool) self::ad_inserter_blocks(get_option('ad_inserter')), self::ADVANCED_ADS => false, ]; if (get_option('advads-ads-placements')) { $found[self::ADVANCED_ADS] = true; } elseif (function_exists('get_posts')) { $found[self::ADVANCED_ADS] = (bool) get_posts([ 'post_type' => 'advanced_ads', 'post_status' => 'any', 'numberposts' => 1, 'fields' => 'ids', ]); } return $found; } public static function run() { $blocks = array_merge( self::from_ad_inserter(self::ad_inserter_blocks(get_option('ad_inserter'))), self::from_advanced_ads(self::advanced_ads_ads(), self::advanced_ads_placements()) ); return self::build($blocks); } public static function ad_inserter_blocks($option) { if (is_string($option) && strncmp($option, ':AI:', 4) === 0) { $raw = base64_decode(substr($option, 4), true); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- the other plugin's own storage format. $option = $raw === false ? [] : @unserialize($raw, ['allowed_classes' => false]); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize,WordPress.PHP.NoSilencedErrors.Discouraged -- the same format; objects refused. } if (!is_array($option)) { return []; } unset($option['global'], $option['extract']); return array_filter($option, function ($block) { return is_array($block) && is_string($block['code'] ?? null) && trim($block['code']) !== ''; }); } public static function from_ad_inserter(array $blocks) { $out = []; foreach ($blocks as $key => $block) { $code = (string) $block['code']; if ($key === 'h' || $key === 'f') { $out[] = ['source' => self::AD_INSERTER, 'name' => $key === 'h' ? 'Header' : 'Footer', 'code' => $code, 'role' => $key === 'h' ? 'head' : 'footer']; continue; } if ($key === 'a') { continue; } $name = trim((string) ($block['name'] ?? '')) ?: sprintf('Block %s', $key); $type = (int) ($block['display_type'] ?? 0); $place = null; if (isset(self::AI_PLACES[$type]) && !self::on($block, 'disable_insertion', '0')) { $place = self::AI_PLACES[$type]; if ($place['position'] === 'incontent') { $place['insertionParagraphs'] = self::paragraphs((string) ($block['paragraph_number'] ?? '1'), (int) ($block['direction_type'] ?? 0) === 1); } if ($place['position'] === 'selector') { $place['selector'] = trim((string) ($block['html_selector'] ?? '')); if ($place['selector'] === '') { $place = null; } } } $out[] = [ 'source' => self::AD_INSERTER, 'name' => $name, 'code' => $code, 'role' => 'block', 'php' => self::on($block, 'process_php', '0'), 'place' => $place, 'devices' => self::ai_devices($block), 'pages' => self::ai_pages($block), 'align' => self::AI_ALIGN[(int) ($block['alignment_type'] ?? 0)] ?? 'center', ]; } return $out; } public static function from_advanced_ads(array $ads, array $placements) { $out = []; $used = []; foreach ($placements as $placement) { $type = (string) ($placement['type'] ?? ''); $item = (string) ($placement['item'] ?? ''); $name = trim((string) ($placement['name'] ?? '')) ?: $type; if (strpos($item, 'group_') === 0) { $out[] = ['source' => self::ADVANCED_ADS, 'name' => $name, 'skip' => __('A group of ads: add its ads one by one.', 'adghost-lite')]; continue; } $id = (int) preg_replace('/^ad_/', '', $item); if (!$id || !isset($ads[$id])) { continue; } $used[$id] = true; $ad = $ads[$id]; if (($type === 'header' || $type === 'footer') && in_array($ad['type'] ?? 'plain', ['plain', 'content'], true)) { $out[] = ['source' => self::ADVANCED_ADS, 'name' => $name, 'code' => (string) $ad['content'], 'role' => $type === 'header' ? 'head' : 'footer']; continue; } $place = self::AA_PLACES[$type] ?? null; if ($place && $place['position'] === 'incontent') { $options = (array) ($placement['options'] ?? []); $place['insertionRule'] = ($options['position'] ?? 'after') === 'before' ? 'before_n' : 'after_n'; $index = max(1, (int) ($options['index'] ?? 1)); $place['insertionParagraphs'] = (string) (empty($options['start_from_bottom']) ? $index : -$index); } $out[] = self::aa_block($ad, $name, $place); } foreach ($ads as $id => $ad) { if (!isset($used[$id])) { $out[] = self::aa_block($ad, (string) $ad['title'], null); } } return $out; } private static function aa_block(array $ad, $name, $place) { $type = (string) ($ad['type'] ?? 'plain'); if ($type === 'gam') { return ['source' => self::ADVANCED_ADS, 'name' => $name, 'skip' => __('Made with the Advanced Ads Google Ad Manager add-on: import these units with Import from GAM, from a CSV downloaded in Ad Manager.', 'adghost-lite')]; } if ($type === 'image') { return ['source' => self::ADVANCED_ADS, 'name' => $name, 'skip' => __('An image ad, not a Google Ad Manager unit.', 'adghost-lite')]; } return [ 'source' => self::ADVANCED_ADS, 'name' => $name, 'code' => (string) ($ad['content'] ?? ''), 'role' => 'block', 'php' => false, 'place' => $place, ]; } public static function build(array $blocks) { $by_div = []; foreach ($blocks as $block) { foreach (self::slots_in((string) ($block['code'] ?? '')) as $slot) { if ($slot['div'] !== '' && !isset($by_div[$slot['div']])) { $by_div[$slot['div']] = $slot; } } } $skipped = []; $code = ['head' => [], 'footer' => []]; $found = []; $placed = []; foreach ($blocks as $block) { if (isset($block['skip'])) { $skipped[] = self::skip($block, $block['skip']); continue; } $raw = (string) $block['code']; if ($block['role'] !== 'block') { $rest = self::without_gpt($raw); if ($rest !== '') { $code[$block['role']][] = $rest; } foreach (self::slots_in($raw) as $slot) { if ($slot['div'] === '' && !isset($slot['skip'])) { $found[] = [$block, $slot]; } } continue; } $slots = self::slots_in($raw); foreach (self::divs_in($raw) as $div) { if (isset($by_div[$div]) && !self::has_slot($slots, $div)) { $slots[] = $by_div[$div]; } } if (!$slots) { $skipped[] = self::skip($block, self::not_gam_reason($raw, !empty($block['php']))); continue; } foreach ($slots as $slot) { $placed[$slot['div']] = true; $found[] = [$block, $slot]; } } $networks = array_count_values(array_map(function ($pair) { return $pair[1]['network']; }, $found)); arsort($networks); $network = (string) (key($networks) ?? ''); $units = []; foreach ($found as [$block, $slot]) { if ($slot['network'] !== $network) { /* translators: 1: a Google Ad Manager network code, 2: the one this import uses. */ $skipped[] = self::skip($block, sprintf(__('Belongs to network %1$s; this import uses %2$s.', 'adghost-lite'), $slot['network'], $network)); continue; } if (isset($slot['skip'])) { $skipped[] = self::skip($block, $slot['skip']); continue; } $unit = self::unit($block, $slot); if (isset($units[$unit['id']])) { $prev = &$units[$unit['id']]; if (($prev['position'] ?? '') === 'incontent' && ($unit['position'] ?? '') === 'incontent' && $prev['insertionRule'] === $unit['insertionRule']) { $prev['insertionParagraphs'] .= ', ' . $unit['insertionParagraphs']; } else { /* translators: %s: an ad unit code. */ $skipped[] = self::skip($block, sprintf(__('Places %s again, already imported from another block.', 'adghost-lite'), $unit['id'])); } unset($prev); continue; } $units[$unit['id']] = $unit; } foreach ($by_div as $div => $slot) { if (!isset($placed[$div]) && $slot['network'] === $network && !isset($slot['skip'])) { /* translators: %s: an ad unit code. */ $skipped[] = ['source' => '', 'name' => $slot['code'], 'reason' => __('Defined in the header code, but no block shows it.', 'adghost-lite')]; } } return [ 'ad_units' => array_values($units), 'network_code' => $network, 'skipped' => $skipped, 'code' => array_map(function ($parts) { return implode("\n", array_unique($parts)); }, $code), ]; } public static function slots_in($code) { $slots = []; $path = '(["\'])\/(\d{4,12}(?:,\d{4,12})?)\/([^"\']+)\1'; if (preg_match_all('/defineSlot\s*\(\s*' . $path . '\s*,\s*([\s\S]*?)\s*,\s*(["\'])([^"\']+)\5\s*\)/', (string) $code, $m, PREG_SET_ORDER)) { foreach ($m as $match) { $sizes = AdGhost_CSV_Importer::sizes(preg_replace('/\[\s*(\d{1,4})\s*,\s*(\d{1,4})\s*\]/', '$1x$2', $match[4])); $slots[] = [ 'network' => $match[2], 'code' => trim($match[3]), 'sizes' => $sizes !== '' ? $sizes : '300x250', 'div' => $match[6], 'type' => 'standard', ]; } } if (preg_match_all('/defineOutOfPageSlot\s*\(\s*' . $path . '\s*(?:,\s*([^)]*))?\)/', (string) $code, $m, PREG_SET_ORDER)) { foreach ($m as $match) { $arg = trim((string) ($match[4] ?? '')); $slot = ['network' => $match[2], 'code' => trim($match[3]), 'sizes' => '', 'div' => '', 'type' => 'standard']; if (preg_match('/OutOfPageFormat\.INTERSTITIAL/', $arg)) { $slot['type'] = 'interstitial'; $slot['sizes'] = 'out-of-page'; } elseif (preg_match('/OutOfPageFormat\.REWARDED/', $arg)) { $slot['type'] = 'reward'; $slot['sizes'] = '1x1'; } elseif (preg_match('/OutOfPageFormat\.(TOP|BOTTOM)_ANCHOR/', $arg, $anchor)) { $slot['sizes'] = '320x50'; $slot['position'] = 'anchor'; $slot['anchorPosition'] = $anchor[1] === 'TOP' ? 'top' : 'bottom'; } else { $slot['div'] = preg_match('/^(["\'])([^"\']+)\1$/', $arg, $div) ? $div[2] : ''; $slot['skip'] = __('An out-of-page slot that is not an interstitial, rewarded or anchor ad.', 'adghost-lite'); } $slots[] = $slot; } } return $slots; } public static function divs_in($code) { preg_match_all('/googletag\s*\.\s*display\s*\(\s*(["\'])([^"\']+)\1|