This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,92 @@
<?php
/**
* Abstract class for an integration module: class Abstract_Integration
*
* @since 2.9.0
* @package Smush\Core\Modules\Integrations
*/
namespace Smush\Core\Integrations;
use Smush\Core\Settings;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Abstract_Integration
*/
abstract class Abstract_Integration {
/**
* Module slug.
*
* @var string $module
*/
protected $module;
/**
* Module class - free module by default, can be pro.
*
* @var string $class Accepts: 'free', 'pro'.
*/
protected $class = 'free';
/**
* Module status.
*
* @var bool $enabled
*/
protected $enabled = false;
/**
* Settings class instance for easier access.
*
* @since 3.0
*
* @var Settings
*/
protected $settings;
/**
* Abstract_Integration constructor.
*/
public function __construct() {
$this->settings = Settings::get_instance();
// Filters the setting variable to add module setting title and description.
add_filter( 'wp_smush_settings', array( $this, 'register' ) );
// Disable setting.
add_filter( 'wp_smush_integration_status_' . $this->module, array( $this, 'setting_status' ) );
// Show submit button if one of the integrations available.
add_filter( 'wp_smush_integration_show_submit', array( $this, 'enable_submit_button' ) );
}
/**
* Update setting status - disable module functionality if not enabled.
*
* @since 2.8.1
*
* @return bool
*/
public function setting_status() {
return ! $this->enabled;
}
/**
* Whether to enable the submit button or not.
*
* @since 3.9.8
*
* @param bool $enabled Current status.
*
* @return bool
*/
public function enable_submit_button( $enabled ) {
return $enabled || $this->enabled;
}
}

View File

@ -0,0 +1,96 @@
<?php
/**
* Avada integration module.
*
* @since 3.3.0
* @package Smush\Core\Integrations
*/
namespace Smush\Core\Integrations;
use Smush\Core\Modules\CDN;
use Smush\Core\Modules\Helpers\Parser;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Avada
*/
class Avada {
/**
* CDN module instance.
*
* @var CDN $cdn
*/
private $cdn;
/**
* Avada constructor.
*
* @since 3.3.0
*
* @param CDN $cdn CDN module.
*/
public function __construct( CDN $cdn ) {
if ( $cdn->is_active() ) {
$this->cdn = $cdn;
add_filter( 'smush_cdn_bg_image_tag', array( $this, 'replace_cdn_links' ) );
if ( defined( 'FUSION_BUILDER_PLUGIN_DIR' ) ) {
add_filter( 'smush_after_process_background_images', array( $this, 'smush_cdn_image_replaced' ), 10, 3 );
}
}
}
/**
* Replace all the image src with cdn link.
*
* @param string $content Content of the current post.
* @param string $image Backround Image tag without src.
* @param string $img_src Image src.
* @return string
*/
public function smush_cdn_image_replaced( $content, $image, $img_src ) {
if ( $this->cdn->is_supported_path( $img_src ) ) {
$new_src = $this->cdn->generate_cdn_url( $img_src );
if ( $new_src ) {
$content = str_replace( $img_src, $new_src, $content );
}
}
return $content;
}
/**
* Replace images from data-bg-url with CDN links.
*
* @since 3.3.0
*
* @param string $img Image.
*
* @return string
*/
public function replace_cdn_links( $img ) {
$image_src = Parser::get_attribute( $img, 'data-bg-url' );
if ( $image_src ) {
// Store the original source to be used later on.
$original_src = $image_src;
// Replace the data-bg-url of the image with CDN link.
if ( $this->cdn->is_supported_path( $image_src ) ) {
$image_src = $this->cdn->generate_cdn_url( $image_src );
if ( $image_src ) {
$img = preg_replace( '#(data-bg-url=["|\'])' . $original_src . '(["|\'])#i', '\1' . $image_src . '\2', $img, 1 );
}
}
}
return $img;
}
}

View File

