get_options(); if ( empty( $options['cache-busting']['enabled'] ) ) { return; } $this->is_ajax = wp_doing_ajax(); if ( $this->is_ajax ) { $this->init_group_refresh(); } } /** * Init group refresh. */ private function init_group_refresh() { add_filter( 'advanced-ads-ad-select-args', [ $this, 'additional_ad_select_args' ], 10, 3 ); add_filter( 'advanced-ads-group-output-ad-ids', [ $this, 'group_output_ad_ids' ], 10, 5 ); add_filter( 'advanced-ads-group-output', [ $this, 'group_output' ], 10, 2 ); add_filter( 'advanced-ads-can-display-ad', [ $this, 'can_display' ], 10, 2 ); add_action( 'advanced-ads-ad-output', [ $this, 'ad_output' ], 10, 2 ); add_action( 'advanced-ads-group-before-output', [ $this, 'before_group_output' ] ); } /** * Add group information to the ad_args prop * * @param Group $group the group. * * @return null output process is interrupted if anything beside `null` is returned. */ public function before_group_output( $group ) { $args = $group->get_prop( 'ad_args' ); if ( ! empty( $args['group_info'] ) ) { return null; } $args['group_info'] = [ 'id' => $group->get_id(), 'name' => $group->get_title(), 'type' => $group->get_type(), 'refresh_enabled' => (bool) $group->get_prop( 'options.refresh.enabled' ), 'ads_displayed' => (int) $group->get_ad_count(), ]; $group->set_prop_temp( 'ad_args', $args ); return null; } /** * Update state with info about the currently displayed ads for the next AJAX call * * @param string $output Output string. * @param Ad $ad Ad instance. * * @return string $output */ public function ad_output( string $output, Ad $ad ) { // phpcs:ignore $element_id = $ad->get_prop( 'ad_args.cache_busting_elementid' ); $group_id = $ad->get_prop( 'ad_args.group_info.id' ); if ( empty( $element_id ) || empty( $group_id ) ) { return $output; } $el_group_id = $element_id . '_' . $group_id; if ( empty( $this->state_groups[ $el_group_id ] ) ) { return $output; } // Save current ad id so that this ad will not be added to next AJAX response. $this->state_groups[ $el_group_id ]['prev_ad_id'] = $ad->get_id(); // Do not track the same ad twice. $this->state_groups[ $el_group_id ]['shown_ad_ids'][ $ad->get_id() ] = true; // Allow to show only 1 ad. $this->state_groups[ $el_group_id ]['limit_exceeded'] = 1; // Get the position of the placement or the ad. if ( ! isset( $this->state_groups[ $el_group_id ]['position'] ) ) { if ( ! empty( $ad->get_prop( 'placement_position' ) ) ) { $this->state_groups[ $el_group_id ]['position'] = $ad->get_prop( 'placement_position' ); } elseif ( ! empty( $options['output']['position'] ) ) { $this->state_groups[ $el_group_id ]['position'] = $options['output']['position']; } } if ( ! wp_doing_ajax() ) { return $output; } $group = $ad->get_parent(); $displayable_ads = []; foreach ( $group->get_ads() as $ad ) { if ( $ad->can_display() && 0.0 !== floatval( $ad->get_prop( 'weight' ) ) ) { $displayable_ads[] = $ad->get_id(); } } if ( count( $displayable_ads ) === count( (array) $this->state_groups[ $el_group_id ]['shown_ad_ids'] ) ) { $this->all_ads_shown[] = $group->get_id(); } return $output; } /** * Save JS query that loads group using AJAX cache-busting. * * @param array $args Arguments. * @param string $method Method. * @param int $id ID. * * @return array $args */ public function additional_ad_select_args( $args, $method, $id ) { if ( ! isset( $args['cache_busting_elementid'] ) || empty( $args['group_refresh'] ) ) { return $args; } if ( // Allow to track each ad of a group with refresh interval enabled only once. ( Constants::ENTITY_AD === $method && ! empty( $args['group_refresh']['shown_ad_ids'][ $id ] ) && isset( $args['group_info']['id'] ) && isset( $args['group_refresh']['group_id'] ) && absint( $args['group_info']['id'] ) === absint( $args['group_refresh']['group_id'] ) ) // Display the same group only once in the "Ads" menu of Admin Bar. || ( Constants::ENTITY_GROUP === $method && ! empty( $args['group_refresh']['shown_group_ids'][ $id ] ) ) ) { $args['global_output'] = false; return $args; } $args['global_output'] = true; return $args; } /** * Change ordered ids of ads that belong to the group. * * @param array $ordered_ad_ids Array of ad ids. * @param string $type Type of the group. * @param array $ads Array of Ad objects. * @param array $weights Array of weights. * @param Group $group Group instance. * * @return array $ordered_ad_ids */ public function group_output_ad_ids( $ordered_ad_ids, $type, $ads, $weights, Group $group ) { if ( ! is_array( $ordered_ad_ids ) || count( $ordered_ad_ids ) < 2 ) { return $ordered_ad_ids; } if ( ! self::is_enabled( $group ) ) { return $ordered_ad_ids; } if ( ! $group->get_prop( 'ad_args.cache_busting_elementid' ) ) { return $ordered_ad_ids; } // TODO: check nested group. $el_group_id = $group->get_prop( 'ad_args.cache_busting_elementid' ) . '_' . $group->get_id(); $this->state_groups[ $el_group_id ]['shown_ad_ids'] = $group->get_prop( 'ad_args.group_refresh.shown_ad_ids' ) ?? []; $this->state_groups[ $el_group_id ]['ad_label'] = $group->get_prop( 'ad_args.ad_label' ) ?? 'default'; $prev_ad_id = absint( $group->get_prop( 'ad_args.group_refresh.prev_ad_id' ) ); if ( empty( $prev_ad_id ) ) { // Show the first ad. return $ordered_ad_ids; } // Do not show previously visible ad. switch ( $type ) { case 'ordered': // At this point ads with the same weight will not be shuffled anymore. // We support the order that was formed before the first ad was shown. arsort( $weights ); $ordered_ad_ids = array_keys( $weights ); $pos = array_search( $prev_ad_id, $ordered_ad_ids, true ); if ( false === $pos ) { return $ordered_ad_ids; } $start = array_slice( $ordered_ad_ids, 0, $pos ); $end = array_slice( $ordered_ad_ids, $pos + 1 ); $ordered_ad_ids = array_merge( $end, $start ); break; default: $pos = array_search( $prev_ad_id, $ordered_ad_ids, true ); if ( false === ( $pos ) ) { return $ordered_ad_ids; } unset( $ordered_ad_ids[ $pos ] ); } return $ordered_ad_ids; } /** * Add JS code that reloads the group using AJAX request. * * @param string $output_string Output string. * @param Group $group Group instance. */ public function group_output( $output_string, Group $group ) { if ( empty( $output_string ) ) { return $output_string; } $element_id = $group->get_prop( 'ad_args.cache_busting_elementid' ); if ( ! $element_id ) { return $output_string; } if ( ! empty( $group->get_prop( 'options.refresh' ) ) ) { $this->shown_group_ids[ $group->get_id() ] = true; } if ( ! self::is_enabled( $group ) ) { return $output_string; } $this->shown_group_ids[ $group->get_id() ] = true; $el_group_id = $element_id . '_' . $group->get_id(); if ( ! isset( $this->state_groups[ $el_group_id ] ) ) { return $output_string; } if ( ! isset( $this->state_groups[ $el_group_id ]['query']['id'] ) ) { $is_first_impression = $this->is_first_impression( $element_id ); if ( $is_first_impression ) { static $count = 0; $element_id .= '-' . ( ++$count ) . '-group-refresh'; } $prev_ad_id = ! empty( $this->state_groups[ $el_group_id ]['prev_ad_id'] ) ? $this->state_groups[ $el_group_id ]['prev_ad_id'] : ''; $shown_ad_ids = ! empty( $this->state_groups[ $el_group_id ]['shown_ad_ids'] ) ? $this->state_groups[ $el_group_id ]['shown_ad_ids'] : []; $shown_group_ids = ! empty( $this->state_groups[ $el_group_id ]['shown_group_ids'] ) ? array_merge( $this->state_groups[ $el_group_id ]['shown_group_ids'], $this->shown_group_ids ) : $this->shown_group_ids; $this->shown_group_ids = []; $query = Advanced_Ads_Pro_Module_Cache_Busting::build_js_query( $group->get_prop( 'ad_args' ) ); $query = Advanced_Ads_Pro_Module_Cache_Busting::get_instance()->get_ajax_query( $query, false ); $query['elementid'] = $element_id; $query['params']['group_refresh']['prev_ad_id'] = $prev_ad_id; $query['params']['group_refresh']['shown_ad_ids'] = $shown_ad_ids; $query['params']['group_refresh']['shown_group_ids'] = $shown_group_ids; $query['params']['group_refresh']['group_id'] = $group->get_id(); if ( $group->get_prop( 'ad_args.group_refresh.is_top_level' ) ) { $is_top_level = $group->get_prop( 'ad_args.group_refresh.is_top_level' ); } else { $is_top_level = $is_first_impression && ! empty( $group->get_prop( 'ad_args.is_top_level' ) ); } $query['params']['group_refresh']['is_top_level'] = $is_top_level; // If it is top level, make it top level again for the next request. // This allows to deprecate `group_refresh > is_top_level` key from above in the future. if ( ! empty( $group->get_prop( 'ad_args.is_top_level' ) ) ) { unset( $query['params']['is_top_level'] ); } if ( isset( $this->state_groups[ $el_group_id ]['ad_label'] ) ) { $query['params']['ad_label'] = $this->state_groups[ $el_group_id ]['ad_label']; } $this->state_groups[ $el_group_id ]['query'] = $query; // If the first ad was shown, do not use Lazy Load anymore. unset( $query['params']['lazy_load'] ); $position = ! empty( $this->state_groups[ $el_group_id ]['position'] ) ? $this->state_groups[ $el_group_id ]['position'] : false; $intervals = self::get_ad_intervals( $group ); $interval = ! empty( $prev_ad_id ) ? $intervals[ $prev_ad_id ] : $group->get_prop( 'options.refresh.interval' ); $js = ''; if ( $is_first_impression ) { $style = in_array( $position, [ 'left', 'right' ], true ) ? 'float:' . $position . ';' : ''; // Create wrapper around group. The following AJAX requests will insert group content into this wrapper. $output_string = $js . '