Your IP : 216.73.217.143


Current Path : /home/ereika83/public_html/wp-content/plugins/wp-hummingbird/core/modules/
Upload File :
Current File : /home/ereika83/public_html/wp-content/plugins/wp-hummingbird/core/modules/class-exclusions.php

<?php
/**
 * Exclusions class.
 *
 * @package Hummingbird\Core\Modules
 * @since 3.11.0
 */

namespace Hummingbird\Core\Modules;

use Hummingbird\Core\Module;
use Hummingbird\Core\Traits\Module as ModuleContract;
use Hummingbird\Core\Settings;
use Hummingbird\Core\Utils;
use Hummingbird\Core\Modules\Minify\Sources_Collector;
use WP_Query;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Exclusion.
 */
class Exclusions extends Module {
	use ModuleContract;

	/**
	 * Retrieve the template or name of the currently active theme.
	 *
	 * @return string
	 */
	public function get_active_theme() {
		$theme = wp_get_theme();

		return array( 'active_theme' => $theme->get( 'Name' ) );
	}

	/**
	 * Retrieve a list of active plugins, including sitewide active plugins in multisite installations.
	 *
	 * @return array
	 */
	public function get_active_plugins() {
		$plugins        = array();
		$active_plugins = array_merge(
			(array) get_option( 'active_plugins', array() ),
			is_multisite() ? array_keys( (array) get_site_option( 'active_sitewide_plugins', array() ) ) : array()
		);

		foreach ( $active_plugins as $plugin ) {
			$plugin_data        = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
			$plugins[ $plugin ] = $plugin_data['Name'];
		}

		return $plugins;
	}

	/**
	 * Retrieve a list of active plugins and the currently active theme.
	 *
	 * @return array
	 */
	public function get_active_theme_and_plugins() {
		return array_merge( $this->get_active_theme(), $this->get_active_plugins() );
	}

	/**
	 * Retrieve all exclusions.
	 *
	 * @param string $type The type of exclusions to retrieve.
	 *
	 * @return array
	 */
	public function get_all_possible_exclusions( $type = 'scripts' ) {
		$all_exclusions = array(
			'posts'   => $this->get_post_types(),
			'files'   => $this->get_assets( $type ),
			'urls'    => $this->get_formatted_post_urls(),
			'plugins' => $this->get_active_theme_and_plugins(),
			'ads'     => $this->get_ads_tracker_exclusion(),
		);

		if ( 'styles' === $type ) {
			return array_intersect_key(
				$all_exclusions,
				array_flip( array( 'posts', 'files', 'plugins', 'urls' ) )
			);
		}

		return $all_exclusions;
	}

