get( 'filter_priority', 'general', false, self::placement_priority_default() ) ); add_filter( 'the_content', array( $this, 'the_content' ), apply_filters( 'adcmdr_priority_the_content', $priority ) ); add_action( 'wp_head', array( $this, 'wp_head' ), apply_filters( 'adcmdr_priority_wp_head', $priority ) ); add_action( 'wp_footer', array( $this, 'wp_footer' ), apply_filters( 'adcmdr_priority_wp_footer', $priority ) ); } /** * The default priority for the insert the_content filter. * * @return int */ public static function placement_priority_default() { return 100; } /** * Create a new WOMeta instance if necessary. * * @return WOMeta */ protected function meta() { if ( ! $this->wo_meta ) { $this->wo_meta = new WOMeta( AdCommander::ns() ); } return $this->wo_meta; } /** * Do we need consent to display? * * @return bool */ private function global_needs_consent() { if ( $this->global_needs_consent === null ) { $global_needs_consent = Consent::instance()->global_needs_consent(); $this->global_needs_consent = $global_needs_consent; } return $this->global_needs_consent; } /** * Do we need to lazy load this insert? * * @return bool */ private function can_lazy() { if ( $this->can_lazy !== null ) { return $this->can_lazy; } if ( ! ProBridge::instance()->pro_feature_check( 'lazyload' ) ) { $this->can_lazy = false; } else { $this->can_lazy = LazyPro::instance()->is_lazy_load_enabled(); } return $this->can_lazy; } private function is_lazy() { if ( ! $this->can_lazy() ) { return false; } } /** * Determine if we should use ajax loading for this insert. * * @param array $meta The current meta. * * @return bool */ public function should_ajax( $meta ) { 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() || $this->can_lazy() ) { return true; } $visitor_conditions = $this->meta()->get_value( $meta, 'visitor_conditions', false ); return ( ( $visitor_conditions && ! empty( $visitor_conditions ) ) ); } /** * Build HTML for inserts. This is used when an insert is loaded over ajax. * * @param array $placement_ids The insert IDs to load. * @param array $args Function arguments. * @return string */ public function display_placements( $placement_ids = array(), $args = array() ) { $force_nocheck = ( isset( $args['force_nocheck'] ) ) ? $args['force_nocheck'] : false; $force_nolazy = ( isset( $args['force_nolazy'] ) ) ? $args['force_nolazy'] : false; $placements = Query::placements( 'publish', array(), Util::arrayify( $placement_ids ) ); $html = ''; if ( ! empty( $placements ) ) { $placements = $this->process_placements( $placements, true ); foreach ( $placements as $position => $placement ) { $disable_wrappers = false; $is_popup = false; $is_sticky = false; if ( $position === 'head_close_tag' ) { $disable_wrappers = true; } elseif ( $position === 'popup' ) { $is_popup = true; } elseif ( $position === 'sticky' ) { $is_sticky = true; } $html .= $this->build_placement_ads( $placement, array( 'disable_wrappers' => $disable_wrappers, 'force_noajax' => true, 'force_nocheck' => $force_nocheck, 'force_nolazy' => $force_nolazy, 'is_popup' => $is_popup, 'is_sticky' => $is_sticky, ) ); } } return $html; } /** * Process items in this Insert. * * @param array $items The ads and groups to process into an array. * * @return array */ private function process_items( $items ) { $processed_items = array(); if ( ! empty( $items ) ) { foreach ( $items as $item ) { $prefix = substr( $item, 0, 2 ); $type = null; if ( $prefix === 'a_' ) { $type = 'ad'; } elseif ( $prefix === 'g_' ) { $type = 'group'; } if ( $type !== null ) { $processed_items[] = array( 'type' => $type, 'id' => absint( substr( $item, 2 ) ), ); } } } return $processed_items; } /** * Process Insert WP_Posts into usable array. * * @param \WP_Post $placement_posts Insert posts to procss. * * @return array */ private function process_placements( $placement_posts, $force_noajax = false ) { $placements = array(); foreach ( $placement_posts as $placement_post ) { $meta = $this->meta()->get_post_meta( $placement_post->ID, PlacementPostMeta::post_meta_keys() ); $position = $this->meta()->get_value( $meta, 'placement_position', false ); $disable_wrappers = false; $force_nolazy = false; $force_noajax_this_placement = $force_noajax; if ( ! $position ) { continue; } if ( ! isset( $placements[ $position ] ) ) { $placements[ $position ] = array(); } if ( $position === 'body_close_tag' ) { if ( ! $force_noajax ) { $force_noajax_this_placement = Util::truthy( $this->meta()->get_value( $meta, 'force_serverside_body', 1 ) ); } $disable_wrappers = Util::truthy( $this->meta()->get_value( $meta, 'disable_wrappers_body', 1 ) ); } $should_ajax = ( ! $force_noajax_this_placement ) ? $this->should_ajax( $meta ) : false; $order = $this->meta()->get_value( $meta, 'order', false ); $items = $this->process_items( $this->meta()->get_value( $meta, 'placement_items', false ) ); if ( ! empty( $items ) ) { $placement = array( 'placement_id' => $placement_post->ID, 'placement_name' => $placement_post->post_title, 'order' => $order ? intval( $order ) : PlacementPostMeta::post_meta_keys()['order']['default'], 'items' => $items, 'meta' => $meta, 'should_ajax' => $should_ajax, 'disable_wrappers' => $disable_wrappers, 'force_nocheck' => $this->meta()->get_value( $meta, 'disable_consent', false ), 'force_nolazy' => $force_nolazy, ); if ( ProBridge::instance()->is_pro_loaded() ) { if ( $position === 'post_list' ) { $placement = apply_filters( 'adcmdr_pro_post_list_create_insert', $placement, $position, array( 'post_list_position' => $this->meta()->get_value( $meta, 'post_list_position', 1 ) ) ); } } $placements[ $position ][] = $placement; } } return $this->sort_placements( $placements ); } /** * Sort inserts by manually set order. * * @param array $placements A processed inserts array. * * @return array */ public function sort_placements( $placements ) { $sorted_placements = array(); foreach ( $placements as $position => $placement_position ) { usort( $placement_position, function ( $a, $b ) { return $a['order'] <=> $b['order']; } ); $sorted_placements[ $position ] = $placement_position; } return $sorted_placements; } /** * Get array of inserts. * * @return array */ public function get_placements() { if ( ! $this->processed_placements ) { $this->processed_placements = $this->process_placements( Query::placements() ); } return $this->processed_placements; } /** * Filter inserts by a given post type. * * @param array $placements An array of processed inserts. * @param string $post_type The post type to filter. * * @return array */ protected function filter_placements_by_post_type( $placements, $post_type ) { return array_filter( $placements, function ( $placement ) use ( $post_type ) { return in_array( $post_type, $placement['post_types'] ); } ); } /** * Determine if an insert needs consent. * * @param int $placement_id The insert ID. * * @return bool */ private function placement_needs_consent( $placement_id, $force_nocheck = false ) { if ( $force_nocheck ) { return false; } /** * Need to check the meta of $placement_id as well as global. This way we can disable consent for an insert. * * TODO: We need some way to store meta so that we aren't re-querying it. */ $meta = $this->meta()->get_post_meta( $placement_id, PlacementPostMeta::post_meta_keys() ); return ! $this->meta()->get_value( $meta, 'disable_consent', false ) && $this->global_needs_consent(); } /** * Create an ajax container for this insert. * * @param int $placement_id The ID of the insert. * * @return string */ public function placement_ajax_container( $placement_id, $force_nolazy = false ) { /** * Filter: adcmdr_insert_ajax_container_start * Filter: adcmdr_insert_ajax_container_end * Filter: adcmdr_insert_ajax_container * * Filters AJAX container. */ $classes = Util::prefixed( 't' ); if ( $this->placement_needs_consent( $placement_id ) ) { $classes .= ' ' . Util::prefixed( 'needs-consent' ); } if ( ! $force_nolazy && $this->can_lazy() ) { $classes .= ' ' . Util::prefixed( 'is-lazy' ); } $html = apply_filters( 'adcmdr_insert_ajax_container_start', '