Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
<?php
namespace Perfmatters;
use WP_CLI;
class CLI {
/**
* Activates a license key.
*
* ## OPTIONS
*
* [<key>]
* : The license key to add and activate.
*
* @subcommand activate-license
*
*/
public function activate_license($args, $assoc_args) {
$network = is_multisite() && empty(WP_CLI::get_config()['url']);
if(!empty($args[0])) {
$network ? update_site_option('perfmatters_edd_license_key', trim($args[0])) : update_option('perfmatters_edd_license_key', trim($args[0]));
}
if(is_multisite()) {
$license_info = perfmatters_check_license($network);
if(empty($license_info->activations_left) || $license_info->activations_left !== 'unlimited') {
WP_CLI::warning(__('Unlimited site license required.', 'perfmatters'));
return;
}
}
if(perfmatters_activate_license($network)) {
WP_CLI::success(__('License activated!', 'perfmatters'));
}
else {
WP_CLI::warning(__('License could not be activated.', 'perfmatters'));
}
}
/**
* Deactivates a license key.
*
* @subcommand deactivate-license
*/
public function deactivate_license() {
$network = is_multisite() && empty(WP_CLI::get_config()['url']);
if(perfmatters_deactivate_license($network)) {
WP_CLI::success(__('License deactivated!', 'perfmatters'));
}
else {
WP_CLI::warning(__('License could not be deactivated.', 'perfmatters'));
}
}
/**
* Deactivates and removes a license key.
*
* @subcommand remove-license
*/
public function remove_license() {
$network = is_multisite() && empty(WP_CLI::get_config()['url']);
if(perfmatters_deactivate_license($network)) {
WP_CLI::success('License deactivated!');
}
$removed = $network ? delete_site_option('perfmatters_edd_license_key') : delete_option('perfmatters_edd_license_key');
if($removed) {
WP_CLI::success(__('License removed!', 'perfmatters'));
}
else {
WP_CLI::warning(__('License could not be removed.', 'perfmatters'));
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,92 @@
<?php
namespace Perfmatters;
class Buffer
{
//initialize buffer class
public static function init()
{
add_action('wp', array('Perfmatters\Buffer', 'queue'));
}
//queue functions
public static function queue()
{
//inital checks
if(is_admin() || perfmatters_is_dynamic_request() || perfmatters_is_page_builder() || isset($_GET['perfmatters']) || isset($_GET['perfmattersoff'])) {
return;
}
//user agent check
if(!empty($_SERVER['HTTP_USER_AGENT'])) {
$excluded_agents = array(
'usercentrics'
);
foreach($excluded_agents as $agent) {
if(stripos($_SERVER['HTTP_USER_AGENT'], $agent) !== false) {
return;
}
}
}
//buffer is allowed
if(!apply_filters('perfmatters_allow_buffer', true)) {
return;
}
//add buffer action
add_action('template_redirect', array('Perfmatters\Buffer', 'start'), -9999);
}
//start buffer
public static function start()
{
if(has_filter('perfmatters_output_buffer_template_redirect')) {
//exclude certain requests
if(is_embed() || is_feed() || is_preview() || is_customize_preview()) {
return;
}
//don't buffer amp
if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
return;
}
ob_start(function($html) {
//check for valid buffer
if(!self::is_valid_buffer($html)) {
return $html;
}
//run buffer filters
$html = (string) apply_filters('perfmatters_output_buffer_template_redirect', $html);
//return processed html
return $html;
});
}
}
//make sure buffer content is valid
private static function is_valid_buffer($html)
{
//check for valid/invalid tags
if(stripos($html, '<html') === false || stripos($html, '</body>') === false || stripos($html, '<xsl:stylesheet') !== false) {
return false;
}
//check for invalid urls
$current_url = home_url($_SERVER['REQUEST_URI']);
$matches = apply_filters('perfmatters_buffer_excluded_extensions', array('.xml', '.txt', '.php'));
foreach($matches as $match) {
if(stripos($current_url, $match) !== false) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace Perfmatters;
class CDN
{
//initialize cdn
public static function init()
{
add_action('wp', array('Perfmatters\CDN', 'queue'));
}
//queue functions
public static function queue()
{
//add cdn rewrite to the buffer
if(!empty(Config::$options['cdn']['enable_cdn']) && !empty(Config::$options['cdn']['cdn_url'])) {
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\CDN', 'rewrite'));
}
}
//rewrite urls in html
public static function rewrite($html)
{
//filter check
if(!apply_filters('perfmatters_cdn', true)) {
return $html;
}
//prep site url
$siteURL = '//' . ((!empty($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : parse_url(home_url(), PHP_URL_HOST));
$escapedSiteURL = quotemeta($siteURL);
$regExURL = 'https?:' . substr($escapedSiteURL, strpos($escapedSiteURL, '//'));
//prep included directories
$directories = 'wp\-content|wp\-includes';
if(!empty(Config::$options['cdn']['cdn_directories'])) {
$directoriesArray = array_map('trim', explode(',', Config::$options['cdn']['cdn_directories']));
if(count($directoriesArray) > 0) {
$directories = implode('|', array_map('quotemeta', array_filter($directoriesArray)));
}
}
//prep included extensions
$extensions_array = apply_filters('perfmatters_cdn_extensions', array(
'avif',
'css',
'gif',
'jpeg',
'jpg',
'js',
'json',
'mp3',
'mp4',
'otf',
'pdf',
'png',
'svg',
'ttf',
'webp',
'woff',
'woff2'
));
$extensions = implode('|', $extensions_array);
//rewrite urls in html
$regEx = '#(?<=[(\"\']|&quot;)(?:' . $regExURL . ')?\/(?:(?:' . $directories . ')[^\"\')]+)\.(' . $extensions . ')[^\"\')]*(?=[\"\')]|&quot;)#';
//base exclusions
$exclusions = array('script-manager.js');
//add user exclusions
if(!empty(Config::$options['cdn']['cdn_exclusions'])) {
$exclusions_user = array_map('trim', explode(',', Config::$options['cdn']['cdn_exclusions']));
$exclusions = array_merge($exclusions, $exclusions_user);
}
//set cdn url
$cdnURL = untrailingslashit(Config::$options['cdn']['cdn_url']);
//replace urls
$html = preg_replace_callback($regEx, function($url) use ($siteURL, $cdnURL, $exclusions) {
//check for exclusions
foreach($exclusions as $exclusion) {
if(!empty($exclusion) && stristr($url[0], $exclusion) != false) {
return $url[0];
}
}
//replace url with no scheme
if(strpos($url[0], '//') === 0) {
return str_replace($siteURL, $cdnURL, $url[0]);
}
//replace non relative site url
if(strstr($url[0], $siteURL)) {
return str_replace(array('http:' . $siteURL, 'https:' . $siteURL), $cdnURL, $url[0]);
}
//replace relative url
return $cdnURL . $url[0];
}, $html);
return $html;
}
}

View File

@@ -0,0 +1,666 @@
<?php
namespace Perfmatters;
use Sabberworm\CSS\CSSList\AtRuleBlockList;
use Sabberworm\CSS\CSSList\CSSBlockList;
use Sabberworm\CSS\CSSList\Document;
use Sabberworm\CSS\OutputFormat;
use Sabberworm\CSS\Parser as CSSParser;
use Sabberworm\CSS\Property\Charset;
use Sabberworm\CSS\RuleSet\DeclarationBlock;
use Sabberworm\CSS\Settings;
use Sabberworm\CSS\Value\URL;
use WP_Admin_Bar;
class CSS
{
private static $used_selectors;
private static $excluded_selectors;
//initialize css functions
public static function init()
{
if(isset($_GET['perfmatterscssoff'])) {
return;
}
if(!empty(Config::$options['assets']['remove_unused_css'])) {
add_action('wp', array('Perfmatters\CSS', 'queue'));
add_action('wp_ajax_perfmatters_clear_post_used_css', array('Perfmatters\CSS', 'clear_post_used_css'));
add_action('admin_bar_menu', array('Perfmatters\CSS', 'admin_bar_menu'));
add_action('admin_notices', array('Perfmatters\CSS', 'admin_notices'));
add_action('admin_post_perfmatters_clear_used_css', array('Perfmatters\CSS', 'admin_bar_clear_used_css'));
}
}
//queue functions
public static function queue()
{
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\CSS', 'remove_unused_css'));
}
//remove unused css
public static function remove_unused_css($html)
{
if(empty(apply_filters('perfmatters_remove_unused_css', true))) {
return $html;
}
if(Utilities::get_post_meta('perfmatters_exclude_unused_css')) {
return $html;
}
//only logged out
if(is_user_logged_in()) {
return $html;
}
//skip woocommerce
if(Utilities::is_woocommerce()) {
return $html;
}
//only known url types
$type = self::get_url_type();
if(empty($type)) {
return $html;
}
//setup file variables
$used_css_path = PERFMATTERS_CACHE_DIR . 'css/' . $type . '.used.css';
$used_css_url = PERFMATTERS_CACHE_URL . 'css/' . $type . '.used.css';
$used_css_exists = file_exists($used_css_path);
//match all stylesheets
preg_match_all('#<link\s[^>]*?href=[\'"]([^\'"]+?\.css.*?)[\'"][^>]*?\/?>#i', $html, $stylesheets, PREG_SET_ORDER);
if(!empty($stylesheets)) {
//create our css cache directory
if(!is_dir(PERFMATTERS_CACHE_DIR . 'css/')) {
@mkdir(PERFMATTERS_CACHE_DIR . 'css/', 0755, true);
}
//populate used selectors
self::get_used_selectors($html);
self::get_excluded_selectors();
$used_css_string = '';
//loop through stylesheets
foreach($stylesheets as $key => $stylesheet) {
//stylesheet check
if(!preg_match('#\srel=[\'"]stylesheet[\'"]#is', $stylesheet[0])) {
continue;
}
//ignore google fonts
if(stripos($stylesheet[1], '//fonts.googleapis.com/css') !== false || stripos($stylesheet[1], '.google-fonts.css') !== false) {
continue;
}
//exclude entire stylesheets
$stylesheet_exclusions = array(
'dashicons.min.css', //core
'/uploads/elementor/css/post-', //elementor
'animations.min.css',
'woocommerce-mobile.min.css', //woocommerce
'woocommerce-smallscreen.css',
'/uploads/oxygen/css/', //oxygen
'/uploads/bb-plugin/cache/', //beaver builder
'/uploads/generateblocks/', //generateblocks
'/et-cache/', //divi
'/widget-google-reviews/assets/css/public-main.css' //plugin for google reviews
);
if(!empty(Config::$options['assets']['rucss_excluded_stylesheets'])) {
$stylesheet_exclusions = array_merge($stylesheet_exclusions, Config::$options['assets']['rucss_excluded_stylesheets']);
}
$stylesheet_exclusions = apply_filters('perfmatters_rucss_excluded_stylesheets', $stylesheet_exclusions);
foreach($stylesheet_exclusions as $exclude) {
if(strpos($stylesheet[1], $exclude) !== false) {
unset($stylesheets[$key]);
continue 2;
}
}
//need to generate used css
if(!$used_css_exists) {
//get local stylesheet path
$url = str_replace(trailingslashit(apply_filters('perfmatters_local_stylesheet_url', (!empty(Config::$options['assets']['rucss_cdn_url']) ? Config::$options['assets']['rucss_cdn_url'] : site_url()))), '', explode('?', $stylesheet[1])[0]);
$file = str_replace('/wp-content', '/', WP_CONTENT_DIR) . $url;
//make sure local file exists
if(!file_exists($file)) {
continue;
}
//get used css from stylesheet
$used_css = self::clean_stylesheet($stylesheet[1], @file_get_contents($file));
//add used stylesheet css to total used
$used_css_string.= $used_css;
}
//delay stylesheets
if(empty(Config::$options['assets']['rucss_stylesheet_behavior'])) {
$new_link = preg_replace('#href=([\'"]).+?\1#', 'data-pmdelayedstyle="' . $stylesheet[1] . '"',$stylesheet[0]);
$html = str_replace($stylesheet[0], $new_link, $html);
}
//async stylesheets
elseif(Config::$options['assets']['rucss_stylesheet_behavior'] == 'async') {
$new_link = preg_replace(array('#media=([\'"]).+?\1#', '#onload=([\'"]).+?\1#'), '', $stylesheet[0]);
$new_link = str_replace('<link', '<link media="print" onload="this.media=\'all\';this.onload=null;"', $new_link);
$html = str_replace($stylesheet[0], $new_link, $html);
}
//remove stylesheets
elseif(Config::$options['assets']['rucss_stylesheet_behavior'] == 'remove') {
$html = str_replace($stylesheet[0], '', $html);
}
}
//store used css file
if(!empty($used_css_string)) {
if(file_put_contents($used_css_path, apply_filters('perfmatters_used_css', $used_css_string)) !== false) {
$time = get_option('perfmatters_used_css_time', array());
$time[$type] = time();
//update stored timestamp
update_option('perfmatters_used_css_time', $time);
}
}
//print used css inline after first title tag
$pos = strpos($html, '</title>');
if($pos !== false) {
//print file
if(!empty(Config::$options['assets']['rucss_method']) && Config::$options['assets']['rucss_method'] == 'file') {
//grab stored timestamp for query string
$time = get_option('perfmatters_used_css_time', array());
if(!empty($time[$type])) {
$used_css_url = add_query_arg('ver', $time[$type], $used_css_url);
}
$used_css_output = "<link rel='preload' href='" . $used_css_url . "' as='style' onload=\"this.rel='stylesheet';this.removeAttribute('onload');\">";
$used_css_output.= '<link rel="stylesheet" id="perfmatters-used-css" href="' . $used_css_url . '" media="all" />';
}
//print inline
else {
$used_css_output = '<style id="perfmatters-used-css">' . file_get_contents($used_css_path) . '</style>';
}
$html = substr_replace($html, '</title>' . $used_css_output, $pos, 8);
}
//delay stylesheet script
if(empty(Config::$options['assets']['rucss_stylesheet_behavior'])) {
$delay_check = !empty(apply_filters('perfmatters_delay_js', !empty(Config::$options['assets']['delay_js']))) && !Utilities::get_post_meta('perfmatters_exclude_delay_js');
if(!$delay_check || empty(Config::$options['assets']['delay_js_behavior']) || isset($_GET['perfmattersjsoff'])) {
$script = '<script type="text/javascript" id="perfmatters-delayed-styles-js">!function(){const e=["keydown","mousemove","wheel","touchmove","touchstart","touchend"];function t(){document.querySelectorAll("link[data-pmdelayedstyle]").forEach(function(e){e.setAttribute("href",e.getAttribute("data-pmdelayedstyle"))}),e.forEach(function(e){window.removeEventListener(e,t,{passive:!0})})}e.forEach(function(e){window.addEventListener(e,t,{passive:!0})})}();</script>';
$html = str_replace('</body>', $script . '</body>', $html);
}
}
}
return $html;
}
//get url type
private static function get_url_type()
{
global $wp_query;
$type = '';
if($wp_query->is_page) {
$type = is_front_page() ? 'front' : 'page-' . $wp_query->post->ID;
}
elseif($wp_query->is_home) {
$type = 'home';
}
elseif($wp_query->is_single) {
$type = get_post_type() !== false ? get_post_type() : 'single';
}
elseif($wp_query->is_category) {
$type = 'category';
}
elseif($wp_query->is_tag) {
$type = 'tag';
}
elseif($wp_query->is_tax) {
$type = 'tax';
}
elseif($wp_query->is_archive) {
$type = $wp_query->is_day ? 'day' : ($wp_query->is_month ? 'month' : ($wp_query->is_year ? 'year' : ($wp_query->is_author ? 'author' : 'archive')));
}
elseif($wp_query->is_search) {
$type = 'search';
}
elseif($wp_query->is_404) {
$type = '404';
}
return $type;
}
//get used selectors in html
private static function get_used_selectors($html) {
if(!$html) {
return;
}
//get dom document
$libxml_previous = libxml_use_internal_errors(true);
$dom = new \DOMDocument();
$result = $dom->loadHTML($html);
libxml_clear_errors();
libxml_use_internal_errors($libxml_previous);
if($result) {
$dom->xpath = new \DOMXPath($dom);
//setup used selectors array
self::$used_selectors = array('tags' => array(), 'ids' => array(), 'classes' => array());
//search for used selectors in dom
$classes = array();
foreach($dom->getElementsByTagName('*') as $tag) {
//add tag
self::$used_selectors['tags'][$tag->tagName] = 1;
//add add tag id
if($tag->hasAttribute('id')) {
self::$used_selectors['ids'][$tag->getAttribute('id')] = 1;
}
//store tag classes
if($tag->hasAttribute('class')) {
$class = $tag->getAttribute('class');
$tag_classes = preg_split('/\s+/', $class);
array_push($classes, ...$tag_classes);
}
}
//add classes
$classes = array_filter(array_unique($classes));
if($classes) {
self::$used_selectors['classes'] = array_fill_keys($classes, 1);
}
}
}
//get excluded selectors
private static function get_excluded_selectors() {
self::$excluded_selectors = array(
'.wp-embed-responsive', //core
'.wp-block-embed',
'.wp-block-embed__wrapper',
'.wp-caption',
'#elementor-device-mode', //elementor
'.elementor-nav-menu',
'.elementor-has-item-ratio',
'.elementor-popup-modal',
'.elementor-sticky--active',
'.dialog-type-lightbox',
'.dialog-widget-content',
'.lazyloaded',
'.elementor-motion-effects-container',
'.elementor-motion-effects-layer',
'.ast-header-break-point', //astra
'.dropdown-nav-special-toggle', //kadence
'rs-fw-forcer' //rev slider
);
if(!empty(Config::$options['assets']['rucss_excluded_selectors'])) {
self::$excluded_selectors = array_merge(self::$excluded_selectors, Config::$options['assets']['rucss_excluded_selectors']);
}
self::$excluded_selectors = apply_filters('perfmatters_rucss_excluded_selectors', self::$excluded_selectors);
}
//remove unusde css from stylesheet
private static function clean_stylesheet($url, $css)
{
//https://github.com/sabberworm/PHP-CSS-Parser/issues/150
$css = preg_replace('/^\xEF\xBB\xBF/', '', $css);
//setup css parser
$settings = Settings::create()->withMultibyteSupport(false);
$parser = new CSSParser($css, $settings);
$parsed_css = $parser->parse();
//convert relative urls to full urls
self::fix_relative_urls($url, $parsed_css);
$css_data = self::prep_css_data($parsed_css);
return self::remove_unused_selectors($css_data);
}
//convert relative urls to full urls
private static function fix_relative_urls($stylesheet_url, Document $data)
{
//get base url from stylesheet
$base_url = preg_replace('#[^/]+(\?.*)?$#', '', $stylesheet_url);
//search css for urls
$values = $data->getAllValues();
foreach($values as $value) {
if(!($value instanceof URL)) {
continue;
}
$url = $value->getURL()->getString();
//not relative
if(preg_match('/^(https?|data):/', $url)) {
continue;
}
$parsed_url = parse_url($url);
//final checks
if(!empty($parsed_url['host']) || empty($parsed_url['path']) || $parsed_url['path'][0] === '/') {
continue;
}
//create full url and replace
$new_url = $base_url . $url;
$value->getUrl()->setString($new_url);
}
}
//prep parsed css for cleaning
private static function prep_css_data(CSSBlockList $data)
{
$items = array();
foreach($data->getContents() as $content) {
//remove charset objects since were printing inline
if($content instanceof Charset) {
continue;
}
if($content instanceof AtRuleBlockList) {
$items[] = array(
'rulesets' => self::prep_css_data($content),
'at_rule' => "@{$content->atRuleName()} {$content->atRuleArgs()}",
);
}
else {
$item = array('css' => $content->render(OutputFormat::createCompact()));
if($content instanceof DeclarationBlock) {
$item['selectors'] = self::sort_selectors($content->getSelectors());
}
$items[] = $item;
}
}
return $items;
}
//sort selectors into different categories we need
private static function sort_selectors($selectors)
{
$selectors = array_map(
function($sel) {
return $sel->__toString();
},
$selectors
);
$selectors_data = array();
foreach($selectors as $selector) {
//setup selector data array
$data = array(
'selector' => trim($selector),
'classes' => array(),
'ids' => array(),
'tags' => array(),
'atts' => array()
);
//eliminate false negatives (:not(), pseudo, etc...)
$selector = preg_replace('/(?<!\\\\)::?[a-zA-Z0-9_-]+(\(.+?\))?/', '', $selector);
//atts
$selector = preg_replace_callback(
'/\[([A-Za-z0-9_:-]+)(\W?=[^\]]+)?\]/',
function($matches) use (&$data) {
$data['atts'][] = $matches[1];
return '';
},
$selector
);
//classes
$selector = preg_replace_callback(
'/\.((?:[a-zA-Z0-9_-]+|\\\\.)+)/',
function($matches) use (&$data) {
$data['classes'][] = stripslashes($matches[1]);
return '';
},
$selector
);
//ids
$selector = preg_replace_callback(
'/#([a-zA-Z0-9_-]+)/',
function($matches) use (&$data) {
$data['ids'][] = $matches[1];
return '';
},
$selector
);
//tags
$selector = preg_replace_callback(
'/[a-zA-Z0-9_-]+/',
function($matches) use (&$data) {
$data['tags'][] = $matches[0];
return '';
},
$selector
);
//add selector data to main array
$selectors_data[] = array_filter($data);
}
return array_filter($selectors_data);
}
//remove unused selectors from css data
private static function remove_unused_selectors($data)
{
$rendered = [];
foreach($data as $item) {
//has css
if(isset($item['css'])) {
//need at least one selector match
$should_render = !isset($item['selectors']) || 0 !== count(array_filter($item['selectors'],
function($selector) {
return self::is_selector_used($selector);
}
));
if($should_render) {
$rendered[] = $item['css'];
}
continue;
}
//nested rulesets
if(!empty($item['rulesets'])) {
$child_rulesets = self::remove_unused_selectors($item['rulesets']);
if($child_rulesets) {
$rendered[] = sprintf('%s{%s}', $item['at_rule'], $child_rulesets);
}
}
}
return implode("", $rendered);
}
//check if selector is used
private static function is_selector_used($selector)
{
//:root selector
if($selector['selector'] === ':root') {
return true;
}
//lone attribute selector
if(!empty($selector['atts']) && (empty($selector['classes']) && empty($selector['ids']) && empty($selector['tags']))) {
return true;
}
//search for excluded selector match
if(!empty(self::$excluded_selectors)) {
foreach(self::$excluded_selectors as $key => $value) {
if(preg_match('#(' . preg_quote($value) . ')(?=\s|\.|\:|,|\[|$)#', $selector['selector'])) {
return true;
}
}
}
//is selector used in the dom
foreach(array('classes', 'ids', 'tags') as $type) {
if(!empty($selector[$type])) {
//cast array if needed
$targets = (array)$selector[$type];
foreach($targets as $target) {
//bail if a target doesn't exist
if(!isset(self::$used_selectors[$type][$target])) {
return false;
}
}
}
}
return true;
}
//delete all files in the css cache directory
public static function clear_used_css()
{
$files = glob(PERFMATTERS_CACHE_DIR . 'css/*');
foreach($files as $file) {
if(is_file($file)) {
unlink($file);
}
}
delete_option('perfmatters_used_css_time');
}
//clear used css file for specific post or post type
public static function clear_post_used_css() {
if(empty($_POST['action']) || empty($_POST['nonce']) || empty($_POST['post_id'])) {
return;
}
if($_POST['action'] != 'perfmatters_clear_post_used_css') {
return;
}
if(!wp_verify_nonce($_POST['nonce'], 'perfmatters_clear_post_used_css')) {
return;
}
$post_id = (int)$_POST['post_id'];
$post_type = get_post_type($post_id);
$path = $post_type == 'page' ? 'page-' . $post_id : $post_type;
$file = PERFMATTERS_CACHE_DIR . 'css/' . $path . '.used.css';
if(is_file($file)) {
unlink($file);
}
wp_send_json_success();
exit;
}
//add admin bar menu item
public static function admin_bar_menu(WP_Admin_Bar $wp_admin_bar) {
if(!current_user_can('manage_options') || !perfmatters_network_access()) {
return;
}
$type = !is_admin() ? self::get_url_type() : '';
$menu_item = array(
'parent' => 'perfmatters',
'id' => 'perfmatters-clear-used-css',
'title' => __('Clear Used CSS', 'perfmatters') . ' (' . (!empty($type) ? __('Current', 'perfmatters') : __('All', 'perfmatters')) . ')',
'href' => add_query_arg(array(
'action' => 'perfmatters_clear_used_css',
'_wp_http_referer' => rawurlencode($_SERVER['REQUEST_URI']),
'_wpnonce' => wp_create_nonce('perfmatters_clear_used_css'),
'type' => $type
),
admin_url('admin-post.php'))
);
$wp_admin_bar->add_menu($menu_item);
}
//display admin notices
public static function admin_notices() {
if(get_transient('perfmatters_used_css_cleared') === false) {
return;
}
delete_transient('perfmatters_used_css_cleared');
echo '<div class="notice notice-success is-dismissible"><p><strong>' . __('Used CSS cleared.', 'perfmatters' ) . '</strong></p></div>';
}
//clear used css from admin bar
public static function admin_bar_clear_used_css() {
if(!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_key($_GET['_wpnonce']), 'perfmatters_clear_used_css')) {
wp_nonce_ays('');
}
if(!empty($_GET['type'])) {
//clear specific type
$file = PERFMATTERS_CACHE_DIR . 'css/' . $_GET['type'] . '.used.css';
if(is_file($file)) {
unlink($file);
}
}
else {
//clear all
self::clear_used_css();
if(is_admin()) {
set_transient('perfmatters_used_css_cleared', 1);
}
}
//go back to url where button was pressed
wp_safe_redirect(esc_url_raw(wp_get_referer()));
exit;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Perfmatters;
use WP_Admin_Bar;
class Config
{
public static $options;
public static $tools;
//initialize config
public static function init()
{
//load plugin options
self::$options = get_option('perfmatters_options');
self::$tools = get_option('perfmatters_tools');
//actions
add_action('admin_bar_menu', array('Perfmatters\Config', 'admin_bar_menu'), 500);
}
//setup admin bar menu
public static function admin_bar_menu(WP_Admin_Bar $wp_admin_bar)
{
if(!current_user_can('manage_options') || !perfmatters_network_access() || !empty(self::$tools['hide_admin_bar_menu'])) {
return;
}
//add top level menu item
$wp_admin_bar->add_menu(array(
'id' => 'perfmatters',
'title' => 'Perfmatters',
'href' => admin_url('options-general.php?page=perfmatters')
));
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace Perfmatters;
class DatabaseOptimizationProcess extends \WP_Background_Process {
//prefix for process
protected $prefix = 'perfmatters';
//action name for process
protected $action = 'database_optimization';
//totals of removed items
protected $counts = array();
//run on each queue item
protected function task($item) {
global $wpdb;
switch($item) {
case 'post_revisions' :
$query = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_type = 'revision'");
if($query) {
$count = 0;
foreach($query as $id) {
$count += wp_delete_post_revision(intval($id)) instanceof \WP_Post ? 1 : 0;
}
$this->counts[$item] = $count;
}
break;
case 'post_auto_drafts' :
$query = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft'");
if($query) {
$count = 0;
foreach($query as $id) {
$count += wp_delete_post(intval($id), true) instanceof \WP_Post ? 1 : 0;
}
$this->counts[$item] = $count;
}
break;
case 'trashed_posts' :
$query = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_status = 'trash'");
if($query) {
$count = 0;
foreach($query as $id) {
$count += wp_delete_post($id, true) instanceof \WP_Post ? 1 : 0;
}
$this->counts[$item] = $count;
}
break;
case 'spam_comments' :
$query = $wpdb->get_col("SELECT comment_ID FROM $wpdb->comments WHERE comment_approved = 'spam'");
if($query) {
$count = 0;
foreach($query as $id) {
$count += (int) wp_delete_comment(intval($id), true);
}
$this->counts[$item] = $count;
}
break;
case 'trashed_comments' :
$query = $wpdb->get_col("SELECT comment_ID FROM $wpdb->comments WHERE (comment_approved = 'trash' OR comment_approved = 'post-trashed')");
if($query) {
$count = 0;
foreach($query as $id) {
$count += (int) wp_delete_comment(intval($id), true);
}
$this->counts[$item] = $count;
}
break;
case 'expired_transients' :
$time = isset($_SERVER['REQUEST_TIME']) ? (int) $_SERVER['REQUEST_TIME'] : time();
$query = $wpdb->get_col($wpdb->prepare("SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s AND option_value < %d", $wpdb->esc_like('_transient_timeout') . '%', $time));
if($query) {
$count = 0;
foreach($query as $transient) {
$key = str_replace('_transient_timeout_', '', $transient);
$count += (int) delete_transient($key);
}
$this->counts[$item] = $count;
}
break;
case 'all_transients' :
$query = $wpdb->get_col($wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s OR option_name LIKE %s", $wpdb->esc_like('_transient_') . '%', $wpdb->esc_like('_site_transient_') . '%'));
if($query) {
$count = 0;
foreach($query as $transient) {
if(strpos($transient, '_site_transient_') !== false) {
$count += (int) delete_site_transient(str_replace('_site_transient_', '', $transient));
} else {
$count += (int) delete_transient(str_replace('_transient_', '', $transient));
}
}
$this->counts[$item] = $count;
}
break;
case 'tables' :
$query = $wpdb->get_results("SELECT table_name, data_free FROM information_schema.tables WHERE table_schema = '" . DB_NAME . "' and Engine <> 'InnoDB' and data_free > 0");
if($query) {
$count = 0;
foreach($query as $table) {
$count += (int) $wpdb->query("OPTIMIZE TABLE $table->table_name");
}
$this->counts[$item] = $count;
}
break;
}
return false;
}
//run background process on queue
public function dispatch() {
//set our working transient
set_transient('perfmatters_database_optimization_process', 'working', HOUR_IN_SECONDS);
//run parent dispatch
return parent::dispatch();
}
//run when background process is complete
protected function complete() {
//delete our working transient
delete_transient('perfmatters_database_optimization_process');
//set complete transient
set_transient('perfmatters_database_optimization_process_complete', $this->counts);
//run parent complete
parent::complete();
}
}

View File

@@ -0,0 +1,216 @@
<?php
namespace Perfmatters;
class DatabaseOptimizer {
//declare our optimizer
protected $optimizer;
//actions + filters
public function __construct() {
//initialize optimizer
$this->optimizer = new DatabaseOptimizationProcess();
add_filter('pre_update_option_perfmatters_tools', array($this, 'perfmatters_database_optimization_action'), 10, 2);
add_action('admin_notices', array($this, 'perfmatters_database_optimization_notices'));
add_filter('cron_schedules', array($this, 'perfmatters_add_database_optimization_cron_schedule'));
add_action('init', array($this, 'perfmatters_schedule_database_optimization'));
add_action('perfmatters_database_optimization', array($this, 'perfmatters_run_scheduled_database_optimization'));
}
//run the background process
public function process_handler($items) {
//push the requested items to the queue
array_map(array($this->optimizer, 'push_to_queue'), $items);
//run the process
$this->optimizer->save()->dispatch();
}
//watch and respond to the optimize button
public function perfmatters_database_optimization_action($new_value, $old_value) {
//optimize button was pressed
if(!empty($new_value['database']['optimize_database'])) {
//stop and show error if process is already running
$working = get_transient('perfmatters_database_optimization_process');
if($working !== false) {
add_settings_error('perfmatters', 'perfmatters-database-optimization-running', __('There is already an existing database optimization process running.', 'perfmatters'), 'error');
return $old_value;
}
//get available options array
$optimize_options = array_keys($this->perfmatters_get_database_options());
//build array of requested items
$items = array();
foreach($optimize_options as $item) {
if(!empty($new_value['database'][$item])) {
$items[] = $item;
}
}
//run process handler
if(!empty($items)) {
$this->process_handler($items);
}
//add hidden notice to prevent save message
add_settings_error('perfmatters', 'perfmatters-hidden-notice', '', 'success');
return $old_value;
}
$new_optimize_schedule = isset($new_value['database']['optimize_schedule']) ? $new_value['database']['optimize_schedule'] : '';
$old_optimize_schedule = isset($old_value['database']['optimize_schedule']) ? $old_value['database']['optimize_schedule'] : '';
//optimize schedule was changed
if($new_optimize_schedule !== $old_optimize_schedule) {
if(wp_next_scheduled('perfmatters_database_optimization')) {
wp_clear_scheduled_hook('perfmatters_database_optimization');
}
}
return $new_value;
}
//display notices for database optimization process
public function perfmatters_database_optimization_notices() {
//permissions check
if(!current_user_can('manage_options')) {
return;
}
//make sure were on our settings page
if(empty($_GET['page']) || $_GET['page'] !== 'perfmatters') {
return;
}
//get working transient
$working = get_transient('perfmatters_database_optimization_process');
if($working !== false) {
$notice_type = "info";
$message = __('Database optimization is running in the background.', 'perfmatters');
}
else {
//get completed optimized transient
$completed = get_transient('perfmatters_database_optimization_process_complete');
if($completed === false) {
return;
}
$notice_type = "success";
//get db options array
$database_options = $this->perfmatters_get_database_options();
//build admin notice message
if(!empty($completed)) {
$message = __('Database optimization completed. The following items were removed:', 'perfmatters');
$message.= "<ul style='margin: 0px; padding: 0px 2px;'>";
foreach($completed as $key => $count) {
$message.= "<li>";
$message.= "<strong>" . $database_options[$key] . ':</strong> ' . $count;
$message.= "</li>";
}
$message.= "</ul>";
}
else {
$message = __('Database optimization completed. No optimizations found.', 'perfmatters');
}
//delete our completed transient
delete_transient('perfmatters_database_optimization_process_complete');
}
//display admin notice
if(!empty($message)) {
echo "<div class='notice notice-" . $notice_type . " is-dismissible'>";
echo "<p>" . $message . "</p>";
echo"</div>";
}
}
//add cron schedule
public function perfmatters_add_database_optimization_cron_schedule($schedules) {
$perfmatters_tools = get_option('perfmatters_tools');
if(empty($perfmatters_tools['database']['optimize_schedule']) || $perfmatters_tools['database']['optimize_schedule'] == 'daily') {
return $schedules;
}
switch($perfmatters_tools['database']['optimize_schedule']) {
case 'weekly' :
$schedules['weekly'] = array(
'interval' => 604800,
'display' => __('Once Weekly', 'perfmatters'),
);
break;
case 'monthly' :
$schedules['monthly'] = array(
'interval' => 2592000,
'display' => __('Once Monthly', 'perfmatters'),
);
break;
default :
break;
}
return $schedules;
}
//create database optimization scheduled event
public function perfmatters_schedule_database_optimization() {
$perfmatters_tools = get_option('perfmatters_tools');
if(!empty($perfmatters_tools['database']['optimize_schedule']) && !wp_next_scheduled('perfmatters_database_optimization')) {
wp_schedule_event(time(), $perfmatters_tools['database']['optimize_schedule'], 'perfmatters_database_optimization');
}
}
//scheduled event action
public function perfmatters_run_scheduled_database_optimization() {
$perfmatters_tools = get_option('perfmatters_tools');
$optimize_options = array_keys($this->perfmatters_get_database_options());
//build array of set items
$items = array();
foreach($optimize_options as $item) {
if(!empty($perfmatters_tools['database'][$item])) {
$items[] = $item;
}
}
//run process handler
if(!empty($items)) {
$this->process_handler($items);
}
}
//return array of database options
protected function perfmatters_get_database_options() {
return array(
'post_revisions' => __('Revisions', 'perfmatters'),
'post_auto_drafts' => __('Auto Drafts', 'perfmatters'),
'trashed_posts' => __('Trashed Posts', 'perfmatters'),
'spam_comments' => __('Spam Comments', 'perfmatters'),
'trashed_comments' => __('Trashed Comments', 'perfmatters'),
'expired_transients' => __('Expired transients', 'perfmatters'),
'all_transients' => __('Transients', 'perfmatters'),
'tables' => __('Tables', 'perfmatters')
);
}
}

View File

@@ -0,0 +1,188 @@
<?php
namespace Perfmatters;
use WpOrg\Requests\Requests; //wp 6.2+
if(!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS')) {
define('REQUESTS_SILENCE_PSR0_DEPRECATIONS', true);
}
use Requests as RequestsOld; //deprecated
class Fonts
{
private static $font_file_cache_url = PERFMATTERS_CACHE_URL;
//initialize fonts
public static function init() {
if(empty(Config::$options['fonts']['disable_google_fonts'])) {
add_action('wp', array('Perfmatters\Fonts', 'queue'));
}
}
//queue functions
public static function queue()
{
//add display swap to the buffer
if(!empty(Config::$options['fonts']['display_swap'])) {
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\Fonts', 'display_swap'));
}
//add local google fonts to the buffer
if(!empty(Config::$options['fonts']['local_google_fonts'])) {
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\Fonts', 'local_google_fonts'));
}
}
//add display swap to google font files
public static function display_swap($html) {
//find google fonts
preg_match_all('#<link[^>]+?href=(["\'])([^>]*?fonts\.googleapis\.com\/css.*?)\1.*?>#i', $html, $google_fonts, PREG_SET_ORDER);
if(!empty($google_fonts)) {
foreach($google_fonts as $google_font) {
//replace display parameter
$new_href = preg_replace('/&display=(auto|block|fallback|optional|swap)/', '', html_entity_decode($google_font[2]));
$new_href.= '&display=swap';
//create font tag with new href
$new_google_font = str_replace($google_font[2], $new_href, $google_font[0]);
//replace original font tag
$html = str_replace($google_font[0], $new_google_font, $html);
}
}
return $html;
}
//download and host google font files locally
public static function local_google_fonts($html) {
//create our fonts cache directory
if(!is_dir(PERFMATTERS_CACHE_DIR . 'fonts/')) {
@mkdir(PERFMATTERS_CACHE_DIR . 'fonts/', 0755, true);
}
//rewrite cdn url in font file cache url
$cdn_url = !empty(Config::$options['fonts']['cdn_url']) ? Config::$options['fonts']['cdn_url'] : (!empty(Config::$options['cdn']['enable_cdn']) && !empty(Config::$options['cdn']['cdn_url']) ? Config::$options['cdn']['cdn_url'] : '');
if(!empty($cdn_url)) {
self::$font_file_cache_url = str_replace(site_url(), untrailingslashit($cdn_url), PERFMATTERS_CACHE_URL);
}
//remove existing google font preconnect + prefetch links
preg_match_all('#<link(?:[^>]+)?href=(["\'])([^>]*?fonts\.(gstatic|googleapis)\.com.*?)\1.*?>#i', $html, $google_links, PREG_SET_ORDER);
if(!empty($google_links)) {
foreach($google_links as $google_link) {
if(preg_match('#rel=(["\'])(.*?(preconnect|prefetch).*?)\1#i', $google_link[0])) {
$html = str_replace($google_link[0], '', $html);
}
}
}
//find google fonts
preg_match_all('#<link[^>]+?href=(["\'])([^>]*?fonts\.googleapis\.com\/css.*?)\1.*?>#i', $html, $google_fonts, PREG_SET_ORDER);
if(!empty($google_fonts)) {
foreach($google_fonts as $google_font) {
//create unique file details
$file_name = substr(md5($google_font[2]), 0, 12) . ".google-fonts.css";
$file_path = PERFMATTERS_CACHE_DIR . 'fonts/' . $file_name;
$file_url = PERFMATTERS_CACHE_URL . 'fonts/' . $file_name;
//download file if it doesn't exist
if(!file_exists($file_path)) {
if(!self::download_google_font($google_font[2], $file_path)) {
continue;
}
}
//create font tag with new url
$new_google_font = str_replace($google_font[2], $file_url, $google_font[0]);
//async
if(!empty(Config::$options['fonts']['async'])) {
$new_google_font = preg_replace(array('#media=([\'"]).+?\1#', '#onload=([\'"]).+?\1#'), '', $new_google_font);
$new_google_font = str_replace('<link', '<link media="print" onload="this.media=\'all\';this.onload=null;"', $new_google_font);
}
//replace original font tag
$html = str_replace($google_font[0], $new_google_font, $html);
}
}
return $html;
}
//download and save google font css file
private static function download_google_font($url, $file_path)
{
//add https if using relative scheme
if(substr($url, 0, 2) === '//') {
$url = 'https:' . $url;
}
//download css file
$css_response = wp_remote_get(html_entity_decode($url), array('user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'));
//check valid response
if(wp_remote_retrieve_response_code($css_response) !== 200) {
return false;
}
//css content
$css = $css_response['body'];
//find font files inside the css
$regex = '/url\((https:\/\/fonts\.gstatic\.com\/.*?)\)/';
preg_match_all($regex, $css, $matches);
$font_urls = array_unique($matches[1]);
$font_requests = array();
foreach($font_urls as $font_url) {
if(!file_exists(PERFMATTERS_CACHE_DIR . 'fonts/' . basename($font_url))) {
$font_requests[] = array('url' => $font_url, 'type' => 'GET');
}
$cached_font_url = self::$font_file_cache_url . 'fonts/' . basename($font_url);
$css = str_replace($font_url, $cached_font_url, $css);
}
//download new font files to cache directory
if(method_exists(WpOrg\Requests\Requests::class, 'request_multiple')) { //wp 6.2+
$font_responses = WpOrg\Requests\Requests::request_multiple($font_requests);
}
elseif(method_exists(RequestsOld::class, 'request_multiple')) { //deprecated
$font_responses = RequestsOld::request_multiple($font_requests);
}
if(!empty($font_responses)) {
foreach($font_responses as $font_response) {
if(is_a($font_response, 'Requests_Response') || is_a($font_response, 'WpOrg\Requests\Response')) {
$font_path = PERFMATTERS_CACHE_DIR . 'fonts/' . basename($font_response->url);
//save font file
file_put_contents($font_path, $font_response->body);
}
}
}
//save final css file
file_put_contents($file_path, $css);
return true;
}
//delete all files in the fonts cache directory
public static function clear_local_fonts()
{
$files = glob(PERFMATTERS_CACHE_DIR . 'fonts/*');
foreach($files as $file) {
if(is_file($file)) {
unlink($file);
}
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Perfmatters;
class Images
{
//initialize image functions
public static function init()
{
add_action('wp', array('Perfmatters\Images', 'queue'));
}
//queue functions
public static function queue()
{
//image dimensions
if(!empty(Config::$options['lazyload']['image_dimensions']) && !empty(Config::$options['lazyload']['image_dimensions'])) {
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\Images', 'image_dimensions'));
}
}
//fix images missing dimensions
public static function image_dimensions($html)
{
//match all img tags without width or height attributes
preg_match_all('#<img((?:[^>](?!(height|width)=[\'\"](?:\S+)[\'\"]))*+)>#is', $html, $images, PREG_SET_ORDER);
if(!empty($images)) {
//remove any duplicate images
$images = array_unique($images, SORT_REGULAR);
//exclude specific images
$image_exclusions = array(
';base64'
);
$image_exclusions = apply_filters('perfmatters_image_dimensions_exclusions', $image_exclusions);
//loop through images
foreach($images as $image) {
//get image attributes array
$image_atts = Utilities::get_atts_array($image[1]);
if(!empty($image_atts['src'])) {
foreach($image_exclusions as $exclude) {
if(strpos($image[1], $exclude) !== false) {
continue 2;
}
}
//get image dimensions
$dimensions = self::get_dimensions_from_url($image_atts['src']);
if(!empty($dimensions)) {
//remove any existing dimension attributes
$new_image = preg_replace('/(height|width)=[\'"](?:\S+)*[\'"]/i', '', $image[0]);
//add dimension attributes to img tag
$new_image = preg_replace('/<\s*img/i', '<img width="' . $dimensions['width'] . '" height="' . $dimensions['height'] . '"', $new_image);
//replace original img tag in html
if(!empty($new_image)) {
$html = str_replace($image[0], $new_image, $html);
}
}
}
}
}
return $html;
}
//return array of dimensions based on image url
private static function get_dimensions_from_url($url)
{
//grab dimensions from file name if available
if(preg_match('/(?:.+)-([0-9]+)x([0-9]+)\.(jpg|jpeg|png|gif|svg)$/', $url, $matches)) {
return array('width' => $matches[1], 'height' => $matches[2]);
}
//get image path
$parsed_url = parse_url($url);
if(empty($parsed_url['path'])) {
return false;
}
$image_path = str_replace('/wp-content', '', WP_CONTENT_DIR) . '/' . $parsed_url['path'];
if(file_exists($image_path)) {
//get dimensions from file
$sizes = getimagesize($image_path);
if(!empty($sizes)) {
return array('width' => $sizes[0], 'height' => $sizes[1]);
}
}
return false;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,725 @@
<?php
namespace Perfmatters;
class LazyLoad
{
//initialize lazyload iframe functions
public static function init_iframes()
{
add_action('wp', array('Perfmatters\LazyLoad', 'queue_iframes'));
}
//queue iframe functions
public static function queue_iframes()
{
//check filters
if(empty(apply_filters('perfmatters_lazyload', !empty(Config::$options['lazyload']['lazy_loading_iframes'])))) {
return;
}
//skip woocommerce
if(Utilities::is_woocommerce()) {
return;
}
//check post meta
if(Utilities::get_post_meta('perfmatters_exclude_lazy_loading')) {
return;
}
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\LazyLoad', 'iframes_buffer'));
}
//process iframe buffer
public static function iframes_buffer($html) {
$clean_html = Utilities::clean_html($html);
$html = self::lazyload_iframes($html, $clean_html);
$html = self::lazyload_videos($html, $clean_html);
return $html;
}
//lazy load iframes
public static function lazyload_iframes($html, $buffer) {
//match all iframes
preg_match_all('#<iframe(\s.+)>.*</iframe>#iUs', $buffer, $iframes, PREG_SET_ORDER);
if(!empty($iframes)) {
//remove any duplicates
$iframes = array_unique($iframes, SORT_REGULAR);
foreach($iframes as $iframe) {
//get iframe attributes array
$iframe_atts = Utilities::get_atts_array($iframe[1]);
//dont check excluded if forced attribute was found
if(!self::lazyload_excluded($iframe[1], self::lazyload_forced_atts())) {
//skip if exluded attribute was found
if(self::lazyload_excluded($iframe[1], self::lazyload_excluded_atts())) {
continue;
}
//skip if no-lazy class is found
if(!empty($iframe_atts['class']) && strpos($iframe_atts['class'], 'no-lazy') !== false) {
continue;
}
}
//skip if no src is found
if(empty($iframe_atts['src'])) {
continue;
}
//try rendering youtube preview placeholder if we need to
if(!empty(Config::$options['lazyload']['youtube_preview_thumbnails'])) {
$iframe['src'] = trim($iframe_atts['src']);
$iframe_lazyload = self::lazyload_youtube_iframe($iframe);
}
//default iframe placeholder
if(empty($iframe_lazyload)) {
$iframe_atts['class'] = !empty($iframe_atts['class']) ? $iframe_atts['class'] . ' ' . 'perfmatters-lazy' : 'perfmatters-lazy';
//migrate src
$iframe_atts['data-src'] = $iframe_atts['src'];
unset($iframe_atts['src']);
//replace iframe attributes string
$iframe_lazyload = str_replace($iframe[1], ' ' . Utilities::get_atts_string($iframe_atts), $iframe[0]);
//add noscript original iframe
if(apply_filters('perfmatters_lazyload_noscript', true)) {
$iframe_lazyload.= '<noscript>' . $iframe[0] . '</noscript>';
}
}
//replace iframe with placeholder
$html = str_replace($iframe[0], $iframe_lazyload, $html);
unset($iframe_lazyload);
}
}
return $html;
}
//prep youtube iframe for lazy loading
public static function lazyload_youtube_iframe($iframe) {
if(!$iframe) {
return false;
}
//attempt to get the id based on url
$result = preg_match('#^(?:https?:)?(?://)?(?:www\.)?(?:youtu\.be|youtube\.com|youtube-nocookie\.com)/(?:embed/|v/|watch/?\?v=)?([\w-]{11})#iU', $iframe['src'], $matches);
//return false if there is no usable id
if(!$result || $matches[1] === 'videoseries') {
return false;
}
$youtube_id = $matches[1];
//parse iframe src url
$query = wp_parse_url(htmlspecialchars_decode($iframe['src']), PHP_URL_QUERY);
//clean up the url
$parsed_url = wp_parse_url($iframe['src'], -1);
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '//';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$youtube_url = $scheme . $host . $path;
//thumbnail resolutions
$resolutions = array(
'default' => array(
'width' => 120,
'height' => 90,
),
'mqdefault' => array(
'width' => 320,
'height' => 180,
),
'hqdefault' => array(
'width' => 480,
'height' => 360,
),
'sddefault' => array(
'width' => 640,
'height' => 480,
),
'maxresdefault' => array(
'width' => 1280,
'height' => 720,
)
);
//filter set resolution
$resolution = apply_filters('perfmatters_lazyload_youtube_thumbnail_resolution', 'hqdefault');
//finished youtube lazy output
$youtube_lazyload = '<div class="perfmatters-lazy-youtube" data-src="' . esc_attr($youtube_url) . '" data-id="' . esc_attr($youtube_id) . '" data-query="' . esc_attr($query) . '" onclick="perfmattersLazyLoadYouTube(this);">';
$youtube_lazyload.= '<div>';
$youtube_lazyload.= '<img src="https://i.ytimg.com/vi/' . esc_attr($youtube_id) .'/' . $resolution . '.jpg" alt="YouTube ' . __('video', 'perfmatters') . '" width="' . $resolutions[$resolution]['width'] . '" height="' . $resolutions[$resolution]['height'] . '" data-pin-nopin="true" nopin="nopin">';
$youtube_lazyload.= '<div class="play"></div>';
$youtube_lazyload.= '</div>';
$youtube_lazyload.= '</div>';
//noscript tag
if(apply_filters('perfmatters_lazyload_noscript', true)) {
$youtube_lazyload.= '<noscript>' . $iframe[0] . '</noscript>';
}
return $youtube_lazyload;
}
//lazy load videos
public static function lazyload_videos($html, $buffer) {
//match all videos
preg_match_all('#<video(\s.+)>.*</video>#iUs', $buffer, $videos, PREG_SET_ORDER);
if(!empty($videos)) {
//remove any duplicates
$videos = array_unique($videos, SORT_REGULAR);
foreach($videos as $video) {
//get video attributes array
$video_atts = Utilities::get_atts_array($video[1]);
//dont check excluded if forced attribute was found
if(!self::lazyload_excluded($video[1], self::lazyload_forced_atts())) {
//skip if exluded attribute was found
if(self::lazyload_excluded($video[1], self::lazyload_excluded_atts())) {
continue;
}
//skip if no-lazy class is found
if(!empty($video_atts['class']) && strpos($video_atts['class'], 'no-lazy') !== false) {
continue;
}
}
//skip if no src is found
if(empty($video_atts['src'])) {
continue;
}
//add lazyload class
$video_atts['class'] = !empty($video_atts['class']) ? $video_atts['class'] . ' ' . 'perfmatters-lazy' : 'perfmatters-lazy';
//migrate src
$video_atts['data-src'] = $video_atts['src'];
unset($video_atts['src']);
//replace video attributes string
$video_lazyload = str_replace($video[1], ' ' . Utilities::get_atts_string($video_atts), $video[0]);
//add noscript original video
if(apply_filters('perfmatters_lazyload_noscript', true)) {
$video_lazyload .= '<noscript>' . $video[0] . '</noscript>';
}
//replace video with placeholder
$html = str_replace($video[0], $video_lazyload, $html);
unset($video_lazyload);
}
}
return $html;
}
//initialize lazy loading
public static function init_images()
{
add_action('wp', array('Perfmatters\LazyLoad', 'queue_images'));
}
//queue image functions
public static function queue_images()
{
//check filters
if(empty(apply_filters('perfmatters_lazyload', !empty(Config::$options['lazyload']['lazy_loading']) || !empty(Config::$options['lazyload']['lazy_loading_iframes']) || !empty(Config::$options['lazyload']['css_background_images'])))) {
return;
}
//skip woocommerce
if(Utilities::is_woocommerce()) {
return;
}
//check post meta
if(Utilities::get_post_meta('perfmatters_exclude_lazy_loading')) {
return;
}
//disable wp native lazy loading
add_filter('wp_lazy_loading_enabled', '__return_false');
//actions + filters
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\LazyLoad', 'lazyload_buffer'));
add_action('wp_enqueue_scripts', array('Perfmatters\LazyLoad', 'enqueue_scripts'));
add_filter('script_loader_tag', array('Perfmatters\LazyLoad', 'async_script'), 10, 2);
add_action('wp_head', array('Perfmatters\LazyLoad', 'lazyload_css'), PHP_INT_MAX);
//images only
if(!empty(Config::$options['lazyload']['lazy_loading'])) {
add_filter('wp_get_attachment_image_attributes', function($attr) {
unset($attr['loading']);
return $attr;
});
}
}
//lazy load buffer
public static function lazyload_buffer($html) {
$buffer = Utilities::clean_html($html);
//replace image tags
if(!empty(Config::$options['lazyload']['lazy_loading'])) {
$html = self::lazyload_parent_exclusions($html, $buffer);
$html = self::lazyload_pictures($html, $buffer);
$html = self::lazyload_background_images($html, $buffer);
$html = self::lazyload_images($html, $buffer);
}
//replace css background elements
if(!empty(Config::$options['lazyload']['css_background_images'])) {
$html = self::lazyload_css_background_images($html, $buffer);
}
return $html;
}
//enqueue lazy load script
public static function enqueue_scripts() {
wp_register_script('perfmatters-lazy-load', plugins_url('perfmatters/js/lazyload.min.js'), array(), PERFMATTERS_VERSION, true);
wp_enqueue_script('perfmatters-lazy-load');
wp_add_inline_script('perfmatters-lazy-load', self::inline_js(), 'before');
}
//add async tag to enqueued script
public static function async_script($tag, $handle) {
if($handle !== 'perfmatters-lazy-load') {
return $tag;
}
return str_replace(' src', ' async src', $tag);
}
//print lazy load styles
public static function lazyload_css() {
//print noscript styles
if(apply_filters('perfmatters_lazyload_noscript', true)) {
echo '<noscript><style>.perfmatters-lazy[data-src]{display:none !important;}</style></noscript>';
}
$styles = '';
//youtube thumbnails
if(!empty(Config::$options['lazyload']['lazy_loading_iframes']) && !empty(Config::$options['lazyload']['youtube_preview_thumbnails'])) {
$styles.= '.perfmatters-lazy-youtube{position:relative;width:100%;max-width:100%;height:0;padding-bottom:56.23%;overflow:hidden}.perfmatters-lazy-youtube img{position:absolute;top:0;right:0;bottom:0;left:0;display:block;width:100%;max-width:100%;height:auto;margin:auto;border:none;cursor:pointer;transition:.5s all;-webkit-transition:.5s all;-moz-transition:.5s all}.perfmatters-lazy-youtube img:hover{-webkit-filter:brightness(75%)}.perfmatters-lazy-youtube .play{position:absolute;top:50%;left:50%;right:auto;width:68px;height:48px;margin-left:-34px;margin-top:-24px;background:url('.plugins_url('perfmatters/img/youtube.svg').') no-repeat;background-position:center;background-size:cover;pointer-events:none}.perfmatters-lazy-youtube iframe{position:absolute;top:0;left:0;width:100%;height:100%;z-index:99}';
if(current_theme_supports('responsive-embeds')) {
$styles.= '.wp-has-aspect-ratio .wp-block-embed__wrapper{position:relative;}.wp-has-aspect-ratio .perfmatters-lazy-youtube{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;padding-bottom:0}';
}
}
//fade in effect
if(!empty(Config::$options['lazyload']['fade_in'])) {
$styles.= '.perfmatters-lazy.pmloaded,.perfmatters-lazy.pmloaded>img,.perfmatters-lazy>img.pmloaded,.perfmatters-lazy[data-ll-status=entered]{animation:' . apply_filters('perfmatters_fade_in_speed', 500) . 'ms pmFadeIn}@keyframes pmFadeIn{0%{opacity:0}100%{opacity:1}}';
}
//css background images
if(!empty(Config::$options['lazyload']['css_background_images'])) {
$styles.='body .perfmatters-lazy-css-bg:not([data-ll-status=entered]),body .perfmatters-lazy-css-bg:not([data-ll-status=entered]) *,body .perfmatters-lazy-css-bg:not([data-ll-status=entered])::before,body .perfmatters-lazy-css-bg:not([data-ll-status=entered])::after{background-image:none!important;will-change:transform;transition:opacity 0.025s ease-in,transform 0.025s ease-in!important;}';
}
//print styles
if(!empty($styles)) {
echo '<style>' . $styles . '</style>';
}
}
//inline lazy load js
private static function inline_js() {
$threshold = apply_filters('perfmatters_lazyload_threshold', !empty(Config::$options['lazyload']['threshold']) ? Config::$options['lazyload']['threshold'] : '0px');
if(ctype_digit($threshold)) {
$threshold.= 'px';
}
//declare lazy load options
$output = 'window.lazyLoadOptions={elements_selector:"img[data-src],.perfmatters-lazy,.perfmatters-lazy-css-bg",thresholds:"' . $threshold . ' 0px",class_loading:"pmloading",class_loaded:"pmloaded",callback_loaded:function(element){if(element.tagName==="IFRAME"){if(element.classList.contains("pmloaded")){if(typeof window.jQuery!="undefined"){if(jQuery.fn.fitVids){jQuery(element).parent().fitVids()}}}}}};';
//lazy loader initialized
$output.= 'window.addEventListener("LazyLoad::Initialized",function(e){var lazyLoadInstance=e.detail.instance;';
//dom monitoring
if(!empty(Config::$options['lazyload']['lazy_loading_dom_monitoring'])) {
$output.= 'var target=document.querySelector("body");var observer=new MutationObserver(function(mutations){lazyLoadInstance.update()});var config={childList:!0,subtree:!0};observer.observe(target,config);';
}
$output.= '});';
//youtube thumbnails
if(!empty(Config::$options['lazyload']['lazy_loading_iframes']) && !empty(Config::$options['lazyload']['youtube_preview_thumbnails'])) {
$autoplay = apply_filters('perfmatters_lazyload_youtube_autoplay', true);
$output.= 'function perfmattersLazyLoadYouTube(e){var t=document.createElement("iframe"),r="ID?";r+=0===e.dataset.query.length?"":e.dataset.query+"&"' . ($autoplay ? ',r+="autoplay=1"' : '') . ',t.setAttribute("src",r.replace("ID",e.dataset.src)),t.setAttribute("frameborder","0"),t.setAttribute("allowfullscreen","1"),t.setAttribute("allow","accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"),e.replaceChild(t,e.firstChild)}';
}
return $output;
}
//lazy load img tags
private static function lazyload_images($html, $buffer) {
//match all img tags
preg_match_all('#<img([^>]+?)\/?>#is', $buffer, $images, PREG_SET_ORDER);
if(!empty($images)) {
$lazy_image_count = 0;
$exclude_leading_images = apply_filters('perfmatters_exclude_leading_images', Config::$options['lazyload']['exclude_leading_images'] ?? 0);
//remove any duplicate images
$images = array_unique($images, SORT_REGULAR);
//loop through images
foreach($images as $image) {
$lazy_image_count++;
if($lazy_image_count <= $exclude_leading_images) {
continue;
}
//prepare lazy load image
$lazy_image = self::lazyload_image($image);
//replace image in html
$html = str_replace($image[0], $lazy_image, $html);
}
}
return $html;
}
//lazy load picture tags for webp
private static function lazyload_pictures($html, $buffer) {
//match all picture tags
preg_match_all('#<picture(.*)?>(.*)<\/picture>#isU', $buffer, $pictures, PREG_SET_ORDER);
if(!empty($pictures)) {
foreach($pictures as $picture) {
//get picture tag attributes
$picture_atts = Utilities::get_atts_array($picture[1]);
//dont check excluded if forced attribute was found
if(!self::lazyload_excluded($picture[1], self::lazyload_forced_atts())) {
//skip if no-lazy class is found
if((!empty($picture_atts['class']) && strpos($picture_atts['class'], 'no-lazy') !== false) || self::lazyload_excluded($picture[0], self::lazyload_excluded_atts())) {
//mark image for exclusion later
preg_match('#<img([^>]+?)\/?>#is', $picture[0], $image);
if(!empty($image)) {
$image_atts = Utilities::get_atts_array($image[1]);
$image_atts['class'] = (!empty($image_atts['class']) ? $image_atts['class'] . ' ' : '') . 'no-lazy';
$new_image = sprintf('<img %1$s />', Utilities::get_atts_string($image_atts));
$html = str_replace($image[0], $new_image, $html);
}
continue;
}
}
//match all source tags inside the picture
preg_match_all('#<source(\s.+)>#isU', $picture[2], $sources, PREG_SET_ORDER);
if(!empty($sources)) {
//remove any duplicate sources
$sources = array_unique($sources, SORT_REGULAR);
foreach($sources as $source) {
//skip if exluded attribute was found
if(self::lazyload_excluded($source[1], self::lazyload_excluded_atts())) {
continue;
}
//migrate srcet
$new_source = preg_replace('/([\s"\'])srcset/i', '${1}data-srcset', $source[0]);
//migrate sizes
$new_source = preg_replace('/([\s"\'])sizes/i', '${1}data-sizes', $new_source);
//replace source in html
$html = str_replace($source[0], $new_source, $html);
}
}
else {
continue;
}
}
}
return $html;
}
//lazy load background images
private static function lazyload_background_images($html, $buffer) {
//match all elements with inline styles
//preg_match_all('#<(?<tag>div|figure|section|span|li)(\s+[^>]+[\'"\s]?style\s*=\s*[\'"].*?background-image.*?[\'"][^>]*)>#is', $buffer, $elements, PREG_SET_ORDER); //alternate to possibly filter some out that don't have background images???
preg_match_all('#<(?<tag>div|figure|section|span|li|a)(\s+[^>]*[\'"\s]?style\s*=\s*[\'"].*?[\'"][^>]*)>#is', $buffer, $elements, PREG_SET_ORDER);
if(!empty($elements)) {
foreach($elements as $element) {
//get element tag attributes
$element_atts = Utilities::get_atts_array($element[2]);
//dont check excluded if forced attribute was found
if(!self::lazyload_excluded($element[2], self::lazyload_forced_atts())) {
//skip if no-lazy class is found
if(!empty($element_atts['class']) && strpos($element_atts['class'], 'no-lazy') !== false) {
continue;
}
//skip if exluded attribute was found
if(self::lazyload_excluded($element[2], self::lazyload_excluded_atts())) {
continue;
}
}
//skip if no style attribute
if(!isset($element_atts['style'])) {
continue;
}
//match background-image in style string
preg_match('#(([^;\s])*background(-(image|url))?)\s*:\s*(\s*url\s*\((?<url>[^)]+)\))\s*;?#is', $element_atts['style'], $url);
if(!empty($url)) {
$url['url'] = trim($url['url'], '\'" ');
//add lazyload class
$element_atts['class'] = !empty($element_atts['class']) ? $element_atts['class'] . ' ' . 'perfmatters-lazy' : 'perfmatters-lazy';
//remove background image url from inline style attribute
$element_atts['style'] = str_replace($url[0], '', $element_atts['style']);
//migrate src
$element_atts['data-bg'] = esc_url(trim(strip_tags(html_entity_decode($url['url'], ENT_QUOTES|ENT_HTML5)), '\'" '));
if(!empty($url[2])) {
$element_atts['data-bg-var'] = $url[1];
}
//build lazy element
$lazy_element = sprintf('<' . $element['tag'] . ' %1$s >', Utilities::get_atts_string($element_atts));
//replace element with placeholder
$html = str_replace($element[0], $lazy_element, $html);
unset($lazy_element);
}
else {
continue;
}
}
}
return $html;
}
//prep img tag for lazy loading
private static function lazyload_image($image) {
//if there are no attributes, return original match
if(empty($image[1])) {
return $image[0];
}
//get image attributes array
$image_atts = Utilities::get_atts_array($image[1]);
//get new attributes
if(empty($image_atts['src']) || (!self::lazyload_excluded($image[1], self::lazyload_forced_atts()) && ((!empty($image_atts['class']) && strpos($image_atts['class'], 'no-lazy') !== false) || self::lazyload_excluded($image[1], self::lazyload_excluded_atts()) || !empty($image_atts['fetchpriority'])))) {
return $image[0];
}
else {
//add lazyload class
$image_atts['class'] = !empty($image_atts['class']) ? $image_atts['class'] . ' ' . 'perfmatters-lazy' : 'perfmatters-lazy';
//migrate src
$image_atts['data-src'] = $image_atts['src'];
//add placeholder src
$width = !empty($image_atts['width']) ? $image_atts['width'] : 0;
$height = !empty($image_atts['height']) ? $image_atts['height'] : 0;
$image_atts['src'] = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='" . $width . "'%20height='" . $height . "'%20viewBox='0%200%20" . $width . "%20" . $height . "'%3E%3C/svg%3E";
//migrate srcset
if(!empty($image_atts['srcset'])) {
$image_atts['data-srcset'] = $image_atts['srcset'];
unset($image_atts['srcset']);
}
//migrate sizes
if(!empty($image_atts['sizes'])) {
$image_atts['data-sizes'] = $image_atts['sizes'];
unset($image_atts['sizes']);
}
//unset existing loading attribute
if(isset($image_atts['loading'])) {
unset($image_atts['loading']);
}
}
//replace attributes
$output = sprintf('<img %1$s />', Utilities::get_atts_string($image_atts));
//original noscript image
if(apply_filters('perfmatters_lazyload_noscript', true)) {
$output.= "<noscript>" . $image[0] . "</noscript>";
}
return $output;
}
//lazy load css background images
private static function lazyload_css_background_images($html, $buffer) {
if(!empty(Config::$options['lazyload']['css_background_selectors'])) {
//match all selectors
preg_match_all('#<(?>div|section)(\s[^>]*?(' . implode('|', Config::$options['lazyload']['css_background_selectors']) . ').*?)>#i', $buffer, $selectors, PREG_SET_ORDER);
if(!empty($selectors)) {
foreach($selectors as $selector) {
$selector_atts = Utilities::get_atts_array($selector[1]);
$selector_atts['class'] = !empty($selector_atts['class']) ? $selector_atts['class'] . ' ' . 'perfmatters-lazy-css-bg' : 'perfmatters-lazy-css-bg';
//replace video attributes string
$selector_lazyload = str_replace($selector[1], ' ' . Utilities::get_atts_string($selector_atts), $selector[0]);
//replace video with placeholder
$html = str_replace($selector[0], $selector_lazyload, $html);
unset($selector_lazyload);
}
}
}
return $html;
}
//mark images inside parent exclusions as no-lazy
private static function lazyload_parent_exclusions($html, $buffer) {
if(!empty(Config::$options['lazyload']['lazy_loading_parent_exclusions'])) {
//match all selectors
preg_match_all('#<(div|section|figure)(\s[^>]*?(' . implode('|', Config::$options['lazyload']['lazy_loading_parent_exclusions']) . ').*?)>.*?<img.*?<\/\g1>#is', $buffer, $selectors, PREG_SET_ORDER);
if(!empty($selectors)) {
foreach($selectors as $selector) {
//match all img tags
preg_match_all('#<img([^>]+?)\/?>#is', $selector[0], $images, PREG_SET_ORDER);
if(!empty($images)) {
//remove any duplicate images
$images = array_unique($images, SORT_REGULAR);
//loop through images
foreach($images as $image) {
$image_atts = Utilities::get_atts_array($image[1]);
$image_atts['class'] = !empty($image_atts['class']) ? $image_atts['class'] . ' ' . 'no-lazy' : 'no-lazy';
//replace video attributes string
$new_image = str_replace($image[1], ' ' . Utilities::get_atts_string($image_atts), $image[0]);
//replace video with placeholder
$html = str_replace($image[0], $new_image, $html);
unset($new_image);
}
}
}
}
}
return $html;
}
//get forced attributes
private static function lazyload_forced_atts() {
return apply_filters('perfmatters_lazyload_forced_attributes', array());
}
//get excluded attributes
private static function lazyload_excluded_atts() {
//base exclusions
$attributes = array(
'data-perfmatters-preload',
'gform_ajax_frame',
';base64'
);
//get exclusions added from settings
if(!empty(Config::$options['lazyload']['lazy_loading_exclusions']) && is_array(Config::$options['lazyload']['lazy_loading_exclusions'])) {
$attributes = array_unique(array_merge($attributes, Config::$options['lazyload']['lazy_loading_exclusions']));
}
return apply_filters('perfmatters_lazyload_excluded_attributes', $attributes);
}
//check for excluded attributes in attributes string
private static function lazyload_excluded($string, $excluded) {
if(!is_array($excluded)) {
(array) $excluded;
}
if(empty($excluded)) {
return false;
}
foreach($excluded as $exclude) {
if(strpos($string, $exclude) !== false) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace Perfmatters;
class Meta
{
private static $meta_options;
//initialize meta functions
public static function init()
{
if(!is_admin() || !current_user_can('manage_options')) {
return;
}
global $pagenow;
if(in_array($pagenow, ['post.php', 'new-post.php'])) {
//setup meta options
self::get_meta_options();
//exclude specific woocommerce pages
if(class_exists('WooCommerce') && !empty($_GET['post'])) {
$wc_pages = array_filter(array(
get_option('woocommerce_cart_page_id'),
get_option('woocommerce_checkout_page_id'),
get_option('woocommerce_myaccount_page_id')
));
if(in_array($_GET['post'], $wc_pages)) {
return;
}
}
//meta actions
add_action('add_meta_boxes', array('Perfmatters\Meta', 'add_meta_boxes'), 1);
add_action('save_post', array('Perfmatters\Meta', 'save_meta'), 1, 2);
}
}
//add meta boxes
public static function add_meta_boxes() {
foreach(self::$meta_options as $id => $details) {
if($details['value']) {
//display meta box if at least one value is set
add_meta_box('perfmatters', 'Perfmatters', array('Perfmatters\Meta', 'load_meta_box'), get_post_types(array('public' => true)), 'side', 'high');
break;
}
}
}
//display meta box
public static function load_meta_box() {
global $post;
//noncename needed to verify where the data originated
echo '<input type="hidden" name="perfmatters_meta_noncename" id="perfmatters_meta_noncename" value="' . wp_create_nonce(plugin_basename(__FILE__)) . '" />';
//print inputs
foreach(self::$meta_options as $id => $details) {
//existing meta value
$meta = get_post_meta($post->ID, 'perfmatters_exclude_' . $id, true);
//individual input
echo '<div' . (!$details['value'] ? ' class="hidden"' : '') . '>';
echo '<label for="perfmatters_exclude_' . $id . '">';
echo '<input type="hidden" name="perfmatters_exclude_' . $id . '" value="1" />';
echo '<input type="checkbox" name="perfmatters_exclude_' . $id . '" id="perfmatters_exclude_' . $id . '"' . (!$meta ? " checked" : "") . ' value="" class="widefat" />';
echo $details['name'];
echo '</label>';
echo '</div>';
}
//clear used css
if(!empty(Config::$options['assets']['remove_unused_css'])) {
echo '<style>.perfmatters-clear-post-used-css{display:inline-block;margin-top:10px}.perfmatters-clear-post-used-css .spinner{display:none;float:none}.perfmatters-clear-post-used-css .spinner.is-active{display:inline-block}.perfmatters-clear-post-used-css .dashicons-yes{display:none;margin:2px 10px;color:green;font-size:26px}</style>';
echo '<div class="perfmatters-clear-post-used-css">';
echo '<a class="button button-secondary" id="perfmatters-clear-post-used-css" value="1">' . __('Clear Used CSS', 'perfmatters') . '</a>';
echo '<span class="spinner"></span>';
echo '<span class="dashicons dashicons-yes"></span>';
echo wp_nonce_field('perfmatters_clear_post_used_css', 'perfmatters_clear_post_used_css', false, false);
echo '</div>';
echo '<script>jQuery(document).ready(function(s){s("#perfmatters-clear-post-used-css").click(function(t){if(t.preventDefault(),$button=s(this),$button.hasClass("disabled"))return!1;$button.addClass("disabled"),$button.siblings(".spinner").addClass("is-active");var e={action:"perfmatters_clear_post_used_css",nonce:$button.siblings("#perfmatters_clear_post_used_css").val(),post_id:parseInt(s("#post_ID").val())};s.post(ajaxurl,e,function(s){$button.siblings(".spinner").removeClass("is-active"),$button.siblings(".dashicons-yes").fadeIn().css("display","inline-block"),setTimeout(function(){$button.siblings(".dashicons-yes").fadeOut()},1e3),$button.removeClass("disabled")})})});</script>';
}
}
//save meta box data
public static function save_meta($post_id, $post) {
//verify this came from the our screen and with proper authorization, because save_post can be triggered at other times
if(empty($_POST['perfmatters_meta_noncename']) || !wp_verify_nonce($_POST['perfmatters_meta_noncename'], plugin_basename(__FILE__))) {
return;
}
//dont save for revisions
if($post->post_type == 'revision') {
return;
}
//saved data
$perfmatters_meta = array();
foreach(self::$meta_options as $id => $details) {
$key = 'perfmatters_exclude_' . $id;
if(!empty($_POST[$key]) || get_post_meta($post->ID, $key, true) != false) {
//update option in post meta
update_post_meta($post->ID, $key, $_POST[$key] ?? "");
}
}
}
//populate meta options array for other functions
private static function get_meta_options() {
self::$meta_options = array(
'defer_js' => array(
'name' => __('Defer JavaScript', 'perfmatters'),
'value' => !empty(Config::$options['assets']['defer_js'])
),
'delay_js' => array(
'name' => __('Delay JavaScript', 'perfmatters'),
'value' => !empty(Config::$options['assets']['delay_js'])
),
'unused_css' => array(
'name' => __('Unused CSS', 'perfmatters'),
'value' => !empty(Config::$options['assets']['remove_unused_css'])
),
'lazy_loading' => array(
'name' => __('Lazy Loading', 'perfmatters'),
'value' => !empty(Config::$options['lazyload']['lazy_loading']) || !empty(Config::$options['lazyload']['lazy_loading_iframes'])
),
'instant_page' => array(
'name' => __('Instant Page', 'perfmatters'),
'value' => !empty(Config::$options['preload']['instant_page'])
)
);
}
}

View File

@@ -0,0 +1,359 @@
<?php
namespace Perfmatters;
class Preload
{
private static $fetch_priority;
private static $preloads;
private static $critical_images;
private static $preloads_ready = array();
private static $used_srcs = array();
//initialize preload functions
public static function init()
{
add_action('wp', array('Perfmatters\Preload', 'queue'));
}
//queue functions
public static function queue()
{
//fetch priority
self::$fetch_priority = apply_filters('perfmatters_fetch_priority', Config::$options['preload']['fetch_priority'] ?? array());
if(!empty(self::$fetch_priority)) {
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\Preload', 'add_fetch_priority'));
}
//preloads
self::$preloads = apply_filters('perfmatters_preloads', Config::$options['preload']['preload'] ?? array());
self::$critical_images = apply_filters('perfmatters_preload_critical_images', Config::$options['preload']['critical_images'] ?? 0);
if(!empty(self::$preloads) || !empty(self::$critical_images)) {
add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\Preload', 'add_preloads'));
}
}
//add fetch priority
public static function add_fetch_priority($html) {
$selectors = array();
$parent_selectors = array();
foreach(self::$fetch_priority as $line) {
//device type check
if(!empty($line['device'])) {
$device_type = wp_is_mobile() ? 'mobile' : 'desktop';
if($line['device'] != $device_type) {
continue;
}
}
//location check
if(!empty($line['locations'])) {
if(!self::location_check($line['locations'])) {
continue;
}
}
//add selector
if(!empty($line['parent'])) {
$parent_selectors[] = $line['selector'];
}
else {
$selectors[] = $line['selector'];
}
}
//parent selectors
if(!empty($parent_selectors)) {
//match all selectors
preg_match_all('#<(div|section|figure)(\s[^>]*?(' . implode('|', $parent_selectors) . ').*?)>.*?<(link|img|script).*?<\/\g1>#is', $html, $parents, PREG_SET_ORDER);
if(!empty($parents)) {
foreach($parents as $parent) {
//match all tags
preg_match_all('#<(?>link|img|script)(\s[^>]*?)>#is', $parent[0], $tags, PREG_SET_ORDER);
if(!empty($tags)) {
//loop through images
foreach($tags as $tag) {
$tag_atts = Utilities::get_atts_array($tag[1]);
$key = array_search($parent[2], array_column(self::$fetch_priority, 'selector'));
$tag_atts['fetchpriority'] = self::$fetch_priority[$key]['priority'];;
//replace video attributes string
$new_tag = str_replace($tag[1], ' ' . Utilities::get_atts_string($tag_atts), $tag[0]);
//replace video with placeholder
$html = str_replace($tag[0], $new_tag, $html);
unset($new_tag);
}
}
}
}
}
//selectors
if(!empty($selectors)) {
preg_match_all('#<(?>link|img|script)(\s[^>]*?(' . implode('|', $selectors) . ').*?)>#is', $html, $matches, PREG_SET_ORDER);
if(!empty($matches)) {
foreach($matches as $tag) {
$atts = Utilities::get_atts_array($tag[1]);
$key = array_search($tag[2], array_column(self::$fetch_priority, 'selector'));
$atts['fetchpriority'] = self::$fetch_priority[$key]['priority'];;
//replace video attributes string
$new_tag = str_replace($tag[1], ' ' . Utilities::get_atts_string($atts), $tag[0]);
//replace video with placeholder
$html = str_replace($tag[0], $new_tag, $html);
}
}
}
return $html;
}
//add preloads to html
public static function add_preloads($html) {
if(!empty(self::$critical_images)) {
self::add_critical_image_preloads($html, Utilities::clean_html($html));
}
if(!empty(self::$preloads)) {
$mime_types = array(
'svg' => 'image/svg+xml',
'ttf' => 'font/ttf',
'otf' => 'font/otf',
'woff' => 'font/woff',
'woff2' => 'font/woff2',
'eot' => 'application/vnd.ms-fontobject',
'sfnt' => 'font/sfnt'
);
foreach(self::$preloads as $line) {
//device type check
if(!empty($line['device'])) {
$device_type = wp_is_mobile() ? 'mobile' : 'desktop';
if($line['device'] != $device_type) {
continue;
}
}
//location check
if(!empty($line['locations'])) {
if(!self::location_check($line['locations'])) {
continue;
}
}
$mime_type = "";
if(!empty($line['as']) && $line['as'] == 'font') {
$path_info = pathinfo($line['url']);
$mime_type = !empty($path_info['extension']) && isset($mime_types[$path_info['extension']]) ? $mime_types[$path_info['extension']] : "";
}
//print script/style handle as preload
if(!empty($line['as']) && in_array($line['as'], array('script', 'style'))) {
if(strpos($line['url'], '.') === false) {
global $wp_scripts;
global $wp_styles;
$scripts_arr = $line['as'] == 'script' ? $wp_scripts : $wp_styles;
if(!empty($scripts_arr)) {
$scripts_arr = $scripts_arr->registered;
if(array_key_exists($line['url'], $scripts_arr)) {
$url = $scripts_arr[$line['url']]->src;
$parsed_url = parse_url($scripts_arr[$line['url']]->src);
if(empty($parsed_url['host'])) {
$url = site_url($url);
}
if(strpos($url, '?') === false) {
$ver = $scripts_arr[$line['url']]->ver;
if(empty($ver)) {
$ver = get_bloginfo('version');
}
}
$line['url'] = $url . (!empty($ver) ? '?ver=' . $ver : '');
}
else {
continue;
}
}
else {
continue;
}
}
}
$preload = "<link rel='preload' href='" . $line['url'] . "'" . (!empty($line['as']) ? " as='" . $line['as'] . "'" : "") . (!empty($mime_type) ? " type='" . $mime_type . "'" : "") . (!empty($line['crossorigin']) ? " crossorigin" : "") . (!empty($line['as']) && $line['as'] == 'style' ? " onload=\"this.rel='stylesheet';this.removeAttribute('onload');\"" : "") . ">";
if($line['as'] == 'image') {
array_unshift(self::$preloads_ready, $preload);
}
else {
self::$preloads_ready[] = $preload;
}
}
}
if(!empty(self::$preloads_ready)) {
$preloads_string = "";
foreach(apply_filters('perfmatters_preloads_ready', self::$preloads_ready) as $preload) {
$preloads_string.= $preload;
}
$pos = strpos($html, '</title>');
if($pos !== false) {
$html = substr_replace($html, '</title>' . $preloads_string, $pos, 8);
}
}
return $html;
}
//add critical image preloads
public static function add_critical_image_preloads(&$html, $clean_html) {
//match all image formats
preg_match_all('#(<picture.*?)?<img([^>]+?)\/?>(?><\/picture>)?#is', $clean_html, $matches, PREG_SET_ORDER);
if(!empty($matches)) {
$exclusions = apply_filters('perfmatters_critical_image_exclusions', array(
';base64',
'w3.org'
));
$count = 0;
foreach($matches as $match) {
if($count >= self::$critical_images) {
break;
}
if(strpos($match[0], 'secure.gravatar.com') !== false) {
continue;
}
if(!empty($exclusions) && is_array($exclusions)) {
foreach($exclusions as $exclusion) {
if(strpos($match[0], $exclusion) !== false) {
continue 2;
}
}
}
//picture tag
if(!empty($match[1])) {
preg_match('#<source([^>]+?image\/(webp|avif)[^>]+?)\/?>#is', $match[0], $source);
if(!empty($source)) {
if(self::generate_critical_image_preload($source[1])) {
$new_picture = str_replace('<picture', '<picture data-perfmatters-preload', $match[0]);
$new_picture = str_replace('<img', '<img data-perfmatters-preload', $new_picture);
$html = str_replace($match[0], $new_picture, $html);
$count++;
}
continue;
}
}
//img tag
if(!empty($match[2])) {
if(self::generate_critical_image_preload($match[2])) {
$new_image = str_replace('<img', '<img data-perfmatters-preload', $match[0]);
$html = str_replace($match[0], $new_image, $html);
$count++;
}
}
}
}
if(!empty(self::$preloads_ready)) {
ksort(self::$preloads_ready);
}
}
//generate preload link from att string
private static function generate_critical_image_preload($att_string) {
if(!empty($att_string)) {
$atts = Utilities::get_atts_array($att_string);
$src = $atts['data-src'] ?? $atts['src'] ?? '';
//dont add if src was already used
if(in_array($src, self::$used_srcs)) {
return false;
}
//generate preload
self::$preloads_ready[] = '<link rel="preload" href="' . $src . '" as="image"' . (!empty($atts['srcset']) ? ' imagesrcset="' . $atts['srcset'] . '"' : '') . (!empty($atts['sizes']) ? ' imagesizes="' . $atts['sizes'] . '"' : '') . ' />';
//mark src used and return
if(!empty($src)) {
self::$used_srcs[] = $src;
}
return true;
}
}
private static function location_check($locations) {
$location_match = false;
$exploded_locations = explode(',', $locations);
$trimmed_locations = array_map('trim', $exploded_locations);
//single post exclusion
if(is_singular()) {
global $post;
if(in_array($post->ID, $trimmed_locations)) {
$location_match = true;
}
}
//posts page exclusion
elseif(is_home() && in_array('blog', $trimmed_locations)) {
$location_match = true;
}
elseif(is_archive()) {
//woocommerce shop check
if(function_exists('is_shop') && is_shop()) {
if(in_array(wc_get_page_id('shop'), $trimmed_locations)) {
$location_match = true;
}
}
}
return $location_match;
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace Perfmatters;
class Utilities
{
//get given post meta option for current post
public static function get_post_meta($option) {
global $post;
if(!is_object($post)) {
return false;
}
if(is_home()) {
$post_id = get_queried_object_id();
}
if(is_singular() && isset($post)) {
$post_id = $post->ID;
}
return (isset($post_id)) ? get_post_meta($post_id, $option, true) : false;
}
//remove unecessary bits from html for search
public static function clean_html($html) {
//remove existing script tags
$html = preg_replace('/<script\b(?:[^>]*)>(?:.+)?<\/script>/Umsi', '', $html);
//remove existing noscript tags
$html = preg_replace('#<noscript>(?:.+)</noscript>#Umsi', '', $html);
return $html;
}
//get array of element attributes from attribute string
public static function get_atts_array($atts_string) {
if(!empty($atts_string)) {
$atts_array = array_map(
function(array $attribute) {
return $attribute['value'];
},
wp_kses_hair($atts_string, wp_allowed_protocols())
);
return $atts_array;
}
return false;
}
//get attribute string from array of element attributes
public static function get_atts_string($atts_array) {
if(!empty($atts_array)) {
$assigned_atts_array = array_map(
function($name, $value) {
if($value === '') {
return $name;
}
return sprintf('%s="%s"', $name, esc_attr($value));
},
array_keys($atts_array),
$atts_array
);
$atts_string = implode(' ', $assigned_atts_array);
return $atts_string;
}
return false;
}
//check for specific woocommerce pages
public static function is_woocommerce() {
if(class_exists('WooCommerce') && (is_cart() || is_checkout() || is_account_page())) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,2 @@
<?php
/* Blank Comments Template */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
<?php
//register network menu and settings
function perfmatters_network_admin_menu() {
//Add Network Settings Menu Item
$perfmatters_network_settings_page = add_submenu_page('settings.php', 'Perfmatters Network Settings', 'Perfmatters', 'manage_network_options', 'perfmatters', 'perfmatters_admin');
//Add Load Action to Enqueue Scripts
add_action('load-' . $perfmatters_network_settings_page, 'perfmatters_settings_load');
//Create Site Option if Not Found
if(get_site_option('perfmatters_network') == false) {
add_site_option('perfmatters_network', true);
}
//Add Settings Section
add_settings_section('perfmatters_network', 'Network Setup', '__return_false', 'perfmatters_network');
//Add Options Fields
add_settings_field(
'access',
perfmatters_title(__('Network Access', 'perfmatters'), 'access', 'https://perfmatters.io/docs/wordpress-multisite/'),
'perfmatters_network_access_callback',
'perfmatters_network',
'perfmatters_network',
array(
'tooltip' => __('Choose who has access to manage Perfmatters plugin settings.', 'perfmatters')
)
);
add_settings_field(
'default',
perfmatters_title(__('Network Default', 'perfmatters'), 'default', 'https://perfmatters.io/docs/wordpress-multisite/'),
'perfmatters_network_default_callback',
'perfmatters_network',
'perfmatters_network',
array(
'tooltip' => __('Choose a subsite that you want to pull default settings from.', 'perfmatters')
)
);
//Clean Uninstall
add_settings_field(
'clean_uninstall',
perfmatters_title(__('Clean Uninstall', 'perfmatters'), 'clean_uninstall', 'https://perfmatters.io/docs/clean-uninstall/'),
'perfmatters_print_input',
'perfmatters_network',
'perfmatters_network',
array(
'id' => 'clean_uninstall',
'option' => 'perfmatters_network',
'tooltip' => __('When enabled, this will cause all Perfmatters options data to be removed from your database when the plugin is uninstalled.', 'perfmatters')
)
);
//Register Setting
register_setting('perfmatters_network', 'perfmatters_network');
}
add_filter('network_admin_menu', 'perfmatters_network_admin_menu');
//network access callback
function perfmatters_network_access_callback() {
$perfmatters_network = get_site_option('perfmatters_network');
echo "<select name='perfmatters_network[access]' id='access'>";
echo "<option value=''>" . __('Site Admins (Default)', 'perfmatters') . "</option>";
echo "<option value='super' " . ((!empty($perfmatters_network['access']) && $perfmatters_network['access'] == 'super') ? "selected" : "") . ">" . __('Super Admins Only', 'perfmatters') . "</option>";
echo "<select>";
//tooltip
if(!empty($args['tooltip'])) {
perfmatters_tooltip($args['tooltip']);
}
}
//network default callback
function perfmatters_network_default_callback() {
$perfmatters_network = get_site_option('perfmatters_network');
echo "<select name='perfmatters_network[default]' id='default'>";
$sites = array_map('get_object_vars', get_sites(array('deleted' => 0, 'number' => 1000)));
if(is_array($sites) && $sites !== array()) {
echo "<option value=''>" . __('None', 'perfmatters') . "</option>";
foreach($sites as $site) {
echo "<option value='" . $site['blog_id'] . "' " . ((!empty($perfmatters_network['default']) && $perfmatters_network['default'] == $site['blog_id']) ? "selected" : "") . ">" . $site['blog_id'] . ": " . $site['domain'] . $site['path'] . "</option>";
}
}
echo "<select>";
//tooltip
if(!empty($args['tooltip'])) {
perfmatters_tooltip($args['tooltip']);
}
}
//update perfmatters network options
function perfmatters_update_network_options() {
//Verify Post Referring Page
check_admin_referer('perfmatters_network-options');
//Get Registered Options
global $new_whitelist_options;
$options = $new_whitelist_options['perfmatters_network'];
//Loop Through Registered Options
foreach($options as $option) {
if(isset($_POST[$option])) {
//Update Site Uption
update_site_option($option, $_POST[$option]);
}
}
//Redirect to Network Settings Page
wp_redirect(add_query_arg(array('page' => 'perfmatters', 'updated' => 'true'), network_admin_url('settings.php')));
exit;
}
add_action('network_admin_edit_perfmatters_update_network_options', 'perfmatters_update_network_options');

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
<?php
//save license key
if(isset($_POST['perfmatters_save_license']) && isset($_POST['perfmatters_edd_license_key'])) {
//save license option
if(is_network_admin()) {
update_site_option('perfmatters_edd_license_key', trim($_POST['perfmatters_edd_license_key']));
}
else {
update_option('perfmatters_edd_license_key', trim($_POST['perfmatters_edd_license_key']));
}
if(is_multisite()) {
//check license info
$license_info = perfmatters_check_license();
if(!empty($license_info->activations_left) && $license_info->activations_left == 'unlimited') {
//activate after save
perfmatters_activate_license();
}
}
else {
//activate after save
perfmatters_activate_license();
}
}
//activate license
if(isset($_POST['perfmatters_edd_license_activate'])) {
perfmatters_activate_license();
}
//deactivate license
if(isset($_POST['perfmatters_edd_license_deactivate'])) {
perfmatters_deactivate_license();
}
//remove license
if(isset($_POST['perfmatters_remove_license'])) {
//deactivate before removing
perfmatters_deactivate_license();
//remove license option
if(is_network_admin()) {
delete_site_option('perfmatters_edd_license_key');
}
else {
delete_option('perfmatters_edd_license_key');
}
}
//get license key
$license = is_network_admin() ? get_site_option('perfmatters_edd_license_key') : get_option('perfmatters_edd_license_key');
//start custom license form
echo "<form method='post' action=''>";
echo '<div class="perfmatters-settings-section">';
//tab header
echo "<h2><span class='dashicons dashicons-admin-network'></span>" . __('License', 'perfmatters') . "</h2>";
echo "<table class='form-table'>";
echo "<tbody>";
//license key
echo "<tr>";
echo "<th>" . perfmatters_title(__('License Key', 'perfmatters'), (empty($license) ? 'perfmatters_edd_license_key' : false), 'https://perfmatters.io/docs/troubleshooting-license-key-activation/') . "</th>";
echo "<td>";
echo "<input id='perfmatters_edd_license_key' name='perfmatters_edd_license_key' type='text' class='regular-text' value='" . (!empty($license) ? substr($license, 0, 4) . '**************************' : '') . "' style='margin-right: 10px;' maxlength='50' />";
if(empty($license)) {
//save license button
echo "<input type='submit' name='perfmatters_save_license' class='button button-primary' value='" . __('Save License', 'perfmatters') . "'>";
}
else {
//remove license button
echo "<input type='submit' class='button perfmatters-button-warning' name='perfmatters_remove_license' value='" . __('Remove License', 'perfmatters') . "' />";
}
perfmatters_tooltip(__('Save or remove your license key.', 'perfmatters'));
echo "</td>";
echo "</tr>";
if(!empty($license)) {
//force disable styles on license input
echo "<style>
input[name=\"perfmatters_edd_license_key\"] {
background: rgba(255,255,255,.5);
border-color: rgba(222,222,222,.75);
box-shadow: inset 0 1px 2px rgba(0,0,0,.04);
color: rgba(51,51,51,.5);
pointer-events: none;
}
</style>";
//check license info
$license_info = perfmatters_check_license();
if(!empty($license_info)) {
//activate/deactivate license
if(!empty($license_info->license) && $license_info->license != 'invalid') {
echo "<tr>";
echo "<th>" . __('Activate License', 'perfmatters') . "</th>";
echo "<td>";
if($license_info->license == 'valid') {
echo "<input type='submit' class='button-secondary' name='perfmatters_edd_license_deactivate' value='" . __('Deactivate License', 'perfmatters') . "' style='margin-right: 10px;' />";
echo "<span style='color:green;line-height:30px;'><span class='dashicons dashicons-cloud'style='line-height:30px;'></span> " . __('License is activated.', 'novashare') . "</span>";
}
elseif(!is_multisite() || (!empty($license_info->activations_left) && $license_info->activations_left == 'unlimited')) {
echo "<input type='submit' class='button-secondary' name='perfmatters_edd_license_activate' value='" . __('Activate License', 'perfmatters') . "' style='margin-right: 10px;' />";
echo "<span style='color:red;line-height:30px;'><span class='dashicons dashicons-warning'style='line-height:30px;'></span> " . __('License is not activated.', 'novashare') . "</span>";
}
else {
echo "<span style='color:red; display: block;'>" . __('Unlimited License needed for use in a multisite environment. Please contact support to upgrade.', 'perfmatters') . "</span>";
}
echo "</td>";
echo "</tr>";
}
//license status (active/expired)
if(!empty($license_info->license)) {
echo "<tr>";
echo "<th>" . __('License Status', 'perfmatters') . "</th>";
echo "<td" . ($license_info->license == "expired" ? " style='color: red;'" : "") . ">";
echo ucfirst($license_info->license);
if($license_info->license == "expired") {
echo "<br />";
echo "<a href='https://perfmatters.io/checkout/?edd_license_key=" . $license . "&download_id=696' class='button-primary' style='margin-top: 10px;' target='_blank'>" . __('Renew Your License for Updates + Support!', 'perfmatters') . "</a>";
}
echo "</td>";
echo "</tr>";
}
//licenses used
if(!empty($license_info->site_count) && !empty($license_info->license_limit) && !is_network_admin()) {
echo "<tr>";
echo "<th>" . __('Licenses Used', 'perfmatters') . "</th>";
echo "<td>" . $license_info->site_count . "/" . $license_info->license_limit . "</td>";
echo "</tr>";
}
//expiration date
if(!empty($license_info->expires)) {
echo "<tr>";
echo "<th>" . __('Expiration Date', 'perfmatters') . "</th>";
echo "<td>" . ($license_info->expires != 'lifetime' ? date("F d, Y", strtotime($license_info->expires)) : __('Lifetime', 'perfmatters')) . "</td>";
echo "</tr>";
}
}
}
echo "</tbody>";
echo "</table>";
echo '</div>';
echo "</form>";

View File

@@ -0,0 +1,162 @@
<?php
if(isset($_POST['perfmatters_apply_defaults'])) {
check_admin_referer('perfmatters-network-apply');
if(isset($_POST['perfmatters_network_apply_blog']) && is_numeric($_POST['perfmatters_network_apply_blog'])) {
$blog = get_blog_details($_POST['perfmatters_network_apply_blog']);
if($blog) {
$perfmatters_network = get_site_option('perfmatters_network');
if(!empty($perfmatters_network['default'])) {
if($blog->blog_id != $perfmatters_network['default']) {
$option_names = array(
'perfmatters_options',
'perfmatters_tools'
);
foreach($option_names as $option_name) {
//clear selected blog previous option
delete_blog_option($blog->blog_id, $option_name);
//grab new option from default blog
$new_option = get_blog_option($perfmatters_network['default'], $option_name);
//remove options we don't want to copy
if($option_name == 'perfmatters_option') {
unset($new_option['cdn']['cdn_url']);
}
//update selected blog with default option
update_blog_option($blog->blog_id, $option_name, $new_option);
}
//Default Settings Updated Notice
echo "<div class='notice updated is-dismissible'><p>" . __('Default settings applied!', 'perfmatters') . "</p></div>";
}
else {
//Can't Apply to Network Default
echo "<div class='notice error is-dismissible'><p>" . __('Select a site that is not already the Network Default.', 'perfmatters') . "</p></div>";
}
}
else {
//Network Default Not Set
echo "<div class='notice error is-dismissible'><p>" . __('Network Default not set.', 'perfmatters') . "</p></div>";
}
}
else {
//Blog Not Found Notice
echo "<div class='notice error is-dismissible'><p>" . __('Error: Blog Not Found.', 'perfmatters') . "</p></div>";
}
}
}
elseif(isset($_POST['perfmatters_apply_defaults_all'])) {
check_admin_referer('perfmatters-network-apply');
$perfmatters_network = get_site_option('perfmatters_network');
if(!empty($perfmatters_network['default'])) {
$sites = array_map('get_object_vars', get_sites(array('deleted' => 0, 'number' => 1000)));
if(is_array($sites) && $sites !== array()) {
$update_count = 0;
foreach($sites as $site) {
$apply = perfmatters_apply_defaults_to_blog($site['blog_id'], $perfmatters_network['default']);
if($apply) {
$update_count++;
}
}
if($update_count > 0) {
//default settings applied
echo "<div class='notice updated is-dismissible'><p>" . __('Default settings applied!', 'perfmatters') . "</p></div>";
}
}
else {
//no sites available
echo "<div class='notice error is-dismissible'><p>" . __('No available sites found.', 'perfmatters') . "</p></div>";
}
}
else {
//network default not set
echo "<div class='notice error is-dismissible'><p>" . __('Network Default not set.', 'perfmatters') . "</p></div>";
}
}
//Options Updated
if(isset($_GET['updated'])) {
echo "<div class='notice updated is-dismissible'><p>" . __('Options saved.', 'perfmatters') . "</p></div>";
}
echo "<form method='POST' action='edit.php?action=perfmatters_update_network_options'>";
settings_fields('perfmatters_network');
perfmatters_settings_section('perfmatters_network', 'perfmatters_network', 'dashicons-networking');
submit_button();
echo "</form>";
echo "<form method='POST' style='margin: 30px auto 10px;'>";
echo '<div>';
echo "<h2>" . __('Apply Default Settings', 'perfmatters') . "</h2>";
wp_nonce_field('perfmatters-network-apply', '_wpnonce', true, true);
echo "<p>" . __('Select a site from the dropdown and click to apply the settings from your network default (above).', 'perfmatters') . "</p>";
echo "<select name='perfmatters_network_apply_blog' style='margin-right: 10px;'>";
$sites = array_map('get_object_vars', get_sites(array('deleted' => 0, 'number' => 1000)));
if(is_array($sites) && $sites !== array()) {
echo "<option value=''>" . __('Select a Site', 'perfmatters') . "</option>";
foreach($sites as $site) {
echo "<option value='" . $site['blog_id'] . "'>" . $site['blog_id'] . ": " . $site['domain'] . $site['path'] . "</option>";
}
}
echo "</select>";
echo "<input type='submit' name='perfmatters_apply_defaults' value='" . __('Apply Default Settings', 'perfmatters') . "' class='button' />";
echo "<br />";
echo "<p>" . __('Apply the settings from your network default to all sites. Depending on the amount, this may take a while.', 'perfmatters') . "</p>";
echo "<input type='submit' name='perfmatters_apply_defaults_all' value='" . __('Apply Default Settings to All Sites', 'perfmatters') . "' class='button' onclick='return confirm(\"" . __('Are you sure? This will permanently overwrite all Perfmatters options for all subsites.', 'perfmatters') . "\");' />";
echo '</div>';
echo "</form>";
//apply defaults to blog
function perfmatters_apply_defaults_to_blog($blog_id, $network_default) {
$blog = get_blog_details($blog_id);
if($blog && !empty($network_default)) {
if($blog->blog_id != $network_default) {
$option_names = array(
'perfmatters_options',
'perfmatters_tools'
);
foreach($option_names as $option_name) {
//clear selected blog previous option
delete_blog_option($blog->blog_id, $option_name);
//grab new option from default blog
$new_option = get_blog_option($network_default, $option_name);
//remove options we don't want to copy
if($option_name == 'perfmatters_options') {
unset($new_option['cdn']['cdn_url']);
}
//update selected blog with default option
update_blog_option($blog->blog_id, $option_name, $new_option);
}
return true;
}
}
return false;
}

View File

@@ -0,0 +1,591 @@
<?php
/*
Plugin Name: Perfmatters MU
Plugin URI: https://perfmatters.io/
Description: Perfmatters is a lightweight performance plugin developed to speed up your WordPress site.
Version: 2.1.4
Author: forgemedia
Author URI: https://forgemedia.io/
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: perfmatters
Domain Path: /languages
*/
add_filter('option_active_plugins', 'perfmatters_mu_disable_plugins', 1);
function perfmatters_mu_disable_plugins($plugins) {
//admin check
if(is_admin()) {
return $plugins;
}
//only filter GET requests
if((!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'GET') && !isset($_GET['perfmatters'])) {
return $plugins;
}
//dont filter if its a rest or ajax request
if((defined('REST_REQUEST') && REST_REQUEST) || (defined('WP_CLI') && WP_CLI) || (function_exists('wp_is_json_request') && wp_is_json_request()) || wp_doing_ajax() || wp_doing_cron()) {
return $plugins;
}
//manual wp-json check
if(stripos(trailingslashit($_SERVER['REQUEST_URI']), '/wp-json/') !== false) {
return $plugins;
}
//manual ajax check
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
return $plugins;
}
//base plugin active check
if(is_array($plugins) && !in_array('perfmatters/perfmatters.php', $plugins)) {
return $plugins;
}
//make sure script manager is enabled
$perfmatters_options = get_option('perfmatters_options');
if(empty($perfmatters_options['assets']['script_manager'])) {
return $plugins;
}
//make sure MU is enabled
$pmsm_settings = get_option('perfmatters_script_manager_settings');
if(empty($pmsm_settings['mu_mode'])) {
return $plugins;
}
//wp login check
$perfmatters_options = get_option('perfmatters_options');
if((!empty($GLOBALS['pagenow']) && $GLOBALS['pagenow'] == 'wp-login.php') || (!empty($perfmatters_options['login_url']) && !empty($GLOBALS['_SERVER']['REQUEST_URI']) && trim($GLOBALS['_SERVER']['REQUEST_URI'], '/') == $perfmatters_options['login_url'])) {
return $plugins;
}
//script manager is being viewed
if(isset($_GET['perfmatters'])) {
//store active plugins for script manager UI in case they get disabled completely
global $pmsm_active_plugins;
$pmsm_active_plugins = $plugins;
//don't filter plugins if script manager is up
return $plugins;
}
if(isset($_GET['perfmattersoff'])) {
return $plugins;
}
//testing mode check
if(!empty($pmsm_settings['testing_mode'])) {
require_once(wp_normalize_path(ABSPATH) . 'wp-includes/pluggable.php');
if(!function_exists('wp_get_current_user') || !current_user_can('manage_options')) {
return $plugins;
}
}
//check for manual override
if(!empty($_GET['mu_mode']) && $_GET['mu_mode'] == 'off') {
return $plugins;
}
//make sure mu hasn't run already
global $mu_run_flag;
global $mu_plugins;
if($mu_run_flag) {
return $mu_plugins;
}
//get script manager configuration
$pmsm = get_option('perfmatters_script_manager');
//we have some plugins that are disabled
if(!empty($pmsm['disabled']['plugins'])) {
//attempt to get post id
$currentID = perfmatters_mu_get_current_ID();
//echo $currentID;
//assign disable/enable arrays
$disabled = $pmsm['disabled']['plugins'];
$enabled = !empty($pmsm['enabled']['plugins']) ? $pmsm['enabled']['plugins'] : '';
//loop through disabled plugins
foreach($disabled as $handle => $data) {
//current plugin disable is set
if(!empty($data['everywhere'])
|| (!empty($data['current']) && in_array($currentID, $data['current']))
|| pmsm_mu_check_post_types($data, $currentID)
|| pmsm_mu_check_user_status($data)
|| pmsm_mu_check_device_type($data)
|| (!empty($data['regex']) && preg_match($data['regex'], home_url(add_query_arg(array(), $_SERVER['REQUEST_URI']))))
) {
if(!empty($enabled[$handle])) {
//enable current url check
if(!empty($enabled[$handle]['current']) && in_array($currentID, $enabled[$handle]['current'])) {
continue;
}
//user status check
if(pmsm_mu_check_user_status($enabled[$handle])) {
continue;
}
//device type check
if(pmsm_mu_check_device_type($enabled[$handle])) {
continue;
}
//enable regex
if(!empty($enabled[$handle]['regex'])) {
if(preg_match($enabled[$handle]['regex'], home_url(add_query_arg(array(), $_SERVER['REQUEST_URI'])))) {
continue;
}
}
//home page as post type
if(pmsm_mu_check_post_types($enabled[$handle], $currentID)) {
continue;
}
}
//remove plugin from list
$m_array = preg_grep('/^' . $handle . '.*/', $plugins);
$single_array = array();
if(!empty($m_array) && is_array($m_array)) {
if(count($m_array) > 1) {
$single_array = preg_grep('/' . $handle . '\.php/', $m_array);
}
if(!empty($single_array)) {
unset($plugins[key($single_array)]);
}
else {
unset($plugins[key($m_array)]);
}
}
}
}
}
$mu_run_flag = true;
$mu_plugins = $plugins;
return $plugins;
}
//remove our filter after plugins have loaded
function perfmatters_mu_remove_filters() {
remove_filter('option_active_plugins', 'perfmatters_mu_disable_plugins', 1, 1);
}
add_action('plugins_loaded', 'perfmatters_mu_remove_filters', 1);
//attempt to get the current id for mu mode
function perfmatters_mu_get_current_ID() {
//load necessary parts for url_to_postid
if(!defined('LOGGED_IN_COOKIE')) {
wp_cookie_constants();
}
require_once(wp_normalize_path(ABSPATH) . 'wp-includes/pluggable.php');
global $wp_rewrite;
global $wp;
$wp_rewrite = new WP_Rewrite();
$wp = new WP();
//attempt to get post id from url
$currentID = (int) perfmatters_url_to_postid(home_url($_SERVER['REQUEST_URI']));
//id wasn't found
if($currentID === 0) {
//check for home url match
//$request = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . strtok($_SERVER['REQUEST_URI']);
$request = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
if(trailingslashit(home_url()) !== trailingslashit($request)) {
$currentID = '';
}
}
//clean up
unset($wp_rewrite);
unset($wp);
return $currentID;
}
//check if current post type is set in option
function pmsm_mu_check_post_types($option, $currentID = '') {
if($currentID === 0) {
if(get_option('show_on_front') == 'page' && !empty($option['post_types']) && in_array('page', $option['post_types'])) {
return true;
}
}
elseif(!empty($currentID)) {
//grab post details
$post = get_post($currentID);
//post type enable check
if(!empty($post->post_type) && !empty($option['post_types']) && in_array($post->post_type, $option['post_types'])) {
return true;
}
}
return false;
}
//check if current user status is set
function pmsm_mu_check_user_status($option) {
if(!empty($option['user_status'])) {
$status = is_user_logged_in();
if(($status && $option['user_status'] == 'loggedin') || (!$status && $option['user_status'] == 'loggedout')) {
return true;
}
}
return false;
}
//check if current device type is set
function pmsm_mu_check_device_type($option) {
if(!empty($option['device_type'])) {
$mobile = wp_is_mobile();
if(($mobile && $option['device_type'] == 'mobile') || (!$mobile && $option['device_type'] == 'desktop')) {
return true;
}
}
return false;
}
//custom url_to_postid() replacement - modified from https://gist.github.com/Webcreations907/ce5b77565dfb9a208738
function perfmatters_url_to_postid($url) {
if ( isset( $_GET['post'] ) && ! empty( $_GET['post'] ) && is_numeric( $_GET['post'] ) ) {
return $_GET['post'];
}
// First, check to see if there is a 'p=N' or 'page_id=N' to match against
// added post to match, even though it's above
if ( preg_match( '#[?&](p|post|page_id|attachment_id)=(\d+)#', $url, $values ) ) {
$id = absint( $values[2] );
if ( $id ) {
return $id;
}
}
// Check to see if we are using rewrite rules
$rewrite = get_option('rewrite_rules');
global $wp_rewrite;
//$rewrite = $wp_rewrite->wp_rewrite_rules();
// Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
if ( empty( $rewrite ) ) {
if ( isset( $_GET ) && ! empty( $_GET ) ) {
/************************************************************************
* ADDED: Trys checks URL for ?posttype=postname
*************************************************************************/
// Assign $url to $tempURL just incase. :)
$tempUrl = $url;
// Get rid of the #anchor
$url_split = explode( '#', $tempUrl );
$tempUrl = $url_split[0];
// Get rid of URL ?query=string
$url_query = explode( '&', $tempUrl );
$tempUrl = $url_query[0];
// Get rid of ? mark
$url_query = explode( '?', $tempUrl);
if(isset($url_query[1]) && !empty($url_query[1]) && strpos( $url_query[1], '=' )){
$url_query = explode( '=', $url_query[1] );
if(isset($url_query[0]) && isset($url_query[1])){
$args = array(
'name' => $url_query[1],
'post_type' => $url_query[0],
'showposts' => 1,
);
if ( $post = get_posts( $args ) ) {
return $post[0]->ID;
}
}
}
/************************************************************************
* END ADD
*************************************************************************/
// Add custom rules for non-rewrite URLs
foreach ( $GLOBALS['wp_post_types'] as $key => $value ) {
if ( isset( $_GET[ $key ] ) && ! empty( $_GET[ $key ] ) ) {
$args = array(
'name' => $_GET[ $key ],
'post_type' => $key,
'showposts' => 1,
);
if ( $post = get_posts( $args ) ) {
return $post[0]->ID;
}
}
}
}
}
// Get rid of the #anchor
$url_split = explode( '#', $url );
$url = $url_split[0];
// Get rid of URL ?query=string
$url_query = explode( '?', $url );
$url = $url_query[0];
// Add 'www.' if it is absent and should be there
if ( false !== strpos( home_url(), '://www.' ) && false === strpos( $url, '://www.' ) ) {
$url = str_replace( '://', '://www.', $url );
}
// Strip 'www.' if it is present and shouldn't be
if ( false === strpos( home_url(), '://www.' ) ) {
$url = str_replace( '://www.', '://', $url );
}
// Strip 'index.php/' if we're not using path info permalinks
if ( isset( $wp_rewrite ) && ! $wp_rewrite->using_index_permalinks() ) {
$url = str_replace( 'index.php/', '', $url );
}
if ( false !== strpos( $url, home_url() ) ) {
// Chop off http://domain.com
$url = str_replace( home_url(), '', $url );
} else {
// Chop off /path/to/blog
$home_path = parse_url( home_url() );
$home_path = isset( $home_path['path'] ) ? $home_path['path'] : '';
$url = str_replace( $home_path, '', $url );
}
// Trim leading and lagging slashes
$url = trim( $url, '/' );
$request = $url;
if ( empty( $request ) && ( ! isset( $_GET ) || empty( $_GET ) ) ) {
return get_option( 'page_on_front' );
}
// Look for matches.
$request_match = $request;
foreach ( (array) $rewrite as $match => $query ) {
// If the requesting file is the anchor of the match, prepend it
// to the path info.
if ( ! empty( $url ) && ( $url != $request ) && ( strpos( $match, $url ) === 0 ) ) {
$request_match = $url . '/' . $request;
}
if ( preg_match( "!^$match!", $request_match, $matches ) ) {
if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) {
// This is a verbose page match, let's check to be sure about it.
$page = get_page_by_path( $matches[ $varmatch[1] ] );
if ( ! $page ) {
continue;
}
$post_status_obj = get_post_status_object( $page->post_status );
if (is_object($post_status_obj) && ! $post_status_obj->public && ! $post_status_obj->protected
&& ! $post_status_obj->private && $post_status_obj->exclude_from_search ) {
continue;
}
}
// Got a match.
// Trim the query of everything up to the '?'.
$query = preg_replace( "!^.+\?!", '', $query );
// Substitute the substring matches into the query.
$query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
// Filter out non-public query vars
global $wp;
parse_str( $query, $query_vars );
$query = array();
foreach ( (array) $query_vars as $key => $value ) {
if ( in_array( $key, $wp->public_query_vars ) ) {
$query[ $key ] = $value;
}
}
/************************************************************************
* ADDED: $GLOBALS['wp_post_types'] doesn't seem to have custom postypes
* Trying below to find posttypes in $rewrite rules
*************************************************************************/
// PostType Array
$custom_post_type = false;
$post_types = array();
foreach ($rewrite as $key => $value) {
if(!is_string($value)) {
continue;
}
if(preg_match('/post_type=([^&]+)/i', $value, $matched)){
if(isset($matched[1]) && !in_array($matched[1], $post_types)){
$post_types[] = $matched[1];
}
}
}
foreach ((array) $query_vars as $key => $value) {
if(in_array($key, $post_types)){
$custom_post_type = true;
$query['post_type'] = $key;
$query['postname'] = $value;
}
}
/************************************************************************
* END ADD
*************************************************************************/
// Taken from class-wp.php
if(!empty($GLOBALS['wp_post_types'])) {
foreach ( $GLOBALS['wp_post_types'] as $post_type => $t ) {
if ( $t->query_var ) {
$post_type_query_vars[ $t->query_var ] = $post_type;
}
}
}
foreach ( $wp->public_query_vars as $wpvar ) {
if ( isset( $wp->extra_query_vars[ $wpvar ] ) ) {
$query[ $wpvar ] = $wp->extra_query_vars[ $wpvar ];
} elseif ( isset( $_POST[ $wpvar ] ) ) {
$query[ $wpvar ] = $_POST[ $wpvar ];
} elseif ( isset( $_GET[ $wpvar ] ) ) {
$query[ $wpvar ] = $_GET[ $wpvar ];
} elseif ( isset( $query_vars[ $wpvar ] ) ) {
$query[ $wpvar ] = $query_vars[ $wpvar ];
}
if ( ! empty( $query[ $wpvar ] ) ) {
if ( ! is_array( $query[ $wpvar ] ) ) {
$query[ $wpvar ] = (string) $query[ $wpvar ];
} else {
foreach ( $query[ $wpvar ] as $vkey => $v ) {
if ( ! is_object( $v ) ) {
$query[ $wpvar ][ $vkey ] = (string) $v;
}
}
}
if ( isset( $post_type_query_vars[ $wpvar ] ) ) {
$query['post_type'] = $post_type_query_vars[ $wpvar ];
$query['name'] = $query[ $wpvar ];
}
}
}
// Do the query
if ( isset( $query['pagename'] ) && ! empty( $query['pagename'] ) ) {
$args = array(
'name' => $query['pagename'],
'post_type' => array('post','page'), // Added post for custom permalink eg postname
'showposts' => 1,
);
if ( $post = get_posts( $args ) ) {
return $post[0]->ID;
}
}
$query = new WP_Query( $query );
if ( ! empty( $query->posts ) && $query->is_singular ) {
return $query->post->ID;
} else {
/************************************************************************
* Added:
* $query->is_singular seems to not be set on custom post types, trying
* below. Made it this far without return, worth a try? :)
*************************************************************************/
if(!empty( $query->posts ) && isset($query->post->ID) && $custom_post_type == true){
return $query->post->ID;
}
/************************************************************************
* Will match posttype when query_var => true, will not work with
* custom query_var set ie query_var => 'acme_books'.
*************************************************************************/
if(isset($post_types)) {
foreach($rewrite as $key => $value) {
if(!is_string($value)) {
continue;
}
if(preg_match('/\?([^&]+)=([^&]+)/i', $value, $matched)) {
if(isset($matched[1]) && !in_array($matched[1], $post_types) && array_key_exists($matched[1], $query_vars)) {
$post_types[] = $matched[1];
$args = array(
'name' => $query_vars[$matched[1]],
'post_type' => $matched[1],
'showposts' => 1,
);
if($post = get_posts($args)) {
return $post[0]->ID;
}
}
elseif(isset($matched[1]) && in_array($matched[1], $post_types) && !empty($query_vars['name'])) {
$args = array(
'name' => $query_vars['name'],
'post_type' => $matched[1],
'showposts' => 1,
);
if($post = get_posts($args)) {
return $post[0]->ID;
}
}
}
}
}
/************************************************************************
* END ADD
*************************************************************************/
return 0;
}
}
}
return 0;
}

View File

@@ -0,0 +1,443 @@
<?php
//security check
if(!function_exists('wp_get_current_user') || !current_user_can('manage_options') || is_admin() || !isset($_GET['perfmatters']) || !perfmatters_network_access() || perfmatters_is_page_builder()) {
return;
}
global $pmsm_print_flag;
//script manager already printed
if($pmsm_print_flag) {
return;
}
$pmsm_print_flag = true;
//set variables
global $perfmatters_tools;
global $wp;
global $wp_scripts;
global $wp_styles;
global $perfmatters_options;
global $currentID;
$currentID = perfmatters_get_current_ID();
$pmsm_tab = !empty($_POST['tab']) ? $_POST['tab'] : 'main';
//filter language locale for script manager ui
switch_to_locale(apply_filters('perfmatters_script_manager_locale', ''));
//process settings form
if(isset($_POST['pmsm_save_settings'])) {
//validate settings nonce
if(!isset($_POST['perfmatters_script_manager_settings_nonce']) || !wp_verify_nonce($_POST['perfmatters_script_manager_settings_nonce'], 'perfmatter_script_manager_save_settings')) {
print 'Sorry, your nonce did not verify.';
exit;
}
else {
//update settings
update_option('perfmatters_script_manager_settings', (!empty($_POST['perfmatters_script_manager_settings']) ? $_POST['perfmatters_script_manager_settings'] : ''));
}
}
//manually closed disclaimer
if(isset($_POST['pmsm_disclaimer_close'])) {
if(isset($_POST['pmsm_disclaimer_close_nonce']) && wp_verify_nonce($_POST['pmsm_disclaimer_close_nonce'], 'pmsm_disclaimer_close')) {
$settings = get_option('perfmatters_script_manager_settings');
if(empty($settings) || !is_array($settings)) {
$settings = array();
}
$settings['hide_disclaimer'] = 1;
update_option('perfmatters_script_manager_settings', $settings);
}
}
//process reset form
if(isset($_POST['perfmatters_script_manager_settings_reset'])) {
delete_option('perfmatters_script_manager');
delete_option('perfmatters_script_manager_settings');
}
//global trash
if(isset($_POST['pmsm_global_trash'])) {
$trash = explode("|", $_POST['pmsm_global_trash']);
if(count($trash) == 4) {
list($category, $type, $script, $detail) = $trash;
$options = get_option('perfmatters_script_manager');
unset($options[$category][$type][$script][$detail]);
if($category == 'disabled' && $detail == 'everywhere') {
unset($options['enabled'][$type][$script]);
}
//clean up the options array before saving
perfmatters_script_manager_filter_options($options);
update_option('perfmatters_script_manager', $options);
}
}
//global refresh
if(isset($_POST['pmsm_global_refresh'])) {
$refresh = explode("|", $_POST['pmsm_global_refresh']);
if(count($refresh) == 3) {
list($category, $type, $script) = $refresh;
$options = get_option('perfmatters_script_manager');
$current = $options[$category][$type][$script]['current'] ?? array();
foreach($current as $key => $post_id) {
if(!get_post_status($post_id)) {
unset($options[$category][$type][$script]['current'][$key]);
}
}
//clean up the options array before saving
perfmatters_script_manager_filter_options($options);
update_option('perfmatters_script_manager', $options);
}
}
//load script manager settings
global $perfmatters_script_manager_settings;
$perfmatters_script_manager_settings = get_option('perfmatters_script_manager_settings');
//build array of existing plugin disables
global $perfmatters_disables;
$perfmatters_disables = array();
if(!empty($perfmatters_options['disable_google_maps'])) {
$perfmatters_disables[] = 'maps.google.com';
$perfmatters_disables[] = 'maps.googleapis.com';
$perfmatters_disables[] = 'maps.gstatic.com';
}
if(!empty($perfmatters_options['disable_google_fonts'])) {
$perfmatters_disables[] = 'fonts.googleapis.com';
}
//setup filters array
global $perfmatters_filters;
$perfmatters_filters = array(
"js" => array (
"title" => "JS",
"scripts" => $wp_scripts
),
"css" => array(
"title" => "CSS",
"scripts" => $wp_styles
)
);
//load script manager options
global $perfmatters_script_manager_options;
$perfmatters_script_manager_options = get_option('perfmatters_script_manager');
//load styles
include('script_manager_css.php');
//disable shortcodes
remove_all_shortcodes();
//wrapper
echo "<div id='perfmatters-script-manager-wrapper'>";
//header
echo "<div id='perfmatters-script-manager-header'>";
echo "<div id='pmsm-header-hero'>";
//menu toggle
echo "<span id='pmsm-menu-toggle'><span class='dashicons dashicons-menu'></span></span>";
//logo
echo "<img src='" . plugins_url('img/logo.svg', dirname(__FILE__)) . "' title='Perfmatters' id='perfmatters-logo' />";
echo "</div>";
//main navigation
echo "<form method='POST'>";
echo "<div id='perfmatters-script-manager-tabs'>";
echo "<button name='tab' value=''" . ($pmsm_tab == 'main' ? " class='active'" : "") . " title='" . __('Script Manager', 'perfmatters') . "'><span class='dashicons dashicons-admin-settings'></span>" . __('Script Manager', 'perfmatters') . "</button>";
echo "<button name='tab' value='global'" . ($pmsm_tab == 'global' ? " class='active'" : "") . " title='" . __('Global View', 'perfmatters') . "'><span class='dashicons dashicons-admin-site'></span>" . __('Global View', 'perfmatters') . "</button>";
echo "<button name='tab' value='settings'" . ($pmsm_tab == 'settings' ? " class='active'" : "") . " title='" . __('Settings', 'perfmatters') . "'><span class='dashicons dashicons-admin-generic'></span>" . __('Settings', 'perfmatters') . "</button>";
echo "</div>";
echo "</form>";
echo "</div>";
//main container
echo "<div id='perfmatters-script-manager'>";
//visible container
echo "<div id='pmsm-viewport'>";
echo '<div id="pmsm-notices">';
//disclaimer
if(empty($perfmatters_script_manager_settings['hide_disclaimer'])) {
echo '<div class="pmsm-notice">';
echo '<form method="POST">';
echo $pmsm_tab != 'main' ? '<input type="hidden" name="tab" value="' . $pmsm_tab . '" />' : '';
wp_nonce_field('pmsm_disclaimer_close', 'pmsm_disclaimer_close_nonce');
echo '<button type="submit" id="pmsm-disclaimer-close" name="pmsm_disclaimer_close"/><span class="dashicons dashicons-dismiss"></span></button>';
echo '</form>';
_e('We recommend testing Script Manager changes on a staging/dev site first, as you could break your site\'s appearance.', 'perfmatters');
echo ' <a href="https://perfmatters.io/docs/disable-scripts-per-post-page/" target="_blank">' . __('View Documentation', 'perfmatters') . '</a>';
echo '</div>';
}
//testing mode
if(!empty($perfmatters_script_manager_settings['testing_mode'])) {
echo '<div class="pmsm-notice pmsm-notice-warning">' . __('You are in Testing Mode. Changes will only be visible to logged-in admins.') . '</div>';
}
echo '</div>';
//universal form
echo "<form method='POST' id='pmsm-" . $pmsm_tab . "-form'>";
//content container
echo "<div id='perfmatters-script-manager-container'>";
//main tab
if($pmsm_tab == 'main') {
//title bar
echo "<div class='perfmatters-script-manager-title-bar'>";
echo "<h1>" . __('Script Manager', 'perfmatters') . "</h1>";
echo "<p>" . __('Manage scripts loading on the current page.', 'perfmatters') . "</p>";
echo "</div>";
//load master array
global $master_array;
$master_array = perfmatters_script_manager_load_master_array();
//print scripts
foreach($master_array['resources'] as $category => $groups) {
echo '<div class="pmsm-category-container">';
if(!empty($groups)) {
echo "<h3>" . $category . "</h3>";
if($category != "misc") {
echo "<div style='background: #ffffff; padding: 10px;'>";
foreach($groups as $group => $details) {
echo "<div class='perfmatters-script-manager-group'>";
echo "<div class='pmsm-group-heading'>";
echo "<h4>" . (!empty($details['name']) ? $details['name'] : "") . "</h4>";
//Status
echo "<div class='perfmatters-script-manager-status' style='display: flex; align-items: center; white-space: nowrap; margin-left: 10px;'>";
if(!empty($details['size'])) {
echo "<span class='pmsm-group-tag pmsm-group-size'>" . __('Total size', 'perfmatters') . ": " . round($details['size'] / 1024, 1) . " KB</span>";
}
perfmatters_script_manager_print_status($category, $group);
echo "</div>";
echo "</div>";
$assets = !empty($details['assets']) ? $details['assets'] : false;
perfmatters_script_manager_print_section($category, $group, $assets);
echo "</div>";
}
echo "</div>";
}
else {
if(!empty($groups['assets'])) {
perfmatters_script_manager_print_section($category, $category, $groups['assets']);
}
}
}
echo '</div>';
}
//loading wrapper
echo "<div id='pmsm-loading-wrapper'>";
if(function_exists('is_amp_endpoint') && is_amp_endpoint()) {
echo "<span class='pmsm-loading-text'>" . __('The Script Manager does not support AMP pages.', 'perfmatters') . "</span>";
}
else {
echo "<span class='pmsm-loading-text'>" . __('Loading Scripts', 'perfmatters') . "<span class='pmsm-spinner'></span></span>";
}
echo "</div>";
}
//global view tab
elseif($pmsm_tab == 'global') {
include('script_manager_global.php');
}
//settings tab
elseif($pmsm_tab == 'settings') {
//title bar
echo "<div class='perfmatters-script-manager-title-bar'>";
echo "<h1>" . __('Settings', 'perfmatters') . "</h1>";
echo "<p>" . __('View and manage all of your Script Manager settings.', 'perfmatters') . "</p>";
echo "</div>";
//settings container
echo "<div id='script-manager-settings' class='perfmatters-script-manager-section'>";
echo "<input type='hidden' name='tab' value='settings' />";
echo "<table>";
echo "<tbody>";
echo "<tr>";
echo "<th>" . perfmatters_title(__('Display Archives', 'perfmatters'), 'separate_archives') . "</th>";
echo "<td>";
$args = array(
'id' => 'separate_archives',
'option' => 'perfmatters_script_manager_settings'
);
perfmatters_print_input($args);
echo "<div>" . __('Add WordPress archives to your Script Manager selection options. Archive posts will no longer be grouped with their post type.', 'perfmatters') . "</div>";
echo "</td>";
echo "</tr>";
echo "<tr>";
echo "<th>" . perfmatters_title(__('Display Dependencies', 'perfmatters'), 'separate_archives') . "</th>";
echo "<td>";
$args = array(
'id' => 'dependencies',
'option' => 'perfmatters_script_manager_settings'
);
perfmatters_print_input($args);
echo "<div>" . __('Show dependencies for each script.', 'perfmatters') . "</div>";
echo "</td>";
echo "</tr>";
echo "<tr>";
echo "<th>" . perfmatters_title(__('Testing Mode', 'perfmatters'), 'testing_mode') . "</th>";
echo "<td>";
$args = array(
'id' => 'testing_mode',
'option' => 'perfmatters_script_manager_settings'
);
perfmatters_print_input($args);
echo "<div>" . __('Restrict your Script Manager configuration to logged-in admins only.', 'perfmatters') . ' <a href="https://perfmatters.io/docs/testing-mode/" target="_blank">' . __('View Documentation', 'perfmatters') . '</a>' . "</div>";
echo "</td>";
echo "</tr>";
echo "<tr>";
echo "<th>" . perfmatters_title(__('MU Mode', 'perfmatters'), 'mu_mode') . "</th>";
echo "<td>";
$args = array(
'id' => 'mu_mode',
'option' => 'perfmatters_script_manager_settings'
);
perfmatters_print_input($args);
echo "<div>" . __('Must-use (MU) mode requires elevated permissions and a file to be copied into the mu-plugins directory. This gives you more control and the ability to disable plugin queries, inline CSS, etc.', 'perfmatters') . ' <a href="https://perfmatters.io/docs/mu-mode/" target="_blank">' . __('View Documentation', 'perfmatters') . '</a>' . "</div>";
echo "<div style='background: #faf3c4; padding: 10px; margin-top: 7px;'><strong>" . __('Warning', 'perfmatters') . ":</strong> " . __('Any previous plugin-level disables will now disable the entire plugin. Please review your existing Script Manager configuration before enabling this option.', 'perfmatters') . "</div>";
//mu plugin file check
if(!empty($perfmatters_script_manager_settings['mu_mode'])) {
if(file_exists(WPMU_PLUGIN_DIR . "/perfmatters_mu.php")) {
//$mu_plugins = get_mu_plugins();
if(!function_exists('get_plugin_data')) {
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
}
//get plugin data
$mu_plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . "/perfmatters_mu.php");
if(empty($mu_plugin_data['Version']) || !defined('PERFMATTERS_VERSION') || ($mu_plugin_data['Version'] != PERFMATTERS_VERSION)) {
$mu_message = __("MU plugin version mismatch.", 'perfmatters');
$mu_class = "pmsm-mu-mismatch";
}
else {
$mu_message = __("MU plugin installed.", 'perfmatters');
$mu_class = "pmsm-mu-found";
}
}
else {
$mu_message = __("MU plugin file not found.", 'perfmatters');
$mu_class = "pmsm-mu-missing";
}
echo "<div class='" . $mu_class . "'>" . $mu_message . "</div>";
}
echo "</td>";
echo "</tr>";
echo "<tr>";
echo "<th>" . perfmatters_title(__('Hide Disclaimer', 'perfmatters'), 'hide_disclaimer') . "</th>";
echo "<td>";
$args = array(
'id' => 'hide_disclaimer',
'option' => 'perfmatters_script_manager_settings'
);
perfmatters_print_input($args);
echo "<div>" . __('Hide the disclaimer message box across all Script Manager views.', 'perfmatters') . "</div>";
echo "</td>";
echo "</tr>";
echo "<tr>";
echo "<th>" . perfmatters_title(__('Reset Script Manager', 'perfmatters'), 'reset_script_manager') . "</th>";
echo "<td>";
//Reset Form
echo "<div>";
echo "<input type='submit' name='pmsm-reset' id='pmsm-reset' class='pmsm-reset' value='" . __('Reset Script Manager', 'perfmatters') . "' />";
echo "</div>";
echo "<div>";
echo "<span class='perfmatters-tooltip-text'>" . __('Remove and reset all of your existing Script Manager settings.', 'perfmatters') . "</span>";
echo "</div>";
echo "</td>";
echo "</tr>";
echo "</tbody>";
echo "</table>";
//Nonce
wp_nonce_field('perfmatter_script_manager_save_settings', 'perfmatters_script_manager_settings_nonce');
echo "</div>";
}
echo "</div>";
//toolbar
echo "<div class='perfmatters-script-manager-toolbar'>";
echo "<div class='perfmatters-script-manager-toolbar-wrapper'>";
echo "<div class='perfmatters-script-manager-toolbar-container'>";
//save button
echo "<div id='pmsm-save'>";
if($pmsm_tab != 'global') {
echo "<input type='submit' name='pmsm_save_" . $pmsm_tab . "' value='" . __('Save', 'perfmatters') . "' />";
echo "<span class='pmsm-spinner'></span>";
}
echo "</div>";
//copyright
echo "<div class='pmsm-copyright'>© " . date("Y") . " Perfmatters</div>";
echo "</div>";
//message
echo "<div id='pmsm-message' class='pmsm-message'></div>";
echo "</div>";
echo "</div>";
echo "</form>";
echo "</div>";
//hidden reset form
if($pmsm_tab == 'settings') {
echo "<form method='POST' id='pmsm-reset-form' pmsm-confirm=\"" . __('Are you sure? This will remove and reset all of your existing Script Manager settings and cannot be undone!') . "\">";
echo "<input type='hidden' name='tab' value='settings' />";
echo "<input type='hidden' name='perfmatters_script_manager_settings_reset' class='pmsm-reset' value='submit' />";
echo "</form>";
}
echo "</div>";
echo "</div>";

View File

@@ -0,0 +1,846 @@
<?php
echo "<style>
html, body {
overflow: hidden !important;
}
#wpadminbar, #wpadminbar .ab-sub-wrapper, #wpadminbar ul, #wpadminbar ul li {
z-index: 2147483647;
}
#perfmatters-script-manager-wrapper {
display: flex;
position: fixed;
z-index: 2147483646;
top: 32px;
bottom: 0px;
left: 0px;
right: 0px;
background: rgba(0,0,0,0.5);
overflow-y: auto;
}
#perfmatters-script-manager {
display: flex;
flex-grow: 1;
position: relative;
background: #EEF2F5;
font-size: 14px;
line-height: 1.5em;
color: #4a545a;
min-height: 100%;
box-sizing: border-box;
overflow: hidden;
}
#perfmatters-script-manager-wrapper * {
font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
}
#perfmatters-script-manager-wrapper .dashicons {
font-family: dashicons;
}
#perfmatters-script-manager a {
color: #4A89DD;
text-decoration: none;
border: none;
}
#perfmatters-script-manager label {
float: none;
opacity: 1;
}
#perfmatters-script-manager-header {
width: 270px;
min-width: 270px;
background: #282E34;
}
#pmsm-header-hero {
display: flex;
}
#pmsm-menu-toggle {
cursor: pointer;
}
#pmsm-header-hero .dashicons-menu {
margin: 20px;
color: #ffffff;
}
#pmsm-header-hero #perfmatters-logo {
display: block;
width: 150px;
}
#perfmatters-script-manager-header h2 {
font-size: 24px;
margin: 0px 0px 10px 0px;
color: #4a545a;
font-weight: bold;
}
#perfmatters-script-manager-header h2 span {
background: #ED5464;
color: #ffffff;
padding: 5px;
vertical-align: middle;
font-size: 10px;
margin-left: 5px;
}
#perfmatters-script-manager-header p {
font-size: 14px;
color: #4a545a;
font-style: italic;
margin: 0px auto 15px auto;
}
#perfmatters-script-manager-close {
position: absolute;
top: 0px;
right: 0px;
height: 26px;
width: 26px;
}
#perfmatters-script-manager-close img {
height: 26px;
width: 26px;
}
#perfmatters-script-manager-tabs button {
display: flex;
align-items: center;
float: left;
padding: 15px 20px;
width: 100%;
font-size: 17px;
line-height: normal;
text-align: left;
background: transparent;
color: #ffffff;
font-weight: normal;
border: none;
cursor: pointer;
border-radius: 0px;
height: 60px;
text-transform: none;
text-decoration: none;
outline: none;
}
#perfmatters-script-manager-tabs {
overflow: hidden;
}
#perfmatters-script-manager-tabs button span {
font: 400 20px/1 dashicons;
margin-right: 20px;
}
#perfmatters-script-manager-tabs button.active {
background: #4A89DD;
color: #ffffff;
}
#perfmatters-script-manager-tabs button:hover {
background: #2f373f;
color: #ffffff;
}
#perfmatters-script-manager-tabs button.active:hover {
background: #4A89DD;
color: #ffffff;
}
#perfmatters-script-manager-header.pmsm-header-minimal {
width: 60px;
min-width: 60px;
}
#perfmatters-script-manager-header.pmsm-header-minimal #perfmatters-logo {
display: none;
}
#perfmatters-script-manager-header.pmsm-header-expanded {
height: auto;
}
#pmsm-viewport {
width: 100%;
overflow-y: scroll;
padding: 20px 20px 0px 20px;
}
.pmsm-notice {
background: #ffffff;
padding: 10px;
margin: 0px 0px 10px 0px;
border-radius: 3px;
border-left: 4px solid #4A89DD;
}
.pmsm-notice-warning {
border-color: #FFC14E;
}
#pmsm-disclaimer-close {
float: right;
padding: 0px;
width: 30px;
height: 30px;
line-height: 30px;
font-weight: bold;
background: none;
color: #787c82
}
#pmsm-disclaimer-close:hover {
color: #d63638;
}
#pmsm-notices .pmsm-notice:last-child {
margin-bottom: 20px;
}
#perfmatters-script-manager-container {
position: relative;
max-width: 1000px;
margin: 0px auto;
}
#perfmatters-script-manager-container .perfmatters-script-manager-title-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
#perfmatters-script-manager-container .perfmatters-script-manager-title-bar h1 {
font-size: 24px;
line-height: normal;
font-weight: 400;
margin: 0px;
color: #282E34;
}
#perfmatters-script-manager-container .perfmatters-script-manager-title-bar p {
margin: 0px;
color: #282E34;
font-size: 13px;
}
.pmsm-category-container {
border-radius: 5px;
margin-top: 20px;
overflow: hidden;
}
#perfmatters-script-manager h3 {
padding: 10px;
margin: 0px;
font-size: 16px;
background: #282E34;
color: #ffffff;
text-transform: capitalize;
font-weight: 400;
}
.perfmatters-script-manager-group {
box-shadow: 0 1px 2px 0 rgba(40,46,52,.1);
margin: 0px 0px 10px 0px;
}
.perfmatters-script-manager-group:last-of-type {
margin-bottom: 0px;
}
.pmsm-group-heading {
display: flex;
justify-content: space-between;
align-items: center;
/*height: 40px;*/
padding: 10px;
background: #edf3f9;
color: #282E34;
font-weight: 700;
box-sizing: content-box;
}
.perfmatters-script-manager-group h4 {
display: inline-block;
margin: 0px;
padding: 0px;
font-size: 18px;
line-height: normal;
font-weight: 700;
color: #282E34;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.perfmatters-script-manager-section {
padding: 0px 10px;
background: #ffffff;
margin: 0px 0px 0px 0px;
}
.perfmatters-script-manager-assets-disabled {
padding: 0px 0px 10px 0px;
}
#perfmatters-script-manager table {
table-layout: fixed;
width: 100%;
margin: 0px;
padding: 0px;
border: none;
text-align: left;
font-size: 14px;
border-collapse: collapse;
}
#perfmatters-script-manager table thead {
background: none;
color: #282E34;
font-weight: bold;
border: none;
}
#perfmatters-script-manager table thead th {
font-size: 12px;
padding: 5px;
vertical-align: middle;
border: none;
background: #ffffff;
color: #4a545a;
width: auto;
opacity: 0.75;
}
#perfmatters-script-manager .pmsm-column-status {
width: 80px;
}
#perfmatters-script-manager .pmsm-column-type, #perfmatters-script-manager .pmsm-column-size {
width: 100px;
text-align: center;
}
#perfmatters-script-manager table tr {
border: none;
border-bottom: 1px solid #EDF3F9;
background: #ffffff;
box-sizing: content-box;
}
#perfmatters-script-manager table tbody tr:last-child {
border-bottom: 0px;
}
#perfmatters-script-manager table td {
padding: 8px 5px;
border: none;
vertical-align: top;
font-size: 14px;
background: #ffffff;
color: #4a545a;
width: auto;
}
#perfmatters-script-manager table td.perfmatters-script-manager-type {
font-size: 14px;
text-align: center;
padding-top: 16px;
text-transform: uppercase;
}
#perfmatters-script-manager .pmsm-script-type-js .pmsm-tag {
background: #FFC14E;
color: #222;
}
#perfmatters-script-manager .pmsm-script-type-css .pmsm-tag {
background: #536AD4;
color: #fff;
}
#perfmatters-script-manager table td.perfmatters-script-manager-size {
font-size: 14px;
text-align: center;
padding-top: 16px;
}
#perfmatters-script-manager table td.perfmatters-script-manager-script a {
white-space: nowrap;
}
#perfmatters-script-manager .perfmatters-script-manager-script .pmsm-script-handle {
display: block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
font-size: 14px;
font-weight: bold;
margin-bottom: 3px;
}
#perfmatters-script-manager .perfmatters-script-manager-script a {
display: block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
font-size: 10px;
color: #4A89DD;
line-height: normal;
}
#perfmatters-script-manager .perfmatters-script-manager-disable, #perfmatters-script-manager .perfmatters-script-manager-enable {
margin: 10px 0px 0px 0px;
}
#perfmatters-script-manager table .perfmatters-script-manager-disable *:after, #perfmatters-script-manager table .perfmatters-script-manager-disable *:before {
display: none;
}
#perfmatters-script-manager select {
display: inline-block;
position: relative;
height: auto;
width: auto;
background: #ffffff;
background-color: #ffffff;
padding: 7px 10px;
margin: 0px;
font-size: 14px;
appearance: menulist;
}
#perfmatters-script-manager select.perfmatters-disable-select, #perfmatters-script-manager select.perfmatters-status-select {
border: 2px solid #27ae60;
}
#perfmatters-script-manager select.perfmatters-disable-select.everywhere, #perfmatters-script-manager select.perfmatters-status-select.disabled {
border: 2px solid #ED5464;
}
#perfmatters-script-manager select.perfmatters-disable-select.current {
border: 2px solid #f1c40f;
}
#perfmatters-script-manager select.perfmatters-disable-select.hide {
display: none;
}
#perfmatters-script-manager .perfmatters-script-manager-enable-placeholder {
color: #bbbbbb;
font-style: italic;
font-size: 14px;
}
#perfmatters-script-manager input[type='radio'] {
position: static;
display: inline-block;
visibility: visible;
margin: 0px 3px 0px 0px;
vertical-align: middle;
opacity: 1;
z-index: 0;
appearance: radio;
vertical-align: baseline;
height: auto;
width: auto;
font-size: 16px;
}
#perfmatters-script-manager input[type='radio']:after {
display: none;
}
#perfmatters-script-manager .perfmatters-script-manager-controls .pmsm-checkbox-container {
display: inline;
}
#perfmatters-script-manager input[type='checkbox'] {
position: static;
display: inline-block;
visibility: visible;
margin: 0px 3px 0px 0px;
vertical-align: middle;
opacity: 1;
z-index: 0;
appearance: checkbox;
vertical-align: baseline;
height: auto;
width: auto;
font-size: 16px;
outline: none;
transform: none;
}
#perfmatters-script-manager input[type='checkbox']:before, #perfmatters-script-manager input[type='checkbox']:after {
display: none;
}
#perfmatters-script-manager .perfmatters-script-manager-controls {
text-align: left;
}
#perfmatters-script-manager .pmsm-input-group {
display: flex;
padding: 3px 0px;
}
#perfmatters-script-manager .pmsm-input-group-label {
min-width: 70px;
font-size: 10px;
font-weight: bold;
margin: 0px 5px 0px 0px;
white-space: nowrap;
color: #282E34;
}
#perfmatters-script-manager .perfmatters-script-manager-controls label {
display: inline-flex;
align-items: center;
margin: 0px 10px 0px 0px;
width: auto;
font-size: 12px;
color: #282E34;
white-space: nowrap;
}
#perfmatters-script-manager .perfmatters-script-manager-controls input[type='text'], #perfmatters-script-manager .perfmatters-script-manager-controls select {
padding: 2px;
width: 100%;
background: #fff;
font-size: 12px;
outline: none;
border: 1px solid #ccc;
margin: 0px;
height: 30px;
color: #282E34;
border-radius: unset;
text-transform: none;
}
#perfmatters-script-manager .pmsm-dependencies {
font-size: 10px;
line-height: 14px;
margin-top: 6px;
}
#perfmatters-script-manager .pmsm-dependencies span {
font-weight: bold;
}
#perfmatters-script-manager .pmsm-reqs span {
color: #ED5464;
}
#perfmatters-script-manager .pmsm-group-tag {
display: inline-block;
vertical-align: top;
font-size: 12px;
background: #fff;
padding: 2px 6px;
height: 20px;
line-height: 20px;
margin: 0px 8px 0px 0px;
border-radius: 3px;
}
#perfmatters-script-manager .pmsm-tag {
display: inline-block;
font-size: 12px;
background: #fff;
padding: 2px 6px;
border-radius: 3px;
}
#perfmatters-script-manager .pmsm-mu-mode-badge {
background: #282E34;
color: #ffffff;
}
#perfmatters-script-manager .perfmatters-script-manager-toolbar {
position: sticky;
bottom: 0px;
left: 0px;
right: 0px;
padding: 0px 20px;
background: #EEF2F5;
box-sizing: content-box;
z-index: 2
}
#perfmatters-script-manager .perfmatters-script-manager-toolbar-wrapper {
position: relative;
max-width: 1000px;
margin: 0px auto;
}
#perfmatters-script-manager .perfmatters-script-manager-toolbar-container {
position: relative;
background: #EEF2F5;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0px;
z-index: 2;
}
#perfmatters-script-manager input[type='submit'] {
background: #4a89dd;
color: #ffffff;
cursor: pointer;
border: none;
font-size: 14px;
margin: 0px;
padding: 0px 20px;
height: 35px;
line-height: 35px;
font-weight: 700;
width: auto;
border-radius: 0px;
text-transform: none;
outline: none;
border-radius: 5px;
}
#perfmatters-script-manager input[type='submit']:hover {
background: #5A93E0;
}
#perfmatters-script-manager input[type='submit']:disabled {
opacity: 0.5;
cursor: default;
}
#script-manager-settings input[type='submit'] {
float: left;
}
#perfmatters-script-manager input[type='submit'].pmsm-reset {
float: none;
background: #ED5464;
height: 35px;
line-height: 35px;
padding: 0px 10px;
font-size: 12px;
margin-bottom: 5px;
}
#perfmatters-script-manager input[type='submit'].pmsm-reset:hover {
background: #c14552;
}
#perfmatters-script-manager .pmsm-message {
display: block;
visibility: hidden;
opacity: 0;
position: absolute;
bottom: 00px;
right: 10px;
background: #282E34;
color: #ffffff;
padding: 10px;
border-radius: 3px;
z-index: 1;
transition: all 500ms ease;
}
#perfmatters-script-manager .pmsm-message.pmsm-fade {
visibility: visible;
opacity: 1;
bottom: 80px;
}
/* On/Off Toggle Switch */
#perfmatters-script-manager .perfmatters-script-manager-switch {
position: relative;
display: inline-block;
width: 48px;
height: 28px;
font-size: 1px;
margin: 0px;
}
#perfmatters-script-manager .perfmatters-script-manager-switch input[type='checkbox'] {
display: block;
margin: 0px;
border: none;
outline: none;
box-shadow: none;
appearance: none;
height: 0px;
width: 0px;
}
#perfmatters-script-manager .perfmatters-script-manager-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #27ae60;
transition: .4s;
border-radius: 50px;
}
#perfmatters-script-manager .perfmatters-script-manager-slider:before {
position: absolute;
content: '';
width: 20px;
top: 4px;
right: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
#perfmatters-script-manager .perfmatters-script-manager-switch input:checked + .perfmatters-script-manager-slider {
background-color: #ED5464;
}
#perfmatters-script-manager .perfmatters-script-manager-switch input:focus + .perfmatters-script-manager-slider {
box-shadow: 0 0 1px #ED5464;
}
#perfmatters-script-manager .perfmatters-script-manager-switch input:checked + .perfmatters-script-manager-slider:before {
transform: translateX(-20px);
}
/*#perfmatters-script-manager .perfmatters-script-manager-slider:after {
content:'" . __('ON', 'perfmatters') . "';
color: white;
display: block;
position: absolute;
transform: translate(-50%,-50%);
top: 50%;
left: 27%;
font-size: 9px;
font-weight: normal;
font-family: Verdana, sans-serif;
}
#perfmatters-script-manager .perfmatters-script-manager-switch input:checked + .perfmatters-script-manager-slider:after {
left: unset;
right: 0%;
content:'" . __('OFF', 'perfmatters') . "';
}*/
#perfmatters-script-manager .perfmatters-script-manager-assets-disabled p {
margin: 20px 0px 0px 0px;
text-align: center;
font-size: 12px;
padding: 10px 0px 0px 0px;
border-top: 1px solid #f8f8f8;
}
/*Global View*/
#pmsm-global-form .perfmatters-script-manager-section tbody tr:hover td {
background: #EDF3F9;
}
.pmsm-action-button {
visibility: hidden;
}
#pmsm-global-form .perfmatters-script-manager-section tbody tr:hover .pmsm-action-button {
visibility: visible;
}
.pmsm-action-button, .pmsm-action-button:hover, .pmsm-action-button:focus, .pmsm-action-button:active {
background: none;
border: none;
padding: 0px;
margin: 0px;
height: auto;
width: auto;
line-height: normal;
}
.pmsm-action-button .dashicons-trash {
color: #ED5464;
}
.pmsm-action-button:hover .dashicons-trash {
color: #c14552;
}
.pmsm-action-button .dashicons-update {
color: orange;
}
.pmsm-action-button:hover .dashicons-update {
color: darkorange;
}
#pmsm-global-form table td {
word-wrap: break-word;
}
/*Settings View*/
#script-manager-settings table th {
width: 200px;
vertical-align: top;
padding: 8px 5px;
border: none;
}
#script-manager-settings .perfmatters-switch {
position: relative;
display: inline-block;
width: 48px;
height: 28px;
font-size: 1px;
margin-bottom: 5px;
}
#script-manager-settings .perfmatters-switch input {
display: block;
margin: 0px;
border: none;
outline: none;
box-shadow: none;
appearance: none;
height: 0px;
width: 0px;
}
#script-manager-settings .perfmatters-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 50px;
}
#script-manager-settings .perfmatters-slider:before {
position: absolute;
content: '';
height: 20px;
width: 20px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
#script-manager-settings input:checked + .perfmatters-slider {
background-color: #2196F3;
}
#script-manager-settings input:focus + .perfmatters-slider {
box-shadow: 0 0 1px #2196F3;
}
#script-manager-settings input:checked + .perfmatters-slider:before {
transform: translateX(20px);
}
.perfmatters-beta {
background: #ED5464;
color: #ffffff;
padding: 5px;
vertical-align: middle;
font-size: 10px;
margin-left: 3px;
}
#script-manager-settings .pmsm-mu-found, #script-manager-settings .pmsm-mu-missing, #script-manager-settings .pmsm-mu-mismatch {
margin-top: 7px;
font-weight: bold;
}
#script-manager-settings .pmsm-mu-found {
color: #27ae60;
}
#script-manager-settings .pmsm-mu-missing {
color: #ED5464;
}
#script-manager-settings .pmsm-mu-mismatch {
color: #F6BF27;
}
#jquery-message {
font-size: 12px;
font-style: italic;
color: #27ae60;
margin-top: 5px;
}
@media (max-width: 1000px) {
#perfmatters-script-manager-header:not(.pmsm-header-minimal) {
width: 60px;
min-width: 60px;
}
#perfmatters-script-manager-header:not(.pmsm-header-minimal) #perfmatters-logo {
display: none;
}
}
@media (max-width: 782px) {
#perfmatters-script-manager-wrapper {
top: 46px;
padding-top: 60px;
}
#perfmatters-script-manager-header {
width: 100% !important;
min-width: 100% !important;
position: absolute;
top: 0px;
z-index: 9999;
height: 60px;
overflow: hidden;
}
#pmsm-header-hero {
justify-content: space-between;
}
#perfmatters-logo {
display: block !important;
margin-right: 20px;
}
#perfmatters-script-manager-container .perfmatters-script-manager-title-bar {
flex-direction: column;
}
#perfmatters-script-manager-container .perfmatters-script-manager-title-bar h1 {
margin-bottom: 5px;
}
#perfmatters-script-manager .pmsm-column-status {
width: 90px;
}
#perfmatters-script-manager .pmsm-column-type, #perfmatters-script-manager .pmsm-column-size {
width: 70px;
}
}
#pmsm-loading-wrapper {
position: absolute;
top: 48px;
bottom: 0;
padding-top: 200px;
width: 100%;
background: rgba(255,255,255,0.75);
text-align: center;
z-index: 1;
}
#pmsm-loading-wrapper .pmsm-loading-text {
font-size: 24px;
}
.pmsm-spinner {
background: url(/wp-admin/images/wpspin_light-2x.gif) no-repeat;
background-size: 16px 16px;
display: none;
vertical-align: middle;
width: 16px;
height: 16px;
margin: 0px 0px 0px 5px;
}
#pmsm-loading-wrapper .pmsm-spinner {
display: inline-block;
background-size: 32px 32px;
width: 32px;
height: 32px;
}
#perfmatters-script-manager .pmsm-hide {
display: none;
}
.pmsm-locked {
opacity: 0.5;
}
</style>";