	/**
	 * Retrieve settings for the exclusion textareas, including default values and descriptions.
	 *
	 * @param string $type   The type of exclusions to retrieve.
	 * @param string $prefix The prefix for the setting keys.
	 *
	 * @return array
	 */
	public function get_exclusion_settings( $type = 'scripts', $prefix = 'delay_js_' ) {
		$is_critical_css = 'critical_css_' === $prefix;
		$keyword_key     = $is_critical_css ? 'keywords' : 'exclusions';

		$settings = array(
			$prefix . 'all_exclusions'           => array(
				'title'           => __( 'All Exclusions', 'wphb' ),
				'exclusion_name'  => 'all_exclusions',
				'description'     => __( 'Choose an exclusion type and add/remove exclusions here.', 'wphb' ),
				'placeholder'     => __( 'Enter URLs, keywords, or file paths to exclude from optimization.', 'wphb' ),
				'class'           => '',
				'type'            => 'all_exclusion',
				'value'           => $this->get_all_possible_exclusions( $type ),
				'selected_values' => array(),
			),
			$prefix . 'files_exclusion'          => array(
				'title'           => __( 'Files', 'wphb' ),
				'exclusion_name'  => 'files',
				'description'     => __( 'Select file paths to exclude from optimization.', 'wphb' ),
				'placeholder'     => __( 'Enter file paths to exclude from optimization.', 'wphb' ),
				'value'           => $this->get_assets( $type ),
				'class'           => 'sui-hidden',
				'type'            => 'select',
				'selected_values' => $this->get_exclusion_setting( $prefix . 'files_exclusion' ),
			),
			$prefix . 'post_types_exclusion'     => array(
				'title'           => __( 'Post Types', 'wphb' ),
				'exclusion_name'  => 'posts',
				'description'     => __( 'Select post types to exclude from optimization.', 'wphb' ),
				'placeholder'     => __( 'Enter post types to exclude from optimization.', 'wphb' ),
				'value'           => $this->get_post_types(),
				'class'           => 'sui-hidden',
				'type'            => 'select',
				'selected_values' => $is_critical_css ? $this->get_critical_css_excluded_post_types() : $this->get_exclusion_setting( $prefix . 'post_types_exclusion' ),
			),
			$prefix . 'post_urls_exclusion'      => array(
				'title'           => __( 'Post URLs', 'wphb' ),
				'exclusion_name'  => 'urls',
				'description'     => __( 'Select post URLs to exclude from optimization.', 'wphb' ),
				'placeholder'     => __( 'Enter post URLs to exclude from optimization.', 'wphb' ),
				'value'           => $this->get_formatted_post_urls( $prefix . 'post_urls_exclusion' ),
				'class'           => 'sui-hidden',
				'type'            => 'select',
				'selected_values' => $this->get_exclusion_setting( $prefix . 'post_urls_exclusion' ),
			),
			$prefix . 'plugins_themes_exclusion' => array(
				'title'           => __( 'Plugins/Themes', 'wphb' ),
				'exclusion_name'  => 'plugins',
				'description'     => __( 'Select plugin or theme names to exclude from optimization.', 'wphb' ),
				'placeholder'     => __( 'Enter plugin or theme names to exclude from optimization.', 'wphb' ),
				'value'           => $this->get_active_theme_and_plugins(),
				'class'           => 'sui-hidden',
				'type'            => 'select',
				'selected_values' => $this->get_exclusion_setting( $prefix . 'plugins_themes_exclusion' ),
			),
			$prefix . $keyword_key               => array(
				'title'           => __( 'Keywords', 'wphb' ),
				'exclusion_name'  => 'keywords',
				'description'     => __( 'Enter keywords to exclude from optimization.', 'wphb' ),
				'placeholder'     => __( 'Enter keywords to exclude from optimization.', 'wphb' ),
				'value'           => $is_critical_css ? $this->get_exclusion_setting( $prefix . 'keywords' ) : $this->get_manually_excluded_keywords(),
				'class'           => 'sui-hidden',
				'type'            => 'select',
				'selected_values' => $is_critical_css ? $this->get_exclusion_setting( $prefix . 'keywords' ) : $this->get_manually_excluded_keywords(),
			),
		);

		if ( ! $is_critical_css ) {
			$settings[ $prefix . 'ads_tracker_exclusion' ] = array(
				'title'           => __( 'Ads/Trackers', 'wphb' ),
				'exclusion_name'  => 'ads',
				'description'     => __( 'Select ads or trackers to exclude from optimization.', 'wphb' ),
				'placeholder'     => __( 'Enter ads or trackers to exclude from optimization.', 'wphb' ),
				'value'           => $this->get_ads_tracker_exclusion(),
				'class'           => 'sui-hidden',
				'type'            => 'select',
				'selected_values' => $this->get_exclusion_setting( 'delay_js_ads_tracker_exclusion' ),
			);

			if ( defined( 'WPHB_INCLUDE_DEFAULT_EXCLUSIONS' ) ) {
				$default_exclusion_lists = Utils::get_module( 'delayjs' )->get_pre_defined_exclusion_list();
				$default_exclusion_lists = array_reduce(
					$default_exclusion_lists,
					function ( $carry, $item ) {
						$carry[ $item ] = $item;
						return $carry;
					},
					array()
				);

				$settings[ $prefix . 'default_exclusions' ] = array(
					'title'           => __( 'Default Exclusions', 'wphb' ),
					'exclusion_name'  => 'default_exclusions',
					'description'     => __( 'List of default exclusions', 'wphb' ),
					'placeholder'     => __( 'Default Exclusions', 'wphb' ),
					'value'           => $default_exclusion_lists,
					'class'           => 'sui-hidden',
					'type'            => 'select',
					'selected_values' => $default_exclusion_lists,
				);
			}
		}

		return $settings;
	}