@ -0,0 +1,676 @@
<?php
/**
* Smush integration with various plugins: Common class
*
* @package Smush\Core\Integrations
* @since 2.8.0
*
* @author Anton Vanyukov <anton@incsub.com>
*
* @copyright (c) 2018, Incsub (http://incsub.com)
*/
namespace Smush\Core\Integrations;
use Smush\Core\Helper;
use Smush\Core\Modules\Helpers\Parser;
use Smush\Core\Modules\Smush;
use WP_Smush;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Singleton class class Common
*
* @since 2.8.0
*/
class Common {
/**
* Common constructor.
*/
public function __construct() {
if ( is_admin() ) {
// AJAX Thumbnail Rebuild integration.
add_filter( 'wp_smush_media_image', array( $this, 'skip_images' ), 10, 2 );
// Optimise WP retina 2x images.
add_action( 'wr2x_retina_file_added', array( $this, 'smush_retina_image' ), 20, 3 );
// Remove any pre_get_posts_filters added by WP Media Folder plugin.
add_action( 'wp_smush_remove_filters', array( $this, 'remove_filters' ) );
}
// WPML integration.
add_action( 'wpml_updated_attached_file', array( $this, 'wpml_undo_ignore_attachment' ) );
add_action( 'wpml_after_duplicate_attachment', array( $this, 'wpml_ignore_duplicate_attachment' ), 10, 2 );
add_action( 'wpml_after_copy_attached_file_postmeta', array( $this, 'wpml_ignore_duplicate_attachment' ), 10, 2 );
// ReCaptcha lazy load.
add_filter( 'smush_skip_iframe_from_lazy_load', array( $this, 'exclude_recaptcha_iframe' ), 10, 2 );
// Compatibility modules for lazy loading.
add_filter( 'smush_skip_image_from_lazy_load', array( $this, 'lazy_load_compat' ), 10, 3 );
// Soliloquy slider CDN support.
add_filter( 'soliloquy_image_src', array( $this, 'soliloquy_image_src' ) );
// Translate Press integration.
add_filter( 'smush_skip_image_from_lazy_load', array( $this, 'trp_translation_editor' ) );
// Jetpack CDN compatibility.
add_filter( 'smush_cdn_skip_image', array( $this, 'jetpack_cdn_compat' ), 10, 2 );
// WP Maintenance Plugin integration.
add_action( 'template_redirect', array( $this, 'wp_maintenance_mode' ) );
// WooCommerce's product gallery thumbnail CDN support.
add_filter( 'woocommerce_single_product_image_thumbnail_html', array( $this, 'woocommerce_cdn_gallery_thumbnails' ) );
// Buddyboss theme and its platform plugin integration.
add_filter( 'wp_smush_cdn_before_process_src', array( $this, 'buddyboss_platform_modify_image_src' ), 10, 2 );
// GiveWP donation form load lazyload images in iframe.
add_action( 'give_donation_form_top', array( $this, 'givewp_skip_image_lazy_load' ), 0 );
// Thumbnail regeneration handler.
add_filter( 'wp_generate_attachment_metadata', array( $this, 'maybe_handle_thumbnail_generation' ) );
}
/**
* Remove any pre_get_posts_filters added by WP Media Folder plugin.
*/
public function remove_filters() {
// Remove any filters added b WP media Folder plugin to get the all attachments.
if ( class_exists( 'Wp_Media_Folder' ) ) {
global $wp_media_folder;
if ( is_object( $wp_media_folder ) ) {
remove_filter( 'pre_get_posts', array( $wp_media_folder, 'wpmf_pre_get_posts1' ) );
remove_filter( 'pre_get_posts', array( $wp_media_folder, 'wpmf_pre_get_posts' ), 0, 1 );
}
}
global $wpml_query_filter;
// If WPML is not installed, return.
if ( ! is_object( $wpml_query_filter ) ) {
return;
}
// Remove language filter and let all the images be smushed at once.
if ( has_filter( 'posts_join', array( $wpml_query_filter, 'posts_join_filter' ) ) ) {
remove_filter( 'posts_join', array( $wpml_query_filter, 'posts_join_filter' ), 10, 2 );
remove_filter( 'posts_where', array( $wpml_query_filter, 'posts_where_filter' ), 10, 2 );
}
}
/**************************************
*
* AJAX Thumbnail Rebuild
*
* @since 2.8
*/
/**
* AJAX Thumbnail Rebuild integration.
*
* If this is a thumbnail regeneration - only continue for selected thumbs
* (no need to regenerate everything else).
*
* @since 2.8.0
*
* @param string $smush_image Image size.
* @param string $size_key Thumbnail size.
*
* @return bool
*/
public function skip_images( $smush_image, $size_key ) {
if ( empty( $_POST['regen'] ) || ! is_array( $_POST['regen'] ) ) { // Input var ok.
return $smush_image;
}
$smush_sizes = wp_unslash( $_POST['regen'] ); // Input var ok.
if ( in_array( $size_key, $smush_sizes, true ) ) {
return $smush_image;
}
// Do not regenerate other thumbnails for regenerate action.
return false;
}
/**************************************
*
* WP Retina 2x
*/
/**
* Smush Retina images for WP Retina 2x, Update Stats.
*
* @param int $id Attachment ID.
* @param string $retina_file Retina image.
* @param string $image_size Image size.
*/
public function smush_retina_image( $id, $retina_file, $image_size ) {
$smush = WP_Smush::get_instance()->core()->mod->smush;
/**
* Allows to Enable/Disable WP Retina 2x Integration
*/
$smush_retina_images = apply_filters( 'smush_retina_images', true );
// Check if Smush retina images is enabled.
if ( ! $smush_retina_images ) {
return;
}
// Check for Empty fields.
if ( empty( $id ) || empty( $retina_file ) || empty( $image_size ) ) {
return;
}
// Do not smush if auto smush is turned off.
if ( ! $smush->should_auto_smush( $id ) ) {
return;
}
/**
* Allows to skip a image from smushing
*
* @param bool $smush_image Smush image or not
* @param string $image_size Size of image being smushed
* @param string $retina_file Retina file path.
* @param int $id Attachment ID.
*
* @since 3.9.6 Add two parameters for the filter.
*/
$smush_image = apply_filters( 'wp_smush_media_image', true, $image_size, $retina_file, $id );
if ( ! $smush_image ) {
return;
}
$stats = $smush->do_smushit( $retina_file );
// If we squeezed out something, Update stats.
if ( ! is_wp_error( $stats ) && ! empty( $stats['data'] ) && isset( $stats['data'] ) && $stats['data']->bytes_saved > 0 ) {
$image_size = $image_size . '@2x';
$this->update_smush_stats_single( $id, $stats, $image_size );
}
}
/**
* Updates the smush stats for a single image size.
*
* @param int $id Attachment ID.
* @param array $smush_stats Smush stats.
* @param string $image_size Image size.
*/
private function update_smush_stats_single( $id, $smush_stats, $image_size = '' ) {
// Return, if we don't have image id or stats for it.
if ( empty( $id ) || empty( $smush_stats ) || empty( $image_size ) ) {
return;
}
$smush = WP_Smush::get_instance()->core()->mod->smush;
$data = $smush_stats['data'];
// Get existing Stats.
$stats = get_post_meta( $id, Smush::$smushed_meta_key, true );
// Update existing Stats.
if ( ! empty( $stats ) ) {
// Update stats for each size.
if ( isset( $stats['sizes'] ) ) {
// if stats for a particular size doesn't exists.
if ( empty( $stats['sizes'][ $image_size ] ) ) {
// Update size wise details.
$stats['sizes'][ $image_size ] = (object) $smush->array_fill_placeholders( $smush->get_size_signature(), (array) $data );
} else {
// Update compression percent and bytes saved for each size.
$stats['sizes'][ $image_size ]->bytes = $stats['sizes'][ $image_size ]->bytes + $data->bytes_saved;
$stats['sizes'][ $image_size ]->percent = $stats['sizes'][ $image_size ]->percent + $data->compression;
}
}
} else {
// Create new stats.
$stats = array(
'stats' => array_merge(
$smush->get_size_signature(),
array(
'api_version' => - 1,
'lossy' => - 1,
)
),
'sizes' => array(),
);
$stats['stats']['api_version'] = $data->api_version;
$stats['stats']['lossy'] = $data->lossy;
$stats['stats']['keep_exif'] = ! empty( $data->keep_exif ) ? $data->keep_exif : 0;
// Update size wise details.
$stats['sizes'][ $image_size ] = (object) $smush->array_fill_placeholders( $smush->get_size_signature(), (array) $data );
}
// Calculate the total compression.
$stats = WP_Smush::get_instance()->core()->total_compression( $stats );
update_post_meta( $id, Smush::$smushed_meta_key, $stats );
}
/**************************************
*
* WPML
*
* @since 3.0
*/
/**
* Ignore WPML duplicated images from Smush.
*
* If WPML is duplicating images, we need to mark them as ignored for Smushing
* because the image is same for all those duplicated attachment posts. This is
* required to avoid wrong Smush stats.
*
* @param int $attachment_id Original attachment ID.
* @param int $duplicated_attachment_id Duplicated attachment ID.
*
* @since 3.9.4
*/
public function wpml_ignore_duplicate_attachment( $attachment_id, $duplicated_attachment_id ) {
// Ignore the image from Smush if duplicate.
update_post_meta( $duplicated_attachment_id, 'wp-smush-ignore-bulk', true );
}
/**
* Remove an image from the ignored list.
*
* When a new image is added instead of duplicate, we need to remove it
* from the ignored list to make it available for Smushing.
*
* @param int $attachment_id Attachment ID.
*
* @since 3.9.4
*/
public function wpml_undo_ignore_attachment( $attachment_id ) {
// Delete ignore flag.
delete_post_meta( $attachment_id, 'wp-smush-ignore-bulk' );
}
/**
* Skip ReCaptcha iframes from lazy loading.
*
* @since 3.4.2
*
* @param bool $skip Should skip? Default: false.
* @param string $src Iframe url.
*
* @return bool
*/
public function exclude_recaptcha_iframe( $skip, $src ) {
return false !== strpos( $src, 'recaptcha/api' );
}
/**************************************
*
* Soliloquy slider
*
* @since 3.6.2
*/
/**
* Replace slider image links with CDN links.
*
* @param string $src Image source.
*
* @return string
*/
public function soliloquy_image_src( $src ) {
$cdn = WP_Smush::get_instance()->core()->mod->cdn;
if ( ! $cdn->get_status() || empty( $src ) ) {
return $src;
}
if ( $cdn->is_supported_path( $src ) ) {
return $cdn->generate_cdn_url( $src );
}
return $src;
}
/**************************************
*
* Translate Press
*
* @since 3.6.3
*/
/**
* Disables "Lazy Load" on Translate Press translate editor
*
* @param bool $skip Should skip? Default: false.
*
* @return bool
*/
public function trp_translation_editor( $skip ) {
if ( ! class_exists( '\TRP_Translate_Press' ) || ! isset( $_GET['trp-edit-translation'] ) ) {
return $skip;
}
return true;
}
/**************************************
*
* Jetpack
*
* @since 3.7.1
*/
/**
* Skips the url from the srcset from our CDN when it's already served by Jetpack's CDN.
*
* @since 3.7.1
*
* @param bool $skip Should skip? Default: false.
* @param string $url Source.
*
* @return bool
*/
public function jetpack_cdn_compat( $skip, $url ) {
if ( ! class_exists( '\Jetpack' ) ) {
return $skip;
}
if ( method_exists( '\Jetpack', 'is_module_active' ) && ! \Jetpack::is_module_active( 'photon' ) ) {
return $skip;
}
$parsed_url = wp_parse_url( $url );
// The image already comes from Jetpack's CDN.
if ( preg_match( '#^i[\d]{1}.wp.com$#i', $parsed_url['host'] ) ) {
return true;
}
return $skip;
}
/**************************************
*
* WP Maintenance Plugin
*
* @since 3.8.0
*/
/**
* Disable page parsing when "Maintenance" is enabled
*
* @since 3.8.0
*/
public function wp_maintenance_mode() {
if ( ! class_exists( '\MTNC' ) ) {
return;
}
global $mt_options;
if ( ! is_user_logged_in() && ! empty( $mt_options['state'] ) ) {
add_filter( 'wp_smush_should_skip_parse', '__return_true' );
}
}
/**************************************
*
* WooCommerce
*
* @since 3.9.0
*/
/**
* Replaces the product's gallery thumbnail URL with the CDN URL.
*
* WC uses a <div data-thumbnail=""> attribute to get the thumbnail
* img src which is then added via JS. Our regex for parsing the page
* doesn't check for this div and attribute (and it shouldn't, it becomes too slow).
*
* We can remove this if we ever use the filter "wp_get_attachment_image_src"
* to replace the images' src URL with the CDN one.
*
* @since 3.9.0
*
* @param string $html The thumbnail markup.
* @return string
*/
public function woocommerce_cdn_gallery_thumbnails( $html ) {
$cdn = WP_Smush::get_instance()->core()->mod->cdn;
// Replace only when the CDN is active.
if ( ! $cdn->get_status() ) {
return $html;
}
preg_match_all( '/<(div)\b(?>\s+(?:data-thumb=[\'"](?P<thumb>[^\'"]*)[\'"])|[^\s>]+|\s+)*>/is', $html, $matches );
if ( ! $matches || ! is_array( $matches ) ) {
return $html;
}
foreach ( $matches as $key => $url ) {
// Only use the match for the thumbnail URL if it's supported.
if ( 'thumb' !== $key || empty( $url[0] ) || ! $cdn->is_supported_path( $url[0] ) ) {
continue;
}
// Replace the data-thumb attribute of the div with the CDN link.
$cdn_url = $cdn->generate_cdn_url( $url[0] );
if ( $cdn_url ) {
$html = str_replace( $url[0], $cdn_url, $html );
}
}
return $html;
}
/**************************************
*
* Various modules
*
* @since 3.5
*/
/**
* Lazy loading compatibility checks.
*
* @since 3.5.0
*
* @param bool $skip Should skip? Default: false.
* @param string $src Image url.
* @param string $image Image.
*
* @return bool
*/
public function lazy_load_compat( $skip, $src, $image ) {
// Avoid conflicts if attributes are set (another plugin, for example).
if ( false !== strpos( $image, 'data-src' ) ) {
return true;
}
// Compatibility with Essential Grid lazy loading.
if ( false !== strpos( $image, 'data-lazysrc' ) ) {
return true;
}
// Compatibility with JetPack lazy loading.
if ( false !== strpos( $image, 'jetpack-lazy-image' ) ) {
return true;
}
// Compatibility with Slider Revolution's lazy loading.
if ( false !== strpos( $image, '/revslider/' ) && false !== strpos( $image, 'data-lazyload' ) ) {
return true;
}
return $skip;
}
/**
* CDN compatibility with Buddyboss platform
*
* @param string $src Image source.
* @param string $image Actual image element.
*
* @return string Original or modified image source.
*/
public function buddyboss_platform_modify_image_src( $src, $image ) {
if ( ! defined( 'BP_PLATFORM_VERSION' ) ) {
return $src;
}
/**
* Compatibility with buddyboss theme and it's platform plugin.
*
* Buddyboss platform plugin uses the placeholder image as it's main src.
* And process_src() method below uses the same placeholder.png to create
* the srcset when "Automatic resizing" options is enabled for CDN.
* ---------
* Replacing the placeholder with actual image source as early as possible.
* Checks:
* 1. The image source contains buddyboss-platform in its string
* 2. The image source contains placeholder.png and is crucial because there are other
* images as well which doesn't uses placeholder.
*/
if ( false !== strpos( $src, 'buddyboss-platform' ) && false !== strpos( $src, 'placeholder.png' ) ) {
$new_src = Parser::get_attribute( $image, 'data-src' );
if ( ! empty( $new_src ) ) {
$src = $new_src;
}
}
return $src;
}
/**
* Skip images from lazy loading on GiveWP forms.
*
* @since 3.8.8
*/
public function givewp_skip_image_lazy_load() {
add_filter( 'wp_smush_should_skip_parse', '__return_true' );
}
/**
* Add method to handle thumbnail generation.
*
* We use this trick to call self::thumbnail_regenerate_handler()
* to avoid it called several times while calling wp_generate_attachment_metadata().
* 1. wp_generate_attachment_metadata -> wp_create_image_subsizes -> wp_update_attachment_metadata().
* 2. wp_generate_attachment_metadata -> _wp_make_subsizes -> wp_update_attachment_metadata().
* 3. After calling wp_generate_attachment_metadata() => We should only add our filter here.
*
* @param array $metadata Image metadata.
* @return array The provided metadata.
*/
public function maybe_handle_thumbnail_generation( $metadata ) {
/**
* Add filter to handle thumbnail generation.
* We use a big priority because it seems WP has an issue for this case,
* after we remove this filter, all registered filters after this priority of this hook will not call.
*/
add_filter( 'wp_update_attachment_metadata', array( $this, 'thumbnail_regenerate_handler' ), 99999, 2 ); // S3 is using priority 110.
return $metadata;
}
/**
* Callback for 'wp_update_attachment_metadata' WordPress hook used by Smush to detect
* regenerated thumbnails and mark them as pending for (re)smush.
*
* @since 3.9.2
*
* @param array $new_meta New metadata.
* @param int $attachment_id The attachment ID.
*
* @since 3.9.6 Disable this filter while async uploading,
* and update compatible with S3, and only call it after generated metadata.
*
* @return array
*/
public function thumbnail_regenerate_handler( $new_meta, $attachment_id ) {
// Remove the filter as we are no longer need it.
remove_filter( 'wp_update_attachment_metadata', array( $this, 'thumbnail_regenerate_handler' ), 99999 );
/**
* Skip if there is WP uploading a new image,
* or the attachment is not an image or does not have thumbnails.
*/
if (
empty( $new_meta['sizes'] )
// Async uploading.
|| isset( $_POST['post_id'] ) || isset( $_FILES['async-upload'] )
// Smushed it, don't need to check it again.
|| did_action( 'wp_smush_before_smush_file' )
// Disable when restoring.
|| did_action( 'wp_smush_before_restore_backup' )
// Only support Image.
|| ! Helper::is_smushable( $attachment_id )
) {
return $new_meta;
}
// Skip if the attachment has an active smush operation or in being restored by Smush or ignored.
if ( get_transient( 'smush-in-progress-' . $attachment_id ) || get_transient( 'wp-smush-restore-' . $attachment_id ) || get_post_meta( $attachment_id, 'wp-smush-ignore-bulk', true ) ) {
return $new_meta;
}
$smush_meta = get_post_meta( $attachment_id, Smush::$smushed_meta_key, true );
// Skip attachments without Smush meta key.
if ( empty( $smush_meta ) ) {
return $new_meta;
}
$size_increased = false;
/**
* Get attached file
* If there is generating the image, S3 also downloaded it,
* so we don't need to download it if it doesn't exist.
*/
$attached_file = Helper::get_attached_file( $attachment_id, 'original' );// S3+.
// If the main file does not exist, there is not generating the thumbnail, return.
if ( ! file_exists( $attached_file ) ) {
return $new_meta;
}
// We need only the last $new_meta['sizes'] element of each subsequent call
// to wp_update_attachment_metadata() made by wp_create_image_subsizes().
$size = array_keys( $new_meta['sizes'] )[ count( $new_meta['sizes'] ) - 1 ];
$file_dir = dirname( $attached_file );
$file_name = $file_dir . '/' . $new_meta['sizes'][ $size ]['file'];
$actual_size = is_file( $file_name ) ? filesize( $file_name ) : false;
$stored_size = isset( $smush_meta['sizes'][ $size ]->size_after ) ? $smush_meta['sizes'][ $size ]->size_after : false;
// Only do the comparison if we have both the actual and the database stored size.
if ( $actual_size && $stored_size ) {
$size_increased = $actual_size > 1.01 * $stored_size;// Not much we can do if save less than 1%.
}
// File size increased? Let's remove all
// Smush related meta keys for this attachment.
if ( $size_increased ) {
/**
* When regenerate an image, we only generate the sub-sizes,
* so we don't need to delete the saving data of PNG2JPG.
* And similar for resizing, we also added filter for big_image_size_threshold,
* and we don't use the resizing meta for any conditional, so it's ok to keep it.
*/
// Remove stats and update cache.
WP_Smush::get_instance()->core()->remove_stats( $attachment_id );
}
return $new_meta;
}
}

View File

