Current File : /home/aventura/www/site/wp-content/plugins/wp-smushit/lib/class-wp-smush-dir.php |
<?php
/**
* Directory Smush: WP_Smush_Dir class
*
* @package WP_Smush
* @subpackage Admin
* @since 2.6
*
* @author Umesh Kumar <umesh@incsub.com>
*
* @copyright (c) 2016, Incsub (http://incsub.com)
*/
include_once 'utils/class-wp-smush-directory-scanner.php';
include_once 'ui/class-wp-smush-dir-ui.php';
if ( ! class_exists( 'WP_Smush_Dir' ) ) {
/**
* Class WP_Smush_Dir
*/
class WP_Smush_Dir {
/**
* Contains a list of optimised images.
*
* @var $optimised_images
*/
public $optimised_images;
/**
* Total Stats for the image optimisation.
*
* @var $stats
*/
public $stats;
/**
* Directory scanner.
*
* @var WP_Smush_Directory_Scanner
*/
public $scanner;
/**
* Directory Smush UI.
*
* @since 2.8.1
*
* @var WP_Smush_Dir_UI
*/
private $ui;
/**
* WP_Smush_Dir constructor.
*/
public function __construct() {
if ( ! $this->should_continue() ) {
// Remove directory smush from tabs if not required.
add_filter( 'smush_setting_tabs', array( $this, 'remove_directory_tab' ) );
return;
}
$this->ui = new WP_Smush_Dir_UI();
$this->scanner = new WP_Smush_Directory_Scanner();
if ( ! $this->scanner->is_scanning() ) {
$this->scanner->reset_scan();
}
// Check to see if the scanner should be running.
add_action( 'admin_footer', array( $this, 'check_scan' ) );
// Handle Ajax request 'smush_get_directory_list'.
add_action( 'wp_ajax_smush_get_directory_list', array( $this, 'directory_list' ) );
// Scan the given directory path for the list of images.
add_action( 'wp_ajax_image_list', array( $this, 'image_list' ) );
// Handle Ajax Request to optimise images.
add_action( 'wp_ajax_optimise', array( $this, 'optimise' ) );
// Handle Ajax request for directory smush stats (stats meta box).
add_action( 'wp_ajax_get_dir_smush_stats', array( $this, 'get_dir_smush_stats' ) );
/**
* Scanner ajax actions.
*
* @since 2.8.1
*/
add_action( 'wp_ajax_directory_smush_start', array( $this, 'directory_smush_start' ) );
add_action( 'wp_ajax_directory_smush_check_step', array( $this, 'directory_smush_check_step' ) );
add_action( 'wp_ajax_directory_smush_finish', array( $this, 'directory_smush_finish' ) );
add_action( 'wp_ajax_directory_smush_cancel', array( $this, 'directory_smush_cancel' ) );
}
/**
* Run the scanner on page refresh (if it's running).
*
* @since 2.8.1
*/
public function check_scan() {
if ( $this->scanner->is_scanning() ) {
?>
<script>
jQuery( document ).ready( function() {
jQuery('#wp-smush-progress-dialog').show();
window.WP_Smush.directory.scanner.scan();
});
</script>
<?php
}
}
/**
* Directory Smush: Start smush.
*
* @since 2.8.1
*/
public function directory_smush_start() {
$this->scanner->init_scan();
wp_send_json_success();
}
/**
* Directory Smush: Smush step.
*
* @since 2.8.1
*/
public function directory_smush_check_step() {
$urls = $this->get_scanned_images();
$current_step = absint( $_POST['step'] ); // Input var ok.
$this->scanner->update_current_step( $current_step );
if ( isset( $urls[ $current_step ] ) ) {
$this->optimise_image( $urls[ $current_step ]['id'] );
}
wp_send_json_success();
}
/**
* Directory Smush: Finish smush.
*
* @since 2.8.1
*/
public function directory_smush_finish() {
$items = isset( $_POST['items'] ) ? absint( $_POST['items'] ) : 0; // Input var ok.
set_transient( 'wp-smush-show-dir-scan-notice', $items, 60 * 5 ); // 5 minutes max.
$this->scanner->reset_scan();
wp_send_json_success();
}
/**
* Directory Smush: Cancel smush.
*
* @since 2.8.1
*/
public function directory_smush_cancel() {
$this->scanner->reset_scan();
wp_send_json_success();
}
/**
* Handles the ajax request for image optimisation in a folder
*
* @param int $image_id Image ID.
*/
private function optimise_image( $image_id ) {
global $wpdb, $wp_smush, $wpsmushit_admin;
$error_msg = '';
// No image ID.
if ( ! isset( $image_id ) ) {
$error_msg = esc_html__( 'Incorrect image id', 'wp-smushit' );
wp_send_json_error( $error_msg );
}
// Check smush limit for free users.
if ( ! $wp_smush->validate_install() ) {
/**
* Free version bulk smush, check the transient counter value.
*
* @var WpSmushitAdmin $wpsmushit_admin
*/
$should_continue = $wpsmushit_admin->check_bulk_limit( false, 'dir_sent_count' );
// Send a error for the limit.
if ( ! $should_continue ) {
wp_send_json_error(
array(
'error' => 'dir_smush_limit_exceeded',
'continue' => false,
)
);
}
}
$id = intval( $image_id );
if ( ! $scanned_images = wp_cache_get( 'wp_smush_scanned_images' ) ) {
$scanned_images = $this->get_scanned_images();
}
$image = $this->get_image( $id, '', $scanned_images );
if ( empty( $image ) ) {
// If there are no stats.
$error_msg = esc_html__( 'Could not find image id in last scanned images', 'wp-smushit' );
wp_send_json_error( $error_msg );
}
$path = $image['path'];
// We have the image path, optimise.
$smush_results = $wp_smush->do_smushit( $path );
if ( is_wp_error( $smush_results ) ) {
/* @var WP_Error $smush_results */
$error_msg = $smush_results->get_error_message();
} elseif ( empty( $smush_results['data'] ) ) {
// If there are no stats.
$error_msg = esc_html__( "Image couldn't be optimized", 'wp-smushit' );
}
if ( ! empty( $error_msg ) ) {
// Store the error in DB. All good, Update the stats.
$wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->prefix}smush_dir_images SET error=%s WHERE id=%d LIMIT 1",
$error_msg, $id
) ); // Db call ok; no-cache ok.
wp_send_json_error(
array(
'error' => $error_msg,
'image' => array(
'id' => $id,
),
)
);
}
// Get file time.
$file_time = @filectime( $path );
// If super-smush enabled, update supersmushed meta value also.
$lossy = $wp_smush->lossy_enabled ? 1 : 0;
// All good, Update the stats.
$wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->prefix}smush_dir_images SET image_size=%d, file_time=%d, lossy=%s WHERE id=%d LIMIT 1",
$smush_results['data']->after_size, $file_time, $lossy, $id
) ); // Db call ok; no-cache ok.
// Update bulk limit transient.
$wpsmushit_admin->update_smush_count( 'dir_sent_count' );
}
/**
* Do not display Directory smush for subsites.
*
* @return bool True/False, whether to display the Directory smush or not
*/
public function should_continue() {
// Do not show directory smush, if not main site in a network.
if ( ! is_main_site() || is_network_admin() ) {
return false;
}
return true;
}
/**
* Create the Smush image table to store the paths of scanned images, and stats
*/
public function create_table() {
global $wpdb;
// Run the query only on directory smush page.
if ( ! isset( $_GET['page'] ) || 'smush' !== $_GET['page'] ) {
return null;
}
$charset_collate = $wpdb->get_charset_collate();
/**
* Table: wp_smush_dir_images
* Columns:
* id -> Auto Increment ID
* path -> Absolute path to the image file
* resize -> Whether the image was resized or not
* lossy -> Whether the image was super-smushed/lossy or not
* image_size -> Current image size post optimisation
* orig_size -> Original image size before optimisation
* file_time -> Unix time for the file creation, to match it against the current creation time,
* in order to confirm if it is optimised or not
* last_scan -> Timestamp, Get images form last scan by latest timestamp
* are from latest scan only and not the whole list from db
* meta -> For any future use
*/
$sql = "CREATE TABLE {$wpdb->prefix}smush_dir_images (
id mediumint(9) NOT NULL AUTO_INCREMENT,
path text NOT NULL,
path_hash CHAR(32),
resize varchar(55),
lossy varchar(55),
error varchar(55) DEFAULT NULL,
image_size int(10) unsigned,
orig_size int(10) unsigned,
file_time int(10) unsigned,
last_scan timestamp DEFAULT '0000-00-00 00:00:00',
meta text,
UNIQUE KEY id (id),
UNIQUE KEY path_hash (path_hash),
KEY image_size (image_size)
) $charset_collate;";
// Include the upgrade library to initialize a table.
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
}
/**
* Get the image ids and path for last scanned images
*
* @return array Array of last scanned images containing image id and path
*/
public function get_scanned_images() {
global $wpdb;
$results = $wpdb->get_results( "SELECT id, path, orig_size FROM {$wpdb->prefix}smush_dir_images WHERE last_scan = (SELECT MAX(last_scan) FROM {$wpdb->prefix}smush_dir_images ) GROUP BY id ORDER BY id", ARRAY_A ); // Db call ok; no-cache ok.
// Return image ids.
if ( is_wp_error( $results ) ) {
error_log( sprintf( "WP Smush Query Error in %s at %s: %s", __FILE__, __LINE__, $results->get_error_message() ) );
$results = array();
}
return $results;
}
/**
* Check if the image file is media library file
*
* @param string $file_path File path.
*
* @return bool
*/
private function is_media_library_file( $file_path ) {
$upload_dir = wp_upload_dir();
$upload_path = $upload_dir['path'];
// Get the base path of file.
$base_dir = dirname( $file_path );
if ( $base_dir === $upload_path ) {
return true;
}
return false;
}
/**
* Return a directory/File list
*
* PHP Connector
*/
public function directory_list() {
// Check For permission.
if ( ! current_user_can( 'manage_options' ) || ! is_user_logged_in() ) {
wp_send_json_error( __( 'Unauthorized', 'wp-smushit' ) );
}
// Verify nonce.
check_ajax_referer( 'smush_get_dir_list', 'list_nonce' );
// Get the root path for a main site or subsite.
$root = realpath( $this->get_root_path() );
$dir = ( isset( $_GET['dir'] ) && ! is_array( $_GET['dir'] ) ) ? ltrim( sanitize_text_field( wp_unslash( $_GET['dir'] ) ), '/' ) : null; // Input var ok.
$post_dir = strlen( $dir ) > 1 ? path_join( $root, $dir ) : $root . $dir;
$post_dir = realpath( rawurldecode( $post_dir ) );
// If the final path doesn't contains the root path, bail out.
if ( ! $root || false === $post_dir || 0 !== strpos( $post_dir, $root ) ) {
wp_send_json_error( __( 'Unauthorized', 'wp-smushit' ) );
}
$supported_image = array(
'gif',
'jpg',
'jpeg',
'png',
);
if ( file_exists( $post_dir ) ) {
$files = scandir( $post_dir );
// Exclude hidden files.
if ( ! empty( $files ) ) {
$files = preg_grep( '/^([^.])/', $files );
}
$return_dir = substr( $post_dir, strlen( $root ) );
natcasesort( $files );
if ( count( $files ) !== 0 && ! $this->skip_dir( $post_dir ) ) {
$tree = array();
foreach ( $files as $file ) {
$html_rel = htmlentities( ltrim( path_join( $return_dir, $file ), '/' ) );
$html_name = htmlentities( $file );
$ext = preg_replace( '/^.*\./', '', $file );
$file_path = path_join( $post_dir, $file );
if ( ! file_exists( $file_path ) || '.' === $file || '..' === $file ) {
continue;
}
// Skip unsupported files and files that are already in the media library.
if ( ! is_dir( $file_path ) && ( ! in_array( $ext, $supported_image, true ) || $this->is_media_library_file( $file_path ) ) ) {
continue;
}
$skip_path = $this->skip_dir( $file_path );
$tree[] = array(
'title' => $html_name,
'key' => $html_rel,
'folder' => is_dir( $file_path ),
'lazy' => ! $skip_path,
'checkbox' => true,
'unselectable' => $skip_path, // Skip Uploads folder - Media Files.
);
}
wp_send_json_success( $tree );
}
} // End if().
}
/**
* Get root path of the installation.
*
* @return string Root path.
*/
public function get_root_path() {
// If main site.
if ( is_main_site() ) {
/**
* Sometimes content directories may reside outside
* the installation sub directory. We need to make sure
* we are selecting the root directory, not installation
* directory.
* @see https://xnau.com/finding-the-wordpress-root-path-for-an-alternate-directory-structure/
* @see https://app.asana.com/0/14491813218786/487682361460247/f
*/
$content_path = explode( '/', WP_CONTENT_DIR );
// Get root path and explod.
$root_path = explode( '/', get_home_path() );
// Find the length of the shortest one.
$end = min( count( $content_path ), count( $root_path ) );
$i = 0;
$common_path = array();
// Add the component if they are the same in both paths.
while ( $content_path[ $i ] === $root_path[ $i ] && $i < $end ) {
$common_path[] = $content_path[ $i ];
$i++;
}
return implode( '/', $common_path );
} else {
$up = wp_upload_dir();
return $up['basedir'];
}
}
/**
* Get the image list in a specified directory path.
*
* @since 2.8.1 Added support for selecting files.
*
* @param string|array $paths Path where to look for images, or selected images.
*
* @return array
*/
private function get_image_list( $paths = '' ) {
global $wpsmush_helper;
// Error with directory tree.
if ( ! is_array( $paths ) ) {
wp_send_json_error( array(
'message' => __( 'There was a problem getting the selected directories', 'wp-smushit' ),
) );
}
$count = 0;
$images = array();
$values = array();
$timestamp = gmdate( 'Y-m-d H:i:s' );
/**
* Temporary increase the limit.
*
* @var WpSmushHelper $wpsmush_helper
*/
$wpsmush_helper->increase_memory_limit();
// Iterate over all the selected items (can be either an image or directory).
foreach ( $paths as $path ) {
/**
* Path is an image.
*/
if ( ! is_dir( $path ) && ! $this->is_media_library_file( $path ) && ! strpos( $path, '.bak' ) ) {
if ( ! $this->is_image( $path ) ) {
continue;
}
// Image already added. Skip.
if ( in_array( $path, $images, true ) ) {
continue;
}
$images[] = $path;
$images[] = md5( $path );
$images[] = @filesize( $path ); // Get the file size.
$images[] = @filectime( $path ); // Get the file modification time.
$images[] = $timestamp;
$values[] = '(%s, %s, %d, %d, %s)';
$count++;
// Store the images in db at an interval of 5k.
if ( $count >= 5000 ) {
$count = 0;
$this->store_images( $values, $images );
$images = $values = array();
}
continue;
}
/**
* Path is a directory.
*/
$base_dir = realpath( rawurldecode( $path ) );
if ( ! $base_dir ) {
wp_send_json_error( array(
'message' => __( 'Unauthorized', 'wp-smushit' ),
) );
}
// Directory Iterator, Exclude . and ..
$filtered_dir = new WPSmushRecursiveFilterIterator( new RecursiveDirectoryIterator( $base_dir ) );
// File Iterator.
$iterator = new RecursiveIteratorIterator( $filtered_dir, RecursiveIteratorIterator::CHILD_FIRST );
foreach ( $iterator as $file ) {
// Used in place of Skip Dots, For php 5.2 compatibility.
if ( basename( $file ) === '..' || basename( $file ) === '.' ) {
continue;
}
// Not a file. Skip.
if ( ! $file->isFile() ) {
continue;
}
$file_path = $file->getPathname();
if ( $this->is_image( $file_path ) && ! $this->is_media_library_file( $file_path ) && strpos( $file, '.bak' ) === false ) {
/** To be stored in DB, Part of code inspired from Ewwww Optimiser */
$images[] = $file_path;
$images[] = md5( $file_path );
$images[] = $file->getSize();
$images[] = @filectime( $file_path ); // Get the file modification time.
$images[] = $timestamp;
$values[] = '(%s, %s, %d, %d, %s)';
$count++;
}
// Store the images in db at an interval of 5k.
if ( $count >= 5000 ) {
$count = 0;
$this->store_images( $values, $images );
$images = $values = array();
}
} // End foreach().
} // End foreach().
// Update rest of the images.
if ( ! empty( $images ) && $count > 0 ) {
$this->store_images( $values, $images );
}
// Remove scanned images from cache.
wp_cache_delete( 'wp_smush_scanned_images' );
// Get the image ids.
$images = $this->get_scanned_images();
// Store scanned images in cache.
wp_cache_add( 'wp_smush_scanned_images', $images );
return $images;
}
/**
* Write to the database.
*
* @since 2.8.1
*
* @param array $values Values for query build.
* @param array $images Array of images.
*/
private function store_images( $values, $images ) {
global $wpdb;
$query = $this->build_query( $values, $images );
$wpdb->query( $query ); // Db call ok; no-cache ok.
}
/**
* Build and prepare query from the given values and image array.
*
* @param array $values Values.
* @param array $images Images.
*
* @return bool|string
*/
private function build_query( $values, $images ) {
if ( empty( $images ) || empty( $values ) ) {
return false;
}
global $wpdb;
$values = implode( ',', $values );
// Replace with image path and respective parameters.
$query = "INSERT INTO {$wpdb->prefix}smush_dir_images (path, path_hash, orig_size,file_time,last_scan) VALUES $values ON DUPLICATE KEY UPDATE image_size = IF( file_time < VALUES(file_time), NULL, image_size ), file_time = IF( file_time < VALUES(file_time), VALUES(file_time), file_time ), last_scan = VALUES( last_scan )";
$query = $wpdb->prepare( $query, $images ); // Db call ok; no-cache ok.
return $query;
}
/**
* Sends a Ajax response if no images are found in selected directory.
*/
private function send_error() {
$message = sprintf( "<div class='sui-notice sui-notice-info'><p>%s</p></div>", esc_html__( 'We could not find any images in the selected directory.', 'wp-smushit' ) );
wp_send_json_error( array(
'message' => $message,
) );
}
/**
* Handles Ajax request to obtain the Image list within a selected directory path
*/
public function image_list() {
// Check For permission.
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( __( 'Unauthorized', 'wp-smushit' ) );
}
// Verify nonce.
check_ajax_referer( 'smush_get_image_list', 'image_list_nonce' );
// Check if directory path is set or not.
if ( empty( $_GET['smush_path'] ) ) { // Input var ok.
wp_send_json_error( __( 'Empty Directory Path', 'wp-smushit' ) );
}
// This will add the images to the database and get the file list.
$files = $this->get_image_list( $_GET['smush_path'] ); // Input var ok.
// If files array is empty, send a message.
if ( empty( $files ) ) {
$this->send_error();
}
// Send response.
wp_send_json_success( count( $files ) );
}
/**
* Check whether the given path is a image or not.
*
* Do not include backup files.
*
* @param string $path Image path.
*
* @return bool
*/
private function is_image( $path ) {
// Check if the path is valid.
if ( ! file_exists( $path ) || ! $this->is_image_from_extension( $path ) ) {
return false;
}
$a = @getimagesize( $path );
// If a is not set.
if ( ! $a || empty( $a ) ) {
return false;
}
$image_type = $a[2];
if ( in_array( $image_type, array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG ) ) ) {
return true;
}
return false;
}
/**
* Obtain the path to the admin directory.
*
* @return string
*
* Thanks @andrezrv (Github)
* TODO: this does not properly get the admin path in Bedrock
*/
private function get_admin_path() {
// Replace the site base URL with the absolute path to its installation directory.
$admin_path = rtrim( str_replace( get_bloginfo( 'url' ) . '/', ABSPATH, get_admin_url() ), '/' );
// Make it filterable, so other plugins can hook into it.
$admin_path = apply_filters( 'wp_smush_get_admin_path', $admin_path );
return $admin_path;
}
/**
* Check if the given file path is a supported image format
*
* @param string $path File path.
*
* @return bool Whether a image or not
*/
private function is_image_from_extension( $path ) {
$supported_image = array( 'gif', 'jpg', 'jpeg', 'png' );
$ext = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) ); // Using strtolower to overcome case sensitive.
if ( in_array( $ext, $supported_image, true ) ) {
return true;
}
return false;
}
/**
* Excludes the Media Upload Directory ( Checks for Year and Month ).
*
* Borrowed from Shortpixel - (y)*
* TODO: Add a option to filter images if User have turned off the Year and Month Organize option
*
* @param string $path Path.
*
* @return bool
*/
public function skip_dir( $path ) {
// Admin directory path.
$admin_dir = $this->get_admin_path();
// Includes directory path.
$includes_dir = ABSPATH . WPINC;
// Upload directory.
$upload_dir = wp_upload_dir();
$base_dir = $upload_dir['basedir'];
$skip = false;
// Skip sites folder for multisite.
if ( false !== strpos( $path, $base_dir . '/sites' ) ) {
$skip = true;
} elseif ( false !== strpos( $path, $base_dir ) ) {
// If matches the current upload path contains one of the year sub folders of the media library.
$path_arr = explode( '/', str_replace( $base_dir . '/', '', $path ) );
if ( count( $path_arr ) >= 1
&& is_numeric( $path_arr[0] ) && $path_arr[0] > 1900 && $path_arr[0] < 2100 // Contains the year sub folder.
&& ( 1 === count( $path_arr ) // If there is another sub folder then it's the month sub folder.
|| ( is_numeric( $path_arr[1] ) && $path_arr[1] > 0 && $path_arr[1] < 13 ) )
) {
$skip = true;
}
} elseif ( ( false !== strpos( $path, $admin_dir ) ) || false !== strpos( $path, $includes_dir ) ) {
$skip = true;
}
// Can be used to skip/include folders matching a specific directory path.
apply_filters( 'wp_smush_skip_folder', $skip, $path );
return $skip;
}
/**
* Search for image from given image id or path.
*
* @param string $id Image id to search for.
* @param string $path Image path to search for.
* @param array $images Image array to search within.
*
* @return array Image array or empty array.
*/
private function get_image( $id = '', $path = '', $images ) {
foreach ( $images as $key => $val ) {
if ( ! empty( $id ) && (int) $val['id'] === $id ) {
return $images[ $key ];
} elseif ( ! empty( $path ) && $val['path'] === $path ) {
return $images[ $key ];
}
}
return array();
}
/**
* Fetch all the optimised image, calculate stats.
*
* @param bool $force_update Should force update?
*
* @return array Total stats.
*/
function total_stats( $force_update = false ) {
// If not forced to update.
if ( ! $force_update ) {
// Get stats from cache.
$total_stats = wp_cache_get( WP_SMUSH_PREFIX . 'dir_total_stats', 'wp-smush' );
// If we have already calculated the stats and found in cache, return it.
if ( false !== $total_stats ) {
return $total_stats;
}
}
global $wpdb;
$offset = 0;
$optimised = 0;
$limit = 1000;
$images = array();
$total = $wpdb->get_col( "SELECT count(id) FROM {$wpdb->prefix}smush_dir_images" ); // Db call ok; no-cache ok.
$total = ! empty( $total ) && is_array( $total ) ? $total[0] : 0;
$continue = true;
while ( $continue && $results = $wpdb->get_results( "SELECT path, image_size, orig_size FROM {$wpdb->prefix}smush_dir_images WHERE image_size IS NOT NULL ORDER BY `id` LIMIT $offset, $limit", ARRAY_A ) ) { // Db call ok; no-cache ok.
if ( ! empty( $results ) ) {
$images = array_merge( $images, $results );
}
$offset += $limit;
// If offset is above total number, do not query.
if ( $offset > $total ) {
$continue = false;
}
}
// Iterate over stats, return count and savings.
if ( ! empty( $images ) ) {
// Init the stats array.
$this->stats = array(
'path' => '',
'image_size' => 0,
'orig_size' => 0,
);
foreach ( $images as $im ) {
foreach ( $im as $key => $val ) {
if ( 'path' === $key ) {
$this->optimised_images[ $val ] = $im;
continue;
}
$this->stats[ $key ] += (int) $val;
}
$optimised++;
}
}
// Get the savings in bytes and percent.
if ( ! empty( $this->stats ) && ! empty( $this->stats['orig_size'] ) ) {
$this->stats['bytes'] = ( $this->stats['orig_size'] > $this->stats['image_size'] ) ? $this->stats['orig_size'] - $this->stats['image_size'] : 0;
$this->stats['percent'] = number_format_i18n( ( ( $this->stats['bytes'] / $this->stats['orig_size'] ) * 100 ), 1 );
// Convert to human readable form.
$this->stats['human'] = size_format( $this->stats['bytes'], 1 );
}
$this->stats['total'] = $total;
$this->stats['optimised'] = $optimised;
// Set stats in cache.
wp_cache_set( WP_SMUSH_PREFIX . 'dir_total_stats', $this->stats, 'wp-smush' );
return $this->stats;
}
/**
* Returns the number of images scanned and optimised
*
* @return array
*/
private function last_scan_stats() {
global $wpdb;
$results = $wpdb->get_results( "SELECT id, image_size, orig_size FROM {$wpdb->prefix}smush_dir_images WHERE last_scan = (SELECT MAX(last_scan) FROM {$wpdb->prefix}smush_dir_images ) GROUP BY id", ARRAY_A ); // Db call ok; no-cache ok.
$total = count( $results );
$smushed = 0;
$stats = array(
'image_size' => 0,
'orig_size' => 0,
);
// Get the Smushed count, and stats sum.
foreach ( $results as $image ) {
if ( ! is_null( $image['image_size'] ) ) {
$smushed ++;
}
// Summation of stats.
foreach ( $image as $k => $v ) {
if ( 'id' === $k ) {
continue;
}
$stats[ $k ] += $v;
}
}
// Stats.
$stats['total'] = $total;
$stats['smushed'] = $smushed;
return $stats;
}
/**
* Handles the ajax request for image optimisation in a folder
*
* TODO: refactor this, don't think that we need all this stuff.
*/
public function optimise() {
global $wpdb, $wp_smush, $wpsmushit_admin;
// Verify the ajax nonce.
check_ajax_referer( 'wp_smush_all', 'nonce' );
$error_msg = '';
if ( empty( $_GET['image_id'] ) ) { // Input var ok.
// If there are no stats.
wp_send_json_error( esc_html__( 'Incorrect image id', 'wp-smushit' ) );
}
// Get the last scan stats.
$last_scan = $this->last_scan_stats();
$stats = array();
// Check smush limit for free users.
if ( ! $wp_smush->validate_install() ) {
// Free version bulk smush, check the transient counter value.
$should_continue = $wpsmushit_admin->check_bulk_limit( false, 'dir_sent_count' );
// Send a error for the limit.
if ( ! $should_continue ) {
wp_send_json_error(
array(
'error' => 'dir_smush_limit_exceeded',
'continue' => false,
)
);
}
}
$id = intval( $_GET['image_id'] ); // Input var ok.
if ( ! $scanned_images = wp_cache_get( 'wp_smush_scanned_images' ) ) {
$scanned_images = $this->get_scanned_images();
}
$image = $this->get_image( $id, '', $scanned_images );
if ( empty( $image ) ) {
// If there are no stats.
wp_send_json_error( esc_html__( 'Could not find image id in last scanned images', 'wp-smushit' ) );
}
$path = $image['path'];
// We have the image path, optimise.
$smush_results = $wp_smush->do_smushit( $path );
if ( is_wp_error( $smush_results ) ) {
/* @var WP_Error $smush_results */
$error_msg = $smush_results->get_error_message();
} elseif ( empty( $smush_results['data'] ) ) {
// If there are no stats.
$error_msg = esc_html__( "Image couldn't be optimized", 'wp-smushit' );
}
if ( ! empty( $error_msg ) ) {
// Store the error in DB. All good, Update the stats.
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}smush_dir_images SET error=%s WHERE id=%d LIMIT 1", $error_msg, $id ) ); // Db call ok; no-cache ok.
$error_msg = '<div class="wp-smush-error">' . $error_msg . '</div>';
wp_send_json_error(
array(
'error' => $error_msg,
'image' => array(
'id' => $id,
),
)
);
}
// Get file time.
$file_time = @filectime( $path );
// If super-smush enabled, update supersmushed meta value also.
$lossy = $wp_smush->lossy_enabled ? 1 : 0;
// All good, Update the stats.
$query = "UPDATE {$wpdb->prefix}smush_dir_images SET image_size=%d, file_time=%d, lossy=%s WHERE id=%d LIMIT 1";
$query = $wpdb->prepare( $query, $smush_results['data']->after_size, $file_time, $lossy, $id );
$wpdb->query( $query );
// Get the global stats if current dir smush completed.
if ( isset( $_GET['get_stats'] ) && 1 == $_GET['get_stats'] ) {
// This will setup directory smush stats too.
$wpsmushit_admin->setup_global_stats();
$stats = $wpsmushit_admin->stats;
$stats['total'] = $wpsmushit_admin->total_count;
$stats['smushed'] = $wpsmushit_admin->smushed_count;
if ( 1 === $lossy ) {
$stats['super_smushed'] = $wpsmushit_admin->super_smushed;
}
// Set tootltip text to update.
$stats['tooltip_text'] = ! empty( $stats['total_images'] ) ? sprintf( __( "You've smushed %d images in total.", "wp-smushit" ), $stats['total_images'] ) : '';
// Get the total dir smush stats.
$total = $wpsmushit_admin->dir_stats;
} else {
$total = $this->total_stats();
}
// Show the image wise stats.
$image = array(
'id' => $id,
'size_before' => $image['orig_size'],
'size_after' => $smush_results['data']->after_size,
);
$bytes = $image['size_before'] - $image['size_after'];
$image['savings'] = size_format( $bytes, 1 );
$image['percent'] = $image['size_before'] > 0 ? number_format_i18n( ( ( $bytes / $image['size_before'] ) * 100 ), 1 ) . '%' : 0;
$data = array(
'image' => $image,
'total' => $total,
'latest_scan' => $last_scan,
);
// If current dir smush completed, include global stats.
if ( ! empty( $stats ) ) {
$data['stats'] = $stats;
}
// Update Bulk Limit Transient.
$wpsmushit_admin->update_smush_count( 'dir_sent_count' );
wp_send_json_success( $data );
}
/**
* Combine the stats from Directory Smush and Media Library Smush.
*
* @param array $stats Directory Smush stats.
*
* @return array Combined array of stats.
*/
private function combined_stats( $stats ) {
if ( empty( $stats ) || empty( $stats['percent'] ) || empty( $stats['bytes'] ) ) {
return array();
}
/* @var WpSmushitAdmin $wpsmushit_admin */
global $wpsmushit_admin;
$dasharray = 125.663706144;
// Initialize global stats.
$wpsmushit_admin->setup_global_stats();
// Get the total/Smushed attachment count.
$total_attachments = $wpsmushit_admin->total_count + $stats['total'];
$total_images = $wpsmushit_admin->stats['total_images'] + $stats['total'];
$smushed = $wpsmushit_admin->smushed_count + $stats['optimised'];
$savings = ! empty( $wpsmushit_admin->stats ) ? $wpsmushit_admin->stats['bytes'] + $stats['bytes'] : $stats['bytes'];
$size_before = ! empty( $wpsmushit_admin->stats ) ? $wpsmushit_admin->stats['size_before'] + $stats['orig_size'] : $stats['orig_size'];
$percent = $size_before > 0 ? ( $savings / $size_before ) * 100 : 0;
// Store the stats in array.
$result = array(
'total_count' => $total_attachments,
'smushed_count' => $smushed,
'savings' => size_format( $savings ),
'percent' => round( $percent, 1 ),
'image_count' => $total_images,
'dash_offset' => $total_attachments > 0 ? $dasharray - ( $dasharray * ( $smushed / $total_attachments ) ) : $dasharray,
/* translators: %s: total number of images */
'tooltip_text' => ! empty( $total_images ) ? sprintf( __( "You've smushed %d images in total.", 'wp-smushit' ), $total_images ) : '',
);
return $result;
}
/**
* Returns Directory Smush stats and Cumulative stats
*/
public function get_dir_smush_stats() {
$result = array();
// Store the Total/Smushed count.
$stats = $this->total_stats();
$result['dir_smush'] = $stats;
// Cumulative Stats.
$result['combined_stats'] = $this->combined_stats( $stats );
// Store the stats in options table.
update_option( 'dir_smush_stats', $result, false );
// Send ajax response.
wp_send_json_success( $result );
}
/**
* Display a admin notice on smush screen if the custom table wasn't created
*
* @return string $notice Notice if table doesn't exists.
*
* @todo: Update text
*/
public function check_for_table_error() {
global $wpdb;
$notice = '';
$current_screen = get_current_screen();
if ( 'toplevel_page_smush' !== $current_screen->id && 'toplevel_page_smush-network' !== $current_screen->id ) {
return $notice;
}
$smush_table = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->prefix . 'smush_dir_images' ) ) ); // Db call ok; no-cache ok.
if ( ! $smush_table ) {
// Display a notice.
$notice = '<div class="sui-notice sui-notice-warning missing_table"><p>';
$notice .= esc_html__( 'Directory smushing requires custom tables and it seems there was an error creating tables. For help, please contact our team on the support forums', 'wp-smushit' );
$notice .= '</p></div>';
}
return $notice;
}
/**
* Remove directory smush from tabs.
*
* If not in main site, do not show directory smush.
*
* @param array $tabs Tabs.
*
* @return array
*/
public function remove_directory_tab( $tabs ) {
if ( isset( $tabs['directory'] ) ) {
unset( $tabs['directory'] );
}
return $tabs;
}
}
global $wpsmush_dir;
$wpsmush_dir = new WP_Smush_Dir();
} // End if().
/**
* Filters the list of directories, exclude the media subfolders.
*/
if ( class_exists( 'RecursiveFilterIterator' ) && ! class_exists( 'WPSmushRecursiveFilterIterator' ) ) {
/**
* Class WPSmushRecursiveFilterIterator
*/
class WPSmushRecursiveFilterIterator extends RecursiveFilterIterator {
/**
* Accept method.
*
* @return bool
*/
public function accept() {
/* @var WP_Smush_Dir $wpsmush_dir */
global $wpsmush_dir;
$path = $this->current()->getPathname();
if ( $this->isDir() && ! $wpsmush_dir->skip_dir( $path ) ) {
return true;
}
if ( ! $this->isDir() ) {
return true;
}
return false;
}
}
}