$protocols Allowed URL schemes. * @return array */ public function allow_data_uri_protocol( $protocols ) { if ( ! is_array( $protocols ) ) { return $protocols; } if ( ! in_array( 'data', $protocols, true ) ) { $protocols[] = 'data'; } return $protocols; } /** * Enqueue the Google AdSense script when fallback is enabled. * * @return void */ public function maybe_enqueue_adsense_script() { $pub_id = get_option( 'adf_google_adsense_id' ); $enabled = get_option( 'adf_google_ads_enabled' ); $fallback = get_option( 'adf_google_fallback_enabled' ); $load = ( '1' === $enabled || '1' === $fallback ) && ! empty( $pub_id ); if ( $load ) { $adsense_js = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=' . rawurlencode( sanitize_text_field( $pub_id ) ); // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- AdSense requires async script in head. echo '' . "\n"; } } /** * Inject ads before and/or after post content based on settings. * * @param string $content The post content. * @return string Modified content with ads injected. */ public function adfusion_inject_ads_into_content( $content ) { if ( function_exists( 'adfusion_premium' ) && adfusion_premium()->is_user_ad_free() ) { return $content; } // Only inject into main single blog posts (not pages, archives, or custom post types). if ( is_single() && is_main_query() && 'post' === get_post_type() ) { $before_group = get_option( 'adfusion_before_content_group' ); $after_group = get_option( 'adfusion_after_content_group' ); $before_html = ''; $after_html = ''; // Utilize our existing shortcode logic to fetch and render the active ad. if ( ! empty( $before_group ) ) { $before_html = do_shortcode( '[adfusion group="' . esc_attr( sanitize_text_field( $before_group ) ) . '"]' ); } if ( ! empty( $after_group ) ) { $after_html = do_shortcode( '[adfusion group="' . esc_attr( sanitize_text_field( $after_group ) ) . '"]' ); } return $before_html . $content . $after_html; } return $content; } /** * Render the [adfusion] shortcode. * Usage: [adfusion id="123"] or [adfusion group="sidebar-ads"] * * @param array $atts Shortcode attributes (id, group). * @return string HTML output or empty string. */ public function render_ad_shortcode( $atts ) { // Ad-free users see no ads. Use filter 'adfusion_rotator_skip_adfree_check' => __return_true to bypass for debugging only. if ( function_exists( 'adfusion_premium' ) && adfusion_premium()->is_user_ad_free() && ! apply_filters( 'adfusion_rotator_skip_adfree_check', false ) ) { return ''; } $atts = shortcode_atts( array( 'id' => '', 'group' => '', ), $atts, 'adfusion' ); // Base query arguments. $args = array( 'post_type' => 'adfusion_ad', 'post_status' => 'publish', 'posts_per_page' => 1, 'orderby' => 'rand', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Exclude ads hidden via Abilities API. 'meta_query' => adf_get_visible_ads_meta_query(), ); $dedupe_this_request = function_exists( 'adf_should_deduplicate_ads_same_request' ) && adf_should_deduplicate_ads_same_request(); $dedupe_explicit_id_targets = apply_filters( 'adfusion_deduplicate_id_shortcode', false ); if ( ! empty( $atts['id'] ) ) { $args['p'] = intval( $atts['id'] ); $args = apply_filters( 'adfusion_shortcode_query_args', $args, $atts ); $ad_query = new WP_Query( $args ); if ( ! $ad_query->have_posts() ) { return ''; } if ( $dedupe_this_request && $dedupe_explicit_id_targets ) { $pid = intval( $atts['id'] ); if ( $pid > 0 && function_exists( 'adf_get_rendered_ad_ids_this_request' ) && in_array( $pid, adf_get_rendered_ad_ids_this_request(), true ) ) { return ''; } } $ads = $ad_query->posts; } elseif ( ! empty( $atts['group'] ) ) { $group_slug = sanitize_text_field( $atts['group'] ); $cache_key = ADFUSION_PLUGIN_SLUG . '_group_' . $group_slug; $ads = get_transient( $cache_key ); if ( false === $ads ) { $args['posts_per_page'] = -1; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Required for ad group filtering. $args['tax_query'] = array( array( 'taxonomy' => 'adfusion_group', 'field' => 'slug', 'terms' => $group_slug, ), ); $args = apply_filters( 'adfusion_shortcode_query_args', $args, $atts ); $ad_query = new WP_Query( $args ); $ads = $ad_query->have_posts() ? $ad_query->posts : array(); set_transient( $cache_key, $ads, DAY_IN_SECONDS ); } if ( empty( $ads ) ) { return ''; } } else { return ''; } $adf_ctx = array(); if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) ) { $adf_ctx = AdFusion_Premium_Ad_Selection::get_request_context(); } // For group queries: use weighted randomizer (A/B split testing support). if ( ! empty( $atts['group'] ) && count( $ads ) > 1 ) { $weighted_ads = array(); foreach ( $ads as $ad ) { // Premium add-ons can block this ad from rotating into the pool. if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) ) { if ( ! AdFusion_Premium_Ad_Selection::apply_allow_ad_render( $ad->ID, $adf_ctx ) ) { continue; } } elseif ( ! apply_filters( 'adfusion_allow_ad_render', true, $ad->ID ) ) { continue; } if ( $dedupe_this_request && function_exists( 'adf_get_rendered_ad_ids_this_request' ) && in_array( (int) $ad->ID, adf_get_rendered_ad_ids_this_request(), true ) ) { continue; } $weight = (int) get_post_meta( $ad->ID, '_adf_weight', true ); $weight = $weight > 0 ? $weight : 1; for ( $i = 0; $i < $weight; $i++ ) { $weighted_ads[] = $ad; } } if ( empty( $weighted_ads ) ) { return ''; } $selected_ad = $weighted_ads[ array_rand( $weighted_ads ) ]; $ads = array( $selected_ad ); } elseif ( ! empty( $atts['group'] ) && 1 === count( $ads ) ) { // Single ad in group: still apply allow filter. if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) ) { if ( ! AdFusion_Premium_Ad_Selection::apply_allow_ad_render( $ads[0]->ID, $adf_ctx ) ) { return ''; } } elseif ( ! apply_filters( 'adfusion_allow_ad_render', true, $ads[0]->ID ) ) { return ''; } if ( $dedupe_this_request && function_exists( 'adf_get_rendered_ad_ids_this_request' ) && in_array( (int) $ads[0]->ID, adf_get_rendered_ad_ids_this_request(), true ) ) { return ''; } } ob_start(); foreach ( $ads as $ad ) { $post_id = $ad->ID; $ab_variant = 'a'; if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) ) { $ab_variant = AdFusion_Premium_Ad_Selection::pick_ab_variant_for_ad( $post_id ); } // 1. Check scheduling (timezone-aware datetime). $selected_tz = get_option( 'adfusion_timezone_setting', get_option( 'timezone_string' ) ); if ( empty( $selected_tz ) ) { $selected_tz = 'UTC'; } $tz = new DateTimeZone( $selected_tz ); $now = new DateTime( 'now', $tz ); $current_datetime = $now->format( 'Y-m-d H:i:s' ); $start = get_post_meta( $post_id, '_adf_start_datetime', true ); $end = get_post_meta( $post_id, '_adf_end_datetime', true ); if ( empty( $start ) ) { $legacy = get_post_meta( $post_id, '_adf_start_date', true ); $start = ! empty( $legacy ) ? $legacy . ' 00:00:00' : ''; } if ( empty( $end ) ) { $legacy = get_post_meta( $post_id, '_adf_end_date', true ); $end = ! empty( $legacy ) ? $legacy . ' 23:59:59' : ''; } if ( ! empty( $start ) && $current_datetime < $start ) { continue; } if ( ! empty( $end ) && $current_datetime > $end ) { continue; } // Premium: Allow Advanced Rules (device targeting, impression cap, etc.) to block render. if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) ) { if ( ! AdFusion_Premium_Ad_Selection::apply_allow_ad_render( $post_id, $adf_ctx ) ) { continue; } } elseif ( ! apply_filters( 'adfusion_allow_ad_render', true, $post_id ) ) { continue; } // 2. Get ad data. $ad_type = get_post_meta( $post_id, '_adf_ad_type', true ); $destination_url = get_post_meta( $post_id, '_adf_destination_url', true ); $ad_code = get_post_meta( $post_id, '_adf_ad_code', true ); if ( empty( $ad_type ) ) { $ad_type = 'image'; } /** * Filter destination URL before ad markup is built. * * @param string $destination_url Stored destination URL. * @param int $post_id Ad post ID. * @param string $ab_variant A/B variant slug. */ $destination_url = apply_filters( 'adfusion_destination_url_for_placement', $destination_url, $post_id, $ab_variant ); // 3. Render based on ad type. $output = ''; switch ( $ad_type ) { case 'image': if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) ) { $image_url = AdFusion_Premium_Ad_Selection::get_image_url_for_placement( $post_id, $ab_variant ); } else { $image_url = get_post_meta( $post_id, '_adf_ad_image', true ); $image_mobile = get_post_meta( $post_id, '_adf_ad_image_mobile', true ); if ( empty( $image_url ) ) { $image_id = get_post_thumbnail_id( $post_id ); $image_url = $image_id ? wp_get_attachment_image_url( $image_id, 'full' ) : ''; } if ( wp_is_mobile() && ! empty( $image_mobile ) ) { $image_url = $image_mobile; } } if ( ! empty( $image_url ) ) { $dest_url = ! empty( $destination_url ) ? $destination_url : home_url( '/' ); $img_style = 'max-width:100%; height:auto;'; if ( class_exists( 'AdFusion_Premium_Group_Dimensions' ) ) { $dims = AdFusion_Premium_Group_Dimensions::get_max_dimensions_for_ad( $post_id ); $img_style = AdFusion_Premium_Group_Dimensions::get_img_style_for_max_dimensions( $dims ); } $output = sprintf( '%s', esc_url( $dest_url ), esc_url( $image_url ), esc_attr( get_the_title( $post_id ) ), esc_attr( $img_style ) ); } break; case 'code': // render_ad_content parses Advanced Ads JSON and converts to AdSense HTML. $output = $this->render_ad_content( $post_id ); if ( empty( $output ) ) { $output = $ad_code; // Fallback: output as-is (stored from trusted admin). } break; case 'text': $output = '
' . nl2br( esc_html( $ad_code ) ) . '
'; if ( ! empty( $destination_url ) ) { $output = '' . $output . ''; } break; } // 4. AdSense fallback when no local content. if ( empty( $output ) ) { $output = $this->render_ad_content( $post_id ); } $shortcode_ctx = array( 'ab_variant' => $ab_variant, ); $output = apply_filters( 'adfusion_shortcode_output', $output, $post_id, $ad_type, $shortcode_ctx ); if ( ! empty( $output ) ) { $imp_ctx = array(); if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) && AdFusion_Premium_Ad_Selection::is_ab_active( $post_id ) ) { $imp_ctx['ab_variant'] = $ab_variant; } do_action( 'adfusion_after_ad_rendered', $post_id, $imp_ctx ); $master_id = absint( apply_filters( 'adfusion_ad_master_id', 0, $post_id ) ); $kses = $this->get_allowed_ad_html_kses(); $ab_attr = ''; if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) && AdFusion_Premium_Ad_Selection::is_ab_active( $post_id ) ) { $ab_attr = ' data-ab-variant="' . esc_attr( $ab_variant ) . '"'; } $stat_attr = class_exists( 'AdFusion_Premium_Analytics', false ) ? AdFusion_Premium_Analytics::get_stat_token_attr_fragment( $post_id ) : ''; if ( $master_id > 0 ) { echo '
' . wp_kses( $output, $kses ) . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $ab_attr is escaped attribute fragment. } else { echo '
' . wp_kses( $output, $kses ) . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $ab_attr is escaped attribute fragment. } } } wp_reset_postdata(); return ob_get_clean(); } /** * Render AdSense fallback when no local ad content exists. * Handles imported Advanced Ads JSON and converts to AdSense HTML. * * @param int $ad_id Post ID of the ad. * @return string HTML output or empty string. */ public function render_ad_content( $ad_id ) { // Ensure even shortcodes respect the Premium Kill Switch. if ( class_exists( 'AdFusion_Premium_Ad_Selection' ) ) { if ( ! AdFusion_Premium_Ad_Selection::apply_allow_ad_render( $ad_id, AdFusion_Premium_Ad_Selection::get_request_context() ) ) { return ''; } } elseif ( ! apply_filters( 'adfusion_allow_ad_render', true, $ad_id ) ) { return ''; } $local_content = get_post_meta( $ad_id, 'ad_content', true ); if ( empty( $local_content ) ) { $local_content = get_post_meta( $ad_id, '_adf_ad_code', true ); } $google_slot = get_post_meta( $ad_id, 'adf_google_ad_slot', true ); if ( empty( $google_slot ) ) { $google_slot = get_post_meta( $ad_id, '_adf_google_ad_slot', true ); } $pub_id = get_option( 'adf_google_adsense_id' ); // 1. Check if the local content is an imported Advanced Ads JSON object. if ( ! empty( $local_content ) && strpos( $local_content, '"slotId":' ) !== false ) { // Strip any accidental

tags WordPress might have added to the JSON. $clean_json = wp_strip_all_tags( wp_unslash( $local_content ) ); $ad_data = json_decode( trim( $clean_json ), true ); if ( JSON_ERROR_NONE === json_last_error() && ! empty( $ad_data['pubId'] ) && ! empty( $ad_data['slotId'] ) ) { // Convert JSON to AdSense HTML. $html = '

'; $html .= '
'; } return ''; } /** * Allowed HTML for ad output (admin-saved content; sanitized on display). * * Extends post context with tags/attributes common for AdSense and third-party ad snippets. * * @return array> Allowed tags for wp_kses(). */ private function get_allowed_ad_html_kses() { return adfusion_get_allowed_ad_html_kses(); } } // Load on frontend and AJAX requests (admin-ajax.php sets is_admin() true even for // frontend-triggered AJAX, so we must explicitly allow DOING_AJAX so shortcodes // registered here — e.g. [adfusion] — are available during wp_ajax_* handlers). if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { new AdFusion_Public(); }