@ -0,0 +1,218 @@
<?php
/**
* Smush integration with WPBakery Page Builder: Composer class
*
* @package Smush\Core\Integrations
* @since 3.2.1
*
* @author Anton Vanyukov <anton@incsub.com>
*
* @copyright (c) 2018, Incsub (http://incsub.com)
*/
namespace Smush\Core\Integrations;
use WP_Smush;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Composer for WPBakery Page Builder integration.
*
* @since 3.2.1
*/
class Composer extends Abstract_Integration {
/**
* Composer constructor.
*
* @since 3.2.1
*/
public function __construct() {
$this->module = 'js_builder';
$this->class = 'free';
$this->check_for_js_builder();
parent::__construct();
// Hook at the end of setting row to output a error div.
add_action( 'smush_setting_column_right_inside', array( $this, 'additional_notice' ) );
if ( $this->settings->get( 'js_builder' ) ) {
add_filter( 'image_make_intermediate_size', array( $this, 'process_image_resize' ) );
// CDN link image handler for ajax based loading.
add_filter( 'wp_get_attachment_image_src', array( $this, 'cdn_attachment_image_src' ) );
}
}
/**************************************
*
* OVERWRITE PARENT CLASS FUNCTIONALITY
*/
/**
* Filters the setting variable to add NextGen setting title and description
*
* @since 3.2.1
*
* @param array $settings Settings.
*
* @return mixed
*/
public function register( $settings ) {
$settings[ $this->module ] = array(
'label' => esc_html__( 'Enable WPBakery Page Builder integration', 'wp-smushit' ),
'short_label' => esc_html__( 'WPBakery Page Builder', 'wp-smushit' ),
'desc' => esc_html__( 'Allow smushing images resized in WPBakery Page Builder editor.', 'wp-smushit' ),
);
return $settings;
}
/**
* Show additional notice if the required plugins are not installed.
*
* @since 3.2.1
*
* @param string $name Setting name.
*/
public function additional_notice( $name ) {
if ( $this->module === $name && ! $this->enabled ) {
?>
<div class="sui-toggle-content">
<div class="sui-notice">
<div class="sui-notice-content">
<div class="sui-notice-message">
<i class="sui-notice-icon sui-icon-info" aria-hidden="true"></i>
<p><?php esc_html_e( 'To use this feature you need be using WPBakery Page Builder.', 'wp-smushit' ); ?></p>
</div>
</div>
</div>
</div>
<?php
}
}
/**************************************
*
* PUBLIC CLASSES
*/
/**
* Check if the file source is a registered attachment and if not - Smush it.
*
* TODO: with little adjustments this can be used for all page builders.
*
* @since 3.2.1
*
* @param string $image_src Image src.
*
* @return string
*/
public function process_image_resize( $image_src ) {
$vc_editable = filter_input( INPUT_GET, 'vc_editable', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
$vc_action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_SPECIAL_CHARS );
global $pagename, $vc_manager;
/**
* There are three types of situations:
* 1. $vc_editable and $vc_action will be set in the frontend page builder
* 2. $pagename in the backend.
* 3. $vc_manager is a fallback (could possibly cause issues).
*/
if ( ( ! $vc_editable || 'vc_load_shortcode' !== $vc_action ) && ( ! isset( $pagename ) || 'page-builder' !== $pagename ) && ( ! $vc_manager || ! is_object( $vc_manager ) ) ) {
return $image_src;
}
// Save the original image source.
$vc_image = $image_src;
// Remove the [width]x[height] params from URL.
$size = array();
if ( preg_match( '/(\d+)x(\d+)\.(?:' . implode( '|', array( 'gif', 'jpg', 'jpeg', 'png' ) ) . '){1}$/i', $image_src, $size ) ) {
$image_src = str_replace( '-' . $size[1] . 'x' . $size[2], '', $image_src );
}
// Convert image src to URL.
$upload_dir = wp_get_upload_dir();
$image_url = str_replace( $upload_dir['path'], $upload_dir['url'], $image_src );
// Try to get the attachment ID.
$attachment_id = attachment_url_to_postid( $image_url );
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return $vc_image;
}
$image = image_get_intermediate_size( $attachment_id, array( $size[1], $size[2] ) );
if ( $image ) {
return $vc_image;
}
// Smush image. TODO: should we update the stats?
WP_Smush::get_instance()->core()->mod->smush->do_smushit( $vc_image );
return $vc_image;
}
/**
* Replace the image src with cdn link for all the Ajax requests.
*
* @since 3.9.10
*
* @see SMUSH-206
*
* @param array|false $image {
* Array of image data, or boolean false if no image is available.
*
* @type string $0 Image source URL.
* @type int $1 Image width in pixels.
* @type int $2 Image height in pixels.
* @type bool $3 Whether the image is a resized image.
* }
*
* @return mixed
*/
public function cdn_attachment_image_src( $image ) {
if ( ! wp_doing_ajax() ) {
return $image;
}
$cdn = WP_Smush::get_instance()->core()->mod->cdn;
if ( ! $cdn->get_status() ) {
return $image;
}
if ( is_array( $image ) && ! empty( $image[0] ) ) {
$image[0] = $cdn->generate_cdn_url( $image[0] );
}
return $image;
}
/**************************************
*
* PRIVATE CLASSES
*/
/**
* Should only be active when WPBakery Page Builder is installed.
*
* @since 3.2.1
*
* @see https://kb.wpbakery.com/docs/inner-api/vc_disable_frontend
*/
private function check_for_js_builder() {
// This function exists since WPBakery 4.0 (02.03.2014) and is listed
// on their API docs. It should be stable enough to rely on it.
$this->enabled = defined( 'WPB_VC_VERSION' ) && function_exists( 'vc_disable_frontend' );
}
}

View File

@ -0,0 +1,170 @@
<?php
/**
* Integration with Envira Gallery
*
* @since 3.3.0
* @package Smush\Core\Integrations
*/
namespace Smush\Core\Integrations;
use Smush\Core\Modules\CDN;
use Smush\Core\Modules\Helpers\Parser;
use Smush\Core\Settings;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Envira
*/
class Envira {
/**
* CDN module instance.
*
* @var CDN $cdn
*/
private $cdn;
/**
* Envira constructor.
*
* @since 3.3.0
*
* @param CDN $cdn CDN module.
*/
public function __construct( CDN $cdn ) {
if ( is_admin() ) {
return;
}
if ( Settings::get_instance()->get( 'lazy_load' ) ) {
add_filter( 'smush_skip_image_from_lazy_load', array( $this, 'skip_lazy_load' ), 10, 3 );
add_filter( 'envira_gallery_indexable_images', array( $this, 'add_no_lazyload_class' ) );
}
if ( $cdn->is_active() ) {
$this->cdn = $cdn;
add_filter( 'smush_cdn_image_tag', array( $this, 'replace_cdn_links' ) );
}
}
/**
* Do not lazy load images from Envira Gallery.
*
* @since 3.3.0
*
* @param bool $lazy_load Should skip? Default: false.
* @param string $src Image url.
* @param string $img Image.
*
* @return bool
*/
public function skip_lazy_load( $lazy_load, $src, $img ) {
$classes = Parser::get_attribute( $img, 'class' );
return false !== strpos( $classes, 'envira-lazy' );
}
/**
* Replace images from data-envira-src and data-envira-srcset with CDN links.
*
* @since 3.3.0
*
* @param string $img Image.
*
* @return string
*/
public function replace_cdn_links( $img ) {
$image_src = Parser::get_attribute( $img, 'data-envira-src' );
if ( $image_src ) {
// Store the original source to be used later on.
$original_src = $image_src;
// Replace the data-envira-src of the image with CDN link.
$image_src = $this->convert_url_to_cdn( $image_src );
if ( $image_src ) {
$img = preg_replace( '#(data-envira-src=["|\'])' . $original_src . '(["|\'])#i', '\1' . $image_src . '\2', $img, 1 );
}
}
$image_srcset = Parser::get_attribute( $img, 'data-envira-srcset' );
if ( $image_srcset ) {
// Do not add our own srcset attributes.
add_filter( 'smush_skip_adding_srcset', '__return_true' );
// Store the original source to be used later on.
$original_src = $image_srcset;
$replace = false;
$images = Parser::get_links_from_content( $image_srcset );
if ( isset( $images[0] ) && is_array( $images[0] ) ) {
foreach ( $images[0] as $image ) {
// Replace the data-envira-srcset of the image with CDN link.
$image_src = $this->convert_url_to_cdn( $image );
if ( $image_src ) {
$replace = true;
$image_srcset = preg_replace( '#' . $image . '#i', '\1' . $image_src . '\2', $image_srcset, 1 );
}
}
}
if ( $replace ) {
$img = preg_replace( '#(data-envira-srcset=["|\'])' . $original_src . '(["|\'])#i', '\1' . $image_srcset . '\2', $img, 1 );
}
}
return $img;
}
/**
* Galleries in Envira will use a noscript tag with images. Smush can't filter the DOM tree, so we will add
* a no-lazyload class to every image.
*
* @since 3.5.0
*
* @param string $images String of img tags that will go inside nocscript element.
*
* @return string
*/
public function add_no_lazyload_class( $images ) {
$parsed = ( new Parser() )->get_images_from_content( $images );
if ( empty( $parsed ) ) {
return $images;
}
foreach ( $parsed[0] as $image ) {
$original = $image;
$class = Parser::get_attribute( $image, 'class' );
if ( ! $class ) {
Parser::add_attribute( $image, 'class', 'no-lazyload' );
} else {
Parser::add_attribute( $image, 'class', $class . ' no-lazyload' );
}
$images = str_replace( $original, $image, $images );
}
return $images;
}
/**
* Convert URL to CDN link.
*
* @since 3.3.0
*
* @param string $url Image URL.
*
* @return bool|string
*/
private function convert_url_to_cdn( $url ) {
if ( ! $this->cdn->is_supported_path( $url ) ) {
return false;
}
return $this->cdn->generate_cdn_url( $url );
}
}

View File

