sive cache-busting can be used. * * @param array $args argument passed to ads. * @return bool */ private function can_override_passive( $args ) { if ( ! empty( $args['wp_the_query']['is_feed'] ) || ! array_key_exists( 'previous_method', $args ) || ! array_key_exists( 'previous_id', $args ) ) { return false; } // Prevent non-header placement from being collected through wp_head. if ( doing_action( 'wp_head' ) && isset( $args['placement_type'] ) && 'header' !== $args['placement_type'] && ! $this->can_inject_during_wp_head() ) { return false; } if ( isset( $args['cache-busting'] ) && $args['cache-busting'] === self::OPTION_IGNORE ) { return false; } return true; } /** * Prepare ad for js handler. * * @param array $query * @return string */ protected function get_override_content( $query ) { $content = ''; // Prevent non-header placement from being collected through wp_head. if ( doing_action( 'wp_head' ) && isset( $query['params']['placement_type'] ) && 'header' !== $query['params']['placement_type'] && ! $this->can_inject_during_wp_head() ) { return $content; } // scripts require no wrapper if ( ! $this->isHead || ( isset( $query['params']['placement_type'] ) && $query['params']['placement_type'] !== 'header' ) ) { $query['elementid'] = $this->generate_elementid(); // Get placement id if ( ! empty( $query['method'] ) && 'placement' === $query['method'] && ! empty( $query['id'] ) ) { // Cache-busting: "ajax" $placement_id = $query['id']; } elseif( ! empty( $query['params']['output']['placement_id'] ) ) { // AJAX fallback $placement_id = $query['params']['output']['placement_id']; } else { $placement_id = ''; } $content .= $this->create_wrapper( $query['elementid'], $placement_id, $query['params'] ); } $query = $this->get_ajax_query( $query ); self::$ajax_queries[] = $query; return $content; } /** * Get ajax query. * * @param array $query * @param bool $request_placement Whether or not to request top level placement. * @return array */ public function get_ajax_query( $query, $request_placement = true ) { // Request placement. if ( $request_placement && isset( $query['params']['output']['placement_id'] ) ) { $query['method'] = 'placement'; $query['id'] = $query['params']['output']['placement_id']; } $query['blog_id'] = get_current_blog_id(); /** * Collect blog data before `restore_current_blog` is called. */ if ( class_exists( 'Advanced_Ads_Tracking_Util', false ) && method_exists( 'Advanced_Ads_Tracking_Util', 'collect_blog_data' ) ) { $tracking_utils = Advanced_Ads_Tracking_Util::get_instance(); $tracking_utils->collect_blog_data(); } // Check if the `advanced-ads-ajax-ad-select-arguments` filter exists. if ( ! empty( $query['params'] ) && version_compare( ADVADS_VERSION, '1.24.0', '>' ) ) { $query['params'] = $this->remove_default_ajax_args( $this->ajax_default_args, $query['params'] ); $query['params'] = $this->extract_general_ajax_args( $query['params'] ); } return $query; } /** * Remove default AJAX arguments to reduce the size of the array printed in footer. * * @param array $default Default arguments. * @param array $source A full list of arguments that we need to be minifed. * @return array Minified array (source array that does not contain default arguments). */ private function remove_default_ajax_args( $default, $source ) { $result = array(); foreach ( $source as $key => $f ) { if ( ! array_key_exists( $key, $default ) ) { $result[ $key ] = $source[ $key ]; continue; } if ( $source[ $key ] === $default[ $key ] ) { continue; } if ( ! is_array( $default[ $key ] ) || ! is_array( $source[ $key ] ) ) { $result[ $key ] = $source[ $key ]; continue; } $key_result = $this->remove_default_ajax_args( $default[ $key ], $source[ $key ] ); if ( $key_result !== array() ) { $result[ $key ] = $key_result; } } return $result; } /** * Extract general AJAX arguments into separate array to reduce the size of the array printed in footer. * * @param array $source A full list of arguments to extract general arguments from. * @return array A list of arguments with general arguments removed. */ private function extract_general_ajax_args( $source ) { if ( wp_doing_ajax() ) { // Do nothing because we are not able to add data to the footer array. return $source; } if ( isset( $source['post'] ) ) { $ref = array_search( $source['post'], $this->ajax_queries_args, true ); if ( $ref === false ) { $ref = 'r' . count( $this->ajax_queries_args ); $this->ajax_queries_args[ $ref ] = $source['post']; } $source['post'] = $ref; } return $source; } /** * Add default AJAX arguments that were removed to reduce the size of the array printed in footer. * * @see self::remove_default_ajax_args * * When the item in the default array is not an array, it will be replaced by the item in the minified array. * When an item exists in either associative array, it will be added. Numeric keys are overridden. * * @param array $arguments Minified arguments. * @param array $request Current ad request. * @return array New arguments. */ public function add_default_ajax_arguments( $arguments, $request ) { if ( ! empty( $request['elementId'] ) ) { $arguments['cache_busting_elementid'] = $request['elementId']; } return array_replace_recursive( $this->ajax_default_args, $arguments ); } /** * Create wrapper for cache-busting. * * @param string $element_id Id of the wrapper. * @param string $placement_id Id of the placement. * @param array $args Custom arguments of ad or group. * * @return string Cache-busting wrapper. */ private function create_wrapper( $element_id, $placement_id = '', $args = array() ) { $class = $element_id; if ( $placement_id ) { $prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix(); $class .= ' ' . $prefix . $placement_id; } $style = ! empty( $args['inline-css'] ) ? 'style="' . $args['inline-css'] . '"' : ''; $wrapper_element = ! empty( $args['inline_wrapper_element'] ) ? 'span' : 'div'; // TODO: `id` is deprecated. return '<' . $wrapper_element . ' ' . $style . ' class="' . $class . '" id="' . $element_id . '">'; } /** * Generate unique element id * * @return string */ public function generate_elementid() { $prefix = Advanced_Ads_Plugin::get_instance()->get_frontend_prefix(); return $prefix . md5( 'advanced-ads-pro-ad-' . uniqid( ++self::$adOffset, true ) ); } /** * Check if placement can be displayed without passive cache-busting. * * @param string $id Placement id. * @see placement_can_display() * @return bool */ private function placement_can_display_not_passive( $id ) { // We force this filter to return true when collecting placements for passive cache-busting. // For now revoke this behavior return apply_filters( 'advanced-ads-can-display-placement', true, $id ); } /** * check if placement was closed before * * @param int $id placement id * @return bool whether placement can be displayed or not */ public function placement_can_display( $return, $id = 0 ){ static $checked_passive = array(); if ( in_array( $id, $checked_passive ) ) { // Ignore current filter when the placement is delivered without passive cache-busting. return $return; } // get all placements $placements = Advanced_Ads::get_ad_placements_array(); $cache_busting_auto = ! isset( $placements[ $id ]['options']['cache-busting'] ) || $placements[ $id ]['options']['cache-busting'] === self::OPTION_AUTO; if ( $cache_busting_auto && $this->is_passive_method_used() ) { $checked_passive[] = $id; return true; } return $return; } /** * determines, whether the "passive" method is used or not * * @return bool true if the "passive" method is used, false otherwise */ public function is_passive_method_used() { return isset( $this->options['default_auto_method'] ) && $this->options['default_auto_method'] === 'passive'; } /** * determines, whether or not to load tracking scripts * * @param bool $need_load_header_scripts * @return bool true if tracking scripts should be loaded, $need_load_header_scripts otherwise */ public function load_tracking_scripts( $need_load_header_scripts ) { //the script is used by: passive cache-busting, 'group refresh' feature return true; } /** * Add ad debug content * * @param arr $content * @param obj $ad Advanced_Ads_Ad * @return arr $content */ public function add_debug_content( $content, Advanced_Ads_Ad $ad ) { $needs_backend = $this->ad_needs_backend_request( $ad ); if ( 'off' === $needs_backend || 'ajax' === $needs_backend ) { $info = __( 'The ad can not work with passive cache-busting', 'advanced-ads-pro' ); } else { $info = __( 'The ad can work with passive cache-busting', 'advanced-ads-pro' ); } if ( $this->is_ajax ) { $name = _x( 'ajax', 'setting label', 'advanced-ads-pro' ); } elseif ( isset( $ad->args['cache-busting'] ) && $ad->args['cache-busting'] === self::OPTION_AUTO ) { $name = __( 'passive', 'advanced-ads-pro' ); $info .= '
##advanced_ads_passive_cb_debug##' . sprintf( '', __( 'The ad is displayed on the page', 'advanced-ads-pro' ), __( 'The ad is not displayed on the page', 'advanced-ads-pro' ) ); } else { $name = _x( 'off', 'setting label', 'advanced-ads-pro' ); } $content[] = sprintf( '%s %s
%s', _x( 'Cache-busting:', 'placement admin label', 'advanced-ads-pro' ), $name, $info ); return $content; } /** * Add placement to current ads. * * @param string $id Placement id. */ private function add_placement_to_current_ads( $id ) { $placements = Advanced_Ads::get_ad_placements_array(); $name = ! empty( $placements[ $id ]['name'] ) ? $placements[ $id ]['name'] : $id; Advanced_Ads::get_instance()->current_ads[] = array('type' => 'placement', 'id' => $id, 'title' => $name ); } /** * Get visitor conditions. * * @param Advanced_Ads_Ad $ad Ad object. * * @return void * @deprecated * */ public function get_visitors( Advanced_Ads_Ad $ad ) { } /** * Check if the ad can be displayed based on display limit. * Handle "Custom position" placements that have cache-busting disabled. * * @param bool $can_display Existing value. * @param obj $ad Advanced_Ads_Ad object * @param array $check_options * @return bool true if limit is not reached, false otherwise */ public function can_display_by_display_limit( $can_display, Advanced_Ads_Ad $ad, $check_options ) { if ( ! $can_display ) { return false; } if ( ! $this->collecting_js_items ) { return $can_display; } $output_options = $ad->options( 'output' ); if ( ! empty( $output_options['once_per_page'] ) ) { foreach ( $this->has_js_items as $item ) { if ( $item['type'] === 'ad' && absint( $item['id'] ) === $ad->id ) { return false; } } } return true; } /** * Check whether the module is enabled. * * @return bool. */ public static function is_enabled() { $options = Advanced_Ads_Pro::get_instance()->get_options(); return ! empty( $options['cache-busting']['enabled'] ); } /** * Check if placements of type other than `header` can be injected during `wp_head` action. */ private function can_inject_during_wp_head() { return class_exists( 'Advanced_Ads_Compatibility' ) && method_exists( 'Advanced_Ads_Compatibility', 'can_inject_during_wp_head' ) && Advanced_Ads_Compatibility::can_inject_during_wp_head(); } /** * Check if TCF privacy is active; only do this when cache-busting is turned off. * If yes, add a script to handle decoded ads due to TCF privacy settings. */ public function check_for_tcf_privacy() { $options = Advanced_Ads_Privacy::get_instance()->options(); if ( ! isset( $options['enabled'] ) || $options['consent-method'] !== 'iab_tcf_20' ) { return; } wp_enqueue_script( // we need the same handle as with cache-busting so tracking still works. 'advanced-ads-pro/cache_busting', AAP_BASE_URL . 'assets/js/privacy' . ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ) . '.js', [ ADVADS_SLUG . '-advanced-js', 'jquery'], AAP_VERSION, true ); } }