	/**
	 * Retrieve settings for delay JS exclusions.
	 *
	 * @return array
	 */
	public function get_delay_js_exclusion_settings() {
		return $this->get_exclusion_settings( 'scripts', 'delay_js_' );
	}

	/**
	 * Retrieve a list of publicly accessible post types.
	 *
	 * @return array
	 */
	public function get_post_types() {
		$post_types = $this->get_custom_post_types();
		$pages_type = Page_Cache::get_page_types();

		return array_merge( $post_types, $pages_type );
	}

	/**
	 * Retrieve a list of custom post types.
	 *
	 * @param bool $keys Whether to return the keys of the post types.
	 * @return array
	 */
	public function get_custom_post_types( $keys = false ) {
		$post_types = get_post_types(
			array(
				'public'             => true,
				'publicly_queryable' => true,
				'_builtin'           => false,
			),
			'objects',
		);

		if ( $keys ) {
			return array_keys( $post_types );
		}

		// Use array_map to create key-value pairs of post type slug => label.
		$post_types_formatted = array_map(
			function ( $post_type ) {
				return $post_type->label;
			},
			$post_types
		);

		return $post_types_formatted;
	}

	/**
	 * Retrieve a list of default exclusions for ads and trackers.
	 *
	 * @return array
	 */
	public function get_ads_tracker_exclusion() {
		return array(
			'amazon_ads'         => array(
				'title'      => __( 'Amazon Ads', 'wphb' ),
				'exclusions' => array( 'amazon-adsystem.com' ),
			),
			'google_adsense'     => array(
				'title'      => __( 'Google AdSense', 'wphb' ),
				'exclusions' => array( 'adsbygoogle' ),
			),
			'google_analytics'   => array(
				'title'      => __( 'Google Analytics', 'wphb' ),
				'exclusions' => array(
					'google-analytics.com\/analytics.js',
					"ga\\( '",
					"ga\\('",
				),
			),
			'google_maps'        => array(
				'title'      => __( 'Google Maps', 'wphb' ),
				'exclusions' => array(
					'maps.googleapis.com',
					'maps.google.com',
				),
			),
			'google_optimize'    => array(
				'title'      => __( 'Google Optimize', 'wphb' ),
				'exclusions' => array(
					'a,s,y,n,c,h,i,d,e',
					'googleoptimize.com\/optimize.js',
					'async-hide',
				),
			),
			'google_recaptcha'   => array(
				'title'      => __( 'Google Recaptcha', 'wphb' ),
				'exclusions' => array( 'recaptcha' ),
			),
			'google_tag_manager' => array(
				'title'      => __( 'Google Tag Manager', 'wphb' ),
				'exclusions' => array(
					'\/gtag\/js',
					'gtag\\(',
					'\/gtm.js',
					'async-hide',
				),
			),
			'hubspot'            => array(
				'title'      => __( 'HubSpot', 'wphb' ),
				'exclusions' => array(
					'\/jquery-?[0-9.](.*)(.min|.slim|.slim.min)?.js',
					'\/jquery-migrate(.min)?.js',
					'js(.*).hsforms.net',
					'hbspt.forms.create',
				),
			),
			'refari'             => array(
				'title'      => __( 'Refari', 'wphb' ),
				'exclusions' => array(
					'widget.refari.co',
					'refari',
				),
			),
			'reviews_io'         => array(
				'title'      => __( 'Reviews.io', 'wphb' ),
				'exclusions' => array(
					'\/carousel-inline-iframeless\/dist.js',
					'carouselInlineWidget',
				),
			),
			'stripe'             => array(
				'title'      => __( 'Stripe', 'wphb' ),
				'exclusions' => array(
					'js.stripe.com',
				),
			),
			'trust_index'        => array(
				'title'      => __( 'Trustindex', 'wphb' ),
				'exclusions' => array(
					'cdn.trustindex.io\/loader.js',
					'cdn.trustindex.io\/loader-cert.js',
				),
			),
			'typeform'           => array(
				'title'      => __( 'Typeform', 'wphb' ),
				'exclusions' => array(
					'\/next\/embed.js',
				),
			),
			'typekit'            => array(
				'title'      => __( 'Typekit', 'wphb' ),
				'exclusions' => array(
					'typekit',
				),
			),
			'venatus_media'      => array(
				'title'      => __( 'Venatus Media', 'wphb' ),
				'exclusions' => array(
					'\/ad-manager.min.js',
					'__vm_add',
				),
			),
			'wistia'             => array(
				'title'      => __( 'Wistia', 'wphb' ),
				'exclusions' => array(
					'fast.wistia.com',
					'\/assets\/external\/E-v1.js',
				),
			),
			'yandex_ads'         => array(
				'title'      => __( 'Yandex Ads', 'wphb' ),
				'exclusions' => array(
					'yandex.ru',
					'window.yaContextCb',
				),
			),
		);
	}

