return $overriden_ad;
}
/**
* return group, prepared for js handler if the conditions are met
*
* @param string $overriden_group group content to override
* @param obj $adgroup Advanced_Ads_Group
* @param array/null $ordered_ad_ids ordered ids of the ads that belong to the group
* @param array $args argument passed to the 'get_ad_by_group' function
* @return string/false group content prepared for js handler if the conditions are met
*/
public function override_ad_select_by_group( $overriden_group, Advanced_Ads_Group $adgroup, $ordered_ad_ids, $args ) {
if ( ! $this->can_override_passive( $args ) ) {
return $overriden_group;
}
if ( $this->cache_busting_module_enabled ) {
// Cache busting 'auto'.
$overriden_group = $this->cache_busting_auto_for_group( $overriden_group, $adgroup, $ordered_ad_ids, $args );
}
if ( false === $overriden_group ) {
// The cache-busting module is disabled or the 'off' fallback has been aplied.
$overriden_group = $this->get_simple_js_group( $overriden_group, $adgroup, $ordered_ad_ids, $args );
}
return $overriden_group;
}
/**
* Return passive ad, prepared for js handler if the conditions are met.
*
* @param bool|string $overriden_ad Ad content to override.
* @param Advanced_Ads_Ad $ad Ad object.
* @param array $args Arguments passed to ads and groups from top level placements/ads/groups.
* @return bool|string
*/
public function cache_busting_auto_for_ad( $overriden_ad, Advanced_Ads_Ad $ad, $args ) {
//if it was requested by placement; if cache-busting option does not exists yet, or exist and = 'auto'
$cache_busting_auto = isset( $args['placement_type'] ) && ( ! isset( $args['cache-busting'] ) || $args['cache-busting'] === self::OPTION_AUTO );
$cache_busting_off = isset( $args['cache-busting'] ) && $args['cache-busting'] === self::OPTION_OFF;
$prev_is_placement = isset( $args['previous_method'] ) && $args['previous_method'] === 'placement' && isset( $args['previous_id'] );
$test_id = isset( $args['test_id'] ) ? $args['test_id'] : null;
$is_passive_all = ! empty( $this->options['passive_all'] );
if ( $cache_busting_auto && ! $this->is_passive_method_used() ) { // ajax method
// ad was requested by group `placement->group->ad` or `group->ad`
if ( isset( $args['previous_method'] ) && $args['previous_method'] === 'group' && isset( $args['previous_id'] ) ) {
return $ad;
}
$ad->args['cache-busting'] = self::OPTION_ON;
$ad->args['cache-busting-orig'] = self::OPTION_AUTO;
$overriden_ad = $this->get_overridden_ajax_ad( $ad, $args );
if ( false === $overriden_ad ) {
// static and not test
return $this->return_ad_with_cb_off( $overriden_ad, $ad, $args );
}
return $overriden_ad;
}
elseif ( ! $cache_busting_off && ( $cache_busting_auto || $is_passive_all ) ) { // passive method
// ad was requested by group `placement->group->ad` or `group->ad`
if ( isset( $args['previous_method'] ) && $args['previous_method'] === 'group' && isset( $args['previous_id'] ) ) {
return $ad;
}
$needs_backend = $this->ad_needs_backend_request( $ad );
// ad was requested by placement `placement->ad` or `ad`
// check if ad can be delivered without any cache-busting
if ( 'static' === $needs_backend && ! $is_passive_all && ! $test_id ) {
return $this->return_ad_with_cb_off( $overriden_ad, $ad, $args );
}
// check if ad cannot be delivered with passive cache-busting
if ( 'off' === $needs_backend || 'ajax' === $needs_backend ) {
$is_ajax_fallbback = 'ajax' === $needs_backend;
if ( isset( $args['output']['placement_id'] ) && ! $this->placement_can_display_not_passive( $args['output']['placement_id'] ) ) {
// prevent selection of this placement using JavaScript
if ( $test_id ){
Advanced_Ads_Pro_Placement_Tests::get_instance()->no_cb_fallbacks[] = $args['previous_id'];
}
return '';
}
if ( $is_ajax_fallbback && $cache_busting_auto ) {
$ad->args['cache-busting'] = self::OPTION_ON;
$ad->args['cache-busting-orig'] = self::OPTION_AUTO;
return $this->get_overridden_ajax_ad( $ad, $args );
}
// `No cache-busting` fallback
if ( $test_id ) {
if ( in_array( $args['previous_id'], Advanced_Ads_Pro_Placement_Tests::get_instance()->get_random_placements() ) ) {
Advanced_Ads_Pro_Placement_Tests::get_instance()->delivered_tests[ $args['previous_id'] ] = $test_id;
} else {
// prevent selection of this placement using JavaScript
Advanced_Ads_Pro_Placement_Tests::get_instance()->no_cb_fallbacks[] = $args['previous_id'];
return '';
}
}
return $this->return_ad_with_cb_off( $overriden_ad, $ad, $args );
}
if ( ! $ad->can_display( array( 'passive_cache_busting' => true ) ) ) {
if ( $test_id && array_key_exists( $args['previous_id'], Advanced_Ads_Pro_Placement_Tests::get_instance()->delivered_tests ) ) {
Advanced_Ads_Pro_Placement_Tests::get_instance()->delivered_tests[ $args['previous_id'] ] = $test_id;
}
return '';
}
// deliver ad using passive cache-busting
// add new info to the passive cache-busting array
$overriden_ad = $this->get_passive_overriden_ad( $ad, $args );
}
if ( $prev_is_placement && false === $overriden_ad && $test_id ) {
Advanced_Ads_Pro_Placement_Tests::get_instance()->delivered_tests[ $args['previous_id'] ] = $test_id ;
}
return $overriden_ad;
}
/**
* Return ad with cache-busting "off" when it is not needed.
*
* @param bool|string $overriden_ad Ad content to override.
* @param Advanced_Ads_Ad $ad Ad object.
* @param array $args Arguments passed to ads and groups from top level placements/ads/groups.
*
* @return bool|string
*/
private function return_ad_with_cb_off( $overriden_ad, Advanced_Ads_Ad $ad, $args ) {
$ad->args['cache-busting'] = self::OPTION_OFF;
$ad->args['cache-busting-orig'] = self::OPTION_AUTO;
$ad->args['global_output'] = true;
$ad->global_output = true;
if ( isset( $args['output']['placement_id'] ) ) {
if ( ! $this->placement_can_display_not_passive( $args['output']['placement_id'] ) ) {
return '';
}
$this->add_placement_to_current_ads( $args['output']['placement_id'] );
}
return $overriden_ad;
}
public function cache_busting_auto_for_group( $overriden_group, Advanced_Ads_Group $adgroup, $ordered_ad_ids, $args ) {
$prev_is_placement = isset( $args['previous_method'] ) && $args['previous_method'] === 'placement' && isset( $args['previous_id'] );
$cache_busting_auto = isset( $args['placement_type'] ) && ( ! isset( $args['cache-busting'] ) || $args['cache-busting'] === self::OPTION_AUTO );
$test_id = isset( $args['test_id'] ) ? $args['test_id'] : null;
$is_passive_all = ! empty( $this->options['passive_all'] );
$cache_busting_off = isset( $args['cache-busting'] ) && $args['cache-busting'] === self::OPTION_OFF;
if ( $cache_busting_auto && ! $this->is_passive_method_used() ) { // ajax method
$group_ads = $this->request_passive_ads_of_group( $adgroup, $ordered_ad_ids, $args );
if ( $test_id || ! $this->group_ads_static( $group_ads, $adgroup ) ) {
$adgroup->ad_args['cache-busting'] = self::OPTION_ON;
$adgroup->ad_args['cache-busting-orig'] = self::OPTION_AUTO;
$query = self::build_js_query( $args);
$overriden_group = $this->get_override_content( $query );
}
if ( false === $overriden_group ) {
// Static and does not belong to a test.
$adgroup->ad_args['cache-busting'] = self::OPTION_OFF;
$adgroup->ad_args['cache-busting-orig'] = self::OPTION_AUTO;
unset( $adgroup->ad_args['cache_busting_elementid'], $args['cache_busting_elementid'] );
$adgroup->ad_args['global_output'] = true;
if ( isset( $args['output']['placement_id'] ) ) {
if ( ! $this->placement_can_display_not_passive( $args['output']['placement_id'] ) ) { return ''; }
$this->add_placement_to_current_ads( $args['output']['placement_id'] );
}
}
return $overriden_group;
}
elseif ( ! $cache_busting_off && ( $cache_busting_auto || $is_passive_all ) ) { // passive method
if ( is_array( $ordered_ad_ids ) && count( $ordered_ad_ids ) > 0 ) {
// add info about the group to the passive cache-busting array
$uniq_key = ++self::$adOffset;
$group_ads = $this->request_passive_ads_of_group( $adgroup, $ordered_ad_ids, $args );
foreach ( $group_ads as $ad ) {
$needs_backend = $this->ad_needs_backend_request( $ad );
if ( 'off' === $needs_backend || 'ajax' === $needs_backend ) {
$is_ajax_fallbback = 'ajax' === $needs_backend;
// delete info from the passive cache-busting array
$this->delete_passive_group( $adgroup, $args, $uniq_key );
if ( isset( $args['output']['placement_id'] ) && ! $this->placement_can_display_not_passive( $args['output']['placement_id'] ) ) {
// prevent selection of this placement using JavaScript
if ( $test_id ){
Advanced_Ads_Pro_Placement_Tests::get_instance()->no_cb_fallbacks[] = $args['previous_id'];
}
return '';
}
if ( $is_ajax_fallbback && $cache_busting_auto ) {
$adgroup->ad_args['cache-busting'] = self::OPTION_ON;
$adgroup->ad_args['cache-busting-orig'] = self::OPTION_AUTO;
$query = self::build_js_query( $args);
return $this->get_override_content( $query );
} else {
// `No cache-busting` fallback
if ( $test_id ) {
if ( in_array( $args['previous_id'], Advanced_Ads_Pro_Placement_Tests::get_instance()->get_random_placements() ) ) {
Advanced_Ads_Pro_Placement_Tests::get_instance()->delivered_tests[ $args['previous_id'] ] = $test_id;
} else {
// prevent selection of this placement using JavaScript
Advanced_Ads_Pro_Placement_Tests::get_instance()->no_cb_fallbacks[] = $args['previous_id'];
return '';
}
}
$adgroup->ad_args['cache-busting'] = self::OPTION_OFF;
$adgroup->ad_args['cache-busting-orig'] = self::OPTION_AUTO;
unset( $adgroup->ad_args['cache_busting_elementid'], $args['cache_busting_elementid'] );
$adgroup->ad_args['global_output'] = true;
if ( isset( $args['output']['placement_id'] ) ) {
if ( ! $this->placement_can_display_not_passive( $args['output']['placement_id'] ) ) { return ''; }
$this->add_placement_to_current_ads( $args['output']['placement_id'] );
}
return $overriden_group;
}
}
}
if ( $this->group_ads_static( $group_ads, $adgroup ) && ! $is_passive_all && ! $test_id ) {
$adgroup->ad_args['cache-busting'] = self::OPTION_OFF;
$adgroup->ad_args['cache-busting-orig'] = self::OPTION_AUTO;
unset( $adgroup->ad_args['cache_busting_elementid'], $args['cache_busting_elementid'] );
$adgroup->ad_args['global_output'] = true;
if ( isset( $args['output']['placement_id'] ) ) {
if ( ! $this->placement_can_display_not_passive( $args['output']['placement_id'] ) ) { return ''; }
$this->add_placement_to_current_ads( $args['output']['placement_id'] );
}
return $overriden_group;
}
$output_string = $this->get_passive_overriden_group( $adgroup, $ordered_ad_ids, $args, $uniq_key, $group_ads );
$overriden_group = $output_string;
}
}
if ( $prev_is_placement && false === $overriden_group && $test_id ) {
Advanced_Ads_Pro_Placement_Tests::get_instance()->delivered_tests[ $args['previous_id'] ] = $test_id;
}
return $overriden_group;
}
/**
* Request passive ads of a group.
*
* @param obj $adgroup Advanced_Ads_Group
* @param array/null $ordered_ad_ids ordered ids of the ads that belong to the group
* @param array $args argument passed to the 'get_ad_by_group' function
*/
private function request_passive_ads_of_group( $adgroup, $ordered_ad_ids, $args ) {
$args['global_output'] = false;
$args['is_top_level'] = false;
$args['ad_label'] = 'disabled';
$args['group_info'] = array (
'passive_cb' => true,
'id' => $adgroup->id,
'name' => $adgroup->name,
'type' => $adgroup->type,
'refresh_enabled' => Advanced_Ads_Pro_Group_Refresh::is_enabled( $adgroup ),
);
$ordered_ad_ids = is_array( $ordered_ad_ids ) ? $ordered_ad_ids : array();
$group_ads = array();
foreach ( $ordered_ad_ids as $_ad_id ) {
// get result from the 'override_ad_select_by_ad' method
$ad = Advanced_Ads_Select::get_instance()->get_ad_by_method( $_ad_id, Advanced_Ads_Select::AD, $args );
// Ignore ads that are hidden for all users.
if ( ! $ad instanceof Advanced_Ads_Ad || ! $ad->can_display( array( 'passive_cache_busting' => true ) ) ) {
continue;
}
$group_ads[] = $ad;
}
return $group_ads;
}
/**
* Get simple js ad.
* Conditions are not checked for every visitor of a cached page.
*
* @param bool|string $overriden_ad Ad content to override.
* @param Advanced_Ads_Ad $ad Ad object.
* @param array $args Arguments passed to ads and groups from top level placements/ads/groups.
* @return string Ad content prepared for js handler if the conditions are met
*/
public function get_simple_js_ad( $overriden_ad, Advanced_Ads_Ad $ad, $args ) {
$cp_placement = isset( $args['placement_type'] ) && $args['placement_type'] === 'custom_position';
if (
! $cp_placement
// Check if collecting of simple ads has been started.
|| $this->collecting_js_items
|| Advanced_Ads_Pro::get_instance()->get_options()['placement-positioning'] === 'php'
) {
return $overriden_ad;
}
$this->collecting_js_items = true;
$elementid = $this->generate_elementid();
$args['cache_busting_elementid'] = $ad->args['cache_busting_elementid'] = $elementid;
$overriden_ad = '';
if ( $ad->can_display() ) {
// Disable global output because the ads will be tracked using an AJAX request.
$ad->args['global_output'] = false;
$ad->global_output = false;
$l = count( $this->has_js_items );
$overriden_ad = $this->add_simple_js_item( $elementid, $ad->output(), $l, $args );
$ad->args['global_output'] = true;
$ad->global_output = true;
}
$this->collecting_js_items = false;
return $overriden_ad;
}
/**
* Get simple js group.
* Conditions are not checked for every visitor of a cached page.
*
* @param bool|string $overriden_group Group content to override.
* @param Advanced_Ads_Group $adgroup Group object.
* @param int[] $ordered_ad_ids ids of the ads that belong to the group ordered by their injection order.
* @param array $args Arguments passed to ads and groups from top level placements/ads/groups.
* @return bool|string $overriden_group Overriden group content if conditions are met.
*/
public function get_simple_js_group( $overriden_group, Advanced_Ads_Group $adgroup, $ordered_ad_ids, $args ) {
$cp_placement = isset( $args['placement_type'] ) && $args['placement_type'] === 'custom_position';
if ( ! $cp_placement
// Check if collecting of simple ads has been started.
|| $this->collecting_js_items
|| Advanced_Ads_Pro::get_instance()->get_options()['placement-positioning'] === 'php'
) {
return $overriden_group;
}
$this->collecting_js_items = true;
$elementid = $this->generate_elementid();
$args['cache_busting_elementid'] = $adgroup->ad_args['cache_busting_elementid'] = $elementid;
// Disable global output because the ads will be tracked using an AJAX request.
$adgroup->ad_args['global_output'] = false;
$l = count( $this->has_js_items );
$overriden_group = $this->add_simple_js_item( $elementid, $adgroup->output( $ordered_ad_ids ), $l, $args );
$adgroup->ad_args['global_output'] = true;
$this->collecting_js_items = false;
return $overriden_group;
}
/**
* Add simple js item.
*
* @param string $elementid Wrapper id.
* @param string $output Ad/Group output.
* @param int $l Number of existing simple js items.
* @param array $args Placement options.
* @return string Wrapper id.
*/
function add_simple_js_item( $elementid, $output, $l, $args ) {
if ( isset( $args['output']["placement_id"] ) ) {
$placements = Advanced_Ads::get_ad_placements_array();
if ( isset( $placements[ $args['output']["placement_id"] ] ) )
$placement = $placements[ $args['output']["placement_id"] ];
$this->has_js_items[] = array(
'id' => $args['output']["placement_id"],
'type' => 'placement',
'title' => ! empty( $placement['name'] ) ? $placement['name'] : '',
'blog_id' => get_current_blog_id()
);
}
$js_item = array(
'output' => $output,
'elementid' => $elementid,
'args' => $args,
'has_js_items' => array_slice( $this->has_js_items, $l ),
);
$js_item = apply_filters(
'advanced-ads-cache-busting-item',
$js_item,
array(
'method' => 'placement',
'args' => $args
)
);
$this->js_items[] = $js_item;
/**
* 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();
}
$placement_id = ! empty( $args['output']['placement_id'] ) ? $args['output']['placement_id'] : '';
return $this->create_wrapper( $elementid, $placement_id, $args );
}
/**
* add data related to ad and ad placement to js array
*
* @param obj $ad Advanced_Ads_Ad
* @param array $args argument passed to the 'get_ad_by_id' function
* @return string
*/
private function get_passive_overriden_ad( Advanced_Ads_Ad $ad, $args ) {
$cache_busting_auto = isset( $args['placement_type'] ) && ( ! isset( $args['cache-busting'] ) || $args['cache-busting'] === self::OPTION_AUTO );
if ( $cache_busting_auto ) {
$js_array = & $this->passive_cache_busting_placements;
$id = $args['previous_id'];
} else {
$js_array = & $this->passive_cache_busting_ads;
$id = $args['id'];
}
$uniq_key = $id . '_' . ++self::$adOffset;
$not_head = ! $this->isHead || ( isset( $args['placement_type'] ) && $args['placement_type'] !== 'header' );
$elementid = $not_head ? $this->generate_elementid() : null;
$args['cache_busting_elementid'] = $ad->args['cache_busting_elementid'] = $elementid;
$placement_id = ! empty( $args['output']['placement_id'] ) ? $args['output']['placement_id'] : '';
$output_string = $not_head ? $this->create_wrapper( $elementid, $placement_id, $args ) : '';
$js_array[ $uniq_key ] = array(
'elementid' => array( $elementid ),
'ads' => array( $ad->id => $this->get_passive_cb_for_ad( $ad ) ), // only 1 ad
);
if ( $cache_busting_auto ) {
$placements = Advanced_Ads::get_ad_placements_array();
$test_id = isset( $args['test_id'] ) ? $args['test_id'] : null;
$js_array[ $uniq_key ]['type'] = 'ad';
$js_array[ $uniq_key ]['id'] = $ad->id;
$js_array[ $uniq_key ]['placement_info'] = $this->get_placement_info( $id );
$js_array[ $uniq_key ]['test_id'] = $test_id;
if ( $ad_for_adblocker = Advanced_Ads_Pro_Module_Ads_For_Adblockers::get_ad_for_adblocker( $args ) ) {
$js_array[ $uniq_key ]['ads_for_ab'] = array( $ad_for_adblocker->id => $this->get_passive_cb_for_ad( $ad_for_adblocker ) );
}
if ( 'ajax' === $this->fallback_method ) {
$ajax_info = $this->server_info->get_ajax_for_passive_placement( $ad, $args, $elementid );
if ( $ajax_info ) {
$js_array[ $uniq_key ] = array_merge( $js_array[ $uniq_key ], $ajax_info );
}
}
}
$js_array[ $uniq_key ] = apply_filters(
'advanced-ads-cache-busting-item',
$js_array[ $uniq_key ],
array(
'method' => $cache_busting_auto ? 'placement' : 'ad',
'args' => $args
)
);
return $output_string;
}
/**
* add data related to group and group placement to js array
*
* @param obj $adgroup Advanced_Ads_Group
* @param array/null $ordered_ad_ids ordered ids of the ads that belong to the group
* @param array $args argument passed to the 'get_ad_by_group' function
* @param str $uniq_key Property name in JS array.
* @param array $group_ads Group ads.
* @return string
*/
private function get_passive_overriden_group( Advanced_Ads_Group $adgroup, $ordered_ad_ids, $args, $uniq_key, $group_ads ) {
$cache_busting_auto = isset( $args['placement_type'] ) && ( ! isset( $args['cache-busting'] ) || $args['cache-busting'] === self::OPTION_AUTO );
if ( $cache_busting_auto ) {
$js_array = & $this->passive_cache_busting_placements;
$id = $args['previous_id'];
} else {
$js_array = & $this->passive_cache_busting_groups;
$id = $args['id'];
}
$uniq_key = $id . '_' . $uniq_key;
$not_head = ! $this->isHead || ( isset( $args['placement_type'] ) && $args['placement_type'] !== 'header' );
$elementid = $not_head ? $this->generate_elementid() : null;
$args['cache_busting_elementid'] = $adgroup->ad_args['cache_busting_elementid'] = $elementid;
$placement_id = ! empty( $args['output']['placement_id'] ) ? $args['output']['placement_id'] : '';
$output_string = $not_head ? $this->create_wrapper( $elementid, $placement_id, $args ) : '';
if ( ( $ad_count = apply_filters( 'advanced-ads-group-ad-count', $adgroup->ad_count, $adgroup ) ) === 'all' ) {
$ad_count = 999;
}
$passive_ads = array();
foreach ( $group_ads as $group_ad ) {
$passive_ads[ $group_ad->id ] = $this->get_passive_cb_for_ad( $group_ad );
}
$js_array[ $uniq_key ] = array (
'type'=> 'group',
'id' => $adgroup->id,
'elementid' => array( $elementid ),
'ads' => $passive_ads,
'group_info' => array(
'id' => $adgroup->id,
'name' => $adgroup->name,
'weights' => $adgroup->get_ad_weights( $ordered_ad_ids ),
'type' => $adgroup->type,
'ordered_ad_ids' => $ordered_ad_ids,
'ad_count' => $ad_count,
),
);
// deprecated after Advaned Ads Slider > 1.3.1
if ( 'slider' === $adgroup->type && defined( 'AAS_VERSION' ) && version_compare( AAS_VERSION, '1.3.1', '<=' ) ) {
$slider_options = Advanced_Ads_Slider::get_slider_options( $adgroup );
$js_array[ $uniq_key ]['group_info']['slider_options'] = $slider_options;
}
if ( Advanced_Ads_Pro_Group_Refresh::is_enabled( $adgroup ) ) {
$js_array[ $uniq_key ]['group_info']['refresh_enabled'] = true;
$js_array[ $uniq_key ]['group_info']['refresh_interval_for_ads'] = Advanced_Ads_Pro_Group_Refresh::get_ad_intervals( $adgroup );
}
$advads_plugin = Advanced_Ads::get_instance();
$label = '';
if ( method_exists( $advads_plugin, 'get_label' ) ) {
$placement_state = isset( $args['ad_label'] ) ? $args['ad_label'] : 'default';
$label = Advanced_Ads::get_instance()->get_label( $placement_state );
}
if ( $cache_busting_auto ) {
$placements = Advanced_Ads::get_ad_placements_array();
$js_array[ $uniq_key ]['placement_info'] = $this->get_placement_info( $id );
$js_array[ $uniq_key ]['test_id'] = isset( $args['test_id'] ) ? $args['test_id'] : null;
if ( $ad_for_adblocker = Advanced_Ads_Pro_Module_Ads_For_Adblockers::get_ad_for_adblocker(
array_diff_key( $args, array( 'ad_label' => false ) )
) ) {
$js_array[ $uniq_key ]['ads_for_ab'] = array( $ad_for_adblocker->id => $this->get_passive_cb_for_ad( $ad_for_adblocker ) );
}
if ( 'ajax' === $this->fallback_method ) {
$ajax_info = $this->server_info->get_ajax_for_passive_placement( $group_ads, $args, $elementid );
if ( $ajax_info ) {
$js_array[ $uniq_key ] = array_merge( $js_array[ $uniq_key ], $ajax_info );
}
}
}
$js_array[ $uniq_key ] = apply_filters( 'advanced-ads-pro-passive-cb-group-data', $js_array[ $uniq_key ], $adgroup, $elementid );
// Add wrapper around group.
if ( ( ! empty( $adgroup->wrapper ) || $label )
&& is_array( $adgroup->wrapper )
&& class_exists( 'Advanced_Ads_Utils' ) && method_exists( 'Advanced_Ads_Utils' , 'build_html_attributes' )
) {
$before = '
wrapper ) . '>'
. $label
. apply_filters( 'advanced-ads-output-wrapper-before-content-group', '', $adgroup );
$after = apply_filters( 'advanced-ads-output-wrapper-after-content-group', '', $adgroup )
. '
';
if ( ! empty( $adgroup->ad_args['placement_clearfix'] ) ) {
$after .= '
';
}
$js_array[ $uniq_key ]['group_wrap'][] = array(
'before' => $before,
'after' => $after,
);
}
$js_array[ $uniq_key ] = apply_filters(
'advanced-ads-cache-busting-item',
$js_array[ $uniq_key ],
array(
'method' => $cache_busting_auto ? 'placement' : 'group',
'args' => $args
)
);
return $output_string;
}
/**
* Get placement information
*
* @param string $id Placement id.
* @param array $placement_info Placement information.
*/
private function get_placement_info( $id ) {
// The information which passive cache-busting (`base.js`) can read.
// When a new placement option is added and passive cache-busting needs to access it, it should be added to the array.
$allowed_keys = array( 'id', 'lazy_load', 'test_id', 'layer_placement', 'close', 'inject_by', 'placement_position', 'pro_custom_element', 'container_id' );
$placements = Advanced_Ads::get_ad_placements_array();
$placement_info = $placements[ $id ];
$placement_info['id'] = (string) $id;
if ( ! empty( $placement_info['options'] ) && is_array( $placement_info['options'] ) ) {
foreach ( $placement_info['options'] as $k => $option ) {
if ( ! in_array( $k, $allowed_keys, true ) ) {
unset( $placement_info['options'][ $k ] );
}
}
}
return $placement_info;
}
/**
* add new passive ad to passive cb js array
*
* @param obj $ad Advanced_Ads_Ad
* @param array $args argument passed to the 'get_ad_by_id' function
* @param str $uniq_key Property name in JS array.
*/
private function add_passive_ad_to_group( Advanced_Ads_Ad $ad, $args, $uniq_key ) {
$cache_busting_auto = isset( $args['placement_type'] ) && ( ! isset( $args['cache-busting'] ) || $args['cache-busting'] === self::OPTION_AUTO );
if ( $cache_busting_auto ) {
$uniq_key = $args['previous_id'] . '_' . $uniq_key;
$this->passive_cache_busting_placements[ $uniq_key ]['ads'][ $ad->id ] = $this->get_passive_cb_for_ad( $ad );
} else {
$uniq_key = $args['id'] . '_' . $uniq_key;
$this->passive_cache_busting_groups[ $uniq_key ]['ads'][ $ad->id ] = $this->get_passive_cb_for_ad( $ad );
}
}
/**
* delete an ad from passive cb js array
*
* @param $adgroup Advanced_Ads_Group
* @param array $args argument passed to the 'get_ad_by_id' function
* @param str $uniq_key Property name in JS array.
*/
private function delete_passive_group( Advanced_Ads_Group $adgroup, $args, $uniq_key ) {
$cache_busting_auto = isset( $args['placement_type'] ) && ( ! isset( $args['cache-busting'] ) || $args['cache-busting'] === self::OPTION_AUTO );
if ( $cache_busting_auto ) {
$uniq_key = $args['previous_id'] . '_' . $uniq_key;
unset( $this->passive_cache_busting_placements[ $uniq_key ] );
} else {
$uniq_key = $args['id'] . '_' . $uniq_key;
unset( $this->passive_cache_busting_groups[ $uniq_key ] );
}
}
/**
* get ad info for passive cache-busting
*
* @param Advanced_Ads_Ad $ad ad object.
* @return array
*/
public function get_passive_cb_for_ad( Advanced_Ads_Ad $ad ) {
$ad_options = $ad->options();
$ad->args['cache-busting'] = self::OPTION_AUTO;
$passive_cb_for_ad = apply_filters( 'advanced-ads-pro-passive-cb-for-ad', array(
'id' => $ad->id,
'title' => $ad->title,
'expiry_date' => (int) $ad->expiry_date,
'visitors' => array_values( $ad->options( 'visitors', array() ) ),
'content' => $ad->output( array( 'global_output' => false ) ),
'once_per_page' => ( ! empty( $ad_options['output']['once_per_page'] ) ) ? 1 : 0,
'debugmode' => isset( $ad->output['debugmode'] ),
'blog_id' => get_current_blog_id(),
'type' => $ad->type,
'position' => isset( $ad->output['position'] ) ? $ad->output['position'] : '',
), $ad );
// Consent overridden for this ad.
$passive_cb_for_ad['privacy']['ignore'] = ! empty( $ad_options['privacy']['ignore-consent'] );
// This ad has custom code and therefore needs consent (if not overridden above).
$passive_cb_for_ad['privacy']['needs_consent'] = ! empty( Advanced_Ads_Pro::get_instance()->get_custom_code( $ad ) );
/**
* 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();
}
return $passive_cb_for_ad;
}
/**
* return wrapper and js code to load the ad
*
* @param obj $ad Advanced_Ads_Ad
* @param array $args argument passed to the 'get_ad_by_id' function
* @return string/bool $overridden_ad
*/
public function get_overridden_ajax_ad( $ad, $args ) {
$overridden_ad = false;
$test_id = isset( $args['test_id'] ) ? $args['test_id'] : null;
$needs_backend = $this->ad_needs_backend_request( $ad );
if ( 'static' !== $needs_backend || $test_id ) {
$query = self::build_js_query( $args);
$overridden_ad = $this->get_override_content( $query );
}
return $overridden_ad;
}
/**
* Determine if backend request is needed.
*
* @param Advanced_Ads_Ad $ad Ad object.
* @return string
* 'static' Do not use cache-busting. There are no dynamic conditions, all users will see the same.
* 'off' Do not use cache-busting (fallback).
* 'ajax' Use AJAX request (fallback).
* 'passive' Use passive cache-busting.
*/
public function ad_needs_backend_request( Advanced_Ads_Ad $ad ) {
$ad_options = $ad->options();
$visitors = $ad->options( 'visitors', array() );
// code is evaluated as php if setting was never saved or php is allowed
$allow_php = ( 'plain' === $ad->type && ( ! isset( $ad->output['allow_php'] ) || $ad->output['allow_php'] ) );
// if there is at least one visitor condition (check old "visitor" and new "visitors" conditions)
$is_visitor_conditions = ! empty( $visitors );
$is_group = 'group' === $ad->type;
$has_shortcode = ! empty( $ad_options['output']['has_shortcode'] )
// The Rich Content ad type saved long time ago.
|| ( ! isset( $ad_options['output']['has_shortcode'] ) && $ad->type === 'content' );
$is_lazy_load = $this->lazy_load_module_enabled && isset( $ad_options['lazy_load'] ) && 'enabled' === $ad_options['lazy_load'];
// Check if there is conditions that need backend request.
$has_not_js_conditions = false;
if ( ! empty( $visitors ) ) {
// Conditions that can be checked using js.
$js_visitor_conditions = array(
'mobile',
'referrer_url',
'user_agent',
'request_uri',
'browser_lang',
'cookie',
'page_impressions',
'ad_impressions',
'new_visitor',
'device_width',
'tablet',
);
if ( $this->fallback_method === 'ajax'
&& isset( $ad_options['placement_type'] )
) {
// Conditions that can be checked by passive cache-busting only if cookies exist.
// If not, ajax cache-busting will not be used.
$all_server_conditions = $this->server_info->get_all_server_conditions();
$js_visitor_conditions = array_merge( $js_visitor_conditions, array_keys( $all_server_conditions ) );
}
$js_visitor_conditions = apply_filters( 'advanced-ads-js-visitor-conditions', $js_visitor_conditions );
foreach ( $visitors as $visitor ) {
if ( ! in_array( $visitor['type'], $js_visitor_conditions ) ) {
// Use AJAX cache-busting, or disable cache-busting.
$has_not_js_conditions = true;
}
}
}
$has_tracking = false;
if ( class_exists( 'Advanced_Ads_Tracking', false ) &&
( ( isset( $ad_options['tracking']['impression_limit'] ) && $ad_options['tracking']['impression_limit'] ) ||
( isset( $ad_options['tracking']['click_limit'] ) && $ad_options['tracking']['click_limit'] ) )
) {
// Use AJAX cache-busting, or disable cache-busting.
$has_tracking = true;
}
$has_test = ! empty( $ad_options['test_id'] );
$hidden_without_consent = false;
if ( empty( $ad_options['privacy']['ignore-consent'] )
&& class_exists( 'Advanced_Ads_Privacy' ) ) {
$privacy_options = Advanced_Ads_Privacy::get_instance()->options();
$npa_adsense = $ad->type === 'adsense' && ! empty( $privacy_options['show-non-personalized-adsense'] );
// Check if the ad is invisible until consent is given.
if ( ! empty( $privacy_options['enabled'] )
// If the content method is 'cookie'.
&& ! empty( $privacy_options['consent-method'] ) // && empty( $privacy_options['show-without-consent'] )
// Non-personalized Adsense are visible even without consent.
&& ! $npa_adsense
) {
$hidden_without_consent = true;
}
}
$specific_days = ! empty( $ad_options['weekdays']['enabled'] );
$checks_placement_cookies = ( ! empty( $ad_options['layer_placement']['close']['enabled'] )
&& ! empty( $ad_options['layer_placement']['close']['timeout_enabled'] ) )
|| ( ! empty( $ad_options['close']['enabled'] )
&& ! empty( $ad_options['close']['timeout_enabled'] ) );
if ( $allow_php || $is_group || $has_shortcode || $has_not_js_conditions || $has_tracking ) {
// Use AJAX cache-busting, or disable cache-busting.
$return = $this->fallback_method;
} elseif ( $is_visitor_conditions || $is_lazy_load || $hidden_without_consent || $specific_days || $checks_placement_cookies ) {
// Passive cache-busting.
$return = 'passive';
} else {
$return = 'static';
}
$return = apply_filters( 'advanced-ads-pro-ad-needs-backend-request', $return, $ad, $this->fallback_method );
return $return;
}
/**
* Determine if all ads of a group are static.
*
* @param Advanced_Ads_Ad[] $group_ads An array of ad objects.
* @param Advanced_Ads_Group $adgroup Group object.
* @return bool
*/
private function group_ads_static( $group_ads, $adgroup ) {
if ( 0 === count( $group_ads ) ) {
return true;
}
if ( 1 === count( $group_ads ) ) {
return 'static' === $this->ad_needs_backend_request( $group_ads[0] );
}
return false;
}
/**
* Prepare query for js handler
*
* @param array $arguments
* @return array query
*/
public static function build_js_query( $arguments ) {
// base query (required keys)
$query = array(
'id' => (string) $arguments['id'],
'method' => (string) $arguments['method'],
);
$arguments['global_output'] = true;
// process further arguments (optional keys)
$params = array_diff_key( $arguments, array( 'id' => false, 'method' => false ) );
if ( $params !== array() ) {
$query['params'] = $params;
}
return $query;
}
/**
* Determine override option for query.
*
* @param array $query
*
* @return boolean
*/
protected function can_override( $query ) {
$params = isset( $query['params'] ) ? $query['params'] : array();
// allow disable cache-busting according to placement settings
if ( $query['method'] === 'placement' && ! isset( $params['cache-busting'] ) ) {
$placement_options = Advanced_Ads::get_ad_placements_array();
if ( isset( $placement_options[ $query['id'] ]['options']['cache-busting'] ) ) {
$params['cache-busting'] = $placement_options[ $query['id'] ]['options']['cache-busting'];
}
}
return isset( $params['cache-busting'] ) && $params['cache-busting'] === self::OPTION_ON;
}
/**
* Check if passive 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 . '">' . $wrapper_element . '>';
}
/**
* 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
);
}
}