insertion_engine = $insertion_engine; } public function init() { add_action('admin_bar_menu', [$this, 'add_admin_bar_button'], 100); add_action('wp_enqueue_scripts', [$this, 'admin_bar_styles']); add_action('admin_enqueue_scripts', [$this, 'admin_bar_styles']); add_action('wp_ajax_adghost_toggle_debug_mode', [$this, 'ajax_toggle_debug_mode']); add_action('wp_ajax_adghost_debug_nonce', [$this, 'ajax_fresh_nonce']); add_action('admin_post_adghost_toggle_debug_mode', [$this, 'handle_toggle_link']); add_action('wp_enqueue_scripts', [$this, 'enqueue_frontend_assets']); add_action('init', [$this, 'setup_for_request'], 20); } public function setup_for_request() { $this->enabled = (bool) $this->is_enabled_for_user(); if ($this->enabled && !is_admin()) { add_filter('the_content', [$this, 'inject_debug_markers'], 50); add_filter('body_class', [$this, 'add_body_class']); add_action('wp_footer', [$this, 'render_footer_marker'], 5); } } private function toggle_url($enable) { $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : ''; $uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : '/'; $current = (is_ssl() ? 'https://' : 'http://') . $host . $uri; return wp_nonce_url(add_query_arg([ 'action' => 'adghost_toggle_debug_mode', 'enabled' => $enable ? '1' : '0', 'redirect_to' => rawurlencode($current), ], admin_url('admin-post.php')), 'adghost_debug_toggle'); } public function handle_toggle_link() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You are not allowed to change AdGhost preview mode.', 'adghost-lite'), 403); } check_admin_referer('adghost_debug_toggle'); update_option('adghost_debug_mode', (isset($_GET['enabled']) ? sanitize_key(wp_unslash($_GET['enabled'])) : '') === '1'); $back = isset($_GET['redirect_to']) ? esc_url_raw(wp_unslash($_GET['redirect_to'])) : ''; wp_safe_redirect($back !== '' ? $back : home_url('/')); exit; } public function ajax_fresh_nonce() { if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized'], 403); } wp_send_json_success(['nonce' => wp_create_nonce('adghost_debug_nonce')]); } public function is_enabled_for_user() { if (!is_user_logged_in() || !current_user_can('manage_options')) { return false; } return get_option('adghost_debug_mode', false); } public function is_enabled() { return $this->enabled; } public function add_admin_bar_button($wp_admin_bar) { if (!current_user_can('manage_options')) { return; } $is_enabled = is_admin() ? (bool) get_option('adghost_debug_mode', false) : $this->enabled; $toggle = $this->toggle_url(!$is_enabled); $wp_admin_bar->add_node([ 'id' => 'adghost-debug-mode', 'title' => '' . '' . esc_html__('AdGhost Debug', 'adghost-lite') . '' . '' . ($is_enabled ? esc_html__('ON', 'adghost-lite') : esc_html__('OFF', 'adghost-lite')) . '', 'href' => $toggle, 'meta' => [ 'class' => 'adghost-debug-toggle' . ($is_enabled ? ' enabled' : ''), 'title' => $is_enabled ? __('Click to turn AdGhost preview off', 'adghost-lite') : __('Click to outline every ad position (administrators only)', 'adghost-lite'), ], ]); $wp_admin_bar->add_node([ 'parent' => 'adghost-debug-mode', 'id' => 'adghost-debug-switch', 'title' => $is_enabled ? esc_html__('Turn preview off', 'adghost-lite') : esc_html__('Turn preview on', 'adghost-lite'), 'href' => $toggle, 'meta' => ['class' => 'adghost-debug-toggle-item'], ]); $wp_admin_bar->add_node([ 'parent' => 'adghost-debug-mode', 'id' => 'adghost-debug-about', 'title' => $is_enabled ? esc_html__('On: only administrators see the outlines. Visitors get normal ads.', 'adghost-lite') : esc_html__('Off: turn on to outline every ad position, for administrators only.', 'adghost-lite'), 'meta' => ['class' => 'adghost-debug-about'], ]); $wp_admin_bar->add_node([ 'parent' => 'adghost-debug-mode', 'id' => 'adghost-debug-settings', 'title' => esc_html__('AdGhost settings', 'adghost-lite'), 'href' => admin_url('admin.php?page=adghost'), ]); } public function admin_bar_styles() { if (!is_admin_bar_showing() || !current_user_can('manage_options')) { return; } wp_add_inline_style('admin-bar', ' #wpadminbar #wp-admin-bar-adghost-debug-mode > .ab-item{display:flex;align-items:center;gap:7px} #wpadminbar .adghost-ab-switch{position:relative;display:inline-block;width:28px;height:16px;margin-top:0;border-radius:8px;background:#646970;transition:background .15s} #wpadminbar .adghost-ab-switch span{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:left .15s} #wpadminbar .adghost-debug-toggle.enabled .adghost-ab-switch{background:#00a32a} #wpadminbar .adghost-debug-toggle.enabled .adghost-ab-switch span{left:14px} #wpadminbar .adghost-ab-state{font-weight:600;min-width:24px} #wpadminbar .adghost-debug-toggle.enabled .adghost-ab-state{color:#68de7c} #wpadminbar #wp-admin-bar-adghost-debug-about .ab-item{height:auto;max-width:280px;white-space:normal;line-height:1.4;padding-top:4px;padding-bottom:6px;color:#c3c4c7;cursor:default} @media screen and (max-width:782px){#wpadminbar #wp-admin-bar-adghost-debug-mode{display:block}#wpadminbar .adghost-ab-label{display:none}} '); } public function ajax_toggle_debug_mode() { if (!current_user_can('manage_options')) { wp_send_json_error(['code' => 'unauthorized', 'message' => 'Only administrators can change preview mode.'], 403); } if (!check_ajax_referer('adghost_debug_nonce', 'nonce', false)) { wp_send_json_error(['code' => 'bad_nonce', 'message' => 'The page was open too long. Try again.'], 403); } $enabled = isset($_POST['enabled']) && $_POST['enabled'] === '1'; update_option('adghost_debug_mode', $enabled); wp_send_json_success([ 'enabled' => $enabled, 'message' => $enabled ? 'AdGhost Debug mode enabled' : 'AdGhost Debug mode disabled' ]); } public function enqueue_frontend_assets() { if (!current_user_can('manage_options')) { return; } wp_enqueue_style( 'adghost-debug', ADGHOST_PLUGIN_URL . 'assets/css/debug.css', [], ADGHOST_VERSION ); wp_enqueue_script( 'adghost-debug', ADGHOST_PLUGIN_URL . 'assets/js/debug.js', ['jquery'], ADGHOST_VERSION, true ); $ad_units = get_option('adghost_ad_units', []); wp_localize_script('adghost-debug', 'adghostDebug', [ 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('adghost_debug_nonce'), 'isEnabled' => $this->enabled ? '1' : '0', 'adUnits' => $ad_units, 'networkCode' => get_option('adghost_network_code', '') ]); } public function add_body_class($classes) { $classes[] = 'adghost-debug-active'; return $classes; } public function inject_debug_markers($content) { if (self::$content_processed) { return $content; } if (!in_the_loop() || !is_main_query()) { return $content; } self::$content_processed = true; $ad_units = AdGhost_Helper::units(); $content = preg_replace_callback( '//', function($matches) use ($ad_units) { $index = intval($matches[2]); $unit = null; foreach ($ad_units as $u) { if (AdGhost_Placement_Engine::unit_key($u) === $matches[1]) { $unit = $u; break; } } if ($unit) { return AdGhost_Ad_Container::render($unit, 'incontent', $index); } return $matches[0]; }, $content ); if (!$this->insertion_engine) { $this->insertion_engine = new AdGhost_Insertion_Engine(); } $decision = AdGhost_Placement_Engine::get_last_decision(); $placed = array_column($decision['plan'], 'paragraph'); $skipped = $decision['skipped']; $allowed = AdGhost_Content_Analyzer::allowed_point_types(); $points = $this->insertion_engine->detect_points($content); usort($points, function($a, $b) { return $b['offset'] - $a['offset']; }); foreach ($points as $point) { $type = $point['type'] ?? ''; if ($type === 'paragraph') { if (in_array($point['paragraph_number'] ?? 0, $placed, true)) { continue; } $reason = $skipped[$point['id']] ?? $decision['notice']; if ($reason === null) { $reason = 'Not selected by any unit rule'; } } elseif (!in_array($type, $allowed, true)) { continue; } else { $reason = $skipped[$point['id']] ?? $decision['notice'] ?? 'Not selected by any unit rule'; } $marker_html = $this->create_marker_html($point, $reason); $content = substr($content, 0, $point['offset']) . $marker_html . substr($content, $point['offset']); } return $content; } private function create_adghost_container_html($unit, $point) { $sizes = $unit['sizes'] ?? '300x250'; $first_size = explode('x', explode(';', $sizes)[0]); $width = $first_size[0] ?? 300; $height = $first_size[1] ?? 250; $html = '