group_id = $group_id; $this->wo_meta = new WOMeta( AdCommander::ns() ); } /** * Get and return the term meta. * * @return array */ private function term_meta() { if ( ! $this->term_meta ) { $this->term_meta = $this->wo_meta->get_term_meta( $this->group_id, GroupTermMeta::tax_group_meta_keys() ); } return $this->term_meta; } /** * The display modes available for groups. * * @return array */ public static function modes() { return array( 'single' => __( 'Single', 'ad-commander' ), 'rotate' => __( 'Rotate', 'ad-commander' ), 'grid' => __( 'Grid', 'ad-commander' ), ); } /** * The mode of this current group. Load meta if it hasn't been loaded. * * @return string */ private function mode() { if ( $this->mode ) { return $this->mode; } $mode = $this->wo_meta->get_value( $this->term_meta(), 'mode', 'single' ); if ( in_array( $mode, ProBridge::instance()->pro_group_modes() ) && ! ProBridge::instance()->is_pro_loaded() ) { $mode = 'single'; } $this->mode = $mode; return $this->mode; } /** * Potentially block a bot from seeing the group. * * @return bool */ private function should_block_bot() { if ( ! Options::instance()->get( 'bots_disable_ads', 'general', true ) ) { return false; } return Bots::is_bot(); } /** * Check if ads are disabled in global settings. * * @return bool */ private function ads_disabled() { if ( Targeting::ads_disabled_all() ) { return true; } if ( TargetingVisitor::ads_disabled_user_role() ) { return true; } return false; } /** * Load the ads in this group into an array. * Optionally process that array into another format. * * @param bool $process Whether to process the array of posts or not. * * @return void */ public function load_ads( $process = true ) { $this->ad_posts = Query::ads_by_group( $this->group_id ); if ( ! empty( $this->ad_posts ) && $process ) { $this->process_ad_posts(); } } /** * Count the number of ads in this group. * * @return int */ private function group_count() { $count = get_transient( GroupTermMeta::group_count_transient( $this->group_id ) ); if ( $count === false ) { if ( ! $this->ad_posts ) { $this->load_ads( false ); } $count = count( $this->ad_posts ); set_transient( GroupTermMeta::group_count_transient( $this->group_id ), $count, HOUR_IN_SECONDS ); } return $count; } /** * Get the group order method. * * @return string */ private function get_order_method() { return $this->wo_meta->get_value( $this->term_meta(), 'order_method', 'random' ); } /** * Do we need consent to display? * * @return bool */ private function needs_consent() { if ( $this->needs_consent === null ) { if ( $this->wo_meta->get_value( $this->term_meta(), 'disable_consent', false ) ) { $this->needs_consent = false; return $this->needs_consent; } $global_needs_consent = Consent::instance()->global_needs_consent( $this->force_no_consent_check ); $this->needs_consent = $global_needs_consent; } return $this->needs_consent; } /** * Determine if ads in this group should be randomized or not. * * @return bool */ private function should_auto_sort() { if ( $this->group_count() < 2 ) { return false; } $order_method = $this->get_order_method(); return $order_method !== 'manual'; } /** * Determine if we should use ajax loading for this group. * * @return bool */ public function should_ajax() { if ( ! ProBridge::instance()->is_pro_loaded() || Amp::instance()->is_amp() ) { return false; } $render = Util::render_method(); if ( $render === 'serverside' ) { return false; } if ( $render === 'clientside' ) { return true; } /** * 'Smart' render method. */ /** * Use consent * Always use client-side if there's a consent cookie, because page caching would cause false positives. */ if ( Consent::instance()->requires_consent() ) { return true; } /** * TODO: Do we need to check visitor conditions here? * This is how it was done in placements. Not sure why it wasn't originally checked in groups. * * In the future, could check only certain visitor conditions. */ $visitor_conditions = $this->wo_meta->get_value( $this->term_meta(), 'visitor_conditions', false ); return ( ( $visitor_conditions && ! empty( $visitor_conditions ) ) || $this->should_auto_sort() ); } /** * Sort ads in this group by their manually selected order. * * @param mixed $ad_posts An array of ads. * @param mixed $sorted_ids An array of ad IDs in the properly sorted order. * * @return array */ public static function sort_ads_by_sorted_ids( $ad_posts, $sorted_ids ) { $sorted_ad_posts = array(); while ( ! empty( $sorted_ids ) ) { $sorted_id = array_shift( $sorted_ids ); foreach ( $ad_posts as $key => $ad_post ) { if ( $ad_post->ID === $sorted_id ) { $sorted_ad_posts[] = $ad_post; unset( $ad_posts[ $key ] ); break; } } } if ( ! empty( $ad_posts ) ) { foreach ( $ad_posts as $ad_post ) { $sorted_ad_posts[] = $ad_post; } } return $sorted_ad_posts; } private function sort_ads_auto( $ad_posts ) { $order_method = $this->get_order_method(); if ( $order_method === 'random' ) { shuffle( $ad_posts ); } elseif ( in_array( $order_method, ProBridge::instance()->pro_group_order_methods() ) && ProBridge::instance()->is_pro_loaded() ) { $args = array(); if ( $order_method === 'weighted' ) { $args['ad_weights'] = $this->wo_meta->get_value( $this->term_meta(), 'ad_weights', array() ); } $ad_posts = apply_filters( 'adcmdr_pro_sort_ads_auto', $ad_posts, $order_method, $this->group_id, $args ); } return $ad_posts; } /** * Process ad posts that have been loaded by this class instance. * Processing includes either randomizing or sorting the ads. * * @return void */ private function process_ad_posts() { if ( $this->should_auto_sort() ) { $this->ad_posts = $this->sort_ads_auto( $this->ad_posts ); } else { $sorted_ids = $this->wo_meta->get_value( $this->term_meta(), 'ad_order', array() ); if ( ! $sorted_ids || ! is_array( $sorted_ids ) ) { $sorted_ids = array(); } $this->ad_posts = self::sort_ads_by_sorted_ids( $this->ad_posts, $sorted_ids ); } } /** * Determine if ad images within this group should be responsively styled. * * @return bool */ private function responsive_ad_images() { return Util::truthy_or_site_default( $this->wo_meta->get_value( $this->term_meta(), 'responsive_banners', 'site_default' ), 'responsive_banners', 'general' ); } /** * Create list of classes for this group. * * @param mixed $classes Classes that should be included in this ad. * * @return string */ private function group_classes( $classes ) { $classes = Util::arrayify( $classes ); $classes[] = Util::prefixed( 'group' ); $classes[] = Util::prefixed( 'group-' . $this->group_id ); $classes[] = Util::prefixed( 'group--' . sanitize_title( $this->mode() ) ); if ( $this->responsive_ad_images() ) { $classes[] = Util::prefixed( 'ad-resp' ); } $custom_classes = $this->wo_meta->get_value( $this->term_meta(), 'custom_classes' ); if ( $custom_classes ) { $classes[] = $custom_classes; } /** * Filter: adcmdr_group_start_class_list * Filter the list of classes for a group, before they are converted into a string. */ $classes = array_unique( apply_filters( 'adcmdr_group_start_class_list', $classes, $this->group_id ) ); return implode( ' ', $classes ); } /** * Create an inline style for manually-set margins. * Add CSS variables for grid layouts. * * @return string */ private function group_inline_styles() { $inline = array(); $margins = Util::inline_styles( array( 'margin-top' => $this->wo_meta->get_value( $this->term_meta(), 'margin_top', false ), 'margin-right' => $this->wo_meta->get_value( $this->term_meta(), 'margin_right', false ), 'margin-bottom' => $this->wo_meta->get_value( $this->term_meta(), 'margin_bottom', false ), 'margin-left' => $this->wo_meta->get_value( $this->term_meta(), 'margin_left', false ), ), true, 'px', true, false ); if ( $margins ) { $inline[] = $margins; } if ( $this->grid_cols && ProBridge::instance()->is_pro_loaded() ) { $inline[] = apply_filters( 'adcmdr_pro_grid_inline_styles', array(), $this->grid_cols ); } return Util::make_inline_style( $inline ); } /** * Create HTML data attributes for rotating groups. * * @return string */ private function group_data_attributes() { $data_attributes = ''; if ( $this->mode() === 'rotate' ) { $interval = absint( $this->wo_meta->get_value( $this->term_meta(), 'refresh', 5 ) ); if ( ! $interval || $interval === 0 ) { $interval = 5; } $interval = $interval * 1000; $data_attributes = ' data-interval="' . esc_attr( $interval ) . '"'; } return $data_attributes; } /** * If necessary, clearfix this group. * * @return string */ private function maybe_clearfix() { if ( Util::truthy( $this->wo_meta->get_value( $this->term_meta(), 'clear_float', false ) ) ) { return Util::clear_float(); } return ''; } /** * If group is set to grid, create the grid wrappers. * * @return string */ private function group_mode_wrapper() { if ( $this->mode() === 'grid' && ProBridge::instance()->is_pro_loaded() ) { return apply_filters( 'adcmdr_pro_grid_wrapper', '' ); } elseif ( $this->mode() === 'rotate' ) { return $this->rotate_wrapper(); } return ''; } /** * Create the rotate wrapper. * * @return string */ private function rotate_wrapper() { return '