$adthrive_options[ $status_type ]['timestamp'], $status_type => $adthrive_options[ $status_type ]['content'], ); } else { $error = "{$status_type} not set in adthrive_options"; } break; case 'cls': if ( isset( $adthrive_options['cls-deployments'] ) ) { $return = $adthrive_options['cls-deployments']['content']; } else { $error = 'cls-deployments not set in adthrive_options'; } break; } } else { $error = 'adthrive_options not set'; } if ( isset( $error ) ) { print wp_json_encode( array( 'error' => $error ) ); } else { print wp_json_encode( $return ); } exit(); } } /** * AJAX method to get terms with the matched search query for the specified taxonomy */ public function ajax_terms() { if ( isset( $_GET['query'] ) ) { $query = sanitize_text_field( wp_unslash( $_GET['query'] ) ); } if ( isset( $_GET['taxonomy'] ) ) { $taxonomy = sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ); } wp_send_json( $this->get_term_selectize( $taxonomy, $query ) ); } /** * Adds classes to disable ads based on the plugin settings * * @param string|array $classes One or more classes to add to the class list. */ public function body_class( $classes ) { if ( is_singular() ) { global $post; $disable = get_post_meta( get_the_ID(), 'adthrive_ads_disable', true ); $disable_content_ads = get_post_meta( get_the_ID(), 'adthrive_ads_disable_content_ads', true ); $disable_auto_insert_videos = get_post_meta( get_the_ID(), 'adthrive_ads_disable_auto_insert_videos', true ); $re_enable_ads_on = get_post_meta( get_the_ID(), 'adthrive_ads_re_enable_ads_on', true ); $disabled_categories = \AdThrive_Ads\Options::get( 'disabled_categories' ); $disabled_tags = \AdThrive_Ads\Options::get( 'disabled_tags' ); $categories = get_the_category( $post->ID ); $tags = get_the_tags( $post->ID ); $category_names = is_array( $categories ) ? array_map( array( $this, 'pluck_name' ), $categories ) : array(); $tag_names = is_array( $tags ) ? array_map( array( $this, 'pluck_name' ), $tags ) : array(); if ( ! $re_enable_ads_on || false === trim( $re_enable_ads_on ) || $re_enable_ads_on > time() ) { if ( $disable ) { $classes[] = 'adthrive-disable-all'; } if ( $disable_content_ads || in_array( 'noads', $tag_names, true ) ) { $classes[] = 'adthrive-disable-content'; } if ( $disable_auto_insert_videos || in_array( 'noads', $tag_names, true ) ) { $classes[] = 'adthrive-disable-video'; } } if ( is_array( $disabled_categories ) && array_intersect( $disabled_categories, $category_names ) ) { $classes[] = 'adthrive-disable-all'; } elseif ( is_array( $disabled_tags ) && array_intersect( $disabled_tags, $tag_names ) ) { $classes[] = 'adthrive-disable-all'; } } elseif ( is_404() ) { $classes[] = 'adthrive-disable-all'; } return $classes; } /** * Adds Content Specific Video Playlist category classes to the body based on the plugin settings * * @param string|array $classes One or more classes to add to the class list. */ public function adthrive_cat_class( $classes ) { if ( is_singular() ) { global $post; $categories = get_the_category( $post->ID ); $category_names = is_array( $categories ) ? array_map( array( $this, 'pluck_name' ), $categories ) : array(); if ( 'on' === \AdThrive_Ads\Options::get( 'content_specific_playlists' ) ) { $category_body_classes = is_array( $categories ) ? array_map( array( $this, 'pluck_body_class' ), $categories ) : array(); $classes = array_merge( $classes, $category_body_classes ); } } return $classes; } /** * Gets the object name * * @param object $obj An object with a name property * * @return string The object name */ private function pluck_name( $obj ) { return $obj->name; } /** * Gets the object nicename * * @param object $obj An object with a nicename property * * @return string Adthrive class name formatted for use in the body tag */ private function pluck_body_class( $obj ) { return 'adthrive-cat-' . $obj->category_nicename; } /** * Gets just the object name property * * @param object $obj An object with a name property * * @return string An object with just a name property */ private function get_selectize( $obj ) { return array( 'text' => $obj->name, 'value' => $obj->name, ); } /** * Enqueue plugin admin styles on the post/page edit screens so the * WP 7.0 :checked override (see PE-1120) applies to the post meta box, * not just the global options page. * * @param string $hook The current admin page hook. */ public function enqueue_post_meta_styles( $hook ) { if ( 'post.php' !== $hook && 'post-new.php' !== $hook ) { return; } $screen = get_current_screen(); if ( ! $screen || ! in_array( $screen->post_type, self::OBJECT_TYPES, true ) ) { return; } wp_enqueue_style( 'adthrive-ads', plugins_url( 'css/adthrive-ads.min.css', ADTHRIVE_ADS_FILE ), false, ADTHRIVE_ADS_VERSION ); } /** * Add fields to the options metabox for page and posts */ public function all_objects() { $post_meta = new_cmb2_box( array( 'id' => 'adthrive_ads_object_metabox', 'title' => __( 'Raptive Ads', 'adthrive_ads' ), 'object_types' => self::OBJECT_TYPES, ) ); $post_meta->add_field( array( 'name' => __( 'Disable all ads', 'adthrive_ads' ), 'id' => 'adthrive_ads_disable', 'type' => 'checkbox', ) ); $post_meta->add_field( array( 'name' => __( 'Disable content ads', 'adthrive_ads' ), 'id' => 'adthrive_ads_disable_content_ads', 'type' => 'checkbox', ) ); $post_meta->add_field( array( 'name' => __( 'Disable auto-insert video players', 'adthrive_ads' ), 'id' => 'adthrive_ads_disable_auto_insert_videos', 'type' => 'checkbox', ) ); $post_meta->add_field( array( 'name' => __( 'Re-enable ads on', 'adthrive_ads' ), 'desc' => __( 'All ads on this post will be enabled on the specified date', 'adthrive_ads' ), 'id' => 'adthrive_ads_re_enable_ads_on', 'type' => 'text_date_timestamp', ) ); if ( \AdThrive_Ads\Options::get( 'disable_video_metadata' ) === 'on' ) { $post_meta->add_field( array( 'name' => __( 'Enable Video Metadata', 'adthrive_ads' ), 'desc' => __( 'Enable adding metadata to video player on this post', 'adthrive_ads' ), 'id' => 'adthrive_ads_enable_metadata', 'type' => 'checkbox', ) ); } else { $post_meta->add_field( array( 'name' => __( 'Disable Video Metadata', 'adthrive_ads' ), 'desc' => __( 'Disable adding metadata to video player on this post', 'adthrive_ads' ), 'id' => 'adthrive_ads_disable_metadata', 'type' => 'checkbox', ) ); } if ( \AdThrive_Ads\Options::get( 'disable_admin_ads' ) === 'on' ) { $post_meta->add_field( array( 'name' => __( 'Enable ads when previewing post', 'adthrive_ads' ), 'desc' => __( 'Enable all ads when previewing a post or customizing a theme in WordPress Admin', 'adthrive_ads' ), 'id' => 'adthrive_ads_enable_admin_ads', 'type' => 'checkbox', ) ); } else { $post_meta->add_field( array( 'name' => __( 'Disable ads when previewing post', 'adthrive_ads' ), 'desc' => __( 'Disable all ads when previewing a post or customizing a theme in WordPress Admin', 'adthrive_ads' ), 'id' => 'adthrive_ads_disable_admin_ads', 'type' => 'checkbox', ) ); } } /** * Add fields to the options metabox for the plugin settings. * * @param CMB $cmb A CMB metabox instance */ public function add_options( $cmb ) { $cmb->add_field( array( 'name' => __( 'Site Id', 'adthrive_ads' ), 'desc' => __( 'Add your Raptive Site ID', 'adthrive_ads' ), 'id' => 'site_id', 'type' => 'text', 'attributes' => array( 'required' => 'required', 'pattern' => '[0-9a-f]{24}', 'title' => 'The site id needs to match the one provided by Raptive exactly', ), ) ); $cmb->add_field( array( 'name' => 'Disabled for Categories', 'desc' => 'Disable ads for the selected categories.', 'id' => 'disabled_categories', 'type' => 'text', 'escape_cb' => array( $this, 'selectize_escape' ), 'sanitization_cb' => array( $this, 'selectize_sanitize' ), ) ); $cmb->add_field( array( 'name' => 'Disabled for Tags', 'desc' => 'Disable ads for the selected tags.', 'id' => 'disabled_tags', 'type' => 'text', 'escape_cb' => array( $this, 'selectize_escape' ), 'sanitization_cb' => array( $this, 'selectize_sanitize' ), ) ); $cmb->add_field( array( 'name' => 'Disable Video Metadata', 'desc' => 'Disable adding metadata to video players. Caution: This is a site-wide change. Only choose if metadata is being loaded another way.', 'id' => 'disable_video_metadata', 'type' => 'checkbox', ) ); $cmb->add_field( array( 'name' => 'CLS Optimization', 'desc' => "Enable solution to reduce ad-related CLS
Clear your site's cache after saving this setting to apply the update across your site. Get more details on CLS optimization here.", 'id' => 'cls_optimization', 'type' => 'checkbox', 'default_cb' => array( $this, 'cls_checkbox_default' ), ) ); $cmb->add_field( array( 'name' => 'Disable ads within WordPress Admin', 'desc' => 'Disable all ads when previewing a post or customizing a theme in WordPress Admin. Recommended when using post or page builders.', 'id' => 'disable_admin_ads', 'type' => 'checkbox', ) ); return $cmb; } /** * Returns true unless the checkbox was explicitly unchecked. * Note: Working with the CMB2 checkbox is odd. This function attempts to: * 1. Default the checkbox to 'on' * 2. If the `adthrive_ads` options are stored in the database, look for the `cls_optimization` * option in they array. * 3. If the `cls_optimization` option is not stored, that means a user explicitly disabled the * option so don't force it to true. */ public function cls_checkbox_default( $field_args, $field ) { $cls_optimization = 'on'; $adthrive_options = get_option( $field->object_id, false ); if ( $adthrive_options ) { if ( isset( $adthrive_options['cls_optimization'] ) ) { $cls_optimization = $adthrive_options['cls_optimization']; } else { $cls_optimization = null; } } return isset( $cls_optimization ) ? true : false; } /** * Convert a selectize field array value to string * * @param mixed $value The actual field value. * @return String Field value converted to a string */ public function selectize_escape( $value ) { if ( is_string( $value ) ) { return $value; } return ! empty( $value ) ? implode( ',', $value ) : null; } /** * Convert a selectize field value to array * * @param mixed $value The actual field value. * @return array Field value converted to an array */ public function selectize_sanitize( $value ) { if ( is_array( $value ) ) { return $value; } return ! empty( $value ) ? explode( ',', $value ) : null; } /** * Check if the user is within admin * * True if * - user is editing a post/page * - user is customizing a theme * - user is previewing an unpublished post/page * - user is viewing a published post/page while logged into WP and has higher roles than subscriber & customer * * @return boolean */ public function is_admin() { $is_admin = false; if ( is_admin() ) { // User is in the admin. $is_admin = true; } elseif ( is_customize_preview() ) { // User is customizing a theme. $is_admin = true; } elseif ( is_preview() ) { // User is previewing an unpublished post/page. $is_admin = true; } elseif ( is_user_logged_in() ) { // User is logged into WP. Check if user has an edit capability ( all roles beyond Subscriber ). if ( current_user_can( 'edit_posts' ) ) { $is_admin = true; } } return $is_admin; } /** * Check if admin ads are disabled * * @return boolean */ public function admin_ads_disabled() { // Check site level settings. $disable_site_admin_ads = \AdThrive_Ads\Options::get( 'disable_admin_ads' ) === 'on'; // Check page/post level settings if it's overriding site level settings. if ( is_singular( self::OBJECT_TYPES ) ) { if ( $disable_site_admin_ads ) { $enable_post_admin_ads = get_post_meta( get_the_ID(), 'adthrive_ads_enable_admin_ads', true ) === 'on'; return ! $enable_post_admin_ads; } else { $disable_post_admin_ads = get_post_meta( get_the_ID(), 'adthrive_ads_disable_admin_ads', true ) === 'on'; return $disable_post_admin_ads; } } return $disable_site_admin_ads; } /** * Add header scripts for ads. */ public function head_scripts() { if ( $this->is_admin() && $this->admin_ads_disabled() ) { // Don't load ads if the user is in the admin and admin ads are disabled. return; } $this->ad_head(); $this->adthrive_preload(); } /** * Add the AdThrive ads script */ private function ad_head() { $data['site_id'] = \AdThrive_Ads\Options::get( 'site_id' ); $cls_optimization = \AdThrive_Ads\Options::get( 'cls_optimization' ); // if cls_optimization is false in the plugin, cls could still be on in ads min, so we set this here $data['plugin_debug'] = isset( $_GET['plugin_debug'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['plugin_debug'] ) ) ? 'true' : 'false'; $thrive_architect_enabled = isset( $_GET['tve'] ) && sanitize_key( $_GET['tve'] ) === 'true'; if ( isset( $_SERVER['REQUEST_URI'] ) ) { $request_uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); $widget_preview_active = strpos( $request_uri, 'wp-admin/widgets.php' ) !== false; } else { $widget_preview_active = false; } if ( isset( $data['site_id'] ) && preg_match( '/[0-9a-f]{24}/i', $data['site_id'] ) && ! $thrive_architect_enabled && ! $widget_preview_active ) { $body_classes = $this->body_class( array() ); if ( 'on' === $cls_optimization ) { $cls_file_service = $this->get_cls_file_service(); $cls_data = $cls_file_service->parse_cls_deployment(); $data = array_merge( $data, $cls_data ); $site_ads = new \AdThrive_Ads\SiteAds(); $data['site_js'] = $site_ads->get_site_js(); $data['site_css'] = $cls_file_service->get_option_value( 'site_css' ); $disable_all = in_array( 'adthrive-disable-all', $body_classes, true ); if ( ! empty( $data['site_js'] ) ) { $decoded_data = json_decode( $data['site_js'] ); if ( $this->has_essential_site_ads_keys( $decoded_data ) ) { require 'partials/insertion-includes.php'; add_action( 'wp_head', function () use ( $data, $cls_file_service ) { $cls_file_service->insert_cls_file( 'comscore-loader', $data ); }, 100 ); add_action( 'wp_head', function () use ( $data, $cls_file_service ) { $cls_file_service->insert_cls_file( 'cls-disable-ads', $data ); }, 100 ); if ( ! $disable_all && null !== $decoded_data && isset( $decoded_data->adUnits ) ) { $adunits = $decoded_data->adUnits; foreach ( $adunits as $adunit ) { if ( 'Header' === $adunit->location && isset( $adunit->dynamic ) ) { if ( ! isset( $adunit->dynamic->spacing ) ) { $adunit->dynamic->spacing = 0; } if ( ! isset( $adunit->dynamic->max ) ) { $adunit->dynamic->max = 0; } else { $adunit->dynamic->max = (int) floor( $adunit->dynamic->max ); } if ( ! isset( $adunit->sequence ) ) { $adunit->sequence = 1; } if ( true === $adunit->dynamic->enabled && 1 === $adunit->dynamic->max && 0 === $adunit->dynamic->spacing && 1 === $adunit->sequence ) { add_action( 'wp_head', function () use ( $data, $cls_file_service ) { $cls_file_service->insert_cls_file( 'cls-header-insertion', $data ); }, 101 ); } } } } add_action( 'wp_footer', function () use ( $data, $cls_file_service ) { $cls_file_service->insert_cls_file( 'cls-insertion', $data ); $this->check_cls_insertion(); }, 1 ); } else { require 'partials/sync-error.php'; } } } require 'partials/ads.php'; } } /** * Adthrive Preload hook - adds dns prefetch and preconnect elements to adthrive resources */ private function adthrive_preload() { echo ''; echo ''; echo ''; } /** * Returns true if all expected siteAds keys exist: siteId, siteName, adOptions, breakpoints, adUnits * If these keys are not present, there was an issue syncing the siteAds data, and we shouldnt load any CLS */ private function has_essential_site_ads_keys( $site_ads_object ) { return ( null !== $site_ads_object && isset( $site_ads_object->siteId ) && isset( $site_ads_object->siteName ) && isset( $site_ads_object->adOptions ) && isset( $site_ads_object->breakpoints ) && isset( $site_ads_object->adUnits ) ); } /** * Check if cls insertion script tag has been inserted to page, if not set injectedFromPlugin to false */ private function check_cls_insertion() { echo ''; } /** * Get the CLS file service instance. */ private function get_cls_file_service() { if ( null === $this->cls_file_service ) { $this->cls_file_service = new Cls_File_Service(); } return $this->cls_file_service; } /** * Gets terms and displays them as options * * @param String $taxonomy Taxonomy terms to retrieve. Default is category. * @param String $search Optional. get_terms optional arguments * * @return array An array of options that matches the CMB2 options array */ public function get_term_selectize( $taxonomy = 'category', $search = '' ) { $terms = (array) get_terms( array( 'taxonomy' => $taxonomy, 'search' => $search, 'number' => 100, ) ); return is_array( $terms ) ? array_map( array( $this, 'get_selectize' ), $terms ) : array(); } }