*
* @copyright (c) 2016, Incsub (http://incsub.com)
*/
if ( ! class_exists( 'WpSmushDir' ) ) {
class WpSmushDir {
/**
* @var Contains a list of optimised images
*/
public $optimised_images;
/**
* @var Total Stats for the image optimisation
*
*/
public $stats;
function __construct() {
if ( ! $this->should_continue() ) {
return;
}
//Hook early for free version, in order to display it before the advanced settings
add_action( 'wp_smush_before_advanced_settings', array( $this, 'ui' ) );
//Hook UI at the end of Settings UI
add_action( 'smush_settings_ui_bottom', array( $this, 'ui' ) );
//Output Stats after Resize savings
add_action( 'stats_ui_after_resize_savings', array( $this, 'stats_ui' ) );
//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 Exclude path request
add_action( 'wp_ajax_smush_exclude_path', array( $this, 'smush_exclude_path' ) );
//Handle Ajax request: resume scan
add_action( 'wp_ajax_resume_scan', array( $this, 'resume_scan' ) );
//Handle Ajax request for directory smush stats
add_action( 'wp_ajax_get_dir_smush_stats', array( $this, 'get_dir_smush_stats' ) );
}
/**
* Do not display Directory smush for Subsites
*
* @return bool True/False, whether to display the Directory smush or not
*
*/
function should_continue() {
//Do not show directory smush, if not main site in a network
if ( is_multisite() && ! is_main_site() ) {
return false;
}
return true;
}
function stats_ui() { ?>
get_charset_collate();
//Use a lower index size
$path_index_size = 191;
/**
* 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,
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 (path($path_index_size)),
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
*/
function get_scanned_images() {
global $wpdb;
$query = "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";
$results = $wpdb->get_results( $query, ARRAY_A );
//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 there is any unsmushed image from last scan
*
* @return bool True/False
*
*/
function get_unsmushed_image() {
global $wpdb, $WpSmush;
// If super-smush enabled, add lossy check.
$lossy_condition = $WpSmush->lossy_enabled ? '(image_size IS NULL OR lossy <> 1)' : 'image_size IS NULL';
$query = $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}smush_dir_images WHERE $lossy_condition && last_scan = (SELECT MAX(last_scan) FROM {$wpdb->prefix}smush_dir_images t2 ) GROUP BY id ORDER BY id LIMIT %d", 1 );
$results = $wpdb->get_col( $query );
//If The query went through
if ( empty( $results ) ) {
return false;
} elseif ( is_wp_error( $results ) ) {
error_log( sprintf( "WP Smush Query Error in %s at %s: %s", __FILE__, __LINE__, $results->get_error_message() ) );
return false;
}
return true;
}
/**
* Prints a resume button if required
*/
function show_resume_button() {
if ( ! $this->get_unsmushed_image() ) {
return null;
}
//Print the button ?>
validate_install() ) {
remove_action( 'wp_smush_before_advanced_settings', array( $this, 'ui' ) );
} else {
remove_action( 'smush_settings_ui_bottom', array( $this, 'ui' ) );
}
//Reset the bulk limit
if ( ! $WpSmush->validate_install() ) {
//Reset Transient
$wpsmushit_admin->check_bulk_limit( true, 'dir_sent_count' );
}
wp_nonce_field( 'smush_get_dir_list', 'list_nonce' );
wp_nonce_field( 'smush_get_image_list', 'image_list_nonce' );
$upgrade_link = '';
/** Directory Browser and Image List **/
$wpsmushit_admin->bulk_ui->container_header( 'wp-smush-dir-browser', 'wp-smush-dir-browser', esc_html__( "DIRECTORY SMUSH", "wp-smushit" ) ); ?>
";
$content .= 0 == $width ? "×" : '';
return $content;
}
/**
* Get the image list in a specified directory path
*
* @param string $path
*
* @return string
*/
function get_image_list( $path = '' ) {
global $wpdb;
$base_dir = empty( $path ) ? $_GET['path'] : $path;
//Directory Path
$base_dir = realpath( $base_dir );
//Store the path in option
update_option( 'wp-smush-dir_path', $base_dir );
//Directory Iterator, Exclude . and ..
$dirIterator = new RecursiveDirectoryIterator(
$base_dir
//PHP 5.2 compatibility
//RecursiveDirectoryIterator::SKIP_DOTS
);
$filtered_dir = new WPSmushRecursiveFilterIterator( $dirIterator );
//File Iterator
$iterator = new RecursiveIteratorIterator( $filtered_dir,
RecursiveIteratorIterator::CHILD_FIRST
);
//Iterate over the file List
$files_arr = array();
$images = array();
$count = 0;
$timestamp = gmdate( 'Y-m-d H:i:s' );
$values = array();
foreach ( $iterator as $path ) {
//Used in place of Skip Dots, For php 5.2 compatability
if ( basename( $path ) == '..' || basename( $path ) == '.' ) {
continue;
}
if ( $path->isFile() ) {
$file_path = $path->getPathname();
$file_name = $path->getFilename();
if ( $this->is_image( $file_path ) && ! $this->is_media_library_file( $file_path ) && strpos( $path, '.bak' ) === false ) {
/** To generate Markup **/
$dir_name = dirname( $file_path );
//Initialize if dirname doesn't exists in array already
if ( ! isset( $files_arr[ $dir_name ] ) ) {
$files_arr[ $dir_name ] = array();
}
$files_arr[ $dir_name ][ $file_name ] = $file_path;
/** End */
//Get the file modification time
$file_time = @filectime( $file_path );
/** To be stored in DB, Part of code inspired from Ewwww Optimiser */
$image_size = $path->getSize();
$images [] = $file_path;
$images [] = $image_size;
$images [] = $file_time;
$images [] = $timestamp;
$values[] = '(%s, %d, %d, %s)';
$count ++;
}
}
//Store the Images in db at an interval of 5k
if ( $count >= 5000 ) {
$count = 0;
$query = $this->build_query( $values, $images );
$images = $values = array();
$wpdb->query( $query );
}
}
//Update rest of the images
if ( ! empty( $images ) && $count > 0 ) {
$query = $this->build_query( $values, $images );
$wpdb->query( $query );
}
//remove scanne dimages 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 array( 'files_arr' => $files_arr, 'base_dir' => $base_dir, 'image_items' => $images );
}
/**
* Build and prepare query from the given values and image array
*
* @param $values
* @param $images
*
* @return bool|string|void
*/
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,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 );
return $query;
}
/**
* Sends a Ajax response if no images are found in selected directory
*/
function send_error() {
$message = sprintf( "
%s
", 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
*
*/
function image_list() {
//Check For Permission
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( "Unauthorized" );
}
//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'] ) ) {
wp_send_json_error( "Empth Directory Path" );
}
//Get the File list
$files = $this->get_image_list( $_GET['smush_path'] );
//If files array is empty, send a message
if ( empty( $files['files_arr'] ) ) {
$this->send_error();
}
//Get the markup from the list
$markup = $this->generate_markup( $files );
//Send response
wp_send_json_success( $markup );
}
/**
* Check whether the given path is a image or not
*
* Do not include backup files
*
* @param $path
*
* @return bool
*
*/
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)
*
*/
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 $path File Path
*
* @return bool Whether a image or not
*/
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 ) ) {
return true;
}
return false;
}
/**
* Excludes the Media Upload Directory ( Checks for Year and Month )
*
* @param $path
*
* @return bool
*
* Borrowed from Shortpixel - (y)
*
* @todo: Add a option to filter images if User have turned off the Year and Month Organize option
*
*/
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;
} else if ( false !== strpos( $path, $base_dir ) ) {
//If matches the current upload path
//contains one of the year subfolders of the media library
$pathArr = explode( '/', str_replace( $base_dir . '/', "", $path ) );
if ( count( $pathArr ) >= 1
&& is_numeric( $pathArr[0] ) && $pathArr[0] > 1900 && $pathArr[0] < 2100 //contains the year subfolder
&& ( count( $pathArr ) == 1 //if there is another subfolder then it's the month subfolder
|| ( is_numeric( $pathArr[1] ) && $pathArr[1] > 0 && $pathArr[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;
}
/**
* Creates a tree out of Given path array
*
* @param $path_list Array of path and images
* @param $base_dir Selected Base Path for the image search
*
* @return array Array of images, Child Directories and images inside
*
*/
function build_tree( $path_list, $base_dir ) {
$path_tree = array();
foreach ( $path_list as $path => $images ) {
$path = str_replace( $base_dir, '', $path );
$list = explode( '/', trim( $path, '/' ), 3 );
$last_dir = &$path_tree;
$length = sizeof( $list );
foreach ( $list as $dir ) {
$length --;
$last_dir =& $last_dir[ $dir ];
}
}
return $path_tree;
}
/**
* Returns the count of optimised image
*
* @param array $images
*
* @return int
*/
function optimised_count( $images = array() ) {
//If we have optimised images
if ( ! empty( $images ) && is_array( $images ) ) {
$optimised = 0;
if ( ! is_array( $this->optimised_images ) ) {
return 0;
}
foreach ( $images as $item ) {
//Check if the image is already in optimised list
if ( array_key_exists( $item, $this->optimised_images ) ) {
$optimised ++;
}
}
}
return $optimised;
}
/**
*
* Search for image id from path
*
* @param $path Image path to be searched
* @param $images Array of images
*
* @return image id
*/
function get_image_id( $path, $images ) {
foreach ( $images as $key => $val ) {
if ( $val['path'] === $path ) {
return $val['id'];
}
}
return null;
}
/**
*
* Search for image id from path
*
* @param $path Image path to be searched
* @param $images Array of images
*
* @return image id
*/
function get_image_path( $id, $images ) {
foreach ( $images as $key => $val ) {
if ( $val['id'] === $id ) {
return $val['path'];
}
}
return null;
}
/**
* 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 $images Image array to search within
*
* @return Image array or Empty array
*/
function get_image( $id = '', $path = '', $images ) {
foreach ( $images as $key => $val ) {
if ( ! empty( $id ) && $val['id'] == $id ) {
return $images[ $key ];
} elseif ( ! empty( $path ) && $val['path'] == $path ) {
return $images[ $key ];
}
}
return array();
}
/*
* Generate the markup for all the images
*/
function generate_markup( $images ) {
if ( empty( $images ) || empty( $images['files_arr'] ) || empty( $images['image_items'] ) ) {
return null;
}
$this->total_stats();
$div = wp_nonce_field( 'wp-smush-exclude-path', 'exclude-path-nonce', '', false );
$div .= '
';
//Flag - Whether to print top hr tag or not
$hr = true;
//Flag - Not to print hr tag, if first element is ul in the scanned image list
$index = 1;
$files_arr = $images['files_arr'];
foreach ( $files_arr as $image_path => $image ) {
$count = sizeof( $image );
$wrapper_class = '';
if ( is_array( $image ) && $count > 1 ) {
//Get the number of optimised images for the given image array
$optimised_count = $this->optimised_count( $image );
if ( $optimised_count > 0 ) {
$wrapper_class = $count == $optimised_count ? 'complete' : 'partial';
}
$div .= "