@ -0,0 +1,221 @@
<?php
/**
* Integration with Gravity Forms: Gravity_Forms class
*
* This integration will automatically compress images on Gravity Forms upload.
*
* @since 3.9.10
*
* @package Smush\Core\Integrations
*/
namespace Smush\Core\Integrations;
use GFFormsModel;
use Smush\Core\Core;
use Smush\Core\Helper;
use WP_Smush;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Gravity_Forms for Gravity Forms integration.
*
* This integration will automatically compress images on Gravity Forms upload.
*
* @since 3.9.10
*
* @see Abstract_Integration
*/
class Gravity_Forms extends Abstract_Integration {
/**
* Gravity_Forms constructor.
*
* @since 3.9.10
*/
public function __construct() {
$this->module = 'gform';
$this->class = 'free';
$this->enabled = defined( 'GF_SUPPORTED_WP_VERSION' ) && class_exists( 'GFForms' );
parent::__construct();
// Hook at the end of setting row to output an error div.
add_action( 'smush_setting_column_right_inside', array( $this, 'additional_notice' ) );
// Return if Gravity Form integration or auto compression is not enabled.
if ( ! $this->enabled || ! $this->settings->get( 'gform' ) || ! $this->settings->get( 'auto' ) ) {
return;
}
// Track gravity form submission and validate if there is any image uploaded in Image or File Upload fields.
add_action( 'gform_after_submission', array( $this, 'smush_gform_after_submission' ), 10, 2 );
}
/*
* ************************************
*
* OVERWRITE PARENT CLASS FUNCTIONALITY
*/
/**
* Filters the setting variable to add Gravity Form setting title and description
*
* @since 3.9.10
*
* @param array $settings Settings.
*
* @return array
*/
public function register( $settings ) {
$settings[ $this->module ] = array(
'label' => esc_html__( 'Enable Gravity Forms integration', 'wp-smushit' ),
'short_label' => esc_html__( 'Gravity Forms', 'wp-smushit' ),
'desc' => esc_html__( 'Allow compressing images uploaded with Gravity Forms.', 'wp-smushit' ),
);
return $settings;
}
/**
* Show additional notice if the required plugins are not installed.
*
* @since 3.9.10
*
* @param string $name Setting name.
*/
public function additional_notice( $name ) {
if ( $this->module === $name && ! $this->enabled ) {
?>
<div class="sui-toggle-content">
<div class="sui-notice">
<div class="sui-notice-content">
<div class="sui-notice-message">
<i class="sui-notice-icon sui-icon-info" aria-hidden="true"></i>
<p><?php esc_html_e( 'To use this feature you need be using Gravity Forms.', 'wp-smushit' ); ?></p>
</div>
</div>
</div>
</div>
<?php
}
}
/**
* Processing automatic image smush on Gravity Forms upload.
*
* @since 3.9.10
*
* @param Object $entry Entry Object.
* @param Object $form Form Object.
*/
public function smush_gform_after_submission( $entry, $form ) {
$fields = $form['fields'];
foreach ( $fields as $field ) {
if ( 'fileupload' !== $field->type && 'post_image' !== $field->type ) {
continue;
}
if ( ! function_exists( 'rgar' ) ) {
continue;
}
$uploaded_files = rgar( $entry, $field->id );
$uploaded_files = $this->smush_parse_files( $uploaded_files, $field );
if ( ! is_array( $uploaded_files ) || empty( $uploaded_files ) ) {
continue;
}
foreach ( $uploaded_files as $_file ) {
$dir = $this->get_gform_upload_dir( $form['id'] );
if ( ! $dir || ! isset( $dir['url'] ) || ! isset( $dir['path'] ) ) {
continue;
}
$file = str_replace( $dir['url'], $dir['path'], $_file );
// Get mime type from file path.
$mime = Helper::get_mime_type( $file );
// If image file not exist or image type not supported.
if ( ! file_exists( $file ) || ! in_array( $mime, Core::$mime_types, true ) ) {
continue;
}
WP_Smush::get_instance()->core()->mod->smush->do_smushit( $file );
}
}
}
/**
* Get upload directory url and path.
*
* @since 3.9.10
*
* @param int $form_id Form ID.
* @return bool|array
*/
public function get_gform_upload_dir( $form_id ) {
if ( ! class_exists( 'GFFormsModel' ) ) {
return false;
}
$dir = GFFormsModel::get_file_upload_path( $form_id, 'PLACEHOLDER' );
$dir['path'] = dirname( $dir['path'] );
$dir['url'] = dirname( $dir['url'] );
return $dir;
}
/**
* Parsing uploaded files.
*
* @since 3.9.10
*
* @param mixed $files File path.
* @param Object $field Form field object.
*
* @return array
*/
public function smush_parse_files( $files, $field ) {
if ( empty( $files ) ) {
return array();
}
if ( $this->smush_is_json( $files ) ) {
$files = json_decode( $files );
} elseif ( 'post_image' === $field->get_input_type() ) {
$file_bits = explode( '|:|', $files );
$files = array( $file_bits[0] );
} else {
$files = array( $files );
}
return $files;
}
/**
* Check entry files in JSON format.
*
* @since 3.9.10
*
* @param String $string File string.
*
* @return bool
*/
public function smush_is_json( $string ) {
// Duplicate contents of GFCommon::is_json() here to supports versions of GF older than GF 2.5.
if ( is_string( $string ) && in_array( substr( $string, 0, 1 ), array( '{', '[' ) ) && is_array( json_decode( $string, ARRAY_A ) ) ) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,168 @@
<?php
/**
* Smush integration with Gutenberg editor: Gutenberg class
*
* @package Smush\Core\Integrations
* @since 2.8.1
*
* @author Anton Vanyukov <anton@incsub.com>
*
* @copyright (c) 2018, Incsub (http://incsub.com)
*/
namespace Smush\Core\Integrations;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Gutenberg for Gutenberg integration.
*
* @since 2.8.1
*/
class Gutenberg extends Abstract_Integration {
/**
* Gutenberg constructor.
*
* @since 2.8.1
*/
public function __construct() {
$this->module = 'gutenberg';
$this->class = 'free';
$this->check_for_gutenberg();
parent::__construct();
if ( ! $this->enabled ) {
// Disable setting if Gutenberg is not active.
add_filter( 'wp_smush_integration_status_' . $this->module, '__return_true' );
// Hook at the end of setting row to output an error div.
add_action( 'smush_setting_column_right_inside', array( $this, 'integration_error' ) );
return;
}
// Register gutenberg block assets.
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gb' ) );
}
/**************************************
*
* OVERWRITE PARENT CLASS FUNCTIONALITY
*/
/**
* Filters the setting variable to add Gutenberg setting title and description.
*
* @since 2.8.1
*
* @param array $settings Settings array.
*
* @return mixed
*/
public function register( $settings ) {
$settings[ $this->module ] = array(
'label' => esc_html__( 'Show Smush stats in Gutenberg blocks', 'wp-smushit' ),
'short_label' => esc_html__( 'Gutenberg Support', 'wp-smushit' ),
'desc' => esc_html__(
'Add statistics and the manual smush button to Gutenberg blocks that display images.',
'wp-smushit'
),
);
return $settings;
}
/**************************************
*
* PUBLIC CLASSES
*/
/**
* Prints the message for Gutenberg setup.
*
* @since 2.8.1
*
* @param string $setting_key Settings key.
*/
public function integration_error( $setting_key ) {
// Return if not Gutenberg integration.
if ( $this->module !== $setting_key ) {
return;
}
?>
<div class="sui-toggle-content">
<div class="sui-notice">
<div class="sui-notice-content">
<div class="sui-notice-message">
<i class="sui-notice-icon sui-icon-info" aria-hidden="true"></i>
<p><?php esc_html_e( 'To use this feature you need to install and activate the Gutenberg plugin.', 'wp-smushit' ); ?></p>
</div>
</div>
</div>
</div>
<?php
}
/**
* Enqueue Gutenberg block assets for backend editor.
*
* `wp-blocks`: includes block type registration and related functions.
* `wp-element`: includes the WordPress Element abstraction for describing the structure of your blocks.
* `wp-i18n`: To internationalize the block's text.
*
* @since 2.8.1
*/
public function enqueue_gb() {
$enabled = $this->settings->get( $this->module );
if ( ! $enabled ) {
return;
}
// Gutenberg block scripts.
wp_enqueue_script(
'smush-gutenberg',
WP_SMUSH_URL . 'app/assets/js/smush-blocks.min.js',
array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
WP_SMUSH_VERSION,
true
);
}
/**************************************
*
* PRIVATE CLASSES
*/
/**
* Make sure we only enqueue when Gutenberg is active.
*
* For WordPress pre 5.0 - only when Gutenberg plugin is installed.
* For WordPress 5.0+ - only when Classic Editor is NOT installed.
*
* @since 3.0.2
*/
private function check_for_gutenberg() {
global $wp_version;
if ( ! function_exists( 'is_plugin_active' ) ) {
/* @noinspection PhpIncludeInspection */
include_once ABSPATH . 'wp-admin/includes/plugin.php';
}
// Check if WordPress 5.0 or higher.
$is_wp5point0 = version_compare( $wp_version, '4.9.9', '>' );
if ( $is_wp5point0 ) {
$this->enabled = ! is_plugin_active( 'classic-editor/classic-editor.php' );
} else {
$this->enabled = is_plugin_active( 'gutenberg/gutenberg.php' );
}
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Smush\Core\Integrations;
use Smush\Core\Helper;
/**
* @method identify( int $user_id )
* @method register( string $property, mixed $value )
* @method registerAll( array $properties )
* @method track( string $event, array $properties = array() )
*/
class Mixpanel {
private $mixpanel;
public function __construct( $project_token ) {
$this->mixpanel = class_exists( '\Mixpanel' ) && method_exists( '\Mixpanel', 'getInstance' )
? \Mixpanel::getInstance( $project_token, array(
'error_callback' => array( $this, 'handle_error' ),
) )
: null;
}
public function handle_error( $code, $data ) {
Helper::logger()->error( "$code: $data" );
}
public function __call( $name, $arguments ) {
if ( method_exists( $this->mixpanel, $name ) ) {
return call_user_func_array(
array( $this->mixpanel, $name ),
$arguments
);
}
return null;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,866 @@
<?php
/**
* Adds the Bulk Page and Smush Column to NextGen Gallery
*
* @package Smush\Core\Integrations\NextGen
* @version 1.0
*
* @author Umesh Kumar <umesh@incsub.com>
*
* @copyright (c) 2016, Incsub (http://incsub.com)
*/
namespace Smush\Core\Integrations\NextGen;
use C_Component_Registry;
use C_Gallery_Storage;
use nggdb;
use Smush\App\Media_Library;
use Smush\Core\Core;
use Smush\Core\Helper;
use Smush\Core\Integrations\NextGen;
use Smush\Core\Settings;
use stdClass;
use WP_Smush;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Admin
*/
class Admin extends NextGen {
/**
* Total image count.
*
* @var int $total_count
*/
public $total_count = 0;
/**
* Count of images ( Attachments ), Does not includes additional sizes that might have been created.
*
* @var int $smushed_count
*/
public $smushed_count = 0;
/**
* Includes the count of different sizes an image might have
*
* @var int $image_count
*/
public $image_count = 0;
/**
* Remaining count.
*
* @var int $remaining_count
*/
public $remaining_count = 0;
/**
* Super Smushed.
*
* @var int $super_smushed
*/
public $super_smushed = 0;
/**
* Smushed images.
*
* @var array $smushed
*/
public $smushed = array();
/**
* Stores all lossless smushed IDs.
*
* @var array $resmush_ids
*/
public $resmush_ids = array();
/**
* Stats class object.
*
* @var Stats
*/
public $ng_stats;
protected $settings;
/**
* Admin constructor.
*
* @param Stats $stats Class object.
*/
public function __construct( Stats $stats ) {
$this->ng_stats = $stats;
$this->settings = Settings::get_instance();
// Update the number of columns.
add_filter( 'ngg_manage_images_number_of_columns', array( $this, 'wp_smush_manage_images_number_of_columns' ) );
// Update resmush list, if a NextGen image is deleted.
add_action( 'ngg_delete_picture', array( $this, 'update_resmush_list' ) );
// Update Stats, if a NextGen image is deleted.
add_action( 'ngg_delete_picture', array( $this, 'update_nextgen_stats' ) );
// Update Stats, Lists - if a NextGen Gallery is deleted.
add_action( 'ngg_delete_gallery', array( $this->ng_stats, 'update_stats_cache' ) );
// Update the Super Smush count, after the smushing.
add_action( 'wp_smush_image_optimised_nextgen', array( $this, 'update_lists_after_optimizing' ), '', 2 );
// Reset smush data after restoring the image.
add_action( 'ngg_recovered_image', array( $this, 'reset_smushdata' ) );
add_action( 'wp_ajax_nextgen_get_stats', array( $this, 'ajax_get_stats' ) );
add_filter( 'wp_smush_nextgen_scan_stats', array( $this, 'scan_images' ) );
}
/**
* Returns a column name for WP Smush.
*
* @param array $columns Current columns.
*
* @return array|string
*/
public function wp_smush_image_column_name( $columns ) {
// Latest next gen takes string, while the earlier WP Smush plugin shows there use to be a array.
if ( is_array( $columns ) ) {
$columns['wp_smush_image'] = esc_html__( 'Smush', 'wp-smushit' );
} else {
$columns = esc_html__( 'Smush', 'wp-smushit' );
}
return $columns;
}
/**
* Returns Smush option / Stats, depending if image is already smushed or not.
*
* @param string $column_name Column name.
* @param object|int $id Image object or ID.
*
* @return array|bool|string|void
*/
public function wp_smush_column_options( $column_name, $id ) {
// NExtGen Doesn't returns Column name, weird? yeah, right, it is proper because hook is called for the particular column.
if ( 'wp_smush_image' === $column_name || '' === $column_name ) {
// We're not using our in-house function Smush\Core\Integrations\Nextgen::get_nextgen_image_from_id()
// as we're already instializing the nextgen gallery object, we need $storage instance later.
// Registry Object for NextGen Gallery.
$registry = C_Component_Registry::get_instance();
/**
* Gallery Storage Object.
*
* @var C_Gallery_Storage $storage
*/
$storage = $registry->get_utility( 'I_Gallery_Storage' );
// We'll get the image object in $id itself, else fetch it using Gallery Storage.
if ( is_object( $id ) ) {
$image = $id;
} else {
// get an image object.
$image = $storage->object->_image_mapper->find( $id );
}
// Check if it is supported image format, get image type to do that get the absolute path.
$file_path = $storage->get_image_abspath( $image, 'full' );
// Get image type from file path.
$image_type = $this->get_file_type( $file_path );
// If image type not supported.
if ( ! $image_type || ! in_array( $image_type, Core::$mime_types, true ) ) {
return;
}
$image->meta_data = $this->get_combined_stats( $image->meta_data );
// Check Image metadata, if smushed, print the stats or super smush button.
if ( ! empty( $image->meta_data['wp_smush'] ) ) {
// Echo the smush stats.
return $this->show_stats( $image->pid, $image->meta_data['wp_smush'], $image_type );
}
// Print the status of image, if Not smushed.
return $this->set_status( $image->pid );
}
}
/**
* Localize Translations And Stats
*/
public function localize() {
$handle = 'smush-admin';
$upgrade_url = add_query_arg(
array(
'utm_source' => 'smush',
'utm_medium' => 'plugin',
'utm_campaign' => 'smush_bulksmush_issues_filesizelimit_notice',
),
'https://wpmudev.com/project/wp-smush-pro/'
);
if ( WP_Smush::is_pro() ) {
$error_in_bulk = esc_html__( '{{smushed}}/{{total}} images were successfully compressed, {{errors}} encountered issues.', 'wp-smushit' );
} else {
$error_in_bulk = sprintf(
/* translators: %1$s - opening link tag, %2$s - Close the link </a> */
esc_html__( '{{smushed}}/{{total}} images were successfully compressed, {{errors}} encountered issues. Are you hitting the 5MB "size limit exceeded" warning? %1$sUpgrade to Smush Pro%2$s to optimize unlimited image files.', 'wp-smushit' ),
'<a href="' . esc_url( $upgrade_url ) . '" target="_blank">',
'</a>'
);
}
$wp_smush_msgs = array(
'nonce' => wp_create_nonce( 'wp-smush-ajax' ),
'resmush' => esc_html__( 'Super-Smush', 'wp-smushit' ),
'smush_now' => esc_html__( 'Smush Now', 'wp-smushit' ),
'error_in_bulk' => $error_in_bulk,
'all_resmushed' => esc_html__( 'All images are fully optimized.', 'wp-smushit' ),
'restore' => esc_html__( 'Restoring image...', 'wp-smushit' ),
'smushing' => esc_html__( 'Smushing image...', 'wp-smushit' ),
);
wp_localize_script( $handle, 'wp_smush_msgs', $wp_smush_msgs );
$data = $this->ng_stats->get_global_stats();
wp_localize_script( $handle, 'wp_smushit_data', $data );
}
/**
* Increase the count of columns for Nextgen Gallery Manage page.
*
* @param int $count Current columns count.
*
* @return int
*/
public function wp_smush_manage_images_number_of_columns( $count ) {
$count ++;
// Add column Heading.
add_filter( "ngg_manage_images_column_{$count}_header", array( $this, 'wp_smush_image_column_name' ) );
// Add Column data.
add_filter( "ngg_manage_images_column_{$count}_content", array( $this, 'wp_smush_column_options' ), 10, 2 );
return $count;
}
/**
* Set send button status
*
* @param int $pid ID.
*
* @return string
*/
private function set_status( $pid ) {
// the status.
$status_txt = __( 'Not processed', 'wp-smushit' );
// we need to show the smush button.
$show_button = true;
// the button text.
$button_txt = __( 'Smush', 'wp-smushit' );
// If we are not showing smush button, append progress bar, else it is already there.
if ( ! $show_button ) {
$status_txt .= Media_Library::progress_bar();
}
return $this->column_html( $pid, $status_txt, $button_txt, $show_button );
}
/**
* Print the column html
*
* @param string $pid Media id.
* @param string $status_txt Status text.
* @param string $button_txt Button label.
* @param boolean $show_button Whether to shoe the button.
* @param bool $smushed Image compressed or not.
*
* @return string|void
*/
public function column_html( $pid, $status_txt = '', $button_txt = '', $show_button = true, $smushed = false ) {
$class = $smushed ? '' : ' sui-hidden';
$html = '<p class="smush-status' . $class . '">' . $status_txt . '</p>';
// if we aren't showing the button.
if ( ! $show_button ) {
return $html;
}
$html .= '<div class="sui-smush-media smush-status-links">';
$html .= wp_nonce_field( 'wp_smush_nextgen', '_wp_smush_nonce', '', false );
$html .= '<button class="button button-primary wp-smush-nextgen-send" data-id="' . $pid . '">
<span>' . $button_txt . '</span>
</button>';
$html .= '</div>';
return $html;
}
/**
* Updates the resmush list for NextGen gallery, remove the given id
*
* @param int $attachment_id Attachment ID.
*/
public function update_resmush_list( $attachment_id ) {
if ( $this->ng_stats->get_reoptimize_list()->has_id( $attachment_id ) ) {
return $this->ng_stats->get_reoptimize_list()->remove_id( $attachment_id );
}
return $this->ng_stats->get_reoptimize_list()->add_id( $attachment_id );
}
/**
* Fetch the stats for the given attachment id, and subtract them from Global stats
*
* @param int $attachment_id Attachment ID.
*
* @return bool
*/
public function update_nextgen_stats( $attachment_id ) {
if ( empty( $attachment_id ) ) {
return false;
}
$image_id = absint( (int) $attachment_id );
// Get the absolute path for original image.
$image = $this->get_nextgen_image_from_id( $image_id );
// Image Metadata.
$metadata = ! empty( $image ) ? $image->meta_data : '';
$smush_stats = ! empty( $metadata['wp_smush'] ) ? $metadata['wp_smush'] : '';
if ( empty( $smush_stats ) ) {
return false;
}
$nextgen_stats = get_option( 'wp_smush_stats_nextgen', false );
if ( ! $nextgen_stats ) {
return false;
}
if ( ! empty( $nextgen_stats['size_before'] ) && ! empty( $nextgen_stats['size_after'] ) && $nextgen_stats['size_before'] > 0 && $nextgen_stats['size_after'] > 0 && $nextgen_stats['size_before'] >= $smush_stats['stats']['size_before'] ) {
$nextgen_stats['size_before'] = $nextgen_stats['size_before'] - $smush_stats['stats']['size_before'];
$nextgen_stats['size_after'] = $nextgen_stats['size_after'] - $smush_stats['stats']['size_after'];
$nextgen_stats['bytes'] = $nextgen_stats['size_before'] - $nextgen_stats['size_after'];
if ( 0 === $nextgen_stats['bytes'] && 0 === $nextgen_stats['size_before'] ) {
$nextgen_stats['percent'] = 0;
} else {
$nextgen_stats['percent'] = ( $nextgen_stats['bytes'] / $nextgen_stats['size_before'] ) * 100;
}
$nextgen_stats['human'] = size_format( $nextgen_stats['bytes'], 1 );
}
// Update Stats.
update_option( 'wp_smush_stats_nextgen', $nextgen_stats, false );
// Remove from Super Smush list.
$this->ng_stats->get_supper_smushed_list()->remove_id( $image_id );
}
/**
* Update the Super Smush count for NextGen Gallery
*
* @param int $image_id Image ID.
* @param array $stats Stats.
*/
public function update_lists_after_optimizing( $image_id, $stats ) {
if ( isset( $stats['stats']['lossy'] ) && 1 === (int) $stats['stats']['lossy'] ) {
$this->ng_stats->get_supper_smushed_list()->add_id( $image_id );
}
$this->update_resmush_list( $image_id );
}
/**
* Initialize NextGen Gallery Stats
*/
public function setup_image_counts() {
$this->total_count = $this->ng_stats->total_count();
$this->smushed_count = $this->ng_stats->get_smushed_count();
$this->image_count = $this->ng_stats->get_smushed_image_count();
$this->resmush_ids = $this->ng_stats->get_reoptimize_list()->get_ids();
$this->super_smushed = $this->ng_stats->get_supper_smushed_count();
$this->remaining_count = $this->ng_stats->get_remaining_count();
}
/**
* Get the image count for nextgen images
*
* @param array $images Array of attachments to get the image count for.
* @param bool $exclude_resmush_ids Whether to exclude resmush ids or not.
*
* @return int
*/
public function get_image_count( $images = array(), $exclude_resmush_ids = true ) {
if ( empty( $images ) || ! is_array( $images ) ) {
return 0;
}
$image_count = 0;
// $image in here is expected to be metadata array
foreach ( $images as $image_k => $image ) {
// Get image object if not there already.
if ( ! is_array( $image ) ) {
$image = $this->get_nextgen_image_from_id( $image );
// Get the meta.
$image = ! empty( $image->meta_data ) ? $image->meta_data : '';
}
// If there are no smush stats, skip.
if ( empty( $image['wp_smush'] ) ) {
continue;
}
// If resmush ids needs to be excluded.
if ( $exclude_resmush_ids && ( ! empty( $this->resmush_ids ) && in_array( $image_k, $this->resmush_ids ) ) ) {
continue;
}
// Get the image count.
if ( ! empty( $image['wp_smush']['sizes'] ) ) {
$image_count += count( $image['wp_smush']['sizes'] );
}
}
return $image_count;
}
/**
* Combine the resizing stats and smush stats , One time operation - performed during the image optimization
*
* @param array $metadata Image metadata.
*
* @return mixed
*/
private function get_combined_stats( $metadata ) {
if ( empty( $metadata ) ) {
return $metadata;
}
$smush_stats = ! empty( $metadata['wp_smush'] ) ? $metadata['wp_smush'] : '';
$resize_savings = ! empty( $metadata['wp_smush_resize_savings'] ) ? $metadata['wp_smush_resize_savings'] : '';
if ( empty( $resize_savings ) || empty( $smush_stats ) ) {
return $metadata;
}
$smush_stats['stats']['bytes'] = ! empty( $resize_savings['bytes'] ) ? $smush_stats['stats']['bytes'] + $resize_savings['bytes'] : $smush_stats['stats']['bytes'];
$smush_stats['stats']['size_before'] = ! empty( $resize_savings['size_before'] ) ? $smush_stats['stats']['size_before'] + $resize_savings['size_before'] : $smush_stats['stats']['size_before'];
$smush_stats['stats']['size_after'] = ! empty( $resize_savings['size_after'] ) ? $smush_stats['stats']['size_after'] + $resize_savings['size_after'] : $smush_stats['stats']['size_after'];
$smush_stats['stats']['percent'] = ! empty( $resize_savings['size_before'] ) ? ( $smush_stats['stats']['bytes'] / $smush_stats['stats']['size_before'] ) * 100 : $smush_stats['stats']['percent'];
// Round off.
$smush_stats['stats']['percent'] = round( $smush_stats['stats']['percent'], 2 );
if ( ! empty( $smush_stats['sizes']['full'] ) ) {
// Full Image.
$smush_stats['sizes']['full']['bytes'] = ! empty( $resize_savings['bytes'] ) ? $smush_stats['sizes']['full']['bytes'] + $resize_savings['bytes'] : $smush_stats['sizes']['full']['bytes'];
$smush_stats['sizes']['full']['size_before'] = ! empty( $resize_savings['size_before'] ) ? $smush_stats['sizes']['full']['size_before'] + $resize_savings['size_before'] : $smush_stats['sizes']['full']['size_before'];
$smush_stats['sizes']['full']['size_after'] = ! empty( $resize_savings['size_after'] ) ? $smush_stats['sizes']['full']['size_after'] + $resize_savings['size_after'] : $smush_stats['sizes']['full']['size_after'];
$smush_stats['sizes']['full']['percent'] = ! empty( $smush_stats['sizes']['full']['bytes'] ) && $smush_stats['sizes']['full']['size_before'] > 0 ? ( $smush_stats['sizes']['full']['bytes'] / $smush_stats['sizes']['full']['size_before'] ) * 100 : $smush_stats['sizes']['full']['percent'];
$smush_stats['sizes']['full']['percent'] = round( $smush_stats['sizes']['full']['percent'], 2 );
} else {
$smush_stats['sizes']['full'] = $resize_savings;
}
$metadata['wp_smush'] = $smush_stats;
return $metadata;
}
/**
* Reset smush data after restoring the image.
*
* @since 3.10.0
*
* @param stdClass $image Image object for NextGen gallery.
* @param false|string $attachment_file_path The full file path, if it's provided we will reset the dimension.
*/
public function reset_smushdata( $image, $attachment_file_path = false ) {
if ( empty( $image->meta_data['wp_smush'] ) && empty( $image->meta_data['wp_smush_resize_savings'] ) ) {
return;
}
$this->ng_stats->subtract_image_stats( $image );
// Remove the Meta, And send json success.
$image->meta_data['wp_smush'] = '';
// Remove resized data.
if ( ! empty( $image->meta_data['wp_smush_resize_savings'] ) ) {
$image->meta_data['wp_smush_resize_savings'] = '';
if ( $attachment_file_path && file_exists( $attachment_file_path ) ) {
// Update the dimension.
list( $width, $height ) = getimagesize( $attachment_file_path );
if ( $width ) {
$image->meta_data['width'] = $width;
$image->meta_data['full']['width'] = $width;
}
if ( $height ) {
$image->meta_data['height'] = $height;
$image->meta_data['full']['height'] = $height;
}
}
}
// Update metadata.
nggdb::update_image_meta( $image->pid, $image->meta_data );
/**
* Called after the image has been successfully restored
*
* @since 3.7.0
*
* @param int $image_id ID of the restored image.
*/
do_action( 'wp_smush_image_nextgen_restored', $image->pid );
}
public function ajax_get_stats() {
check_ajax_referer( 'wp-smush-ajax', '_nonce' );
// Check capability.
if ( ! Helper::is_user_allowed( 'manage_options' ) ) {
wp_send_json_error(
array(
'notice' => esc_html__( "You don't have permission to do this.", 'wp-smushit' ),
'noticeType' => 'error',
)
);
}
$stats = $this->get_global_stats_with_bulk_smush_content_and_notice();
wp_send_json_success( $stats );
}
private function get_global_stats_with_bulk_smush_content_and_notice() {
$stats = $this->get_global_stats_with_bulk_smush_content();
$remaining_count = $stats['remaining_count'];
if ( $remaining_count > 0 ) {
$stats['noticeType'] = 'warning';
$stats['notice'] = sprintf(
/* translators: %1$d - number of images, %2$s - opening a tag, %3$s - closing a tag */
esc_html__( 'Image check complete, you have %1$d images that need smushing. %2$sBulk smush now!%3$s', 'wp-smushit' ),
$remaining_count,
'<a href="#" class="wp-smush-trigger-nextgen-bulk">',
'</a>'
);
} else {
$stats['notice'] = esc_html__( 'Yay! All images are optimized as per your current settings.', 'wp-smushit' );
$stats['noticeType'] = 'success';
}
return $stats;
}
private function get_global_stats_with_bulk_smush_content() {
$stats = $this->ng_stats->get_global_stats();
$remaining_count = $stats['remaining_count'];
$reoptimize_count = $stats['count_resmush'];
$optimize_count = $stats['count_unsmushed'];
if ( $remaining_count > 0 ) {
ob_start();
WP_Smush::get_instance()->admin()->print_pending_bulk_smush_content(
$remaining_count,
$reoptimize_count,
$optimize_count
);
$content = ob_get_clean();
$stats['content'] = $content;
}
return $stats;
}
public function scan_images() {
$resmush_list = array();
$attachments = $this->ng_stats->get_ngg_images();
// Check if any of the smushed image needs to be resmushed.
if ( ! empty( $attachments ) && is_array( $attachments ) ) {
foreach ( $attachments as $attachment_k => $metadata ) {
$smush_data = ! empty( $metadata['wp_smush'] ) ? $metadata['wp_smush'] : array();
if ( $this->should_resmush( $smush_data ) ) {
$resmush_list[] = $attachment_k;
}
}// End of Foreach Loop
// Store the resmush list in Options table.
$this->ng_stats->get_reoptimize_list()->update_ids( $resmush_list );
}
// Delete resmush list if empty.
if ( empty( $resmush_list ) ) {
$this->ng_stats->get_reoptimize_list()->delete_ids();
}
return $this->get_global_stats_with_bulk_smush_content_and_notice();
}
private function should_resmush( $smush_data ) {
if ( empty( $smush_data['stats'] ) ) {
return false;
}
return $this->lossy_optimization_required( $smush_data )
|| $this->strip_exif_optimization_required( $smush_data )
|| $this->original_optimization_required( $smush_data );
}
private function lossy_optimization_required( $smush_data ) {
$required_lossy_level = $this->settings->get_lossy_level_setting();
$current_lossy_level = ! empty( $smush_data['stats']['lossy'] ) ? (int) $smush_data['stats']['lossy'] : 0;
return $current_lossy_level < $required_lossy_level;
}
private function strip_exif_optimization_required( $smush_data ) {
return $this->settings->get( 'strip_exif' ) && ! empty( $smush_data['stats']['keep_exif'] ) && ( 1 === (int) $smush_data['stats']['keep_exif'] );
}
private function original_optimization_required( $smush_data ) {
return $this->settings->get( 'original' ) && empty( $smush_data['sizes']['full'] );
}
/**
* Display the smush stats for the image
*
* @param int $pid Image Id stored in nextgen table.
* @param bool|array $wp_smush_data Stats, stored after smushing the image.
* @param string $image_type Used for determining if not gif, to show the Super Smush button.
*
* @uses Admin::column_html(), WP_Smush::get_restore_link(), WP_Smush::get_resmush_link()
*
* @return bool|array|string
*/
public function show_stats( $pid, $wp_smush_data = false, $image_type = '' ) {
if ( empty( $wp_smush_data ) ) {
return false;
}
$button_txt = '';
$show_button = false;
$show_resmush = false;
$bytes = isset( $wp_smush_data['stats']['bytes'] ) ? $wp_smush_data['stats']['bytes'] : 0;
$bytes_readable = ! empty( $bytes ) ? size_format( $bytes, 1 ) : '';
$percent = isset( $wp_smush_data['stats']['percent'] ) ? $wp_smush_data['stats']['percent'] : 0;
$percent = $percent < 0 ? 0 : $percent;
$status_txt = '';
if ( isset( $wp_smush_data['stats']['size_before'] ) && $wp_smush_data['stats']['size_before'] == 0 && ! empty( $wp_smush_data['sizes'] ) ) {
$status_txt = __( 'Already Optimized', 'wp-smushit' );
} else {
if ( 0 === (int) $bytes || 0 === (int) $percent ) {
$status_txt = __( 'Already Optimized', 'wp-smushit' );
// Add resmush option if needed.
$show_resmush = $this->should_resmush( $wp_smush_data );
if ( $show_resmush ) {
$status_txt .= '<div class="sui-smush-media smush-status-links">';
$status_txt .= $this->get_resmsuh_link( $pid );
$status_txt .= '</div>';
}
} elseif ( ! empty( $percent ) && ! empty( $bytes_readable ) ) {
$status_txt = sprintf( /* translators: %1$s: reduced by bytes, %2$s: size format */
__( 'Reduced by %1$s (%2$01.1f%%)', 'wp-smushit' ),
$bytes_readable,
number_format_i18n( $percent, 2 )
);
$status_txt .= '<div class="sui-smush-media smush-status-links">';
$show_resmush = $this->should_resmush( $wp_smush_data );
if ( $show_resmush ) {
$status_txt .= $this->get_resmsuh_link( $pid );
}
// Restore Image: Check if we need to show the restore image option.
$show_restore = $this->show_restore_option( $pid, $wp_smush_data );
if ( $show_restore ) {
if ( $show_resmush ) {
// Show Separator.
$status_txt .= ' | ';
}
$status_txt .= $this->get_restore_link( $pid );
}
// Show detailed stats if available.
if ( ! empty( $wp_smush_data['sizes'] ) ) {
if ( $show_resmush || $show_restore ) {
// Show Separator.
$status_txt .= ' | ';
} else {
// Show the link in next line.
$status_txt .= '<br />';
}
// Detailed Stats Link.
$status_txt .= '<a href="#" class="smush-stats-details">' . esc_html__( 'Smush stats', 'wp-smushit' ) . ' [<span class="stats-toggle">+</span>]</a>';
// Get metadata For the image
// Registry Object for NextGen Gallery.
$registry = C_Component_Registry::get_instance();
/**
* Gallery Storage Object.
*
* @var C_Gallery_Storage $storage
*/
$storage = $registry->get_utility( 'I_Gallery_Storage' );
// get an array of sizes available for the $image.
$sizes = $storage->get_image_sizes();
$image = $storage->object->_image_mapper->find( $pid );
$full_image = $storage->get_image_abspath( $image, 'full' );
// Stats.
$stats = $this->get_detailed_stats( $pid, $wp_smush_data, array( 'sizes' => $sizes ), $full_image );
$status_txt .= $stats;
$status_txt .= '</div>';
}
}
}
// If show button is true for some reason, column html can print out the button for us.
return $this->column_html( $pid, $status_txt, $button_txt, $show_button, true );
}
/**
* Returns the Stats for a image formatted into a nice table
*
* @param int $image_id Image ID.
* @param array $wp_smush_data Smush data.
* @param array $attachment_metadata Attachment metadata.
* @param string $full_image Full sized image.
*
* @return string
*/
private function get_detailed_stats( $image_id, $wp_smush_data, $attachment_metadata, $full_image ) {
$stats = '<div id="smush-stats-' . $image_id . '" class="smush-stats-wrapper hidden">
<table class="wp-smush-stats-holder">
<thead>
<tr>
<th><strong>' . esc_html__( 'Image size', 'wp-smushit' ) . '</strong></th>
<th><strong>' . esc_html__( 'Savings', 'wp-smushit' ) . '</strong></th>
</tr>
</thead>
<tbody>';
$size_stats = $wp_smush_data['sizes'];
// Reorder Sizes as per the maximum savings.
uasort( $size_stats, array( $this, 'cmp' ) );
// Show Sizes and their compression.
foreach ( $size_stats as $size_key => $size_value ) {
$size_value = ! is_object( $size_value ) ? (object) $size_value : $size_value;
if ( $size_value->bytes > 0 ) {
$stats .= '<tr>
<td>' . strtoupper( $size_key ) . '</td>
<td>' . size_format( $size_value->bytes, 1 );
}
// Add percentage if set.
if ( isset( $size_value->percent ) && $size_value->percent > 0 ) {
$stats .= " ( $size_value->percent% )";
}
$stats .= '</td>
</tr>';
}
$stats .= '</tbody>
</table>
</div>';
return $stats;
}
/**
* Compare Values
*
* @param object|array $a First object.
* @param object|array $b Second object.
*
* @return int
*/
public function cmp( $a, $b ) {
if ( is_object( $a ) ) {
// Check and typecast $b if required.
$b = is_object( $b ) ? $b : (object) $b;
return $b->bytes - $a->bytes ;
} elseif ( is_array( $a ) ) {
$b = is_array( $b ) ? $b : (array) $b;
return $b['bytes'] - $a['bytes'];
}
}
/**
* Generates a Resmush link for a image.
*
* @param int $image_id Attachment ID.
* @param string $type Type of attachment.
*
* @return bool|string
*/
private function get_resmsuh_link( $image_id ) {
if ( empty( $image_id ) ) {
return false;
}
$class = 'wp-smush-action wp-smush-title sui-tooltip sui-tooltip-constrained wp-smush-nextgen-resmush';
return sprintf(
'<a href="#" data-tooltip="%s" data-id="%d" data-nonce="%s" class="%s">%s</a>',
esc_html__( 'Smush image including original file', 'wp-smushit' ),
$image_id,
wp_create_nonce( 'wp-smush-resmush-' . $image_id ),
$class,
esc_html__( 'Resmush', 'wp-smushit' )
);
}
/**
* Returns a restore link for given image id
*
* @param int $image_id Attachment ID.
* @param string $type Attachment type.
*
* @return bool|string
*/
private function get_restore_link( $image_id ) {
if ( empty( $image_id ) ) {
return false;
}
$class = 'wp-smush-action wp-smush-title sui-tooltip wp-smush-nextgen-restore';
return sprintf(
'<a href="#" data-tooltip="%s" data-id="%d" data-nonce="%s" class="%s">%s</a>',
esc_html__( 'Restore original image', 'wp-smushit' ),
$image_id,
wp_create_nonce( 'wp-smush-restore-' . $image_id ),
$class,
esc_html__( 'Restore', 'wp-smushit' )
);
}
}

View File

@ -0,0 +1,686 @@
<?php
/**
* Handles all the stats related functions
*
* @package Smush\Core\Integrations\NextGen
* @version 1.0
*
* @author Umesh Kumar <umesh@incsub.com>
*
* @copyright (c) 2016, Incsub (http://incsub.com)
*/
namespace Smush\Core\Integrations\NextGen;
use C_Component_Registry;
use C_Gallery_Storage;
use C_NextGen_Serializable;
use Exception;
use Ngg_Serializable;
use Smush\Core\Attachment_Id_List;
use Smush\Core\Integrations\NextGen;
use WP_Smush;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class
*
* TODO refactor stats by using the new core stats to clean the code.
*/
class Stats extends NextGen {
const REOPTIMIZE_LIST_OPTION_ID = 'wp-smush-nextgen-reoptimize-list';
const SUPPER_SMUSHED_LIST_OPTION_ID = 'wp-smush-nextgen-super-smushed-list';
const SMUSH_STATS_OPTION_ID = 'wp_smush_stats_nextgen';
/**
* Contains the total Stats, for displaying it on bulk page
*
* @var array
*/
public $stats = array();
/**
* PRO user status.
*
* @var bool
*/
private $is_pro_user;
/**
* @var Attachment_Id_List
*/
private $reoptimize_list;
/**
* @var Attachment_Id_List
*/
private $supper_smushed_list;
/**
* @var null|array
*/
private $global_stats;
/**
* @var null|array
*/
private $unsmushed_images;
/**
* @var null|int.
*/
private $remaining_count;
/**
* @var int
*/
private $percent_optimized = 0;
/**
* Stats constructor.
*/
public function __construct() {
parent::__construct();
$this->is_pro_user = WP_Smush::is_pro();
$this->reoptimize_list = new Attachment_Id_List( self::REOPTIMIZE_LIST_OPTION_ID );
$this->supper_smushed_list = new Attachment_Id_List( self::SUPPER_SMUSHED_LIST_OPTION_ID );
// Clear stats cache when an image is restored.
add_action( 'wp_smush_image_nextgen_restored', array( $this, 'clear_cache' ) );
// Add the resizing stats to Global stats.
add_action( 'wp_smush_image_nextgen_resized', array( $this, 'update_stats' ), '', 2 );
// Get the stats for single image, update the global stats.
add_action( 'wp_smush_nextgen_image_stats', array( $this, 'update_stats' ), '', 2 );
}
/**
* Get the images id for nextgen gallery
*
* @param bool $force_refresh Optional. Whether to force the cache to be refreshed.
* Default false.
*
* @param bool $return_ids Whether to return the ids array, set to false by default.
*
* @return int|mixed Returns the images ids or the count
*/
public static function total_count( $force_refresh = false, $return_ids = false ) {
// Check for the wp_smush_images in the 'nextgen' group.
$attachment_ids = wp_cache_get( 'wp_smush_images', 'nextgen' );
// If nothing is found, build the object.
if ( true === $force_refresh || false === $attachment_ids ) {
// Get the nextgen image IDs.
$attachment_ids = self::get_nextgen_attachments();
if ( ! is_wp_error( $attachment_ids ) ) {
// In this case we don't need a timed cache expiration.
wp_cache_set( 'wp_smush_images', $attachment_ids, 'nextgen' );
}
}
return $return_ids ? $attachment_ids : count( $attachment_ids );
}
/**
* Returns the ngg images list(id and meta ) or count
*
* @param string $type Whether to return smushed images or unsmushed images.
* @param bool|false $count Return count only.
* @param bool|false $force_update True/false to update the cache or not.
*
* @return bool|mixed Returns assoc array of image ids and meta or Image count
*
* @throws Exception Exception.
*/
public function get_ngg_images( $type = 'smushed', $count = false, $force_update = false ) {
global $wpdb;
$limit = apply_filters( 'wp_smush_nextgen_query_limit', 1000 );
$offset = 0;
// Check type of images being queried.
if ( ! in_array( $type, array( 'smushed', 'unsmushed' ), true ) ) {
return false;
}
// Check for the wp_smush_images_smushed in the 'nextgen' group.
$images = wp_cache_get( 'wp_smush_images_' . $type, 'nextgen' );
// If nothing is found, build the object.
if ( ! $images || $force_update ) {
// Query Attachments for meta key.
$attachments = $wpdb->get_results( $wpdb->prepare( "SELECT pid, meta_data FROM {$wpdb->nggpictures} LIMIT %d, %d", $offset, $limit ) ); // Db call ok.
while ( ! empty( $attachments ) ) {
foreach ( $attachments as $attachment ) {
// Check if it has `wp_smush` key.
if ( class_exists( 'Ngg_Serializable' ) ) {
$meta = ( new Ngg_Serializable() )->unserialize( $attachment->meta_data );
} elseif ( class_exists( 'C_NextGen_Serializable' ) && method_exists( 'C_NextGen_Serializable', 'unserialize' ) ) {
$meta = C_NextGen_Serializable::unserialize( $attachment->meta_data );
} else {
// If you can't parse it without NextGen - don't parse at all.
continue;
}
// Store pid in image meta.
if ( is_array( $meta ) && empty( $meta['pid'] ) ) {
$meta['pid'] = $attachment->pid;
} elseif ( is_object( $meta ) && empty( $meta->pid ) ) {
$meta->pid = $attachment->pid;
}
// Check meta for wp_smush.
if ( ! is_array( $meta ) || empty( $meta['wp_smush'] ) ) {
$unsmushed_images[ $attachment->pid ] = $meta;
continue;
}
$smushed_images[ $attachment->pid ] = $meta;
}
// Set the offset.
$offset += $limit;
$attachments = $wpdb->get_results( $wpdb->prepare( "SELECT pid, meta_data FROM {$wpdb->nggpictures} LIMIT %d, %d", $offset, $limit ) ); // Db call ok.
}
if ( ! empty( $smushed_images ) ) {
wp_cache_set( 'wp_smush_images_smushed', $smushed_images, 'nextgen', 300 );
}
if ( ! empty( $unsmushed_images ) ) {
wp_cache_set( 'wp_smush_images_unsmushed', $unsmushed_images, 'nextgen', 300 );
}
}
if ( 'smushed' === $type ) {
$smushed_images = ! empty( $smushed_images ) ? $smushed_images : $images;
if ( ! $smushed_images ) {
return 0;
}
return $count ? count( $smushed_images ) : $smushed_images;
}
$unsmushed_images = ! empty( $unsmushed_images ) ? $unsmushed_images : $images;
if ( ! $unsmushed_images ) {
return 0;
}
return $count ? count( $unsmushed_images ) : $unsmushed_images;
}
/**
* Updated the global smush stats for NextGen gallery
*
* @param int $image_id Image ID.
* @param array $stats Compression stats fo respective image.
*/
public function update_stats( $image_id, $stats ) {
$stats = ! empty( $stats['stats'] ) ? $stats['stats'] : '';
$smush_stats = $this->get_cache_smush_stats();
if ( ! empty( $stats ) ) {
// Human Readable.
$smush_stats['human'] = ! empty( $smush_stats['bytes'] ) ? size_format( $smush_stats['bytes'], 1 ) : '';
// Size of images before the compression.
$smush_stats['size_before'] = ! empty( $smush_stats['size_before'] ) ? ( $smush_stats['size_before'] + $stats['size_before'] ) : $stats['size_before'];
// Size of image after compression.
$smush_stats['size_after'] = ! empty( $smush_stats['size_after'] ) ? ( $smush_stats['size_after'] + $stats['size_after'] ) : $stats['size_after'];
$smush_stats['bytes'] = ! empty( $smush_stats['size_before'] ) && ! empty( $smush_stats['size_after'] ) ? ( $smush_stats['size_before'] - $smush_stats['size_after'] ) : 0;
// Compression Percentage.
$smush_stats['percent'] = ! empty( $smush_stats['size_before'] ) && ! empty( $smush_stats['size_after'] ) && $smush_stats['size_before'] > 0 ? ( $smush_stats['bytes'] / $smush_stats['size_before'] ) * 100 : $stats['percent'];
}
update_option( self::SMUSH_STATS_OPTION_ID, $smush_stats, false );
$this->clear_cache();
}
/**
* Clears the object cache for NextGen stats.
*
* @since 3.7.0
*/
public function clear_cache() {
wp_cache_delete( 'wp_smush_images_smushed', 'nextgen' );
wp_cache_delete( 'wp_smush_images_unsmushed', 'nextgen' );
wp_cache_delete( 'wp_smush_images', 'nextgen' );
}
/**
* Get the attachment stats for a image
*
* @param object|array|int $id Attachment ID.
*
* @return array
*/
private function get_attachment_stats( $image ) {
// We'll get the image object in $image itself, else fetch it using Gallery Storage.
if ( is_numeric( $image ) ) {
// Registry Object for NextGen Gallery.
$registry = C_Component_Registry::get_instance();
// Gallery Storage Object.
$storage = $registry->get_utility( 'I_Gallery_Storage' );
// get an image object.
$image = $storage->object->_image_mapper->find( $image );
}
$smush_savings = $this->get_image_smush_savings( $image );
$resize_savings = $this->get_image_resize_savings( $image );
return $this->recalculate_stats( 'add', $smush_savings, $resize_savings );
}
/**
* Get the Nextgen Smush stats
*
* @return bool|mixed|void
*/
public function get_smush_stats() {
$smushed_stats = array(
'bytes' => 0,
'size_before' => 0,
'size_after' => 0,
'percent' => 0,
);
// Clear up the stats.
if ( 0 == $this->total_count() || $this->get_smushed_count() < 1 ) {
delete_option( self::SMUSH_STATS_OPTION_ID );
}
// Check for the wp_smush_images in the 'nextgen' group.
$stats = $this->get_cache_smush_stats();
$size_before = (int) $this->get_array_value( $stats, 'size_before' );
if ( empty( $size_before ) ) {
return $smushed_stats;
}
$size_after = (int) $this->get_array_value( $stats, 'size_after' );
$stats['bytes'] = $size_before - $size_after;
$stats['bytes'] = $stats['bytes'] > 0 ? $stats['bytes'] : 0;
$stats['percent'] = ( $stats['bytes'] / $stats['size_before'] ) * 100;
// Round off precentage.
$stats['percent'] = ! empty( $stats['percent'] ) ? round( $stats['percent'], 1 ) : 0;
$stats['human'] = size_format( $stats['bytes'], $stats['bytes'] >= 1024 ? 1 : 0 );
$smushed_stats = array_merge( $smushed_stats, $stats );
// Gotta remove the stats for re-smush ids.
if ( $this->get_reoptimize_list()->get_count() ) {
$resmush_stats = $this->get_stats_for_ids( $this->get_reoptimize_list()->get_ids() );
// Recalculate stats, Remove stats for resmush ids.
$smushed_stats = $this->recalculate_stats( 'sub', $smushed_stats, $resmush_stats );
}
return $smushed_stats;
}
/**
* Get the combined stats for given Ids
*
* @param array $ids Image IDs.
*
* @return array|bool Array of Stats for the given ids
*/
public function get_stats_for_ids( $ids = array() ) {
// Return if we don't have an array or no ids.
if ( ! is_array( $ids ) || empty( $ids ) ) {
return false;
}
// Initialize the Stats array.
$stats = array(
'size_before' => 0,
'size_after' => 0,
);
// Calculate the stats, Expensive Operation.
foreach ( $ids as $id ) {
$image_stats = $this->get_attachment_stats( $id );
$stats = $this->recalculate_stats( 'add', $stats, $image_stats );
}
return $stats;
}
/**
* Add/Subtract the values from 2nd array to First array
* This function is very specific to current requirement of stats re-calculation
*
* @param string $op 'add', 'sub' Add or Subtract the values.
* @param array $a1 First array.
* @param array $a2 Second array.
*
* @return array Return $a1
*/
private function recalculate_stats( $op = 'add', $a1 = array(), $a2 = array() ) {
// If the first array itself is not set, return.
if ( empty( $a1 ) ) {
return $a1;
}
// Iterate over keys in first array, and add/subtract the values.
foreach ( $a1 as $k => $v ) {
// If the key is not set in 2nd array, skip.
if ( empty( $a2[ $k ] ) || ! in_array( $k, array( 'size_before', 'size_after' ) ) ) {
continue;
}
// Else perform the operation, Considers the value to be integer, doesn't performs a check.
if ( 'sub' === $op ) {
// Subtract the value.
$a1[ $k ] -= $a2[ $k ];
} elseif ( 'add' === $op ) {
// Add the value.
$a1[ $k ] += $a2[ $k ];
}
}
// Recalculate percentage and human savings.
$a1['bytes'] = $a1['size_before'] - $a1['size_after'];
$a1['percent'] = $a1['bytes'] > 0 ? round( ( $a1['bytes'] / $a1['size_before'] ) * 100, 1 ) : 0;
$a1['human'] = $a1['bytes'] > 0 ? size_format( $a1['bytes'], 1 ) : 0;
return $a1;
}
/**
* Get Super smushed images from the given images array
*
* @param array $images Array of images containing metadata.
*
* @return array Array containing ids of supersmushed images
*/
private function get_super_smushed_images( $images = array() ) {
if ( empty( $images ) ) {
return array();
}
$super_smushed = array();
// Iterate Over all the images.
foreach ( $images as $image_k => $image ) {
if ( empty( $image ) || ! is_array( $image ) || ! isset( $image['wp_smush'] ) ) {
continue;
}
// Check for lossy compression.
if ( ! empty( $image['wp_smush']['stats'] ) && ! empty( $image['wp_smush']['stats']['lossy'] ) ) {
$super_smushed[] = $image_k;
}
}
return $super_smushed;
}
/**
* Recalculate stats for the given smushed ids and update the cache
* Update Super smushed image ids
*
* @throws Exception Exception.
*/
public function update_stats_cache() {
// Get the Image ids.
$smushed_images = $this->get_ngg_images( 'smushed' );
$super_smushed = array(
'ids' => array(),
'timestamp' => '',
);
$stats = $this->get_stats_for_ids( $smushed_images );
$lossy = $this->get_super_smushed_images( $smushed_images );
if ( empty( $stats['bytes'] ) && ! empty( $stats['size_before'] ) ) {
$stats['bytes'] = $stats['size_before'] - $stats['size_after'];
}
$stats['human'] = size_format( ! empty( $stats['bytes'] ) ? $stats['bytes'] : 0 );
if ( ! empty( $stats['size_before'] ) ) {
$stats['percent'] = ( $stats['bytes'] / $stats['size_before'] ) * 100;
$stats['percent'] = round( $stats['percent'], 2 );
}
// Update Re-smush list.
if ( is_array( WP_Smush::get_instance()->core()->nextgen->ng_admin->resmush_ids ) && is_array( $smushed_images ) ) {
$resmush_ids = array_intersect( WP_Smush::get_instance()->core()->nextgen->ng_admin->resmush_ids, array_keys( $smushed_images ) );
}
// If we have resmush ids, add it to db.
if ( ! empty( $resmush_ids ) ) {
// Update re-smush images to db.
$this->get_reoptimize_list()->update_ids( $resmush_ids );
}
// Update Super smushed images in db.
$this->get_supper_smushed_list()->update_ids( $lossy );
// Update Stats Cache.
update_option( self::SMUSH_STATS_OPTION_ID, $stats, false );
}
public function get_reoptimize_list() {
return $this->reoptimize_list;
}
public function get_supper_smushed_list() {
return $this->supper_smushed_list;
}
public function get_supper_smushed_count() {
return count( $this->get_supper_smushed() );
}
private function get_supper_smushed() {
$super_smushed = $this->get_supper_smushed_list()->get_ids();
// If we have images to be resmushed, Update supersmush list.
$resmush_ids = $this->get_reoptimize_list()->get_ids();
if ( ! empty( $resmush_ids ) && ! empty( $super_smushed ) ) {
$super_smushed = array_diff( $super_smushed, $resmush_ids );
}
// If supersmushed images are more than total, clean it up.
if ( count( $super_smushed ) > self::total_count() ) {
$super_smushed = $this->cleanup_super_smush_data();
}
return (array) $super_smushed;
}
/**
* Cleanup Super-smush images array against the all ids in gallery
*
* @return array|mixed|void
*/
private function cleanup_super_smush_data() {
$supper_smushed_list = $this->get_supper_smushed_list();
$super_smushed = $supper_smushed_list->get_ids();
$ids = self::total_count( false, true );
if ( ! empty( $super_smushed ) && is_array( $ids ) ) {
$super_smushed = array_intersect( $super_smushed, $ids );
}
$supper_smushed_list->update_ids( $super_smushed );
}
public function get_global_stats() {
if ( $this->global_stats ) {
return $this->global_stats;
}
$stats = $this->get_smush_stats();
$human_bytes = $this->get_array_value( $stats, 'human' );
if ( empty( $human_bytes ) ) {
$human_bytes = '0 B';
}
$this->global_stats = array(
'count_supersmushed' => $this->get_supper_smushed_count(),
'count_smushed' => $this->get_smushed_count(),
'count_total' => $this->total_count(),
'count_images' => $this->get_smushed_image_count(),
'count_resize' => 0,
'count_skipped' => 0,
'unsmushed' => $this->get_unsmushed_images(),
'count_unsmushed' => count( $this->get_unsmushed_images() ),
'resmush' => $this->get_reoptimize_list()->get_ids(),
'count_resmush' => $this->get_reoptimize_list()->get_count(),
'size_before' => $this->get_array_value( $stats, 'size_before' ),
'size_after' => $this->get_array_value( $stats, 'size_after' ),
'savings_bytes' => $this->get_array_value( $stats, 'bytes' ),
'human_bytes' => $human_bytes,
'savings_resize' => 0,
'savings_resize_human' => 0,
'savings_conversion' => 0,
'savings_dir_smush' => 0,
'savings_percent' => $this->get_array_value( $stats, 'percent' ),
'percent_grade' => $this->get_grade_class(),
'percent_metric' => $this->get_percent_metric(),
'percent_optimized' => $this->get_percent_optimized(),
'remaining_count' => $this->get_remaining_count(),
);
return $this->global_stats;
}
public function get_smushed_image_count() {
$ng_smushed_images = $this->get_ngg_images( 'smushed' );
if ( empty( $ng_smushed_images ) ) {
return 0;
}
$image_count = 0;
// $image in here is expected to be metadata array
foreach ( $ng_smushed_images as $pid => $image ) {
// If there are no smush stats, skip.
if ( empty( $image['wp_smush'] ) || $this->get_reoptimize_list()->has_id( $pid ) ) {
continue;
}
// Get the image count.
if ( ! empty( $image['wp_smush']['sizes'] ) ) {
$image_count += count( $image['wp_smush']['sizes'] );
}
}
return $image_count;
}
public function get_smushed_count() {
return $this->total_count() - $this->get_remaining_count();
}
public function get_unsmushed_images() {
if ( null !== $this->unsmushed_images ) {
return $this->unsmushed_images;
}
$ng_unsmushed_images = $this->get_ngg_images( 'unsmushed' );
if ( ! $ng_unsmushed_images ) {
return array();
}
$this->unsmushed_images = array_keys( $ng_unsmushed_images );
return $this->unsmushed_images;
}
public function get_remaining_count() {
if ( null === $this->remaining_count ) {
$unsmushed_images = $this->get_unsmushed_images();
$resmush_ids = $this->get_reoptimize_list()->get_ids();
$remaining_images = array_unique( array_merge( $resmush_ids, $unsmushed_images ) );
$this->remaining_count = count( $remaining_images );
}
return $this->remaining_count;
}
private function get_percent_optimized() {
$smushed_count = $this->get_smushed_count();
if ( $smushed_count < 1 ) {
return $this->percent_optimized;
}
$total_optimizable_count = $this->total_count();
$remaining_count = $this->get_remaining_count();
$this->percent_optimized = floor( ( $total_optimizable_count - $remaining_count ) * 100 / $total_optimizable_count );
if ( $this->percent_optimized > 100 ) {
$this->percent_optimized = 100;
} elseif ( $this->percent_optimized < 0 ) {
$this->percent_optimized = 0;
}
return $this->percent_optimized;
}
private function get_percent_metric() {
$percent_optimized = $this->get_percent_optimized();
return 0.0 === (float) $percent_optimized ? 100 : $percent_optimized;
}
private function get_grade_class() {
$percent_optimized = $this->get_percent_optimized();
if ( 0 === $percent_optimized ) {
return 'sui-grade-dismissed';
}
$grade = 'sui-grade-f';
if ( $percent_optimized >= 60 && $percent_optimized < 90 ) {
$grade = 'sui-grade-c';
} elseif ( $percent_optimized >= 90 ) {
$grade = 'sui-grade-a';
}
return $grade;
}
public function get_array_value( $array, $key ) {
return isset( $array[ $key ] ) ? $array[ $key ] : null;
}
public function subtract_image_stats( $image ) {
$stats = $this->get_cache_smush_stats();
$stats = $this->recalculate_stats( 'sub', $stats, $this->get_attachment_stats( $image ) );
$this->update_smush_stats( $stats );
}
private function get_image_smush_savings( $image ) {
$image = (array) $image;
if ( ! empty( $image['meta_data']['wp_smush']['stats'] ) ) {
return $image['meta_data']['wp_smush']['stats'];
}
if ( ! empty( $image['wp_smush']['stats'] ) ) {
return $image['wp_smush']['stats'];
}
return array();
}
private function get_image_resize_savings( $image ) {
$image = (array) $image;
if ( ! empty( $image['meta_data']['wp_smush_resize_savings'] ) ) {
return $image['meta_data']['wp_smush_resize_savings'];
}
if ( ! empty( $image['wp_smush_resize_savings'] ) ) {
return $image['wp_smush_resize_savings'];
}
return array();
}
private function update_smush_stats( $stats ) {
return update_option( self::SMUSH_STATS_OPTION_ID, $stats );
}
private function get_cache_smush_stats() {
return get_option( self::SMUSH_STATS_OPTION_ID, array() );
}
}

View File

@ -0,0 +1,101 @@
<?php
/**
* Extend NextGen Mixin class to smush dynamic images.
*
* @package Smush\Core\Integrations\NextGen
*/
namespace Smush\Core\Integrations\NextGen;
use C_Component_Registry;
use C_Gallery_Storage;
use C_Image;
use Mixin;
use nggdb;
use WP_Smush;
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Thumbs
*/
class Thumbs extends Mixin {
/**
* Overrides the NextGen Gallery function, to smush the dynamic images and thumbnails created by gallery
*
* @param int|object|C_Image $image Image object.
* @param string $size Size.
* @param array|null $params Optional. Parameters array.
* @param bool $skip_defaults Optional. Skip defaults.
*
* @return bool|object
*/
function generate_image_size( $image, $size, $params = null, $skip_defaults = false ) {
$smush = WP_Smush::get_instance()->core()->mod->smush;
$image_id = ! empty( $image->pid ) ? $image->pid : '';
// Get image from storage object if we don't have it already.
if ( empty( $image_id ) ) {
// Get metadata For the image.
// Registry Object for NextGen Gallery.
$registry = C_Component_Registry::get_instance();
/**
* Gallery Storage Object.
*
* @var C_Gallery_Storage $storage
*/
$storage = $registry->get_utility( 'I_Gallery_Storage' );
$image_id = $storage->object->_get_image_id( $image );
}
// Call the actual function to generate the image, and pass the image to smush.
$success = $this->call_parent( 'generate_image_size', $image, $size, $params, $skip_defaults );
if ( $success ) {
$filename = $success->fileName;
// Smush it, if it exists.
if ( file_exists( $filename ) ) {
$response = $smush->do_smushit( $filename );
// If the image was smushed.
if ( ! is_wp_error( $response ) && ! empty( $response['data'] ) && $response['data']->bytes_saved > 0 ) {
// Check for existing stats.
if ( ! empty( $image->meta_data ) && ! empty( $image->meta_data['wp_smush'] ) ) {
$stats = $image->meta_data['wp_smush'];
} else {
// Initialize stats array.
$stats = array(
'stats' => array_merge(
$smush->get_size_signature(),
array(
'api_version' => - 1,
'lossy' => - 1,
'keep_exif' => false,
)
),
'sizes' => array(),
);
$stats['bytes'] = $response['data']->bytes_saved;
$stats['percent'] = $response['data']->compression;
$stats['size_after'] = $response['data']->after_size;
$stats['size_before'] = $response['data']->before_size;
$stats['time'] = $response['data']->time;
}
$stats['sizes'][ $size ] = (object) $smush->array_fill_placeholders( $smush->get_size_signature(), (array) $response['data'] );
if ( isset( $image->metadata ) ) {
$image->meta_data['wp_smush'] = $stats;
nggdb::update_image_meta( $image->pid, $image->meta_data );
}
// Allows To get the stats for each image, after the image is smushed.
do_action( 'wp_smush_nextgen_image_stats', $image_id, $stats );
}
}
}
return $success;
}
}