wp_back/wp-content/plugins/wp-asset-clean-up/classes/Main.php

3544 lines
125 KiB
PHP
Raw Permalink Normal View History

2024-05-20 15:37:46 +03:00
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\CombineJs;
use WpAssetCleanUp\OptimiseAssets\DynamicLoadedAssets;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
/**
* Class Main
* @package WpAssetCleanUp
*/
class Main
{
/**
*
*/
const START_DEL_ENQUEUED = 'BEGIN WPACU PLUGIN JSON ENQUEUED';
/**
*
*/
const END_DEL_ENQUEUED = 'END WPACU PLUGIN JSON ENQUEUED';
/**
*
*/
const START_DEL_HARDCODED = 'BEGIN WPACU PLUGIN JSON HARDCODED';
/**
*
*/
const END_DEL_HARDCODED = 'END WPACU PLUGIN JSON HARDCODED';
/**
* Option "Plugin Usage Preferences" -> "Manage in the Dashboard" -> "Select a retrieval way:" ('Direct' or 'WP Remote Post')
*
* @var string
*/
public static $domGetType = 'direct';
/**
* Record them for debugging purposes when using /?wpacu_debug
*
* @var array
*/
public $allUnloadedAssets = array( 'styles' => array(), 'scripts' => array() );
/**
* @var array
*/
public $globalUnloaded = array();
/**
* @var array
*/
public $loadExceptionsPageLevel = array( 'styles' => array(), 'scripts' => array(), '_set' => false );
/**
* @var array[]
*/
public $loadExceptionsPostType = array( 'styles' => array(), 'scripts' => array(), '_set' => false );
/**
* Rule that applies site-wide: if the user is logged-in
*
* @var array
*/
public $loadExceptionsLoggedInGlobal = array( 'styles' => array(), 'scripts' => array(), '_set' => false );
/**
* @var
*/
public $fetchUrl;
// [wpacu_lite]
/**
* @var
*/
public $isUpdateable = true;
// [/wpacu_lite]
/**
* @var int
*/
public $currentPostId = 0;
/**
* @var array
*/
public $currentPost = array();
/**
* @var array
*/
public static $vars = array( 'woo_url_not_match' => false, 'is_woo_shop_page' => false, 'for_type' => '', 'current_post_id' => 0, 'current_post_type' => '' );
/**
* This is set to `true` only if "Manage in the Front-end?" is enabled in plugin's settings
* and the logged-in administrator with plugin activation privileges
* is outside the Dashboard viewing the pages like a visitor
*
* @var bool
*/
public $isFrontendEditView = false;
/**
* @var array
*/
public $stylesInHead = array();
/**
* @var array
*/
public $scriptsInHead = array();
/**
* @var array
*/
public $assetsInFooter = array( 'styles' => array(), 'scripts' => array() );
/**
* @var array
*/
public $wpAllScripts = array();
/**
* @var array
*/
public $wpAllStyles = array();
/**
* @var array
*/
public $ignoreChildren = array();
/**
* @var array
*/
public $ignoreChildrenHandlesOnTheFly = array();
/**
* @var int
*/
public static $wpStylesSpecialDelimiters = array(
'start' => '<!--START-WPACU-SPECIAL-STYLES',
'end' => 'END-WPACU-SPECIAL-STYLES-->'
);
/**
* @var array
*/
public $postTypesUnloaded = array();
/**
* @var array
*/
public $settings = array();
/**
* @var bool
*/
public $isAjaxCall = false;
/**
* Fetch CSS/JS list from the Dashboard
*
* @var bool
*/
public $isGetAssetsCall = false;
/**
* Populated in the Parser constructor
*
* @var array
*/
public $skipAssets = array( 'styles' => array(), 'scripts' => array() );
/**
* For these handles, it's strongly recommended to use 'Ignore dependency rule and keep the "children" loaded'
* if any of them are unloaded in any page
*
* @var \string[][]
*/
public $keepChildrenLoadedForHandles = array(
'css' => array(
'elementor-icons'
),
'js' => array(
'swiper',
'elementor-waypoints',
'share-link'
)
);
/**
* @var Main|null
*/
private static $singleton;
/**
* @return null|Main
*/
public static function instance()
{
if ( self::$singleton === null ) {
self::$singleton = new self();
}
return self::$singleton;
}
/**
* Parser constructor.
*/
public function __construct()
{
$this->skipAssets['styles'] = array_merge(
array(
'admin-bar',
// The top admin bar
'yoast-seo-adminbar',
// Yoast "WordPress SEO" plugin
'autoptimize-toolbar',
'query-monitor',
'wp-fastest-cache-toolbar',
// WP Fastest Cache plugin toolbar CSS
'litespeed-cache',
// LiteSpeed toolbar
'siteground-optimizer-combined-styles-header'
// Combine CSS in SG Optimiser (irrelevant as it made from the combined handles)
),
// Own Scripts (for admin use only)
OwnAssets::getOwnAssetsHandles('styles')
);
$this->skipAssets['scripts'] = array_merge(
array(
'admin-bar', // The top admin bar
'autoptimize-toolbar',
'query-monitor',
'wpfc-toolbar' // WP Fastest Cache plugin toolbar JS
),
// Own Scripts (for admin use only)
OwnAssets::getOwnAssetsHandles('scripts')
);
// Filter before triggering the actual unloading through "wp_deregister_script", "wp_dequeue_script", "wp_deregister_style", "wp_dequeue_style"
$this->fallbacks();
$this->isAjaxCall = ( ! empty( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) === 'xmlhttprequest' );
$this->isGetAssetsCall = isset( $_REQUEST[ WPACU_LOAD_ASSETS_REQ_KEY ] ) && $_REQUEST[ WPACU_LOAD_ASSETS_REQ_KEY ];
if ( $this->isGetAssetsCall ) {
$currentTheme = strtolower(wp_get_theme());
$noRocketInit = true;
if (strpos($currentTheme, 'uncode') !== false) {
$noRocketInit = false; // make exception for the "Uncode" Theme as it doesn't check if the get_rocket_option() function exists
}
if ($noRocketInit) {
add_filter('rocket_cache_reject_uri', function($urls) {
$urls[] = '/?wpassetcleanup_load=1';
return $urls;
});
}
// Do not output Query Monitor's information as it's irrelevant in this context
if ( class_exists( '\QueryMonitor' ) && class_exists( '\QM_Plugin' ) ) {
add_filter( 'user_has_cap', static function( $user_caps ) {
$user_caps['view_query_monitor'] = false;
return $user_caps;
}, 10, 1 );
}
add_filter( 'style_loader_tag', static function( $styleTag, $tagHandle ) {
// This is used to determine if the LINK is enqueued later on
// If the handle name is not showing up, then the LINK stylesheet has been hardcoded (not enqueued the WordPress way)
return str_replace( '<link ', '<link data-wpacu-style-handle=\'' . $tagHandle . '\' ', $styleTag );
}, PHP_INT_MAX, 2 ); // Trigger it later in case plugins such as "Ronneby Core" plugin alters it
add_filter( 'script_loader_tag', static function( $scriptTag, $tagHandle ) {
// This is used to determine if the SCRIPT is enqueued later on
// If the handle name is not showing up, then the SCRIPT has been hardcoded (not enqueued the WordPress way)
$reps = array( '<script ' => '<script data-wpacu-script-handle=\'' . $tagHandle . '\' ' );
return str_replace( array_keys( $reps ), array_values( $reps ), $scriptTag );
}, PHP_INT_MAX, 2 );
}
// "File Size:" value from the asset row
add_filter('wpacu_get_asset_file_size', array($this, 'getAssetFileSize'), 10, 2);
// Triggers only if the administrator is logged in ('wp_ajax_nopriv' is not required)
// Used to determine the total size of an external loaded assets (e.g. a CSS file from Google APIs)
add_action('wp_ajax_'.WPACU_PLUGIN_ID.'_get_external_file_size', array($this, 'ajaxGetExternalFileSize'));
// Early Triggers
$wpacuAction = Misc::isElementorMaintenanceModeOn() ? 'template_redirect' : 'wp';
if ($wpacuAction === 'wp') {
add_action( 'wp', array( $this, 'setVarsBeforeUpdate' ), 8 );
add_action( 'wp', array( $this, 'setVarsAfterAnyUpdate' ), 10 );
} else {
add_action( 'template_redirect', array( $this, 'setVarsBeforeUpdate' ), 12 ); // over 11 which is set in Elementor's maintenance-mode.php
add_action( 'template_redirect', array( $this, 'setVarsAfterAnyUpdate' ), 13 );
}
// Fetch Assets AJAX Call? Make sure the output is as clean as possible (no plugins interfering with it)
// It can also be used for debugging purposes (via /?wpacu_clean_load) when you want to view all the CSS/JS
// that are loaded in the HTML source code before they are unloaded or altered in any way
if ( $this->isGetAssetsCall || isset($_GET['wpacu_clean_load']) ) {
$wpacuCleanUp = new CleanUp();
$wpacuCleanUp->cleanUpHtmlOutputForAssetsCall();
}
// "Direct" AJAX call or "WP Remote Post" method used?
// Do not trigger the admin bar as it's not relevant
if ( $this->isAjaxCall || $this->isGetAssetsCall ) {
add_filter( 'show_admin_bar', '__return_false' );
}
// This is triggered AFTER "saveSettings" from 'Settings' class
// In case the settings were just updated, the script will get the latest values
add_action( 'init', array( $this, 'triggersAfterInit' ), 10 );
// Front-end View - Unload the assets
// If there are reasons to prevent the unloading in case 'test mode' is enabled,
// then the prevention will trigger within filterStyles() and filterScripts()
if ( ! is_admin() ) {
$this->alterWpStylesScriptsObj();
}
/*
* [START] /?wpassetcleanup_load=1 is called
*/
if ( $this->isGetAssetsCall && ! is_admin() ) {
// These actions are also called when the page is loaded without query string (regular load)
// This time, the CSS/JS will not be unloaded, but the CSS/JS marked for unload will be collected
// and passed to the AJAX call for the option "Group by loaded or unloaded status"
if ( get_option( 'siteground_optimizer_combine_css' ) ) {
add_action( 'wp_print_styles', array( $this, 'filterStyles' ), 9 ); // priority should be below 10
}
add_action( 'wp_print_styles', array( $this, 'filterStyles' ), 100000 );
add_action( 'wp_print_scripts', array( $this, 'filterScripts' ), 100000 );
add_action( 'wp_print_footer_scripts', array( $this, 'onPrintFooterScriptsStyles' ), 1 );
}
/*
* [END] /?wpassetcleanup_load=1 is called
*/
/*
* [START] Front-end page visited (e.g. by the admin or a guest visitor)
*/
if ( ! $this->isGetAssetsCall && ! is_admin() ) {
// [START] Unload CSS/JS on page request (for debugging)
add_filter( 'wpacu_ignore_child_parent_list', array( $this, 'filterIgnoreChildParentList' ) );
// [END] Unload CSS/JS on page request (for debugging)
// SG Optimizer Compatibility: Unload Styles - HEAD (Before pre_combine_header_styles() from Combinator)
if ( get_option( 'siteground_optimizer_combine_css' ) ) {
add_action( 'wp_print_styles', array( $this, 'filterStyles' ), 9 ); // priority should be below 10
}
$this->filterStylesSpecialCases(); // e.g. CSS enqueued in a different way via Oxygen Builder
add_action( 'wp_print_styles', array( $this, 'filterStyles' ), 100000 ); // Unload Styles - HEAD
add_action( 'wp_print_scripts', array( $this, 'filterScripts' ), 100000 ); // Unload Scripts - HEAD
add_action( 'wp_print_styles', array($this, 'printAnySpecialCss'), PHP_INT_MAX );
// Unload Styles & Scripts - FOOTER
// Needs to be triggered very soon as some old plugins/themes use wp_footer() to enqueue scripts
// Sometimes styles are loaded in the BODY section of the page
add_action( 'wp_print_footer_scripts', array( $this, 'onPrintFooterScriptsStyles' ), 1 );
// Preloads
add_action( 'wp_head', static function() {
if ( Plugin::preventAnyFrontendOptimization() || self::isTestModeActive() ) {
return;
}
// Only place the market IF there's at least one preload OR combine JS is activated
$preloadsClass = new Preloads();
if ( isset( $preloadsClass->preloads[ 'styles' ] ) && ! empty( $preloadsClass->preloads[ 'styles' ] ) ) {
echo Preloads::DEL_STYLES_PRELOADS;
}
if ( (isset( $preloadsClass->preloads[ 'scripts' ] ) && ! empty( $preloadsClass->preloads[ 'scripts' ] )) || CombineJs::proceedWithJsCombine() ) {
echo Preloads::DEL_SCRIPTS_PRELOADS;
}
}, 1 );
add_filter( 'style_loader_tag', static function( $styleTag, $tagHandle ) {
if ( Plugin::preventAnyFrontendOptimization() ) {
return $styleTag;
}
// Preload the plugin's CSS for assets management layout (for faster content paint if the user is logged-in and manages the assets in the front-end)
// For a better admin experience
if ( $tagHandle === WPACU_PLUGIN_ID . '-style' ) {
$styleTag = str_ireplace(
array( '<link ', 'rel=\'stylesheet\'', 'rel="stylesheet"', 'id=\'', 'id="' ),
array(
'<link rel=\'preload\' as=\'style\' data-wpacu-preload-it-async=\'1\' ',
'onload="this.onload=null;this.rel=\'stylesheet\'"',
'onload="this.rel=\'stylesheet\'"',
'id=\'wpacu-preload-',
'id="wpacu-preload-'
),
$styleTag
);
}
// Irrelevant for Critical CSS as the top admin bar is for logged-in users
// and if it's not included in the critical CSS it would cause a flash of unstyled content which is not pleasant for the admin
if ( $tagHandle === 'admin-bar' ) {
$styleTag = str_replace( '<link ', '<link data-wpacu-skip-preload=\'1\' ', $styleTag );
}
if ( Plugin::preventAnyFrontendOptimization() || self::isTestModeActive() ) {
return $styleTag;
}
// Alter for debugging purposes; triggers before anything else
// e.g. you're working on a website and there is no Dashboard access and you want to determine the handle name
// if the handle name is not showing up, then the LINK stylesheet has been hardcoded (not enqueued the WordPress way)
if ( isset($_GET['wpacu_show_handle_names']) ) {
$styleTag = str_replace( '<link ', '<link data-wpacu-debug-style-handle=\'' . $tagHandle . '\' ', $styleTag );
}
if ( strpos( $styleTag, 'data-wpacu-style-handle' ) === false ) {
$styleTag = str_replace( '<link ', '<link data-wpacu-style-handle=\'' . $tagHandle . '\' ', $styleTag );
}
return $styleTag;
}, PHP_INT_MAX, 2 ); // Trigger it later in case plugins such as "Ronneby Core" plugin alters it
add_filter( 'script_loader_tag', static function( $scriptTag, $tagHandle ) {
if ( Plugin::preventAnyFrontendOptimization() ) {
return $scriptTag;
}
// Alter for debugging purposes; triggers before anything else
// e.g. you're working on a website and there is no Dashboard access and you want to determine the handle name
// if the handle name is not showing up, then the SCRIPT has been hardcoded (not enqueued the WordPress way)
if ( isset($_GET['wpacu_show_handle_names']) ) {
$scriptTag = str_replace( '<script ', '<script data-wpacu-debug-script-handle=\'' . $tagHandle . '\' ', $scriptTag );
}
if ( strpos( $scriptTag, 'data-wpacu-script-handle' ) === false
&& Menu::userCanManageAssets()
&& self::instance()->isFrontendEditView ) {
$scriptTag = str_replace( '<script ', '<script data-wpacu-script-handle=\'' . $tagHandle . '\' ', $scriptTag );
}
if ( Plugin::preventAnyFrontendOptimization() || self::isTestModeActive() ) {
return $scriptTag;
}
if ( strpos( $scriptTag, 'data-wpacu-script-handle' ) === false ) {
$scriptTag = str_replace( '<script ', '<script data-wpacu-script-handle=\'' . $tagHandle . '\' ', $scriptTag );
}
if ( $tagHandle === 'jquery-core' ) {
$scriptTag = str_replace( '<script ', '<script data-wpacu-jquery-core-handle=1 ', $scriptTag );
}
if ( $tagHandle === 'jquery-migrate' ) {
$scriptTag = str_replace( '<script ', '<script data-wpacu-jquery-migrate-handle=1 ', $scriptTag );
}
return $scriptTag;
}, PHP_INT_MAX, 2 );
Preloads::instance()->init();
}
/*
* [END] Front-end page visited (e.g. by the admin or a guest visitor)
*/
// Only trigger it within the Dashboard when an Asset CleanUp (Pro) page is accessed and the transient is non-existent or expired
if ( is_admin() ) {
add_action( 'admin_footer', array( $this, 'ajaxFetchActivePluginsJsFooterCode' ) );
add_action( 'wp_ajax_' . WPACU_PLUGIN_ID . '_fetch_active_plugins_icons', array( $this, 'ajaxFetchActivePluginsIconsFromWordPressOrg' ) );
}
add_filter( 'duplicate_post_meta_keys_filter', static function( $meta_keys ) {
// Get the original post ID
$postId = isset( $_GET['post'] ) ? (int)$_GET['post'] : false;
if ( ! $postId ) {
$postId = isset( $_POST['post'] ) ? (int)$_POST['post'] : false;
}
if ( $postId ) {
global $wpdb;
$metaKeyLike = '_' . WPACU_PLUGIN_ID . '_%';
$assetCleanUpMetaKeysQuery = <<<SQL
SELECT `meta_key` FROM {$wpdb->postmeta} WHERE meta_key LIKE '{$metaKeyLike}' AND post_id='{$postId}'
SQL;
$assetCleanUpMetaKeys = $wpdb->get_col( $assetCleanUpMetaKeysQuery );
if ( ! empty( $assetCleanUpMetaKeys ) ) {
$meta_keys = array_merge( $meta_keys, $assetCleanUpMetaKeys );
}
}
return $meta_keys;
} );
$this->wpacuHtmlNoticeForAdmin();
add_action( 'wp_ajax_' . WPACU_PLUGIN_ID . '_check_external_urls_for_status_code', array( $this, 'ajaxCheckExternalUrlsForStatusCode' ) );
}
/**
*
*/
public function triggersAfterInit()
{
$wpacuSettingsClass = new Settings();
$this->settings = $wpacuSettingsClass->getAll();
if ( $this->settings['dashboard_show'] && $this->settings['dom_get_type'] ) {
self::$domGetType = $this->settings['dom_get_type'];
}
// Fetch the page in the background to see what scripts/styles are already loading
// This applies only for front-end loading
if ( ! is_admin() && ($this->isGetAssetsCall || $this->frontendShow()) ) {
if ( $this->isGetAssetsCall ) {
add_filter( 'show_admin_bar', '__return_false' );
}
// Save CSS handles list that is printed in the <HEAD>
// No room for errors, some developers might enqueue (although not ideal) assets via "wp_head" or "wp_print_styles"/"wp_print_scripts"
add_action( 'wp_enqueue_scripts', array( $this, 'saveHeadAssets' ), PHP_INT_MAX - 1 );
// Save CSS/JS list that is printed in the <BODY>
add_action( 'wp_print_footer_scripts', array( $this, 'saveFooterAssets' ), 100000000 );
add_action( 'wp_footer', array( $this, 'printScriptsStyles' ), ( PHP_INT_MAX - 1 ) );
}
// Send an AJAX request to get the list of the loaded hardcoded scripts and styles and print it
add_action( 'wp_ajax_' . WPACU_PLUGIN_ID . '_print_loaded_hardcoded_assets', array( $this, 'ajaxPrintLoadedHardcodedAssets' ) );
if ( is_admin() ) {
$metaboxes = new MetaBoxes;
// Do not load the meta box nor do any AJAX calls
// if the asset management is not enabled for the Dashboard
if ( $this->settings['dashboard_show'] == 1 ) {
// Send an AJAX request to get the list of loaded scripts and styles and print it nicely
add_action( 'wp_ajax_' . WPACU_PLUGIN_ID . '_get_loaded_assets', array( $this, 'ajaxGetJsonListCallback' ) );
// This is valid when the Gutenberg editor (not via "Classic Editor" plugin) is used and the user used the following option:
// "Do not load Asset CleanUp Pro on this page (this will disable any functionality of the plugin)"
add_action( 'wp_ajax_' . WPACU_PLUGIN_ID . '_load_page_restricted_area', array( $this, 'ajaxLoadRestrictedPageAreaCallback' ) );
}
// If assets management within the Dashboard is not enabled, an explanation message will be shown within the box unless the meta box is hidden completely
if ( $this->settings['show_assets_meta_box'] ) {
$metaboxes->initMetaBox( 'manage_page_assets' );
}
}
/*
DO NOT disable the features below if the following apply:
- The option is not enabled
- Test Mode Enabled & Admin Logged in
- The user is in the Dashboard (any changes are applied in the front-end view)
*/
if ( ! ( $this->preventAssetsSettings() || is_admin() ) ) {
if ( $this->settings['disable_emojis'] == 1 ) {
$wpacuCleanUp = new CleanUp();
$wpacuCleanUp->doDisableEmojis();
}
if ( $this->settings['disable_oembed'] == 1 ) {
$wpacuCleanUp = new CleanUp();
$wpacuCleanUp->doDisableOembed();
}
}
}
/**
* Priority: 8 (earliest)
*/
public function setVarsBeforeUpdate()
{
// Conditions
// 1) User has rights to manage the assets and the option is enabled in plugin's Settings
// 2) Not an AJAX call from the Dashboard
// 3) Not inside the Dashboard
$this->isFrontendEditView = ( $this->frontendShow() && Menu::userCanManageAssets() // 1
&& ! $this->isGetAssetsCall // 2
&& ! is_admin() ); // 3
if ( $this->isFrontendEditView ) {
$wpacuCleanUp = new CleanUp();
$wpacuCleanUp->cleanUpHtmlOutputForAssetsCall();
}
$this->getCurrentPostId();
define( 'WPACU_CURRENT_PAGE_ID', $this->getCurrentPostId() );
}
/**
* Priority: 10 (latest)
*/
public function setVarsAfterAnyUpdate()
{
if ( ! is_admin() ) {
$this->globalUnloaded = $this->getGlobalUnload();
// [wpacu_lite]
if (! $this->isUpdateable) {
return;
}
// [/wpacu_lite]
$getCurrentPost = $this->getCurrentPost();
$post = $type = false;
if (Misc::isHomePage() && empty($getCurrentPost) && ! get_option('page_on_front')) {
$type = 'front_page';
} elseif ( ! empty($getCurrentPost) ) {
$type = 'post';
$post = $getCurrentPost;
$this->postTypesUnloaded = (isset($post->post_type) && $post->post_type) ? $this->getBulkUnload('post_type', $post->post_type) : array();
}
// [wpacu_lite]
if (! $type) {
// The request is done for a page such as is_archive(), is_author(), 404, search
// and the premium extension is not available, thus no load exceptions are available
return;
}
// [/wpacu_lite]
self::$vars['for_type'] = $type;
self::$vars['current_post_id'] = $this->currentPostId;
if ($post && $type === 'post' && isset($post->post_type) && $post->post_type) {
self::$vars['current_post_type'] = $post->post_type;
}
}
}
/**
* This is useful to change via hooks the "src", "ver" or other values of the loaded handle
* Example: You have your theme's main style.css that is needed on every page
* On some pages, you only need 20% of it to load, and you can manually trim the other 80% (if you're sure you know which CSS is not used)
* You can use a filter hook such as 'wpacu_{main_theme_handle_name_here}_css_handle_obj' to filter the "src" of the object and load an alternative purified CSS file
*/
public function alterWpStylesScriptsObj()
{
add_action('wp_print_styles', function() {
global $wp_styles;
if ( ! empty($wp_styles->registered) ) {
foreach (array_keys($wp_styles->registered) as $styleHandle) {
$wp_styles->registered[$styleHandle] = $this->maybeFilterAssetObject($wp_styles->registered[$styleHandle], 'css');
}
}
});
foreach (array('wp_print_scripts', 'wp_print_footer_scripts') as $actionToAdd) {
add_action( $actionToAdd, function() {
global $wp_scripts;
if ( ! empty($wp_scripts->registered) ) {
foreach (array_keys($wp_scripts->registered) as $scriptHandle) {
$wp_scripts->registered[$scriptHandle] = $this->maybeFilterAssetObject($wp_scripts->registered[$scriptHandle], 'js');
}
}
});
}
}
/**
* @param $object | as returned from $wp_styles or $wp_scripts
* @param $assetType | "css" or "js"
*
* @return mixed
*/
public function maybeFilterAssetObject($object, $assetType)
{
if ( isset($_GET['wpacu_clean_load']) || isset($_GET['wpacu_load_original']) ) {
return $object; // this is for debugging purposes, load the original source
}
if ( ! isset($object->handle, $object->src) ) {
return $object;
}
$filterTagName = 'wpacu_'.$object->handle.'_'.$assetType.'_handle_data';
if ( has_filter($filterTagName) ) {
$originData = (array)$object;
$newData = apply_filters( $filterTagName, $originData );
if ( isset($originData['src'], $newData['src']) && $newData['src'] !== $originData['src'] ) {
$object->src = $newData['src'];
$object->src_origin = $originData['src'];
$object->ver = $newData['ver'] ?: null;
$object->ver_origin = isset($originData['ver']) ? $originData['ver'] : null;
}
}
return $object;
}
/**
* In case there were assets enqueued within "wp_footer" action hook, instead of the standard "wp_enqueue_scripts"
*/
public function onPrintFooterScriptsStyles()
{
$this->filterStyles();
$this->filterScripts();
}
/* [START] Styles Dequeue */
/**
* See if there is any list with styles to be removed in JSON format
* Only the handles (the ID of the styles) is stored
*/
public function filterStyles()
{
/* [wpacu_timing] */ Misc::scriptExecTimer( 'filter_dequeue_styles' );/* [/wpacu_timing] */
if (is_admin()) {
return;
}
global $wp_styles;
if (current_action() === 'wp_print_styles') {
ObjectCache::wpacu_cache_set('wpacu_styles_object_after_wp_print_styles', $wp_styles);
}
$list = array();
if (current_action() === 'wp_print_footer_scripts') {
$cachedWpStyles = ObjectCache::wpacu_cache_get('wpacu_styles_object_after_wp_print_styles');
if (isset($cachedWpStyles->registered) && count($cachedWpStyles->registered) === count($wp_styles->registered)) {
// The list was already generated in "wp_print_styles" and the number of registered assets are the same
// Save resources and do not re-generate it
$list = ObjectCache::wpacu_cache_get('wpacu_styles_handles_marked_for_unload');
}
}
if ( empty($list) || ! is_array($list) ) {
// [wpacu_lite]
$nonAssetConfigPage = ( ! $this->isUpdateable && ! Misc::getShowOnFront() );
// [/wpacu_lite]
// It looks like the page loaded is neither a post, page or the front-page
// We'll see if there are assets unloaded globally and unload them
/*
* [START] Build unload list
*/
$globalUnload = $this->globalUnloaded;
// [wpacu_lite]
if ( $nonAssetConfigPage && ! empty( $globalUnload['styles'] ) ) {
$list = $globalUnload['styles'];
} else {
// [/wpacu_lite]
// Post, Page, Front-page
// and more (if the Premium Extension is activated)
$toRemove = $this->getAssetsUnloadedPageLevel();
$jsonList = @json_decode( $toRemove );
$list = array();
if ( isset( $jsonList->styles ) ) {
$list = (array) $jsonList->styles;
}
if (! is_array($list)) {
$list = array();
}
// Any global unloaded styles? Append them
if ( ! empty( $globalUnload['styles'] ) ) {
foreach ( $globalUnload['styles'] as $handleStyle ) {
$list[] = $handleStyle;
}
}
if ( self::isSingularPage() ) {
// Any bulk unloaded styles (e.g. for all pages belonging to a post type)? Append them
if ( empty( $this->postTypesUnloaded ) ) {
$post = $this->getCurrentPost();
$this->postTypesUnloaded = ( isset( $post->post_type ) && $post->post_type ) ? $this->getBulkUnload( 'post_type', $post->post_type ) : array();
}
if ( isset( $this->postTypesUnloaded['styles'] ) && ! empty( $this->postTypesUnloaded['styles'] ) ) {
foreach ( $this->postTypesUnloaded['styles'] as $handleStyle ) {
$list[] = $handleStyle;
}
}
}
// [wpacu_lite]
}
// [/wpacu_lite]
// Site-Wide Unload for "Dashicons" if user is not logged-in
if ( $this->settings['disable_dashicons_for_guests'] && ! is_user_logged_in() ) {
$list[] = 'dashicons';
}
// Any bulk unloaded styles for 'category', 'post_tag' and more?
// If the Pro version is enabled, any of the unloaded CSS will be added to the list
$list = apply_filters( 'wpacu_filter_styles_list_unload', array_unique( $list ) );
/*
* [END] Build unload list
*/
// Add handles such as the Oxygen Builder CSS ones that are missing and added differently to the queue
$allStyles = $this->wpStylesFilter( $wp_styles, 'registered', $list );
if ( $allStyles !== null && ! empty( $allStyles->registered ) ) {
// Going through all the registered styles
foreach ( $allStyles->registered as $handle => $value ) {
// This could be triggered several times, check if the style already exists
if ( ! isset( $this->wpAllStyles['registered'][ $handle ] ) ) {
$this->wpAllStyles['registered'][ $handle ] = $value;
if ( in_array( $handle, $allStyles->queue ) ) {
$this->wpAllStyles['queue'][] = $handle;
}
}
}
if ( isset( $this->wpAllStyles['queue'] ) && ! empty( $this->wpAllStyles['queue'] ) ) {
$this->wpAllStyles['queue'] = array_unique( $this->wpAllStyles['queue'] );
}
}
if ( isset( $this->wpAllStyles['registered'] ) && ! empty( $this->wpAllStyles['registered'] ) ) {
ObjectCache::wpacu_cache_set( 'wpacu_all_styles_handles', array_keys( $this->wpAllStyles['registered'] ) );
}
// e.g. for test/debug mode or AJAX calls (where all assets have to load)
if ( isset($_REQUEST['wpacu_no_css_unload']) ) {
/* [wpacu_timing] */Misc::scriptExecTimer( 'filter_dequeue_styles', 'end' ); /* [/wpacu_timing] */
return;
}
if ( $this->preventAssetsSettings(array('assets_call')) ) {
/* [wpacu_timing] */Misc::scriptExecTimer( 'filter_dequeue_styles', 'end' ); /* [/wpacu_timing] */
return;
}
/*
* [START] Load Exception Check
* */
// Load exception rules ALWAYS have priority over the unloading ones
// Thus, if an exception is found, the handle will be removed from the unloading list
// Let's see if there are load exceptions for this page or site-wide (e.g. for logged-in users)
// Only check for any load exceptions if the unloading list has at least one item
// Otherwise the action is irrelevant since the assets are loaded anyway by default
// These are LITE rules
$list = ( ! empty($list) ) ? $this->filterAssetsUnloadList($list, 'styles','load_exception') : $list;
// These are PRO rules or rules added via custom coding
$list = ( ! empty($list) ) ? apply_filters('wpacu_filter_styles_list_load_exception', $list) : $list;
/*
* [END] Load Exception Check
* */
// Is $list still empty? Nothing to unload? Stop here
if (empty($list)) {
/* [wpacu_timing] */ Misc::scriptExecTimer( 'filter_dequeue_styles', 'end' ); /* [/wpacu_timing] */
return;
}
}
$ignoreChildParentList = apply_filters('wpacu_ignore_child_parent_list', $this->getIgnoreChildren());
foreach ($list as $handle) {
if (isset($ignoreChildParentList['styles'], $this->wpAllStyles['registered'][$handle]->src) && is_array($ignoreChildParentList['styles']) && array_key_exists($handle, $ignoreChildParentList['styles'])) {
// Do not dequeue it as it's "children" will also be dequeued (ignore rule is applied)
// It will be stripped by cleaning its LINK tag from the HTML Source
$this->ignoreChildren['styles'][$handle] = $this->wpAllStyles['registered'][$handle]->src;
$this->ignoreChildren['styles'][$handle.'_has_unload_rule'] = 1;
$this->allUnloadedAssets['styles'][] = $handle;
continue;
}
$handle = trim($handle);
// Ignore auto generated handles for the hardcoded CSS as they were added for reference purposes
// They will get stripped later on via OptimizeCommon.php
// This is in case there are references from using the Pro version
if (strpos($handle, 'wpacu_hardcoded_link_') === 0) {
continue;
}
if (strpos($handle, 'wpacu_hardcoded_style_') === 0) {
continue;
}
// Do not unload "dashicons" if the top WordPress admin bar is showing up
if ($handle === 'dashicons' && is_admin_bar_showing()) {
continue;
}
$this->allUnloadedAssets['styles'][] = $handle;
// Only trigger the unloading on regular page load, not when the assets list is collected
if ( ! $this->isGetAssetsCall ) {
wp_deregister_style( $handle );
wp_dequeue_style( $handle );
}
}
if (current_action() === 'wp_print_styles') {
ObjectCache::wpacu_cache_set( 'wpacu_styles_handles_marked_for_unload', $list );
}
/* [wpacu_timing] */ Misc::scriptExecTimer( 'filter_dequeue_styles', 'end' ); /* [/wpacu_timing] */
}
/**
* @param $wpStylesFilter
* @param string $listType
* @param array $unloadedList
*
* @return mixed
*/
public function wpStylesFilter($wpStylesFilter, $listType, $unloadedList = array())
{
global $wp_styles, $oxygen_vsb_css_styles;
if ( ( $listType === 'registered' ) && isset( $oxygen_vsb_css_styles->registered ) && is_object( $oxygen_vsb_css_styles ) && ! empty( $oxygen_vsb_css_styles->registered ) ) {
$stylesSpecialCases = array();
foreach ($oxygen_vsb_css_styles->registered as $oxygenHandle => $oxygenValue) {
if (! array_key_exists($oxygenHandle, $wp_styles->registered)) {
$wpStylesFilter->registered[$oxygenHandle] = $oxygenValue;
$stylesSpecialCases[$oxygenHandle] = $oxygenValue->src;
}
}
$unloadedSpecialCases = array();
foreach ($unloadedList as $unloadedHandle) {
if (array_key_exists($unloadedHandle, $stylesSpecialCases)) {
$unloadedSpecialCases[$unloadedHandle] = $stylesSpecialCases[$unloadedHandle];
}
}
if (! empty($unloadedSpecialCases)) {
// This will be later used in 'wp_loaded' below to extract the special styles
echo self::$wpStylesSpecialDelimiters['start'] . wp_json_encode($unloadedSpecialCases) . self::$wpStylesSpecialDelimiters['end'];
}
}
if ( ( $listType === 'done' ) && isset( $oxygen_vsb_css_styles->done ) && is_object( $oxygen_vsb_css_styles ) ) {
foreach ($oxygen_vsb_css_styles->done as $oxygenHandle) {
if (! in_array($oxygenHandle, $wp_styles->done)) {
$wpStylesFilter[] = $oxygenHandle;
}
}
}
if ( ( $listType === 'queue' ) && isset( $oxygen_vsb_css_styles->queue ) && is_object( $oxygen_vsb_css_styles ) ) {
foreach ($oxygen_vsb_css_styles->queue as $oxygenHandle) {
if (! in_array($oxygenHandle, $wp_styles->queue)) {
$wpStylesFilter[] = $oxygenHandle;
}
}
}
return $wpStylesFilter;
}
/**
*
*/
public function filterStylesSpecialCases()
{
if ( isset($_REQUEST['wpacu_no_css_unload']) ) {
return;
}
add_action('wp_loaded', static function() {
ob_start(static function($htmlSource) {
if (strpos($htmlSource, self::$wpStylesSpecialDelimiters['start']) === false && strpos($htmlSource, self::$wpStylesSpecialDelimiters['end']) === false) {
return $htmlSource;
}
$jsonStylesSpecialCases = Misc::extractBetween($htmlSource, self::$wpStylesSpecialDelimiters['start'], self::$wpStylesSpecialDelimiters['end']);
$stylesSpecialCases = json_decode($jsonStylesSpecialCases, ARRAY_A);
if (Misc::jsonLastError() === JSON_ERROR_NONE && ! empty($stylesSpecialCases)) {
foreach ($stylesSpecialCases as $styleSrc) {
$styleLocalSrc = Misc::getLocalSrc($styleSrc);
$styleRelSrc = isset($styleLocalSrc['rel_src']) ? $styleLocalSrc['rel_src'] : $styleSrc;
$htmlSource = CleanUp::cleanLinkTagFromHtmlSource($styleRelSrc, $htmlSource);
}
// Strip the info HTML comment
$htmlSource = str_replace(
self::$wpStylesSpecialDelimiters['start'] . $jsonStylesSpecialCases . self::$wpStylesSpecialDelimiters['end'],
'',
$htmlSource
);
}
return $htmlSource;
});
}, 1);
}
/**
*
*/
public function printAnySpecialCss()
{
if (isset($this->allUnloadedAssets['styles']) && ! empty($this->allUnloadedAssets['styles']) && in_array('photoswipe', $this->allUnloadedAssets['styles'])) {
?>
<?php if (current_user_can('administrator')) { ?><!-- Asset CleanUp: "photoswipe" unloaded (avoid printing useless HTML) --><?php } ?>
<style <?php echo Misc::getStyleTypeAttribute(); ?>>.pswp { display: none; }</style>
<?php
}
}
/* [END] Styles Dequeue */
/* [START] Scripts Dequeue */
/**
* See if there is any list with scripts to be removed in JSON format
* Only the handles (the ID of the scripts) are saved
*/
public function filterScripts()
{
/* [wpacu_timing] */ Misc::scriptExecTimer( 'filter_dequeue_scripts' );/* [/wpacu_timing] */
if (is_admin()) {
return;
}
global $wp_scripts;
if (current_action() === 'wp_print_scripts') {
ObjectCache::wpacu_cache_set('wpacu_scripts_object_after_wp_print_scripts', $wp_scripts);
}
$list = array();
if (current_action() === 'wp_print_footer_scripts') {
$cachedWpScripts = ObjectCache::wpacu_cache_get('wpacu_scripts_object_after_wp_print_scripts');
if (isset($cachedWpScripts->registered) && count($cachedWpScripts->registered) === count($wp_scripts->registered)) {
// The list was already generated in "wp_print_scripts" and the number of registered assets are the same
// Save resources and do not re-generate it
$list = ObjectCache::wpacu_cache_get('wpacu_scripts_handles_marked_for_unload');
}
}
if ( empty($list) ) {
// [wpacu_lite]
$nonAssetConfigPage = ( ! $this->isUpdateable && ! Misc::getShowOnFront() );
// [/wpacu_lite]
// It looks like the page loaded is neither a post, page or the front-page
// We'll see if there are assets unloaded globally and unload them
/*
* [START] Build unload list
*/
$globalUnload = $this->globalUnloaded;
// [wpacu_lite]
if ( $nonAssetConfigPage && ! empty( $globalUnload['scripts'] ) ) {
$list = $globalUnload['scripts'];
} else { // [/wpacu_lite]
// Post, Page or Front-page?
$toRemove = $this->getAssetsUnloadedPageLevel();
$jsonList = @json_decode( $toRemove );
$list = array();
if ( isset( $jsonList->scripts ) ) {
$list = (array) $jsonList->scripts;
}
// Any global unloaded styles? Append them
if ( ! empty( $globalUnload['scripts'] ) ) {
foreach ( $globalUnload['scripts'] as $handleScript ) {
$list[] = $handleScript;
}
}
if ( self::isSingularPage() ) {
// Any bulk unloaded styles (e.g. for all pages belonging to a post type)? Append them
if ( empty( $this->postTypesUnloaded ) ) {
$post = $this->getCurrentPost();
// Make sure the post_type is set; it's not in specific pages (e.g. BuddyPress ones)
$this->postTypesUnloaded = ( isset( $post->post_type ) && $post->post_type ) ? $this->getBulkUnload( 'post_type', $post->post_type ) : array();
}
if ( isset( $this->postTypesUnloaded['scripts'] ) && ! empty( $this->postTypesUnloaded['scripts'] ) ) {
foreach ( $this->postTypesUnloaded['scripts'] as $handleStyle ) {
$list[] = $handleStyle;
}
}
}
// [wpacu_lite]
}
// [/wpacu_lite]
// Any bulk unloaded styles for 'category', 'post_tag' and more?
// These are PRO rules or rules added via custom coding
$list = apply_filters( 'wpacu_filter_scripts_list_unload', array_unique( $list ) );
global $wp_scripts;
$allScripts = $wp_scripts;
if ( $allScripts !== null && ! empty( $allScripts->registered ) ) {
foreach ( $allScripts->registered as $handle => $value ) {
// This could be triggered several times, check if the script already exists
if ( ! isset( $this->wpAllScripts['registered'][ $handle ] ) ) {
$this->wpAllScripts['registered'][ $handle ] = $value;
if ( in_array( $handle, $allScripts->queue ) ) {
$this->wpAllScripts['queue'][] = $handle;
}
}
}
if ( isset( $this->wpAllScripts['queue'] ) && ! empty( $this->wpAllScripts['queue'] ) ) {
$this->wpAllScripts['queue'] = array_unique( $this->wpAllScripts['queue'] );
}
}
/*
* [END] Build unload list
*/
/*
* [START] Load Exception Check
* */
// Load exception rules ALWAYS have priority over the unloading ones
// Thus, if an exception is found, the handle will be removed from the unloading list
// Let's see if there are load exceptions for this page or site-wide (e.g. for logged-in users)
// These are LITE rules
$list = ( ! empty($list) ) ? $this->filterAssetsUnloadList($list, 'scripts', 'load_exception') : $list;
// [wpacu_pro]
// These are PRO rules or rules added via custom coding
// Only check for any load exceptions if the unloading list has at least one item
// Otherwise the action is irrelevant since the assets are loaded anyway by default
$list = ( ! empty($list) ) ? apply_filters('wpacu_filter_scripts_list_load_exception', $list) : $list;
// [/wpacu_pro]
/*
* [END] Load Exception Check
* */
// Nothing to unload
if ( empty( $list ) ) {
/* [wpacu_timing] */Misc::scriptExecTimer( 'filter_dequeue_scripts', 'end' ); /* [/wpacu_timing] */
return;
}
// e.g. for test/debug mode or AJAX calls (where all assets have to load)
if ( isset($_REQUEST['wpacu_no_js_unload']) || $this->preventAssetsSettings(array('assets_call')) ) {
/* [wpacu_timing] */Misc::scriptExecTimer( 'filter_dequeue_scripts', 'end' ); /* [/wpacu_timing] */
return;
}
}
$ignoreChildParentList = apply_filters('wpacu_ignore_child_parent_list', $this->getIgnoreChildren());
foreach ($list as $handle) {
$handle = trim($handle);
// Ignore auto generated handles for the hardcoded CSS as they were added for reference purposes
// They will get stripped later on via OptimizeCommon.php
// The handle is used just for reference for later stripping via altering the DOM
if (strpos($handle, 'wpacu_hardcoded_script_inline_') !== false || strpos($handle, 'wpacu_hardcoded_noscript_inline_') !== false) {
// [wpacu_pro]
$saveMarkedHandles = ObjectCache::wpacu_cache_get('wpacu_hardcoded_scripts_noscripts_inline') ?: array();
$saveMarkedHandles[] = $handle;
$this->allUnloadedAssets['scripts'][] = $handle; // for "wpacu_no_load" on hardcoded list
ObjectCache::wpacu_cache_set( 'wpacu_hardcoded_scripts_noscripts_inline', $saveMarkedHandles );
// [/wpacu_pro]
continue;
}
if (strpos($handle, 'wpacu_hardcoded_script_src_') !== false) {
// [wpacu_pro]
$saveMarkedHandles = ObjectCache::wpacu_cache_get('wpacu_hardcoded_scripts_src') ?: array();
$saveMarkedHandles[] = $handle;
$this->allUnloadedAssets['scripts'][] = $handle; // for "wpacu_no_load" on hardcoded list
ObjectCache::wpacu_cache_set( 'wpacu_hardcoded_scripts_src', $saveMarkedHandles );
// [/wpacu_pro]
continue;
}
// Special Action for 'jquery-migrate' handler as it's tied to 'jquery'
if ($handle === 'jquery-migrate' && isset($this->wpAllScripts['registered']['jquery'])) {
$jQueryRegScript = $this->wpAllScripts['registered']['jquery'];
if (isset($jQueryRegScript->deps)) {
$jQueryRegScript->deps = array_diff($jQueryRegScript->deps, array('jquery-migrate'));
}
if (Misc::isPluginActive('jquery-updater/jquery-updater.php')) {
wp_dequeue_script($handle);
}
continue;
}
if (isset($ignoreChildParentList['scripts'], $this->wpAllScripts['registered'][$handle]->src) && is_array($ignoreChildParentList['scripts']) && array_key_exists($handle, $ignoreChildParentList['scripts'])) {
// Do not dequeue it as it's "children" will also be dequeued (ignore rule is applied)
// It will be stripped by cleaning its SCRIPT tag from the HTML Source
$this->ignoreChildren['scripts'][$handle] = $this->wpAllScripts['registered'][$handle]->src;
$this->ignoreChildren['scripts'][$handle.'_has_unload_rule'] = 1;
$this->allUnloadedAssets['scripts'][] = $handle;
continue;
}
$this->allUnloadedAssets['scripts'][] = $handle;
// Only trigger the unloading on regular page load, not when the assets list is collected
if ( ! $this->isGetAssetsCall ) {
wp_deregister_script( $handle );
wp_dequeue_script( $handle );
}
}
if (current_action() === 'wp_print_scripts') {
ObjectCache::wpacu_cache_set( 'wpacu_scripts_handles_marked_for_unload', $list );
}
/* [wpacu_timing] */ Misc::scriptExecTimer( 'filter_dequeue_scripts', 'end' ); /* [/wpacu_timing] */
}
/* [END] Scripts Dequeue */
/**
* @param $list (should never be empty when called)
* @param $assetType
* @param $ruleType
*
* @return mixed
*/
public function filterAssetsUnloadList($list, $assetType, $ruleType)
{
// Remove any assets from the list in case there are any load exceptions detected
if ($ruleType === 'load_exception') {
$this->loadExceptionsPageLevel = $this->getLoadExceptionsPageLevel(self::$vars['for_type'], self::$vars['current_post_id']);
$this->loadExceptionsLoggedInGlobal = $this->getHandleLoadLoggedIn();
$this->loadExceptionsPostType = $this->getLoadExceptionsPostType(self::$vars['current_post_type']);
$anyAssetsLoadExceptions = ( ! empty( $this->loadExceptionsPageLevel[ $assetType ] )
|| ( ! empty( $this->loadExceptionsLoggedInGlobal[ $assetType ] ) )
|| ( ! empty( $this->loadExceptionsPostType[$assetType] ) )
);
if ( $anyAssetsLoadExceptions ) {
foreach ( $list as $handleKey => $handle ) {
$loadAssetAsException = in_array( $handle, $this->loadExceptionsPageLevel[ $assetType ] ) // 1) per page, per group pages OR
|| in_array( $handle, $this->loadExceptionsPostType[ $assetType ] ) // 2) On all pages belonging to a specific post type
|| ( in_array( $handle, $this->loadExceptionsLoggedInGlobal[ $assetType ] ) && is_user_logged_in() ); // 3) site-wide if the user is logged-in
if ( $loadAssetAsException ) {
unset( $list[ $handleKey ] );
}
}
}
}
return $list;
}
/**
* Alter CSS/JS list marked for dequeue
* @param $for
* @return array
*/
public function unloadAssetOnTheFly($for)
{
$assetType = ($for === 'css') ? 'styles' : 'scripts';
$assetIndex = 'wpacu_unload_'.$for;
if (! ($unloadAsset = Misc::getVar('get', $assetIndex))) {
return array();
}
$assetHandles = array();
if (strpos($unloadAsset, ',') === false) { // No comma, just one asset targeted
if (strpos($unloadAsset, '[ignore-deps]') !== false) {
$unloadAsset = str_replace('[ignore-deps]', '', $unloadAsset);
$this->ignoreChildrenHandlesOnTheFly[$assetType][] = $unloadAsset;
}
$assetHandles[] = $unloadAsset;
} else { // There are commas, multiple assets targeted
foreach (explode(',', $unloadAsset) as $unloadAsset) {
$unloadAsset = trim($unloadAsset);
if ($unloadAsset) {
if (strpos($unloadAsset, '[ignore-deps]') !== false) {
$unloadAsset = str_replace('[ignore-deps]', '', $unloadAsset);
$this->ignoreChildrenHandlesOnTheFly[$assetType][] = $unloadAsset;
}
$assetHandles[] = $unloadAsset;
}
}
}
return $assetHandles;
}
/**
* @param $exceptionsList
*
* @return array
*/
public function makeLoadExceptionOnTheFly($exceptionsList)
{
$exceptionsListDebug = array('styles' => array(), 'scripts' => array());
foreach (array('css', 'js') as $assetExt) {
$assetKey = ($assetExt === 'css') ? 'styles' : 'scripts';
$indexToCheck = 'wpacu_load_'.$assetExt;
if ($loadAsset = Misc::getVar('get', $indexToCheck)) {
if (strpos($loadAsset, ',') === false && (! in_array($loadAsset, $exceptionsList[$assetKey]))) {
$exceptionsList[$assetKey][] = $loadAsset;
$exceptionsListDebug[$assetKey][] = $loadAsset;
} else {
foreach (explode(',', $loadAsset) as $loadAsset) {
if (($loadAsset = trim($loadAsset)) && (! in_array($loadAsset, $exceptionsList[$assetKey]))) {
$exceptionsList[$assetKey][] = $loadAsset;
$exceptionsListDebug[$assetKey][] = $loadAsset;
}
}
}
}
}
ObjectCache::wpacu_cache_add('wpacu_exceptions_list_page_request', $exceptionsListDebug);
return $exceptionsList;
}
/**
* This fetches the "Load it on this page" exceptions
*
* @param string $type ("post", "front_page")
* @param string $postId
*
* @return array|array[]
*/
public function getLoadExceptionsPageLevel($type = 'post', $postId = '')
{
if ( $this->loadExceptionsPageLevel['_set'] ) {
return $this->loadExceptionsPageLevel; // it was set
}
$exceptionsListDefault = $exceptionsList = array( 'styles' => array(), 'scripts' => array() );
if ( $type === 'post' && ! $postId ) {
// $postId needs to have a value if $type is a 'post' type
$this->loadExceptionsPageLevel = $exceptionsListDefault;
$this->loadExceptionsPageLevel['_set'] = true;
return $this->loadExceptionsPageLevel;
}
if ( ! $type ) {
// Invalid request
$this->loadExceptionsPageLevel = $exceptionsListDefault;
$this->loadExceptionsPageLevel['_set'] = true;
return $this->loadExceptionsPageLevel;
}
// Default
$exceptionsListJson = '';
// Post or Post of the Homepage (if chosen in the Dashboard)
if ( $type === 'post' || ( Misc::getShowOnFront() === 'page' && $postId ) ) {
$exceptionsListJson = get_post_meta( $postId, '_' . WPACU_PLUGIN_ID . '_load_exceptions', true );
}
// The home page could also be the list of the latest blog posts
if ( $type === 'front_page' ) {
$exceptionsListJson = get_option( WPACU_PLUGIN_ID . '_front_page_load_exceptions' );
}
if ( $exceptionsListJson ) {
$exceptionsList = json_decode( $exceptionsListJson, true );
if ( Misc::jsonLastError() !== JSON_ERROR_NONE ) {
$exceptionsList = $exceptionsListDefault;
}
}
// Any exceptions on the fly added for debugging purposes? Make sure to grab them
$exceptionsList = $this->makeLoadExceptionOnTheFly( $exceptionsList );
// Avoid any notice errors
foreach ( array( 'styles', 'scripts' ) as $assetType ) {
if ( ! isset( $exceptionsList[ $assetType ] ) ) {
$exceptionsList[ $assetType ] = array();
}
}
$this->loadExceptionsPageLevel = $exceptionsList;
$this->loadExceptionsPageLevel['_set'] = true;
return $this->loadExceptionsPageLevel;
}
/**
* Option: 'Make an exception from any unload rule & always load it' -> 'On all pages of "post" post type'
*
* @param $postType
*
* @return \array[][]
*/
public function getLoadExceptionsPostType($postType)
{
if ($this->loadExceptionsPostType['_set']) {
return $this->loadExceptionsPostType;
}
// Not on a singular page
if ($postType === '') {
$this->loadExceptionsPostType['_set'] = true;
return $this->loadExceptionsPostType;
}
$exceptionsListDefault = array('styles' => array(), 'scripts' => array());
$exceptionsListJson = get_option(WPACU_PLUGIN_ID . '_post_type_load_exceptions');
$exceptionsList = @json_decode($exceptionsListJson, true);
// Issues with decoding the JSON file? Return an empty list
if (Misc::jsonLastError() !== JSON_ERROR_NONE) {
$this->loadExceptionsPostType = $exceptionsListDefault;
$this->loadExceptionsPostType['_set'] = true;
return $this->loadExceptionsPostType;
}
// Return any handles added as load exceptions for the requested $postType
if (isset($exceptionsList[$postType])) {
foreach ( $exceptionsList[$postType] as $assetType => $assetList ) {
foreach ( $assetList as $assetHandle => $assetValue ) {
if ( $assetValue ) {
$this->loadExceptionsPostType[ $assetType ][] = $assetHandle;
}
}
}
}
$this->loadExceptionsPostType['_set'] = true;
return $this->loadExceptionsPostType;
}
/**
* Option: Unload site-wide * everywhere
*
* @return array
*/
public function getGlobalUnload()
{
$existingListEmpty = array('styles' => array(), 'scripts' => array());
$existingListJson = get_option( WPACU_PLUGIN_ID . '_global_unload');
$existingListData = $this->existingList($existingListJson, $existingListEmpty);
// No 'styles' or 'scripts' - Set them as empty to avoid any PHP warning errors
foreach ( array('styles', 'scripts') as $assetType ) {
if ( ! isset( $existingListData['list'][$assetType] ) || ! is_array( $existingListData['list'][$assetType] ) ) {
$existingListData['list'][$assetType] = array();
}
}
return $existingListData['list'];
}
/**
* @param string $for (could be 'post_type', 'taxonomy' for the Pro version etc.)
* @param string $type
*
* @return array
*/
public function getBulkUnload($for, $type = 'all')
{
$existingListEmpty = array('styles' => array(), 'scripts' => array());
$existingListAllJson = get_option( WPACU_PLUGIN_ID . '_bulk_unload');
if (! $existingListAllJson) {
return $existingListEmpty;
}
$existingListAll = json_decode($existingListAllJson, true);
if (Misc::jsonLastError() !== JSON_ERROR_NONE) {
return $existingListEmpty;
}
$existingList = $existingListEmpty;
foreach (array('styles', 'scripts') as $assetType) {
if ( isset( $existingListAll[$assetType][ $for ][ $type ] ) && is_array( $existingListAll[$assetType][ $for ][ $type ] ) ) {
$existingList[$assetType] = $existingListAll[$assetType][ $for ][ $type ];
}
}
return $existingList;
}
/**
* Option: Add Note
*
* @return array
*/
public function getHandleNotes()
{
$handleNotes = array('styles' => array(), 'scripts' => array());
$handleNotesListJson = get_option(WPACU_PLUGIN_ID . '_global_data');
if ($handleNotesListJson) {
$handleNotesList = @json_decode($handleNotesListJson, true);
// Issues with decoding the JSON file? Return an empty list
if (Misc::jsonLastError() !== JSON_ERROR_NONE) {
return $handleNotes;
}
// Are new positions set for styles and scripts?
foreach (array('styles', 'scripts') as $assetKey) {
if ( isset( $handleNotesList[$assetKey]['notes'] ) && ! empty( $handleNotesList[$assetKey]['notes'] ) ) {
$handleNotes[$assetKey] = $handleNotesList[$assetKey]['notes'];
}
}
}
return $handleNotes;
}
/**
* Option: 'Make an exception from any unload rule & always load it' -> 'If the user is logged-in'
*
* @return array
*/
public function getHandleLoadLoggedIn()
{
if ($this->loadExceptionsLoggedInGlobal['_set']) {
return $this->loadExceptionsLoggedInGlobal;
}
$targetGlobalKey = 'load_it_logged_in';
$handleData = array( 'styles' => array(), 'scripts' => array() );
$handleDataListJson = get_option( WPACU_PLUGIN_ID . '_global_data' );
if ( $handleDataListJson ) {
$handleDataList = @json_decode( $handleDataListJson, true );
// Issues with decoding the JSON file? Return an empty list
if ( Misc::jsonLastError() !== JSON_ERROR_NONE ) {
$this->loadExceptionsLoggedInGlobal = $handleData;
$this->loadExceptionsLoggedInGlobal['_set'] = true;
return $handleData;
}
// Are load exceptions set for styles and scripts?
foreach ( array( 'styles', 'scripts' ) as $assetKey ) {
if ( isset( $handleDataList[ $assetKey ][ $targetGlobalKey ] ) && ! empty( $handleDataList[ $assetKey ][ $targetGlobalKey ] ) ) {
$handleData[ $assetKey ] = array_keys($handleDataList[ $assetKey ][ $targetGlobalKey ]);
}
}
}
$this->loadExceptionsLoggedInGlobal = $handleData;
// Avoid any PHP notice errors
foreach (array('styles', 'scripts') as $assetType) {
if ( ! isset($this->loadExceptionsLoggedInGlobal[$assetType]) ) {
$this->loadExceptionsLoggedInGlobal[$assetType] = array();
}
}
$this->loadExceptionsLoggedInGlobal['_set'] = true;
return $this->loadExceptionsLoggedInGlobal;
}
/**
* Option: 'Ignore dependency rule and keep the "children" loaded'
*
* @return array
*/
public function getIgnoreChildren()
{
if (empty($this->ignoreChildren)) {
$ignoreChildListJson = get_option(WPACU_PLUGIN_ID . '_global_data');
if ($ignoreChildListJson) {
$ignoreChildList = @json_decode($ignoreChildListJson, true);
// Issues with decoding the JSON file? Return an empty list
if (Misc::jsonLastError() !== JSON_ERROR_NONE) {
return $this->ignoreChildren;
}
// Are ignore "children" rules set for styles and scripts?
foreach (array('styles', 'scripts') as $assetKey) {
if (isset($ignoreChildList[$assetKey]['ignore_child']) && $ignoreChildList[$assetKey]['ignore_child']) {
$this->ignoreChildren[$assetKey] = $ignoreChildList[$assetKey]['ignore_child'];
}
}
}
}
return $this->ignoreChildren;
}
/**
* @return array
*/
public static function getHandlesInfo()
{
$assetsInfo = array('styles' => array(), 'scripts' => array());
$wpacuGlobalDataJson = get_option(WPACU_PLUGIN_ID . '_global_data');
$wpacuGlobalData = json_decode($wpacuGlobalDataJson, ARRAY_A);
if (Misc::jsonLastError() === JSON_ERROR_NONE) {
foreach (array('styles', 'scripts') as $assetKey) {
if ( isset( $wpacuGlobalData[$assetKey]['assets_info'] ) && ! empty( $wpacuGlobalData[$assetKey]['assets_info'] ) ) {
$assetsInfo[$assetKey] = Misc::filterList( $wpacuGlobalData[$assetKey]['assets_info'] );
}
}
}
// Fallback for those who still use the old transient way of fetching the assets info
if ($assetsInfoTransient = get_transient(WPACU_PLUGIN_ID . '_assets_info')) {
$assetsInfoTransientArray = @json_decode($assetsInfoTransient, ARRAY_A);
if (is_array($assetsInfoTransientArray) && ! empty($assetsInfoTransientArray)) {
foreach ($assetsInfoTransientArray as $assetKeyTransient => $handlesList) {
if (! in_array($assetKeyTransient, array('styles', 'scripts'))) {
continue;
}
foreach ($handlesList as $handleName => $handleData) {
if (! isset($assetsInfo[$assetKeyTransient][$handleName])) {
$assetsInfo[$assetKeyTransient][$handleName] = $handleData;
}
}
}
}
}
return $assetsInfo;
}
/**
* @param $ignoreChildParentList
*
* @return array
*/
public function filterIgnoreChildParentList($ignoreChildParentList)
{
if (isset($this->ignoreChildrenHandlesOnTheFly['styles']) && ! empty($this->ignoreChildrenHandlesOnTheFly['styles'])) {
foreach ($this->ignoreChildrenHandlesOnTheFly['styles'] as $cssHandle) {
$ignoreChildParentList['styles'][$cssHandle] = 1;
}
}
if (isset($this->ignoreChildrenHandlesOnTheFly['scripts']) && ! empty($this->ignoreChildrenHandlesOnTheFly['scripts'])) {
foreach ($this->ignoreChildrenHandlesOnTheFly['scripts'] as $jsHandle) {
$ignoreChildParentList['scripts'][$jsHandle] = 1;
}
}
return $ignoreChildParentList;
}
/**
*
*/
public function saveHeadAssets()
{
global $wp_styles, $wp_scripts;
if (isset($this->wpAllStyles['queue']) && ! empty($this->wpAllStyles['queue'])) {
$this->stylesInHead = $this->wpAllStyles['queue'];
}
if (isset($wp_styles->queue) && ! empty($wp_styles->queue)) {
foreach ($wp_styles->queue as $styleHandle) {
$this->stylesInHead[] = $styleHandle;
}
}
$this->stylesInHead = array_unique($this->stylesInHead);
if (isset($this->wpAllScripts['queue']) && ! empty($this->wpAllScripts['queue'])) {
$this->scriptsInHead = $this->wpAllScripts['queue'];
}
if (isset($wp_scripts->queue) && ! empty($wp_scripts->queue)) {
foreach ($wp_scripts->queue as $scriptHandle) {
$this->scriptsInHead[] = $scriptHandle;
}
}
$this->scriptsInHead = array_unique($this->scriptsInHead);
}
/**
*
*/
public function saveFooterAssets()
{
global $wp_scripts, $wp_styles;
// [Styles Collection]
$footerStyles = array();
if (isset($this->wpAllStyles['queue']) && ! empty($this->wpAllStyles['queue'])) {
foreach ( $this->wpAllStyles['queue'] as $handle ) {
if ( ! in_array( $handle, $this->stylesInHead ) ) {
$footerStyles[] = $handle;
}
}
}
if (isset($wp_styles->queue) && ! empty($wp_styles->queue)) {
foreach ( $wp_styles->queue as $handle ) {
if ( ! in_array( $handle, $this->stylesInHead ) ) {
$footerStyles[] = $handle;
}
}
}
$this->assetsInFooter['styles'] = array_unique($footerStyles);
// [/Styles Collection]
// [Scripts Collection]
$this->assetsInFooter['scripts'] = (isset($wp_scripts->in_footer) && ! empty($wp_scripts->in_footer)) ? $wp_scripts->in_footer : array();
if (isset($this->wpAllScripts['queue']) && ! empty($this->wpAllScripts['queue'])) {
foreach ( $this->wpAllScripts['queue'] as $handle ) {
if ( ! in_array( $handle, $this->scriptsInHead ) ) {
$this->assetsInFooter['scripts'][] = $handle;
}
}
}
if (isset($wp_scripts->queue) && ! empty($wp_scripts->queue)) {
foreach ( $wp_scripts->queue as $handle ) {
if ( ! in_array( $handle, $this->scriptsInHead ) ) {
$this->assetsInFooter['scripts'][] = $handle;
}
}
}
$this->assetsInFooter['scripts'] = array_unique($this->assetsInFooter['scripts']);
// [/Scripts Collection]
}
/**
* This output will be extracted and the JSON will be processed
* in the WP Dashboard when editing a post
*
* It will also print the asset list in the front-end
* if the option was enabled in the Settings
*/
public function printScriptsStyles()
{
// Not for WordPress AJAX calls
if (self::$domGetType === 'direct' && defined('DOING_AJAX') && DOING_AJAX) {
return;
}
$isFrontEndEditView = $this->isFrontendEditView;
$isDashboardEditView = (! $isFrontEndEditView && $this->isGetAssetsCall);
if (! $isFrontEndEditView && ! $isDashboardEditView) {
return;
}
if ($isFrontEndEditView && isset($_GET['elementor-preview']) && $_GET['elementor-preview']) {
return;
}
/* [wpacu_timing] */ $wpacuTimingName = 'output_css_js_manager'; Misc::scriptExecTimer($wpacuTimingName); /* [/wpacu_timing] */
// Prevent plugins from altering the DOM
add_filter('w3tc_minify_enable', '__return_false');
Misc::w3TotalCacheFlushObjectCache();
// This is the list of the scripts and styles that were eventually loaded
// We have also the list of the ones that were unloaded
// located in $this->wpScripts and $this->wpStyles
// We will add it to the list as they will be marked
$stylesBeforeUnload = $this->wpAllStyles;
$scriptsBeforeUnload = $this->wpAllScripts;
global $wp_scripts, $wp_styles;
$list = array();
// e.g. for "Loaded" and "Unloaded" statuses
$currentUnloadedAll = isset($this->allUnloadedAssets) ? $this->allUnloadedAssets : array('styles' => array(), 'scripts' => array());
foreach (array('styles', 'scripts') as $assetType) {
if ( isset( $currentUnloadedAll[$assetType] ) ) {
$currentUnloadedAll[$assetType] = array_unique( $currentUnloadedAll[$assetType] );
}
}
$manageStylesCore = isset($wp_styles->done) && is_array($wp_styles->done) ? $wp_styles->done : array();
$manageStyles = $this->wpStylesFilter($manageStylesCore, 'done');
$manageScripts = isset($wp_scripts->done) && is_array($wp_scripts->done) ? $wp_scripts->done : array();
if ($isFrontEndEditView) {
if (! empty($this->wpAllStyles) && isset($this->wpAllStyles['queue'])) {
$manageStyles = $this->wpStylesFilter($this->wpAllStyles['queue'], 'queue');
}
if (! empty($this->wpAllScripts) && isset($this->wpAllScripts['queue'])) {
$manageScripts = $this->wpAllScripts['queue'];
}
if (! empty($currentUnloadedAll['styles'])) {
foreach ( $currentUnloadedAll['styles'] as $currentUnloadedStyleHandle ) {
if ( ! in_array( $currentUnloadedStyleHandle, $manageStyles ) ) {
$manageStyles[] = $currentUnloadedStyleHandle;
}
}
}
if (! empty($manageStylesCore)) {
foreach ($manageStylesCore as $wpDoneStyle) {
if ( ! in_array( $wpDoneStyle, $manageStyles ) ) {
$manageStyles[] = $wpDoneStyle;
}
}
}
$manageStyles = array_unique($manageStyles);
if (! empty($currentUnloadedAll['scripts'])) {
foreach ( $currentUnloadedAll['scripts'] as $currentUnloadedScriptHandle ) {
if ( ! in_array( $currentUnloadedScriptHandle, $manageScripts ) ) {
$manageScripts[] = $currentUnloadedScriptHandle;
}
}
}
if (isset($wp_scripts->done) && ! empty($wp_scripts->done)) {
foreach ($wp_scripts->done as $wpDoneScript) {
if ( ! in_array( $wpDoneScript, $manageScripts ) ) {
$manageScripts[] = $wpDoneScript;
}
}
}
$manageScripts = array_unique($manageScripts);
}
/*
* Style List
*/
if ($isFrontEndEditView) { // "Manage in the Front-end"
$stylesList = $stylesBeforeUnload['registered'];
} else { // "Manage in the Dashboard"
$stylesListFilterAll = $this->wpStylesFilter($wp_styles, 'registered');
$stylesList = $stylesListFilterAll->registered;
}
if (! empty($stylesList)) {
foreach ($manageStyles as $handle) {
if (! isset($stylesList[$handle]) || in_array($handle, $this->skipAssets['styles'])) {
continue;
}
$list['styles'][] = $stylesList[$handle];
}
// Append unloaded ones (if any)
if (! empty($stylesBeforeUnload) && ! empty($currentUnloadedAll['styles'])) {
foreach ($currentUnloadedAll['styles'] as $sbuHandle) {
if (! in_array($sbuHandle, $manageStyles)) {
// Could be an old style that is not loaded anymore
// We have to check that
if (! isset($stylesBeforeUnload['registered'][$sbuHandle])) {
continue;
}
$sbuValue = $stylesBeforeUnload['registered'][$sbuHandle];
$list['styles'][] = $sbuValue;
}
}
}
ksort($list['styles']);
}
/*
* Scripts List
*/
$scriptsList = $wp_scripts->registered;
if ($isFrontEndEditView) {
$scriptsList = $scriptsBeforeUnload['registered'];
}
if (! empty($scriptsList)) {
/* These scripts below are used by this plugin (except admin-bar) and they should not show in the list
as they are loaded only when you (or other admin) manage the assets, never for your website visitors */
foreach ($manageScripts as $handle) {
if (! isset($scriptsList[$handle]) || in_array($handle, $this->skipAssets['scripts'])) {
continue;
}
$list['scripts'][] = $scriptsList[$handle];
}
// Append unloaded ones (if any)
if (! empty($scriptsBeforeUnload) && ! empty($currentUnloadedAll['scripts'])) {
foreach ($currentUnloadedAll['scripts'] as $sbuHandle) {
if (! in_array($sbuHandle, $manageScripts)) {
// Could be an old script that is not loaded anymore
// We have to check that
if (! isset($scriptsBeforeUnload['registered'][$sbuHandle])) {
continue;
}
$sbuValue = $scriptsBeforeUnload['registered'][$sbuHandle];
$list['scripts'][] = $sbuValue;
}
}
}
ksort($list['scripts']);
}
if (! empty($list)) {
Update::updateHandlesInfo( $list );
}
// [wpacu_lite]
if ($isFrontEndEditView && ! $this->isUpdateable) {
$this->parseTemplate('settings-frontend-lite-locked', array(), true);
}
// [/wpacu_lite]
// Front-end View while admin is logged in
elseif ($isFrontEndEditView) {
$wpacuSettings = new Settings();
$data = array(
'is_frontend_view' => true,
'post_type' => '',
'bulk_unloaded' => array( 'post_type' => array() ),
'plugin_settings' => $wpacuSettings->getAll(),
'current_unloaded_all' => $currentUnloadedAll,
'current_unloaded_page_level' => $this->getAssetsUnloadedPageLevel( $this->getCurrentPostId(), true )
);
$data['wpacu_page_just_updated'] = false;
if (isset($_GET['wpacu_time'], $_GET['nocache']) && get_transient('wpacu_page_just_updated')) {
$data['wpacu_page_just_updated'] = true;
delete_transient('wpacu_page_just_updated');
}
if ($currentDebug = ObjectCache::wpacu_cache_get('wpacu_assets_unloaded_list_page_request')) {
foreach ( array( 'styles', 'scripts' ) as $assetType ) {
if ( isset( $data['current_unloaded_all'][ $assetType ] ) && ! empty( $data['current_unloaded_all'][ $assetType ] ) ) {
foreach ( $data['current_unloaded_all'][ $assetType ] as $handleKey => $handle ) {
if ( isset( $currentDebug[ $assetType ] ) && in_array( $handle, $currentDebug[ $assetType ] ) ) {
unset( $data['current_unloaded_all'][ $assetType ][ $handleKey ] );
}
}
}
}
}
// e.g. /?wpacu_unload_(css|js)=
$data['current_debug'] = ObjectCache::wpacu_cache_get('wpacu_assets_unloaded_list_page_request');
$data['all']['scripts'] = $list['scripts'];
$data['all']['styles'] = $list['styles'];
if ($data['plugin_settings']['assets_list_layout'] === 'by-location') {
$data['all'] = Sorting::appendLocation($data['all']);
} else {
$data['all'] = Sorting::sortListByAlpha($data['all']);
}
$this->fetchUrl = Misc::getPageUrl($this->getCurrentPostId());
$data['fetch_url'] = $this->fetchUrl;
$data['nonce_action'] = Update::NONCE_ACTION_NAME;
$data['nonce_name'] = Update::NONCE_FIELD_NAME;
$data = $this->alterAssetObj($data);
$data['global_unload'] = $this->globalUnloaded;
$type = false;
if (Misc::isHomePage() && $this->getCurrentPostId() < 1 && ! get_option('page_on_front')) {
$type = 'front_page';
} elseif ($this->getCurrentPostId() > 0) {
$type = 'post';
}
$data['wpacu_type'] = $type;
$data['load_exceptions_per_page'] = $this->getLoadExceptionsPageLevel($type, $this->getCurrentPostId());
// Avoid the /?wpacu_load_(css|js) to interfere with the form inputs
if ($loadExceptionsDebug = ObjectCache::wpacu_cache_get( 'wpacu_exceptions_list_page_request' )) {
foreach ( array( 'styles', 'scripts' ) as $assetType ) {
if ( isset( $data['load_exceptions_per_page'][ $assetType ], $loadExceptionsDebug[ $assetType ] ) && ! empty( $data['load_exceptions_per_page'][ $assetType ] ) ) {
foreach ( $data['load_exceptions_per_page'][ $assetType ] as $handleKey => $handle ) {
if ( in_array( $handle, $loadExceptionsDebug[ $assetType ] ) ) {
unset( $data['load_exceptions_per_page'][ $assetType ][ $handleKey ] );
}
}
}
}
// e.g. /?wpacu_load_(css|js)=
$data['load_exceptions_debug'] = $loadExceptionsDebug;
}
// WooCommerce Shop Page?
$data['is_woo_shop_page'] = self::$vars['is_woo_shop_page'];
$data['is_bulk_unloadable'] = $data['bulk_unloaded_type'] = false;
$data['bulk_unloaded']['post_type'] = array('styles' => array(), 'scripts' => array());
$data['load_exceptions_post_type'] = array();
if (self::isSingularPage()) {
$post = $this->getCurrentPost();
$data['post_id'] = $post->ID;
// Current Post Type
$data['post_type'] = $post->post_type;
$data['load_exceptions_post_type'] = $this->getLoadExceptionsPostType($data['post_type']);
// Are there any assets unloaded for this specific post type?
// (e.g. page, post, product (from WooCommerce) or other custom post type)
$data['bulk_unloaded']['post_type'] = $this->getBulkUnload('post_type', $data['post_type']);
$data['bulk_unloaded_type'] = 'post_type';
$data['is_bulk_unloadable'] = true;
$data['post_type_has_tax_assoc'] = self::getAllSetTaxonomies($data['post_type']);
$data = $this->setPageTemplate($data);
}
$data['total_styles'] = ! empty($data['all']['styles']) ? count($data['all']['styles']) : false;
$data['total_scripts'] = ! empty($data['all']['scripts']) ? count($data['all']['scripts']) : false;
$data['all_deps'] = $this->getAllDeps($data['all']);
$data['preloads'] = Preloads::instance()->getPreloads();
// Load exception: If the user is logged in (applies globally)
$data['handle_load_logged_in'] = $this->getHandleLoadLoggedIn();
$data['handle_notes'] = $this->getHandleNotes();
$data['handle_rows_contracted'] = self::getHandleRowStatus();
$data['ignore_child'] = $this->getIgnoreChildren();
switch (assetCleanUpHasNoLoadMatches($data['fetch_url'])) {
case 'is_set_in_settings':
// The rules from "Settings" -> "Plugin Usage Preferences" -> "Do not load the plugin on certain pages" will be checked
$data['status'] = 5;
break;
case 'is_set_in_page':
// The following option from "Page Options" (within the CSS/JS manager of the targeted page) is set: "Do not load Asset CleanUp Pro on this page (this will disable any functionality of the plugin)"
$data['status'] = 6;
break;
default:
$data['status'] = 1;
}
$data['page_options'] = array();
$data['show_page_options'] = false;
if (in_array($type, array('post', 'front_page'))) {
$data['show_page_options'] = true;
$data['page_options'] = MetaBoxes::getPageOptions($this->getCurrentPostId(), $type);
}
$data['post_id'] = ($type === 'front_page') ? 0 : $this->getCurrentPostId();
ObjectCache::wpacu_cache_set('wpacu_settings_frontend_data', $data);
$this->parseTemplate('settings-frontend', $data, true);
} elseif ($isDashboardEditView && ! isset($_GET['wpacu_just_hardcoded'])) {
// AJAX call (not the classic WP one) from the WP Dashboard
// Send the altered value that has the initial position too
// Taken front the front-end view
$data = array();
$data['all']['scripts'] = $list['scripts'];
$data['all']['styles'] = $list['styles'];
$data = $this->alterAssetObj($data);
$list['styles'] = $data['all']['styles'];
$list['scripts'] = $data['all']['scripts'];
if ( isset($_GET['wpacu_print']) ) {
echo '<!-- '."\n".print_r(Misc::filterList($list), true)."\n".' -->';
}
// e.g. for "Loaded" and "Unloaded" statuses
$list['current_unloaded_all'] = isset($this->allUnloadedAssets) ? $this->allUnloadedAssets : array('styles' => array(), 'scripts' => array());
echo self::START_DEL_ENQUEUED . base64_encode(wp_json_encode($list)) . self::END_DEL_ENQUEUED; // Loaded via wp_enqueue_scripts()
echo self::START_DEL_HARDCODED . '{wpacu_hardcoded_assets}' . self::END_DEL_HARDCODED; // Make the user aware of any hardcoded CSS/JS (if any)
add_action('shutdown', static function() {
// Do not allow further processes as cache plugins such as W3 Total Cache could alter the source code
// and we need the non-minified version of the DOM (e.g. to determine the position of the elements)
exit();
});
} elseif ($isDashboardEditView && isset($_GET['wpacu_just_hardcoded'])) {
// AJAX call just for the hardcoded assets
echo self::START_DEL_HARDCODED . '{wpacu_hardcoded_assets}' . self::END_DEL_HARDCODED; // Make the user aware of any hardcoded CSS/JS (if any)
add_action('shutdown', static function() {
// Do not allow further processes as cache plugins such as W3 Total Cache could alter the source code,
// and we need the non-minified version of the DOM (e.g. to determine the position of the elements)
exit();
});
}
/* [wpacu_timing] */ Misc::scriptExecTimer($wpacuTimingName, 'end'); /* [/wpacu_timing] */
}
/**
* @param $name
* @param array $data (if present $data values are used within the included template)
* @param bool|false $echo
* @param bool|false $returnData (relevant when $echo is set to true)
* @return string|array
*/
public function parseTemplate($name, $data = array(), $echo = false, $returnData = false)
{
$templateFile = apply_filters(
'wpacu_template_file', // tag
dirname(__DIR__) . '/templates/' . $name . '.php', // value
$name // extra argument
);
if (! is_file($templateFile)) {
return 'Template '.$templateFile.' not found.';
}
ob_start();
include $templateFile;
$result = ob_get_clean();
// $echo is set to true ($returnData is not relevant), thus print the output
if ($echo) {
echo $result;
return true;
}
// $echo is set to false and $returnData to true
if ($returnData) {
return array(
'output' => $result,
'data' => $data
);
}
/// $echo is set to false (default) and $returnData to $false (default)
return $result;
}
/**
*
*/
public function ajaxGetJsonListCallback()
{
if ( ! isset($_POST['wpacu_nonce']) ) {
echo 'Error: The security nonce was not sent for verification. Location: '.__METHOD__;
return;
}
if ( ! wp_verify_nonce($_POST['wpacu_nonce'], 'wpacu_ajax_get_loaded_assets_nonce') ) {
echo 'Error: The nonce security check has failed. Location: '.__METHOD__;
return;
}
$postId = (int)Misc::getVar('post', 'post_id'); // if any (could be home page for instance)
$pageUrl = Misc::getVar('post', 'page_url'); // post, page, custom post type, home page etc.
$postStatus = $postId > 0 ? get_post_status($postId) : false;
// Not homepage, but a post/page? Check if it's published in case AJAX call
// wasn't stopped due to JS errors or other reasons
if ($postId > 0 && ! in_array($postStatus, array('publish', 'private'))) {
exit(__('The CSS/JS files will be available to manage once the post/page is published.', 'wp-asset-clean-up'));
}
if ($postId > 0) {
$type = 'post';
} elseif ($postId == 0) {
$type = 'front_page';
}
$wpacuListE = $wpacuListH = '';
$settings = new Settings();
// If the post status is 'private' only direct method can be used to fetch the assets
// as the remote post one will return a 404 error since the page is accessed as a guest visitor
if (self::$domGetType === 'direct' || $postStatus === 'private') {
$wpacuListE = Misc::getVar('post', 'wpacu_list_e');
$wpacuListH = Misc::getVar('post', 'wpacu_list_h');
} elseif (self::$domGetType === 'wp_remote_post') {
$wpRemotePost = wp_remote_post($pageUrl, array(
'body' => array(
WPACU_LOAD_ASSETS_REQ_KEY => 1
)
));
$contents = (is_array($wpRemotePost) && isset($wpRemotePost['body']) && (! is_wp_error($wpRemotePost))) ? $wpRemotePost['body'] : '';
// Enqueued List
if ($contents
&& ( strpos($contents, self::START_DEL_ENQUEUED) !== false)
&& ( strpos($contents, self::END_DEL_ENQUEUED) !== false)) {
// Enqueued CSS/JS (most of them or all)
$wpacuListE = Misc::extractBetween(
$contents,
self::START_DEL_ENQUEUED,
self::END_DEL_ENQUEUED
);
}
// Hardcoded List
if ($contents
&& ( strpos($contents, self::START_DEL_HARDCODED) !== false)
&& ( strpos($contents, self::END_DEL_HARDCODED) !== false)) {
// Hardcoded (if any)
$wpacuListH = Misc::extractBetween(
$contents,
self::START_DEL_HARDCODED,
self::END_DEL_HARDCODED
);
}
// The list of assets COULD NOT be retrieved via "WP Remote Post" for this server
// EITHER the enqueued or hardcoded list of assets HAS TO BE RETRIEVED
// Print out the 'error' response to make the user aware about it
if ( ! ($wpacuListE || $wpacuListH) ) {
// 'body' is set, and it's not an array
if ( is_wp_error($wpRemotePost) ) {
$wpRemotePost['response']['message'] = $wpRemotePost->get_error_message();
} elseif ( isset( $wpRemotePost['body']) ) {
if ( trim( $wpRemotePost['body'] ) === '' ) {
$wpRemotePost['body'] = '<strong>Error (blank page):</strong> It looks the targeted page is loading, but it has no content. The page seems to be blank. Please load it in incognito mode (when you are not logged-in) via your browser.';
} elseif ( ! is_array( $wpRemotePost['body'] ) ) {
$wpRemotePost['body'] = strip_tags( $wpRemotePost['body'], '<p><a><strong><b><em><i>' );
}
}
$data = array(
'is_dashboard_view' => true,
'plugin_settings' => $settings->getAll(),
'wp_remote_post' => $wpRemotePost
);
if (isset($type) && $type) {
$data['page_options'] = MetaBoxes::getPageOptions( $postId, $type );
}
$this->parseTemplate('meta-box-loaded', $data, true);
exit();
}
}
$data = array(
'is_dashboard_view' => true,
'post_id' => $postId,
'plugin_settings' => $settings->getAll()
);
// [START] Enqueued CSS/JS (most of them or all)
$jsonE = base64_decode($wpacuListE);
$data['all'] = (array)json_decode($jsonE);
// Make sure if there are no STYLES enqueued, the list will be empty to avoid any notice errors
if (! isset($data['all']['styles'])) {
$data['all']['styles'] = array();
}
// Make sure if there are no SCRIPTS enqueued, the list will be empty to avoid any notice errors
if (! isset($data['all']['scripts'])) {
$data['all']['scripts'] = array();
}
// [END] Enqueued CSS/JS (most of them or all)
// [START] Hardcoded (if any)
if ($wpacuListH) {
// Only set the following variables if there is at least one hardcoded LINK/STYLE/SCRIPT
$jsonH = base64_decode( $wpacuListH );
$data['all']['hardcoded'] = (array) json_decode( $jsonH, ARRAY_A );
if (isset($data['all']['hardcoded']['within_conditional_comments']) && ! empty($data['all']['hardcoded']['within_conditional_comments'])) {
ObjectCache::wpacu_cache_set( 'wpacu_hardcoded_content_within_conditional_comments', $data['all']['hardcoded']['within_conditional_comments'] );
}
}
// [END] Hardcoded (if any)
$data['current_unloaded_page_level'] = $this->getAssetsUnloadedPageLevel( $postId, true );
// e.g. for "Loaded" and "Unloaded" statuses
$data['current_unloaded_all'] = isset($data['all']['current_unloaded_all']) ? (array)$data['all']['current_unloaded_all'] : array('styles' => array(), 'scripts' => array());
if ($data['plugin_settings']['assets_list_layout'] === 'by-location') {
$data['all'] = Sorting::appendLocation($data['all']);
} else {
$data['all'] = Sorting::sortListByAlpha($data['all']);
}
$data['fetch_url'] = $pageUrl;
$data['global_unload'] = $this->getGlobalUnload();
// [wpacu_pro]
//ObjectCache::wpacu_cache_set('wpacu_data_global_unload', $data['global_unload']);
// [/wpacu_pro]
$data['is_bulk_unloadable'] = $data['bulk_unloaded_type'] = false;
$data['bulk_unloaded']['post_type'] = array('styles' => array(), 'scripts' => array());
// Post Information
if ($postId > 0) {
$postData = get_post($postId);
if (isset($postData->post_type) && $postData->post_type) {
// Current Post Type
$data['post_type'] = $postData->post_type;
// Are there any assets unloaded for this specific post type?
// (e.g. page, post, product (from WooCommerce) or other custom post type)
$data['bulk_unloaded']['post_type'] = $this->getBulkUnload('post_type', $data['post_type']);
$data['bulk_unloaded_type'] = 'post_type';
$data['is_bulk_unloadable'] = true;
$data['post_type_has_tax_assoc'] = self::getAllSetTaxonomies($data['post_type']);
}
}
// DO NOT alter any position as it's already verified and set
// This AJAX call is for printing the assets that were already fetched
$data = $this->alterAssetObj($data, false);
$data['wpacu_type'] = $type;
// e.g. LITE rules: Load it on this page & on all pages of a specific post type
$data['load_exceptions_per_page'] = $this->getLoadExceptionsPageLevel($type, $postId);
$data['load_exceptions_post_type'] = ($type === 'post' && $data['post_type']) ? $this->getLoadExceptionsPostType($data['post_type']) : array();
$data['handle_rows_contracted'] = self::getHandleRowStatus();
$data['total_styles'] = ! empty($data['all']['styles']) ? count($data['all']['styles']) : 0;
$data['total_scripts'] = ! empty($data['all']['scripts']) ? count($data['all']['scripts']) : 0;
$data['all_deps'] = $this->getAllDeps($data['all']);
$data['preloads'] = Preloads::instance()->getPreloads();
$data['handle_load_logged_in'] = $this->getHandleLoadLoggedIn();
$data['handle_notes'] = $this->getHandleNotes();
$data['ignore_child'] = $this->getIgnoreChildren();
$data['is_for_singular'] = (Misc::getVar('post', 'is_for_singular') === 'true');
$data['page_options'] = array();
$data['show_page_options'] = false;
if (in_array($type, array('post', 'front_page'))) {
$data['show_page_options'] = true;
$data['page_options'] = MetaBoxes::getPageOptions($postId, $type);
}
$this->parseTemplate('meta-box-loaded', $data, true);
exit();
}
/**
*
*/
public function ajaxLoadRestrictedPageAreaCallback()
{
if ( ! isset( $_POST['wpacu_nonce'] ) || ! wp_verify_nonce( $_POST['wpacu_nonce'], 'wpacu_ajax_load_page_restricted_area_nonce' ) ) {
echo 'Error: The security nonce is not valid.';
exit();
}
$postId = (int)Misc::getVar('post', 'post_id'); // if any (could be home page for instance)
$data = array();
$data['post_id'] = $this->currentPostId = $postId;
$data['fetch_url'] = Misc::getPageUrl($postId);
$data['show_page_options'] = true;
$data['page_options'] = MetaBoxes::getPageOptions($postId);
$post = get_post($postId);
// Current Post Type
$data['post_type'] = $post->post_type;
$data['bulk_unloaded_type'] = 'post_type';
$data['is_bulk_unloadable'] = true;
$data = $this->setPageTemplate($data);
switch (assetCleanUpHasNoLoadMatches($data['fetch_url'])) {
case 'is_set_in_settings':
// The rules from "Settings" -> "Plugin Usage Preferences" -> "Do not load the plugin on certain pages" will be checked
$data['status'] = 5;
break;
case 'is_set_in_page':
// The following option from "Page Options" (within the CSS/JS manager of the targeted page) is set: "Do not load Asset CleanUp Pro on this page (this will disable any functionality of the plugin)"
$data['status'] = 6;
break;
default:
$data['status'] = 1;
}
$this->parseTemplate('meta-box-restricted-page-load', $data, true);
exit();
}
/**
*
*/
public function ajaxPrintLoadedHardcodedAssets()
{
if ( ! isset( $_POST['wpacu_nonce'] ) || ! wp_verify_nonce( $_POST['wpacu_nonce'], 'wpacu_print_loaded_hardcoded_assets_nonce' ) ) {
echo 'Error: The security nonce is not valid.';
exit();
}
$wpacuListH = Misc::getVar('post', 'wpacu_list_h');
$wpacuSettingsJson = base64_decode(Misc::getVar('post', 'wpacu_settings'));
$wpacuSettings = (array)json_decode($wpacuSettingsJson, ARRAY_A); // $data values are passed here
// Only set the following variables if there is at least one hardcoded LINK/STYLE/SCRIPT
$jsonH = base64_decode( $wpacuListH );
function wpacuPrintHardcodedManagementList($jsonH, $wpacuSettings) {
$data = $wpacuSettings ?: array();
$data['do_not_print_list'] = true;
$data['print_outer_html'] = false;
$data['all']['hardcoded'] = (array)json_decode($jsonH, ARRAY_A);
$totalHardcodedTags = 0; // default
if (isset($data['all']['hardcoded']['within_conditional_comments']) && ! empty($data['all']['hardcoded']['within_conditional_comments'])) {
ObjectCache::wpacu_cache_set( 'wpacu_hardcoded_content_within_conditional_comments', $data['all']['hardcoded']['within_conditional_comments'] );
}
ob_start();
// $totalHardcodedTags is set here
include_once WPACU_PLUGIN_DIR.'/templates/meta-box-loaded-assets/_assets-hardcoded-list.php'; // generate $hardcodedTagsOutput
$output = ob_get_clean();
return wp_json_encode( array(
'output' => $output,
'total_hardcoded_assets' => $totalHardcodedTags
) );
}
echo wpacuPrintHardcodedManagementList( $jsonH, $wpacuSettings );
exit();
}
/**
*
*/
public function ajaxCheckExternalUrlsForStatusCode()
{
if ( ! isset( $_POST['wpacu_nonce'] ) || ! wp_verify_nonce( $_POST['wpacu_nonce'], 'wpacu_ajax_check_external_urls_nonce' ) ) {
echo 'Error: The security nonce is not valid.';
exit();
}
if (! isset($_POST['action'], $_POST['wpacu_check_urls'])) {
echo 'Error: The post parameters are not the right ones.';
exit();
}
// Check privileges
if (! Menu::userCanManageAssets()) {
echo 'Error: Not enough privileges to perform this action.';
exit();
}
$checkUrls = explode('-at-wpacu-at-', $_POST['wpacu_check_urls']);
$checkUrls = array_filter(array_unique($checkUrls));
foreach ($checkUrls as $index => $checkUrl) {
if (strpos($checkUrl, '//') === 0) { // starts with // (append the right protocol)
if (strpos($checkUrl, 'fonts.googleapis.com') !== false) {
$checkUrl = 'https:'.$checkUrl;
} else {
// either HTTP or HTTPS depending on the current page situation (that the admin has loaded)
$checkUrl = (Misc::isHttpsSecure() ? 'https:' : 'http:') . $checkUrl;
}
}
$response = wp_remote_get($checkUrl);
// Remove 200 OK ones as the other ones will remain for highlighting
if (wp_remote_retrieve_response_code($response) === 200) {
unset($checkUrls[$index]);
}
}
echo wp_json_encode($checkUrls);
exit();
}
/**
* @return void
*/
public function ajaxFetchActivePluginsIconsFromWordPressOrg()
{
if ( ! isset($_POST['wpacu_nonce']) ) {
echo 'Error: The security nonce was not sent for verification. Location: '.__METHOD__;
return;
}
if ( ! wp_verify_nonce($_POST['wpacu_nonce'], 'wpacu_fetch_active_plugins_icons') ) {
echo 'Error: The security check has failed. Location: '.__METHOD__;
return;
}
if (! isset($_POST['action'])) {
return;
}
if (! Menu::userCanManageAssets()) {
return;
}
echo 'POST DATA: '.print_r($_POST, true)."\n\n";
echo '- Downloading from WordPress.org'."\n\n";
$activePluginsIcons = Misc::fetchActiveFreePluginsIconsFromWordPressOrg();
if ($activePluginsIcons && is_array($activePluginsIcons) && ! empty($activePluginsIcons)) {
echo print_r($activePluginsIcons, true)."\n";
exit;
}
}
/**
*
*/
public function ajaxFetchActivePluginsJsFooterCode()
{
if (! Menu::isPluginPage()) {
return;
}
if (! Menu::userCanManageAssets()) {
return;
}
$forcePluginIconsDownload = isset($_GET['wpacu_force_plugin_icons_fetch']);
$triggerPluginIconsDownload = $forcePluginIconsDownload || ! get_transient('wpacu_active_plugins_icons');
if (! $triggerPluginIconsDownload) {
return;
}
?>
<script type="text/javascript" >
jQuery(document).ready(function($) {
var wpacuDataToSend = {
'action': '<?php echo WPACU_PLUGIN_ID.'_fetch_active_plugins_icons'; ?>',
'wpacu_nonce': '<?php echo wp_create_nonce('wpacu_fetch_active_plugins_icons'); ?>'
};
jQuery.post(ajaxurl, wpacuDataToSend, function(response) {
console.log(response);
});
});
</script>
<?php
}
/**
* @param $data
* @return mixed
*/
public function alterAssetObj($data, $alterPosition = true)
{
$siteUrl = get_site_url();
if (! empty($data['all']['styles'])) {
$data['core_styles_loaded'] = false;
foreach ($data['all']['styles'] as $key => $obj) {
if (! isset($obj->handle)) {
unset($data['all']['styles']['']);
continue;
}
// From WordPress directories (false by default, unless it was set to true before: in Sorting.php for instance)
if (! isset($data['all']['styles'][$key]->wp)) {
$data['all']['styles'][$key]->wp = false;
}
if ($alterPosition) {
if ( in_array( $obj->handle, $this->assetsInFooter['styles'] ) ) {
$data['all']['styles'][ $key ]->position = 'body';
} else {
$data['all']['styles'][ $key ]->position = 'head';
}
}
if (isset($data['all']['styles'][$key], $obj->src) && $obj->src) {
$localSrc = Misc::getLocalSrc($obj->src);
if (! empty($localSrc)) {
$data['all']['styles'][$key]->baseUrl = $localSrc['base_url'];
if (Sorting::matchesWpCoreCriteria($obj, 'styles')) {
$data['all']['styles'][ $key ]->wp = true;
$data['core_styles_loaded'] = true;
}
}
// Determine source href (starting with '/' but not starting with '//')
if (strpos($obj->src, '/') === 0 && strpos($obj->src, '//') !== 0) {
$obj->srcHref = $siteUrl . $obj->src;
} else {
$obj->srcHref = $obj->src;
}
$data['all']['styles'][$key]->size = apply_filters('wpacu_get_asset_file_size', $data['all']['styles'][$key], 'for_print');
$data['all']['styles'][$key]->sizeRaw = apply_filters('wpacu_get_asset_file_size', $data['all']['styles'][$key], 'raw');
}
}
}
if (! empty($data['all']['scripts'])) {
$data['core_scripts_loaded'] = false;
foreach ($data['all']['scripts'] as $key => $obj) {
if (! isset($obj->handle)) {
unset($data['all']['scripts']['']);
continue;
}
// From WordPress directories (false by default, unless it was set to true before: in Sorting.php for instance)
if (! isset($data['all']['scripts'][$key]->wp)) {
$data['all']['scripts'][$key]->wp = false;
}
if ($alterPosition) {
$initialScriptPos = ObjectCache::wpacu_cache_get( $obj->handle, 'wpacu_scripts_initial_positions' );
if ( $initialScriptPos === 'body' || in_array( $obj->handle, $this->assetsInFooter['scripts'] ) ) {
$data['all']['scripts'][ $key ]->position = 'body';
} else {
$data['all']['scripts'][ $key ]->position = 'head';
}
}
if (isset($data['all']['scripts'][$key])) {
if (isset($obj->src) && $obj->src) {
$localSrc = Misc::getLocalSrc($obj->src);
if (! empty($localSrc)) {
$data['all']['scripts'][$key]->baseUrl = $localSrc['base_url'];
if (Sorting::matchesWpCoreCriteria($obj, 'scripts')) {
$data['all']['scripts'][ $key ]->wp = true;
$data['core_scripts_loaded'] = true;
}
}
// Determine source href
if (substr($obj->src, 0, 1) === '/' && substr($obj->src, 0, 2) !== '//') {
$obj->srcHref = $siteUrl . $obj->src;
} else {
$obj->srcHref = $obj->src;
}
}
if (in_array($obj->handle, array('jquery', 'jquery-core', 'jquery-migrate'))) {
$data['all']['scripts'][$key]->wp = true;
$data['core_scripts_loaded'] = true;
}
$data['all']['scripts'][$key]->size = apply_filters('wpacu_get_asset_file_size', $data['all']['scripts'][$key], 'for_print');
$data['all']['scripts'][$key]->sizeRaw = apply_filters('wpacu_get_asset_file_size', $data['all']['scripts'][$key], 'raw');
}
}
}
return $data;
}
/**
* This method retrieves only the assets that are unloaded per page
* Including 404, date and search pages (they are considered as ONE page with the same rules for any URL variation)
*
* @param int $postId
* @param bool $returnAsArray
*
* @return string|array (The returned value must be a JSON one)
*/
public function getAssetsUnloadedPageLevel($postId = 0, $returnAsArray = false)
{
$defaultEmptyArrayValue = array( 'styles' => array(), 'scripts' => array() );
// Post Type (Overwrites 'front' - home page - if we are in a singular post)
if ($postId == 0) {
$postId = (int)$this->getCurrentPostId();
}
$isInAdminPageViaAjax = (is_admin() && defined('DOING_AJAX') && DOING_AJAX);
$assetsRemovedPageLevel = wp_json_encode( $defaultEmptyArrayValue );
// For Home Page (latest blog posts)
if ( $postId < 1 && ( $isInAdminPageViaAjax || Misc::isHomePage() ) ) {
$assetsRemovedPageLevel = get_option( WPACU_PLUGIN_ID . '_front_page_no_load' );
} elseif ( $postId > 0 ) { // Singular Page
$assetsRemovedPageLevel = get_post_meta( $postId, '_' . WPACU_PLUGIN_ID . '_no_load', true );
}
@json_decode( $assetsRemovedPageLevel );
if ( ! ( Misc::jsonLastError() === JSON_ERROR_NONE ) || empty( $assetsRemovedPageLevel ) || $assetsRemovedPageLevel === '[]' ) {
// Reset value to a JSON formatted one
$assetsRemovedPageLevel = wp_json_encode( $defaultEmptyArrayValue );
}
$assetsRemovedDecoded = json_decode( $assetsRemovedPageLevel, ARRAY_A );
if (! isset($assetsRemovedDecoded['styles'])) {
$assetsRemovedDecoded['styles'] = array();
}
if (! isset($assetsRemovedDecoded['scripts'])) {
$assetsRemovedDecoded['scripts'] = array();
}
/* [START] Unload CSS/JS on page request for debugging purposes */
$assetsUnloadedOnTheFly = $defaultEmptyArrayValue;
if ( Misc::getVar( 'get', 'wpacu_unload_css' ) ) {
$cssOnTheFlyList = $this->unloadAssetOnTheFly( 'css' );
if ( ! empty( $cssOnTheFlyList ) ) {
foreach ( $cssOnTheFlyList as $cssHandle ) {
if ( ! in_array( $cssHandle, $assetsRemovedDecoded['styles'] ) ) {
$assetsRemovedDecoded['styles'][] = $assetsUnloadedOnTheFly['styles'][] = $cssHandle;
}
}
}
}
if ( Misc::getVar( 'get', 'wpacu_unload_js' ) ) {
$jsOnTheFlyList = $this->unloadAssetOnTheFly( 'js' );
if ( ! empty( $jsOnTheFlyList ) ) {
foreach ( $jsOnTheFlyList as $jsHandle ) {
if ( ! in_array( $jsHandle, $assetsRemovedDecoded['scripts'] ) ) {
$assetsRemovedDecoded['scripts'][] = $assetsUnloadedOnTheFly['scripts'][] = $jsHandle;
}
}
}
}
ObjectCache::wpacu_cache_add( 'wpacu_assets_unloaded_list_page_request', $assetsUnloadedOnTheFly );
/* [END] Unload CSS/JS on page request for debugging purposes */
$assetsRemovedPageLevel = wp_json_encode( $assetsRemovedDecoded );
if ($returnAsArray) {
$assetsRemovedPageLevel = (array)@json_decode($assetsRemovedPageLevel);
// Make sure there are no objects in the array to avoid any PHP errors later on in PHP 8+
foreach ( array( 'styles', 'scripts' ) as $assetType ) {
if ( isset( $assetsRemovedPageLevel[ $assetType ] ) ) {
$assetsRemovedPageLevel[ $assetType ] = (array)$assetsRemovedPageLevel[ $assetType ];
}
}
}
// Hmm, perhaps one of the filters was incorrectly used and returned an array instead of a JSON format; autocorrect this to avoid PHP errors
if (! $returnAsArray && is_array($assetsRemovedPageLevel)) {
return wp_json_encode( $defaultEmptyArrayValue );
}
return $assetsRemovedPageLevel;
}
/**
* @param $allAssets
*
* @return array
*/
public function getAllDeps($allAssets)
{
$allDepsParentToChild = $allDepsChildToParent = array('styles' => array(), 'scripts' => array());
foreach (array('styles', 'scripts') as $assetType) {
if ( ! (isset($allAssets[$assetType]) && ! empty($allAssets[$assetType])) ) {
continue;
}
foreach ($allAssets[$assetType] as $assetObj) {
if (isset($assetObj->deps) && ! empty($assetObj->deps)) {
foreach ($assetObj->deps as $dep) {
$allDepsParentToChild[$assetType][$dep][] = $assetObj->handle;
$allDepsChildToParent[$assetType][$assetObj->handle][] = $dep;
}
}
}
}
return array(
'parent_to_child' => $allDepsParentToChild,
'child_to_parent' => $allDepsChildToParent
);
}
/**
* @param $obj
* @param $format | 'for_print': Calculates the format in KB / MB - 'raw': The actual size in bytes
* @return string
*/
public function getAssetFileSize($obj, $format = 'for_print')
{
if (isset($obj->src) && $obj->src) {
$src = $obj->src;
$siteUrl = site_url();
// Starts with / but not with //
// Or starts with ../ (very rare cases)
$isRelInternalPath = (strpos($src, '/') === 0 && strpos($src, '//') !== 0) || (strpos($src, '../') === 0);
// Source starts with '//' - check if the file exists
if (strpos($obj->src, '//') === 0) {
list ($urlPrefix) = explode('//', $siteUrl);
$srcToCheck = $urlPrefix . $obj->src;
$hostSiteUrl = parse_url($siteUrl, PHP_URL_HOST);
$hostSrc = parse_url($obj->src, PHP_URL_HOST);
$siteUrlAltered = str_replace(array($hostSiteUrl, $hostSrc), '{site_host}', $siteUrl);
$srcAltered = str_replace(array($hostSiteUrl, $hostSrc), '{site_host}', $srcToCheck);
$srcMaybeRelPath = str_replace($siteUrlAltered, '', $srcAltered);
$possibleStrips = array('?ver', '?cache=');
foreach ($possibleStrips as $possibleStrip) {
if ( strpos( $srcMaybeRelPath, $possibleStrip ) !== false ) {
list ( $srcMaybeRelPath ) = explode( $possibleStrip, $srcMaybeRelPath );
}
}
if (is_file(Misc::getWpRootDirPath() . $srcMaybeRelPath)) {
$fileSize = filesize(Misc::getWpRootDirPath() . $srcMaybeRelPath);
if ($format === 'raw') {
return (int)$fileSize;
}
return Misc::formatBytes($fileSize);
}
}
// e.g. /?scss=1 (Simple Custom CSS Plugin)
if (str_replace($siteUrl, '', $src) === '/?sccss=1') {
$customCss = DynamicLoadedAssets::getSimpleCustomCss();
$sizeInBytes = mb_strlen($customCss);
if ($format === 'raw') {
return $sizeInBytes;
}
return Misc::formatBytes($sizeInBytes);
}
// External file? Use a different approach
// Return an HTML code that will be parsed via AJAX through JavaScript
$isExternalFile = (! $isRelInternalPath &&
(! (isset($obj->wp) && $obj->wp === 1))
&& strpos($src, $siteUrl) !== 0);
// e.g. /?scss=1 (Simple Custom CSS Plugin) From External Domain
// /?custom-css (JetPack Custom CSS)
$isLoadedOnTheFly = (strpos($src, '?sccss=1') !== false)
|| (strpos($src, '?custom-css') !== false);
if ($isExternalFile || $isLoadedOnTheFly) {
return '<a class="wpacu-external-file-size" data-src="' . $src . '" href="#">🔗 Get File Size</a>'.
'<span style="display: none;"><img style="width: 20px; height: 20px;" alt="" align="top" width="20" height="20" src="'.includes_url('images/spinner-2x.gif').'"></span>';
}
$forAssetType = $pathToFile = false;
if ( stripos( $src, '.css' ) !== false ) {
$forAssetType = 'css';
} elseif ( stripos( $src, '.js' ) !== false ) {
$forAssetType = 'js';
}
if ($forAssetType) {
$pathToFile = OptimizeCommon::getLocalAssetPath( $src, $forAssetType );
}
if ( ! is_file($pathToFile) ) { // Fallback, old code...
// Local file? Core or from a plugin / theme?
if ( strpos( $obj->src, $siteUrl ) !== false ) {
// Local Plugin / Theme File
// Could be a Staging site that is having the Live URL in the General Settings
$src = ltrim( str_replace( $siteUrl, '', $obj->src ), '/' );
} elseif ( ( isset( $obj->wp ) && $obj->wp === 1 ) || $isRelInternalPath ) {
// Local WordPress Core File
$src = ltrim( $obj->src, '/' );
}
$srcAlt = $src;
if ( strpos( $src, '../' ) === 0 ) {
$srcAlt = str_replace( '../', '', $srcAlt );
}
$pathToFile = Misc::getWpRootDirPath() . $srcAlt;
if ( strpos( $pathToFile, '?ver' ) !== false ) {
list( $pathToFile ) = explode( '?ver', $pathToFile );
}
// It can happen that the CSS/JS has extra parameters (rare cases)
foreach ( array( '.css?', '.js?' ) as $needlePart ) {
if ( strpos( $pathToFile, $needlePart ) !== false ) {
list( $pathToFile ) = explode( '?', $pathToFile );
}
}
}
if (is_file($pathToFile)) {
$sizeInBytes = filesize($pathToFile);
if ($format === 'raw') {
return (int)$sizeInBytes;
}
return Misc::formatBytes($sizeInBytes);
}
return '<em>Error: Could not read '.$pathToFile.'</em>';
}
if ($obj->handle === 'jquery' && isset($obj->src) && ! $obj->src) {
return '"jquery-core" size';
}
// External or nothing to be shown (perhaps due to an error)
return '';
}
/**
* Source: https://stackoverflow.com/questions/2602612/remote-file-size-without-downloading-file
*/
public function ajaxGetExternalFileSize()
{
// Check nonce
if ( ! isset( $_POST['wpacu_nonce'] ) || ! wp_verify_nonce( $_POST['wpacu_nonce'], 'wpacu_ajax_check_remote_file_size_nonce' ) ) {
echo 'Error: The security nonce is not valid.';
exit();
}
// Check privileges
if (! Menu::userCanManageAssets()) {
echo 'Error: Not enough privileges to perform this action.';
exit();
}
// Assume failure.
$result = -1;
$remoteFile = Misc::getVar('post', 'wpacu_remote_file', false);
if (! $remoteFile) {
echo 'N/A (external file)';
exit;
}
// If it starts with //
if (strpos($remoteFile, '//') === 0) {
$remoteFile = 'http:'.$remoteFile;
}
// Check if the URL is valid
if (! filter_var($remoteFile, FILTER_VALIDATE_URL)) {
echo 'The asset\'s URL - '.$remoteFile.' - is not valid.';
exit();
}
$curl = curl_init($remoteFile);
// Issue a HEAD request and follow any redirects.
curl_setopt($curl, CURLOPT_NOBODY, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
$data = curl_exec($curl);
curl_close($curl);
$content_length = $status = 'unknown';
if ($data) {
if (preg_match( '/^HTTP\/1\.[01] (\d\d\d)/', $data, $matches ) ) {
$status = (int)$matches[1];
}
if ( preg_match( '/Content-Length: (\d+)/', $data, $matches ) ) {
$content_length = (int)$matches[1];
}
// http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
if ( $status === 200 || ($status > 300 && $status <= 308) ) {
$result = $content_length;
}
}
if ($content_length === 'unknown') {
// One more try
$response = wp_remote_get($remoteFile);
$responseCode = wp_remote_retrieve_response_code($response);
if ($responseCode === 200) {
$result = mb_strlen(wp_remote_retrieve_body($response));
}
}
echo Misc::formatBytes($result);
if (stripos($remoteFile, '//fonts.googleapis.com/') !== false) {
// Google Font APIS CDN
echo ' + the sizes of the loaded "Google Font" files (see "url" from @font-face within the Source file)';
} elseif (stripos($remoteFile, '/font-awesome.css') || stripos($remoteFile, '/font-awesome.min.css')) {
// FontAwesome CDN
echo ' + the sizes of the loaded "FontAwesome" font files (see "url" from @font-face within the Source file)';
}
exit();
}
/**
* @return bool
*/
public static function isSingularPage()
{
return (self::$vars['is_woo_shop_page'] || is_singular() || Misc::isBlogPage());
}
/**
* @return int|mixed|string
*/
public function getCurrentPostId()
{
if ($this->currentPostId > 0) {
return $this->currentPostId;
}
if (Misc::isElementorMaintenanceModeOnForCurrentAdmin() && defined('WPACU_IS_ELEMENTOR_MAINTENANCE_MODE_TEMPLATE_ID')) {
$this->currentPostId = WPACU_IS_ELEMENTOR_MAINTENANCE_MODE_TEMPLATE_ID;
return $this->currentPostId;
}
// Are we on the `Shop` page from WooCommerce?
// Only check option if function `is_shop` exists
$wooCommerceShopPageId = function_exists('is_shop') ? get_option('woocommerce_shop_page_id') : 0;
// Check if we are on the WooCommerce Shop Page
// Do not mix the WooCommerce Search Page with the Shop Page
if (function_exists('is_shop') && is_shop()) {
$this->currentPostId = $wooCommerceShopPageId;
if ($this->currentPostId > 0) {
self::$vars['is_woo_shop_page'] = true;
}
} else {
if ($wooCommerceShopPageId > 0 && Misc::isHomePage() && strpos(get_site_url(), '://') !== false) {
list($siteUrlAfterProtocol) = explode('://', get_site_url());
$currentPageUrlAfterProtocol = parse_url(site_url(), PHP_URL_HOST) . $_SERVER['REQUEST_URI'];
if ($siteUrlAfterProtocol != $currentPageUrlAfterProtocol && (strpos($siteUrlAfterProtocol, '/shop') !== false)) {
self::$vars['woo_url_not_match'] = true;
}
}
}
// Blog Home Page (aka: Posts page) is not a singular page, it's checked separately
if (Misc::isBlogPage()) {
$this->currentPostId = get_option('page_for_posts');
}
// It has to be a single page (no "Posts page")
if (($this->currentPostId < 1) && is_singular()) {
global $post;
$this->currentPostId = isset($post->ID) ? $post->ID : 0;
}
// [wpacu_lite]
// Undetectable? The page is not a singular one nor the home page
// It's likely an archive, category page (WooCommerce), 404 page manageable in the Pro version etc.
if (! $this->currentPostId && ! Misc::isHomePage()) {
$this->isUpdateable = false;
}
// [/wpacu_lite]
return $this->currentPostId;
}
/**
* @return array|null|\WP_Post
*/
public function getCurrentPost()
{
// Already set? Return it
if (! empty($this->currentPost)) {
return $this->currentPost;
}
// Not set? Create and return it
if (! $this->currentPost && $this->getCurrentPostId() > 0) {
$this->currentPost = get_post($this->getCurrentPostId());
return $this->currentPost;
}
// Empty
return $this->currentPost;
}
/**
* Get all contracted rows
*
* @return array
*/
public static function getHandleRowStatus()
{
$handleRowStatus = array('styles' => array(), 'scripts' => array());
$handleRowStatusListJson = get_option(WPACU_PLUGIN_ID . '_global_data');
$globalKey = 'handle_row_contracted';
if ($handleRowStatusListJson) {
$handleRowStatusList = @json_decode($handleRowStatusListJson, true);
// Issues with decoding the JSON file? Return an empty list
if (Misc::jsonLastError() !== JSON_ERROR_NONE) {
return $handleRowStatus;
}
// Are new positions set for styles and scripts?
foreach (array('styles', 'scripts') as $assetKey) {
if ( isset( $handleRowStatusList[$assetKey][$globalKey] ) && ! empty( $handleRowStatusList[$assetKey][$globalKey] ) ) {
$handleRowStatus[$assetKey] = $handleRowStatusList[$assetKey][$globalKey];
}
}
}
return $handleRowStatus;
}
/**
* @param $postType
*
* @return array
*/
public static function getAllSetTaxonomies($postType)
{
if ( ! $postType ) {
return array();
}
$postTaxonomies = get_object_taxonomies($postType);
if ($postType === 'post') {
$postFormatKey = array_search('post_format', $postTaxonomies);
unset($postTaxonomies[$postFormatKey]);
}
if (empty($postTaxonomies)) {
// There are no taxonomies associate with the $postType or $postType is not valid
return array();
}
$allPostTypeTaxonomyTerms = get_terms( array(
'taxonomy' => $postTaxonomies,
'hide_empty' => true,
) );
$finalList = array();
foreach ($allPostTypeTaxonomyTerms as $obj) {
$taxonomyObj = get_taxonomy($obj->taxonomy);
if ( ! $taxonomyObj->show_ui ) {
continue;
}
$finalList[$taxonomyObj->label][] = (array)$obj;
}
if ( ! empty($finalList) ) {
foreach ( array_keys( $finalList ) as $taxonomyLabel ) {
usort( $finalList[ $taxonomyLabel ], static function( $a, $b ) {
return strcasecmp( $a['name'], $b['name'] );
} );
}
ksort($finalList);
}
return $finalList;
}
/**
* @param $data
*
* @return mixed
*/
public function setPageTemplate($data)
{
global $template;
$getPageTpl = get_post_meta($this->getCurrentPostId(), '_wp_page_template', true);
// Could be a custom post type with no template set
if (! $getPageTpl) {
$getPageTpl = get_page_template();
if (in_array(basename($getPageTpl), array('single.php', 'page.php'))) {
$getPageTpl = 'default';
}
}
if (! $getPageTpl) {
return $data;
}
$data['page_template'] = $getPageTpl;
$data['all_page_templates'] = wp_get_theme()->get_page_templates();
// Is the default template shown? Most of the time it is!
if ($data['page_template'] === 'default') {
$pageTpl = (isset($template) && $template) ? $template : get_page_template();
$data['page_template'] = basename( $pageTpl );
$data['all_page_templates'][ $data['page_template'] ] = 'Default Template';
}
if (isset($template) && $template && defined('ABSPATH')) {
$data['page_template_path'] = str_replace(
Misc::getWpRootDirPath(),
'',
'/'.$template
);
}
return $data;
}
/**
* @return bool
*/
public static function isWpDefaultSearchPage()
{
// It will not interfere with the WooCommerce search page
// which is considered to be the "Shop" page that has its own unload rules
return (is_search() && (! (function_exists('is_shop') && is_shop())));
}
/**
* @param $existingListJson
* @param $existingListEmpty
*
* @return array
*/
public function existingList($existingListJson, $existingListEmpty)
{
$validJson = $notEmpty = true;
if (! $existingListJson) {
$existingList = $existingListEmpty;
$notEmpty = false;
} else {
$existingList = json_decode($existingListJson, true);
if (Misc::jsonLastError() !== JSON_ERROR_NONE) {
$validJson = false;
$existingList = $existingListEmpty;
}
}
return array(
'list' => $existingList,
'valid_json' => $validJson,
'not_empty' => $notEmpty
);
}
/**
* Situations when the assets will not be prevented from loading
* e.g. test mode and a visitor accessing the page, an AJAX request from the Dashboard to print all the assets
*
* @param array $ignoreList
*
* @return bool
*/
public function preventAssetsSettings($ignoreList = array())
{
// This request specifically asks for all the assets to be loaded in order to print them in the assets management list
// This is for the AJAX requests within the Dashboard, thus the admin needs to see all the assets,
// including ones marked for unload, in case he/she decides to change their rules
if ($this->isGetAssetsCall && ! in_array('assets_call', $ignoreList)) {
return true;
}
// Is test mode enabled? Unload assets ONLY for the admin
if (self::isTestModeActive()) {
return true; // visitors (non-logged in) will view the pages with all the assets loaded
}
$isSingularPage = defined('WPACU_CURRENT_PAGE_ID') && WPACU_CURRENT_PAGE_ID > 0 && is_singular();
if ($isSingularPage || Misc::isHomePage()) {
if ($isSingularPage) {
$pageOptions = MetaBoxes::getPageOptions( WPACU_CURRENT_PAGE_ID ); // Singular page
} else {
$pageOptions = MetaBoxes::getPageOptions(0, 'front_page'); // Home page
}
if (isset($pageOptions['no_assets_settings']) && $pageOptions['no_assets_settings']) {
return true;
}
}
return false;
}
/**
* @param array $settings
*
* @return bool
*/
public static function isTestModeActive($settings = array())
{
if (defined('WPACU_IS_TEST_MODE_ACTIVE')) {
return WPACU_IS_TEST_MODE_ACTIVE;
}
if (! $settings) {
$settings = self::instance()->settings;
}
$wpacuIsTestModeActive = isset($settings['test_mode']) && $settings['test_mode'] && ! Menu::userCanManageAssets();
define('WPACU_IS_TEST_MODE_ACTIVE', $wpacuIsTestModeActive);
return $wpacuIsTestModeActive;
}
/**
* @return bool
*/
public static function currentUserCanViewAssetsList()
{
if ( Main::instance()->settings['allow_manage_assets_to'] === 'chosen' && ! empty(Main::instance()->settings['allow_manage_assets_to_list']) ) {
$wpacuCurrentUserId = get_current_user_id();
if ( ! in_array( $wpacuCurrentUserId, Main::instance()->settings['allow_manage_assets_to_list'] ) ) {
return false; // the current logged-in admin is not in the list of "Allow managing assets to:"
}
}
return true;
}
/**
* @return bool
*/
public function frontendShow()
{
// The option is disabled
if (! $this->settings['frontend_show']) {
return false;
}
// The asset list is hidden via query string: /?wpacu_no_frontend_show
if (isset($_REQUEST['wpacu_no_frontend_show'])) {
return false;
}
// Page loaded via Yellow Pencil Editor within an iframe? Do not show it as it's irrelevant there
if (isset($_GET['yellow_pencil_frame'], $_GET['yp_page_type'])) {
return false;
}
// The option is enabled, but there are show exceptions, check if the list should be hidden
if ($this->settings['frontend_show_exceptions']) {
$frontendShowExceptions = trim( $this->settings['frontend_show_exceptions'] );
// We want to make sure the RegEx rules will be working fine if certain characters (e.g. Thai ones) are used
$requestUriAsItIs = rawurldecode($_SERVER['REQUEST_URI']);
if ( strpos( $frontendShowExceptions, "\n" ) !== false ) {
foreach ( explode( "\n", $frontendShowExceptions ) as $frontendShowException ) {
$frontendShowException = trim($frontendShowException);
if ( strpos( $requestUriAsItIs, $frontendShowException ) !== false ) {
return false;
}
}
} elseif ( strpos( $requestUriAsItIs, $frontendShowExceptions ) !== false ) {
return false;
}
}
// Allows managing assets to chosen admins and the user is not in the list
if ( ! self::currentUserCanViewAssetsList() ) {
return false;
}
return true;
}
/**
* Make administrator more aware if "TEST MODE" is enabled or not
*/
public function wpacuHtmlNoticeForAdmin()
{
add_action('wp_footer', static function() {
if ((WPACU_GET_LOADED_ASSETS_ACTION === true) || (! apply_filters('wpacu_show_admin_console_notice', true)) || Plugin::preventAnyFrontendOptimization()) {
return;
}
if ( ! (Menu::userCanManageAssets() && ! is_admin())) {
return;
}
if (Main::instance()->settings['test_mode']) {
$consoleMessage = sprintf(esc_html__('%s: "TEST MODE" ENABLED (any settings or unloads will be visible ONLY to you, the logged-in administrator)', 'wp-asset-clean-up'), WPACU_PLUGIN_TITLE);
$testModeNotice = esc_html__('"Test Mode" is ENABLED. Any settings or unloads will be visible ONLY to you, the logged-in administrator.', 'wp-asset-clean-up');
} else {
$consoleMessage = sprintf(esc_html__('%s: "LIVE MODE" (test mode is not enabled, thus, all the plugin changes are visible for everyone: you, the logged-in administrator and the regular visitors)', 'wp-asset-clean-up'), WPACU_PLUGIN_TITLE);
$testModeNotice = esc_html__('The website is in LIVE MODE as "Test Mode" is not enabled. All the plugin changes are visible for everyone: logged-in administrators and regular visitors.', 'wp-asset-clean-up');
}
?>
<!--
<?php echo sprintf(esc_html__('NOTE: These "%s: Page Speed Booster" messages are only shown to you, the HTML comment is not visible for the regular visitor.', 'wp-asset-clean-up'), WPACU_PLUGIN_TITLE); ?>
<?php echo esc_html__($testModeNotice); ?>
-->
<script <?php echo Misc::getScriptTypeAttribute(); ?> data-wpacu-own-inline-script="true">
console.log('<?php echo esc_js($consoleMessage); ?>');
</script>
<?php
});
}
/**
*
*/
public function fallbacks()
{
// Fallback for the old filters (e.g. Pro version below 1.2.0.7)
add_filter('wpacu_filter_styles_list_unload', function ($list) { return apply_filters('wpacu_filter_styles', $list); });
add_filter('wpacu_filter_scripts_list_unload', function ($list) { return apply_filters('wpacu_filter_scripts', $list); });
}
}