	/**
	 * Retrieves an associative array of post IDs and their corresponding post titles.
	 *
	 * @param string $setting_key The setting key to retrieve the post URLs from.
	 *
	 * @return array
	 */
	public function get_formatted_post_urls( $setting_key = 'delay_js_post_urls_exclusion' ) {
		$post_urls_exclusion = $this->get_exclusion_setting( $setting_key );

		$post_id_name_map = array();
		foreach ( $post_urls_exclusion as $post_id ) {
			$post = get_post( $post_id );
			if ( $post ) {
				$post_id_name_map[ $post_id ] = $post->post_title . ' (' . get_permalink( $post->ID ) . ')';
			}
		}

		return $post_id_name_map;
	}

	/**
	 * Search posts based on the provided query.
	 *
	 * @param string $query The search query to find posts.
	 *
	 * @return array The list of posts that match the search query.
	 */
	public function search_posts( $query ) {
		$args = array(
			'posts_per_page' => 10,
			'post_status'    => 'publish',
			's'              => $query,
		);

		$query   = new WP_Query( $args );
		$results = array();

		if ( $query->have_posts() ) {
			while ( $query->have_posts() ) {
				$query->the_post();
				$results[] = array(
					'id'    => get_the_ID(),
					'name'  => get_the_title() . ' (' . get_permalink() . ')',
					'label' => 'urls',
				);
			}
		}

		wp_reset_postdata();

		return $results;
	}

	/**
	 * Reset exclusion settings to their default values.
	 *
	 * @param string $reset_exclusions The type of exclusions to reset.
	 * @param string $type             The type of exclusion to reset.
	 */
	public function reset_exclusion_to_defaults( $reset_exclusions, $type ) {
		$exclusion_settings_map = array(
			'delay_js'     => array(
				'delay_js_files_exclusion',
				'delay_js_post_types_exclusion',
				'delay_js_post_urls_exclusion',
				'delay_js_plugins_themes_exclusion',
				'delay_js_ads_tracker_exclusion',
				'delay_js_exclusions',
			),
			'critical_css' => array(
				'critical_page_types',
				'critical_skipped_custom_post_types',
				'critical_css_files_exclusion',
				'critical_css_post_urls_exclusion',
				'critical_css_plugins_themes_exclusion',
				'critical_css_keywords',
			),
		);

		if ( isset( $exclusion_settings_map[ $type ] ) ) {
			$this->reset_exclusion( $reset_exclusions, $exclusion_settings_map[ $type ] );
		}
	}