View File

@@ -0,0 +1,131 @@
<?php
echo '<input type="hidden" name="tab" value="global" />';
//title bar
echo '<div class="perfmatters-script-manager-title-bar">';
echo '<h1>' . __('Global View', 'perfmatters') . '</h1>';
echo '<p>' . __('This is a visual representation of the Script Manager configuration across your entire site.', 'perfmatters') . '</p>';
echo '</div>';
$options = get_option('perfmatters_script_manager');
//global scripts display
if(!empty($options)) {
foreach($options as $category => $types) {
//category header
echo '<h3>' . $category . '</h3>';
if(!empty($types)) {
$type_names = array(
'js' => 'JavaScript',
'css' => 'CSS',
'plugins' => 'Plugins'
);
echo '<div style="background: #ffffff; padding: 10px;">';
foreach($types as $type => $scripts) {
echo '<div class="perfmatters-script-manager-group">';
echo '<div class="pmsm-group-heading">';
echo '<h4>' . $type_names[$type] . '</h4>';
echo '</div>';
echo '<div class="perfmatters-script-manager-section">';
echo '<table>';
echo '<thead>';
echo '<tr>';
echo '<th>' . __('Handle', 'perfmatters') . '</th>';
echo '<th>' . __('Setting', 'perfmatters') . '</th>';
echo '<th style="width: 40px;"></th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';
if(!empty($scripts)) {
foreach($scripts as $script => $details) {
if(!empty($details)) {
foreach($details as $detail => $values) {
$contains_junk = false;
echo '<tr>';
echo '<td>' . $script . '</td>';
echo '<td>';
echo '' . $detail . '';
if($detail == 'current' || $detail == 'post_types' || $detail == 'archives') {
if(!empty($values)) {
echo ' (';
$valueString = '';
foreach($values as $key => $value) {
if($detail == 'current') {
if((int)$value !== 0) {
if($value == 'pmsm-404') {
$valueString.= '404, ';
}
else {
$permalink = get_permalink($value);
if($permalink) {
$valueString.= '<a href="' . $permalink . '" title="' . get_the_title($value) . '" target="_blank">' . $value . '</a>, ';
}
else {
$valueString.= '<a style="color: orange; text-decoration: line-through;">' . $value . '</a>, ';
$contains_junk = true;
}
}
}
else {
$valueString.= '<a href="' . get_home_url() . '" target="_blank">homepage</a>, ';
}
}
else {
$valueString.= $value . ', ';
}
}
echo rtrim($valueString, ", ");
echo ')';
}
}
elseif($detail !== 'everywhere') {
echo ' (' . $values . ')';
}
echo '</td>';
echo '<td style="text-align: right;">';
//refresh button
if($contains_junk) {
echo '<button class="pmsm-action-button" name="pmsm_global_refresh" title="Refresh" value="' . $category . '|' . $type . '|' . $script . '">';
echo '<span class="dashicons dashicons-update"></span>';
echo '</button>';
}
//trash button
echo '<button class="pmsm-action-button" name="pmsm_global_trash" title="Delete" value="' . $category . '|' . $type . '|' . $script . '|' . $detail . '" onClick="return confirm(\'Are you sure you want to delete this option?\');">';
echo '<span class="dashicons dashicons-trash"></span>';
echo '</button>';
echo '</td>';
echo '</tr>';
}
}
}
}
echo '</tbody>';
echo '</table>';
echo '</div>';
echo '</div>';
}
echo '</div>';
}
}
}
else {
echo '<div class="perfmatters-script-manager-section">';
echo '<p style="padding: 20px; text-align: center;">' . __("You don't have any scripts disabled yet.", 'perfmatters') . '</p>';
echo '</div>';
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,45 @@
<?php
echo '<div class="perfmatters-settings-section">';
//page title
echo '<h2><span class="dashicons dashicons-editor-help"></span>' . __("Support", "novashare") . '</h2>';
echo '<div style="margin-bottom: 20px;"></div>'; //spacer
//documentation
echo '<h2>' . __('Documentation', 'perfmatters') . '</h2>';
echo '<div class="form-table">';
echo '<div style="margin: 1em auto;">' . __('Need help? Check out our in-depth documentation. Every feature has a step-by-step walkthrough.', 'perfmatters') . '</div>';
echo '<a class="button-secondary" href="https://perfmatters.io/docs/?utm_source=perfmatters&utm_medium=support-page&utm_campaign=documentation-cta" target="_blank">' . __('Documentation', 'perfmatters') . '</a>';
echo '</div>';
echo '</div>';
//contact us
echo '<div class="perfmatters-settings-section">';
echo '<h2>' . __('Contact Us', 'perfmatters') . '</h2>';
echo '<div class="form-table">';
echo '<div style="margin: 1em auto;">' . __('If you have questions or problems, please send us a message. Well get back to you as soon as possible.', 'perfmatters') . '</div>';
echo '<a class="button-secondary" href="https://perfmatters.io/contact/?utm_source=perfmatters&utm_medium=support-page&utm_campaign=contact-us-cta" target="_blank">' . __('Contact Us', 'perfmatters') . '</a>';
echo '</div>';
echo '</div>';
//faq
echo '<div class="perfmatters-settings-section">';
echo '<h2>' . __('Frequently Asked Questions', 'perfmatters') . '</h2>';
echo '<div class="form-table" style="display: inline-flex; flex-wrap: wrap;">';
$faq_utm = '?utm_source=perfmatters&utm_medium=support-page&utm_campaign=faq';
echo '<ul style="margin-right: 40px;">';
echo '<li><a href="https://perfmatters.io/docs/how-to-install-perfmatters/' . $faq_utm . '" target="_blank">' . __('How do I license activate the plugin?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/docs/update-perfmatters-plugin/' . $faq_utm . '" target="_blank">' . __('How do I update the plugin?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/docs/upgrade-license/' . $faq_utm . '" target="_blank">' . __('How do I upgrade my license?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/docs/changelog/' . $faq_utm . '" target="_blank">' . __('Where can I view the changelog?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/affiliate-program/' . $faq_utm . '" target="_blank">' . __('Where can I sign up for the affiliate program?', 'perfmatters') . '</a></li>';
echo '</ul>';
echo '<ul>';
echo '<li><a href="https://perfmatters.io/docs/disable-scripts-per-post-page/' . $faq_utm . '" target="_blank">' . __('How do I disable scripts on a per post/page basis?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/docs/delay-javascript/' . $faq_utm . '" target="_blank">' . __('How do I delay JavaScript?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/docs/remove-unused-css/' . $faq_utm . '" target="_blank">' . __('How do I remove unused CSS?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/docs/lazy-load-wordpress/' . $faq_utm . '" target="_blank">' . __('How do I lazy load images and videos?', 'perfmatters') . '</a></li>';
echo '<li><a href="https://perfmatters.io/docs/local-analytics/' . $faq_utm . '" target="_blank">' . __('How do I host Google Analytics locally?', 'perfmatters') . '</a></li>';
echo '</ul>';
echo '</div>';
echo '</div>';