	/**
	 * Reset specified exclusion settings to their default values.
	 *
	 * @param string $reset_exclusions     The type of exclusions to reset.
	 * @param array  $exclusion_settings   The exclusion settings keys to reset.
	 */
	private function reset_exclusion( $reset_exclusions, $exclusion_settings ) {
		$defaults = Settings::get_default_settings();
		$minify   = $defaults['minify'];

		// Reset all exclusions if requested.
		if ( in_array( $reset_exclusions, array( 'delay_js_all_exclusions', 'critical_css_all_exclusions' ), true ) ) {
			foreach ( $exclusion_settings as $exclusion ) {
				Settings::update_setting( $exclusion, $minify[ $exclusion ], 'minify' );
			}
		} elseif ( isset( $minify[ $reset_exclusions ] ) && ! empty( $reset_exclusions ) ) {
			Settings::update_setting( $reset_exclusions, $minify[ $reset_exclusions ], 'minify' );
		} elseif ( 'critical_css_post_types_exclusion' === $reset_exclusions ) {
			Settings::update_setting( 'critical_page_types', $minify['critical_page_types'], 'minify' );
			Settings::update_setting( 'critical_skipped_custom_post_types', $minify['critical_skipped_custom_post_types'], 'minify' );
		}
	}

	/**
	 * Check if the current post type should be excluded from delay.
	 *
	 * @return bool
	 */
	public function is_current_post_type_excluded() {
		$excluded_post_type = $this->get_exclusion_setting( 'delay_js_post_types_exclusion' );
		$page_type_checks   = array(
			'frontpage' => is_front_page(),
			'home'      => is_home() && 'posts' !== get_option( 'show_on_front' ) && ! is_multisite(),
			'page'      => is_page(),
			'single'    => is_single(),
			'archive'   => is_archive(),
			'category'  => is_category(),
			'tag'       => is_tag(),
		);

		if ( ! empty( $excluded_post_type ) ) {
			foreach ( $page_type_checks as $type => $check ) {
				if ( $check && in_array( $type, $excluded_post_type, true ) ) {
					return true;
				}
			}
		}

		if ( in_array( get_post_type(), $excluded_post_type, true ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Check if the current page should be excluded from delay and critical.
	 *
	 * @param string $setting_key The setting key to retrieve the post URLs from.
	 *
	 * @return bool
	 */
	public function is_current_page_excluded( $setting_key = 'delay_js_post_urls_exclusion' ) {
		global $post;
		$setting_key        = 'critical_css' === $setting_key ? 'critical_css_post_urls_exclusion' : $setting_key;
		$selected_exclusion = array_map( 'intval', $this->get_exclusion_setting( $setting_key ) );

		if ( isset( $post->ID ) && in_array( $post->ID, $selected_exclusion, true ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Returns and array of excluded URLs.
	 *
	 * @param string $setting_key The setting key to retrieve the post URLs from.
	 * @return array
	 */
	public function get_formatted_theme_plugin_exclusions( $setting_key = 'delay_js_plugins_themes_exclusion' ) {
		$excluded_urls               = array();
		$excluded_plugins_and_themes = $this->get_exclusion_setting( $setting_key );
		foreach ( $excluded_plugins_and_themes as $plugin_or_theme ) {
			if ( 'active_theme' === $plugin_or_theme ) {
				$excluded_urls[] = get_stylesheet_directory_uri();
			} else {
				$excluded_urls[] = plugins_url( '', WP_PLUGIN_DIR . '/' . $plugin_or_theme );
			}
		}

		return $excluded_urls;
	}

	/**
	 * Returns excluded ads and tracker for delay JS.
	 *
	 * @return array
	 */
	public function get_excluded_js_ads_tracker() {
		$selected_exclusion = $this->get_exclusion_setting( 'delay_js_ads_tracker_exclusion' );
		$excluded_item      = array();
		$all_exclusions     = $this->get_ads_tracker_exclusion();

		foreach ( $selected_exclusion as $item ) {
			if ( isset( $all_exclusions[ $item ]['exclusions'] ) ) {
				$excluded_item = array_merge( $excluded_item, $all_exclusions[ $item ]['exclusions'] );
			}
		}

		return $excluded_item;
	}

	/**
	 * Check if the current page should be excluded from delay.
	 *
	 * @return array
	 */
	public function get_excluded_keywords() {
		$excluded = (array) Utils::get_module( 'delayjs' )->get_pre_defined_exclusion_list();
		$excluded = array_unique( array_merge( $excluded, $this->get_manually_excluded_keywords() ) );

		return $excluded;
	}

	/**
	 * Get manually excluded keywords.
	 *
	 * @return array
	 */
	public function get_manually_excluded_keywords() {
		$options             = Utils::get_module( 'minify' )->get_options();
		$delay_js_exclusions = $options['delay_js_exclusions'];

		if ( ! is_array( $delay_js_exclusions ) ) {
			$delay_js_exclusions = explode( "\n", $delay_js_exclusions );
		}

		$delay_js_exclusions = array_filter( $delay_js_exclusions );

		if ( ! $delay_js_exclusions ) {
			$delay_js_exclusions = array();
		}

		return $delay_js_exclusions;
	}

	/**
	 * Get all exclusions related to assets path.
	 *
	 * @return array
	 */
	public function get_combined_asset_path_exclusion_list_for_delay_js() {
		$exclusions = array_merge(
			$this->get_exclusion_setting( 'delay_js_files_exclusion' ),
			$this->get_excluded_js_ads_tracker(),
			$this->get_formatted_theme_plugin_exclusions(),
			$this->get_excluded_keywords(),
		);

		$exclusions = array_map( array( $this, 'wphb_clean_scripts' ), $exclusions );

		/**
		 * Filter the list of all exclusions related to assets path.
		 *
		 * @param array $exclusions The list of exclusions.
		 */
		return apply_filters( 'wphb_delay_js_exclusions', $exclusions );
	}

	/**
	 * Cleans JS scripts.
	 *
	 * @since 3.11.0
	 *
	 * @param array $value Script src value.
	 *
	 * @return string
	 */
	public function wphb_clean_scripts( $value ) {
		return trim( str_replace( array( '+', '?ver', '#' ), array( '\+', '\?ver', '\#' ), $value ) );
	}

	/**
	 * Retrieve settings for the exclusion textareas, including default values and descriptions for critical css.
	 *
	 * @return array
	 */
	public function get_critical_css_exclusion_settings() {
		return $this->get_exclusion_settings( 'styles', 'critical_css_' );
	}

	/**
	 * Get selected exclusion for page types and custom post types.
	 *
	 * @return array
	 */
	public function get_critical_css_excluded_post_types() {
		$page_types              = $this->get_exclusion_setting( 'critical_page_types' );
		$all_pages_type          = Page_Cache::get_page_types( true );
		$all_excluded_pages_type = array_diff( $all_pages_type, $page_types );
		$custom_post_types       = $this->get_exclusion_setting( 'critical_skipped_custom_post_types' );

		return array_merge( $all_excluded_pages_type, $custom_post_types );
	}

	/**
	 * Get the exclusion setting.
	 *
	 * @param string $setting_key The setting key to retrieve the post URLs from.
	 *
	 * @return array
	 */
	private function get_exclusion_setting( $setting_key ) {
		return Settings::get_setting( $setting_key, 'minify' );
	}

	/**
	 * Get an array of assets based on the type.
	 *
	 * @param string $type Type.
	 *
	 * @return array
	 */
	public function get_assets( $type ) {
		$assets = Sources_Collector::get_collection();

		return 'styles' === $type ? $assets['styles'] : $assets['scripts'];
	}

	/**
	 * Get all criticL CSS exclusions related to assets path.
	 *
	 * @return array
	 */
	public function get_combined_asset_path_exclusion_list_for_critical_css() {
		$exclusions = array_merge(
			$this->get_exclusion_setting( 'critical_css_files_exclusion' ),
			$this->get_exclusion_setting( 'critical_css_keywords' ),
			$this->get_formatted_theme_plugin_exclusions( 'critical_css_plugins_themes_exclusion' ),
		);

		/**
		 * Filter the list of all exclusions related to assets path for critical CSS.
		 *
		 * @param array $exclusions The list of exclusions.
		 */
		return apply_filters( 'wphb_critical_css_exclusions', $exclusions );
	}
}