* * @copyright (c) 2016, Incsub (http://incsub.com) */ // Include Bulk UI. require_once WP_SMUSH_DIR . 'lib/ui/class-wp-smush-ui.php'; if ( ! class_exists( 'WpSmushitAdmin' ) ) { /** * Class WpSmushitAdmin * * @property int $remaining_count * @property int $total_count * @property int $smushed_count */ class WpSmushitAdmin extends WP_Smush { /** * Settings array. * * @var array Settings */ public $settings; public $bulk; /** * Total count of attachments for smushing. * * @var int $total_count */ public $total_count; /** * Smushed attachments out of total attachments. * * @var int $smushed_count */ public $smushed_count; /** * Smushed attachments from selected directories. * * @var array $dir_stats */ public $dir_stats; /** * Smushed attachments out of total attachments. * * @var int $remaining_count */ public $remaining_count; /** * Super Smushed attachments count. * * @var int $super_smushed */ public $super_smushed; /** * Attachment IDs. * * @var array $attachments */ public $attachments = array(); /** * Unsmushed image ids. * * @var array $unsmushed_attachments */ public $unsmushed_attachments = array(); /** * Attachment ids which are smushed. * * @var array $smushed_attachments */ public $smushed_attachments = array(); /** * Image ids that needs to be resmushed. * * @var array $resmush_ids */ public $resmush_ids = array(); /** * Allowed mime types of image. * * @var array $mime_types */ public $mime_types = array( 'image/jpg', 'image/jpeg', 'image/x-citrix-jpeg', 'image/gif', 'image/png', 'image/x-png' ); /** * Stores the stats for all the images. * * @var array $stats */ public $stats; /** * Limit for allowed number of images per bulk request. * * This is enforced at api level too. * * @var int $max_free_bulk */ private $max_free_bulk = 50; /** * Link to upgrade. * * @var string $upgrade_url */ public $upgrade_url = 'https://premium.wpmudev.org/project/wp-smush-pro/'; /** * Images dimensions array. * * @var array $image_sizes */ public $image_sizes = array(); /** * Stores the headers returned by the latest API call. * * @var string $api_headers */ public $api_headers = array(); /** * List of pages where smush needs to be loaded. * * @var array $pages */ public $pages = array( 'nggallery-manage-images', 'gallery_page_nggallery-manage-gallery', 'gallery_page_wp-smush-nextgen-bulk', 'post', 'post-new', 'upload', 'toplevel_page_smush-network', 'toplevel_page_smush', ); /** * List of smush settings pages. * * @var array $plugin_pages */ public $plugin_pages = array( 'gallery_page_wp-smush-nextgen-bulk', 'toplevel_page_smush-network', 'toplevel_page_smush', ); /** * List of featurws/settings that are free. * * @var array $basic_features */ public $basic_features = array( 'networkwide', 'auto', 'strip_exif', 'resize', 'gutenberg', ); /** * WpSmushitAdmin constructor. */ public function __construct() { // Hook scripts and styles. add_action( 'admin_init', array( $this, 'register' ) ); // Hook custom screen. add_action( 'admin_menu', array( $this, 'screen' ) ); // Network Settings Page. add_action( 'network_admin_menu', array( $this, 'screen' ) ); // Ignore image from bulk Smush. add_action( 'wp_ajax_ignore_bulk_image', array( $this, 'ignore_bulk_image' ) ); // Handle Smush Bulk Ajax. add_action( 'wp_ajax_wp_smushit_bulk', array( $this, 'process_smush_request' ) ); // Handle Smush Single Ajax. add_action( 'wp_ajax_wp_smushit_manual', array( $this, 'smush_manual' ) ); // Handle resmush operation. add_action( 'wp_ajax_smush_resmush_image', array( $this, 'resmush_image' ) ); // Scan images as per the latest settings. add_action( 'wp_ajax_scan_for_resmush', array( $this, 'scan_images' ) ); add_filter( 'plugin_action_links_' . WP_SMUSH_BASENAME, array( $this, 'settings_link', ) ); add_filter( 'network_admin_plugin_action_links_' . WP_SMUSH_BASENAME, array( $this, 'settings_link', ) ); // Smush Upgrade. add_action( 'admin_notices', array( $this, 'smush_upgrade' ) ); // Handle the smush pro dismiss features notice ajax. add_action( 'wp_ajax_dismiss_upgrade_notice', array( $this, 'dismiss_upgrade_notice' ) ); // Handle the smush pro dismiss features notice ajax. add_action( 'wp_ajax_dismiss_welcome_notice', array( $this, 'dismiss_welcome_notice' ) ); // Handle the smush pro dismiss features notice ajax. add_action( 'wp_ajax_dismiss_update_info', array( $this, 'dismiss_update_info' ) ); // Handle ajax request to dismiss the s3 warning. add_action( 'wp_ajax_dismiss_s3support_alert', array( $this, 'dismiss_s3support_alert' ) ); // Ajax request for quick Setup. add_action( 'wp_ajax_setupSmush', array( $this, 'setupSmush' ) ); // Update the Super Smush count, after the smushing. add_action( 'wp_smush_image_optimised', array( $this, 'update_lists' ), '', 2 ); // Delete ReSmush list. add_action( 'wp_ajax_delete_resmush_list', array( $this, 'delete_resmush_list' ), '', 2 ); add_action( 'admin_init', array( $this, 'init_settings' ) ); /** * Prints a membership validation issue notice in Media Library */ add_action( 'admin_notices', array( $this, 'media_library_membership_notice' ) ); /** * Handle Skip Quick Setup action */ add_action( 'wp_ajax_skipSmushSetup', array( $this, 'skipSmushSetup' ) ); /** * Hide Pagespeed Suggestion */ add_action( 'wp_ajax_hide_pagespeed_suggestion', array( $this, 'hide_pagespeed_suggestion' ) ); /** * Hide API Message */ add_action( 'wp_ajax_hide_api_message', array( $this, 'hide_api_message' ) ); add_filter( 'wp_prepare_attachment_for_js', array( $this, 'smush_send_status' ), 99, 3 ); // Send smush stats. add_action( 'wp_ajax_get_stats', array( $this, 'get_stats' ) ); // Load js and css on pages with Media Uploader - WP Enqueue Media. add_action( 'wp_enqueue_media', array( $this, 'enqueue' ) ); // Admin pointer for new Smush installation. add_action( 'admin_enqueue_scripts', array( $this, 'admin_pointer' ) ); // Smush image filter from Media Library. add_filter( 'ajax_query_attachments_args', array( $this, 'filter_media_query' ) ); } /** * Init settings. */ function init_settings() { $this->settings = array( 'networkwide' => array( 'label' => esc_html__( 'Use network settings for all the sub-sites.', 'wp-smushit' ), 'short_label' => esc_html__( 'Multisite Control', 'wp-smushit' ), 'desc' => esc_html__( 'Choose whether you want to use network settings for all sub-sites or whether sub-site admins can control Smush’s settings.', 'wp-smushit' ), ), 'auto' => array( 'label' => esc_html__( 'Automatically smush my images on upload', 'wp-smushit' ), 'short_label' => esc_html__( 'Automatic Smush', 'wp-smushit' ), 'desc' => esc_html__( 'When you upload images to your site, Smush will automatically optimize and compress them for you.', 'wp-smushit' ), ), 'lossy' => array( 'label' => esc_html__( 'Super-smush my images', 'wp-smushit' ), 'short_label' => esc_html__( 'Super-smush', 'wp-smushit' ), 'desc' => esc_html__( 'Optimize images up to 2x more than regular smush with our multi-pass lossy compression.', 'wp-smushit' ), ), 'strip_exif' => array( 'label' => esc_html__( 'Strip my image metadata', 'wp-smushit' ), 'short_label' => esc_html__( 'Metadata', 'wp-smushit' ), 'desc' => esc_html__( 'Whenever you take a photo, your camera stores metadata, such as focal length, date, time and location, within the image.', 'wp-smushit' ), ), 'resize' => array( 'label' => esc_html__( 'Resize my full size images', 'wp-smushit' ), 'short_label' => esc_html__( 'Image resizing', 'wp-smushit' ), 'desc' => esc_html__( 'Detect unnecessarily large oversize images on your pages to reduce their size and decrease load times.', 'wp-smushit' ), ), 'detection' => array( 'label' => esc_html__( 'Detect and show incorrectly sized images', 'wp-smushit' ), 'short_label' => esc_html__( 'Detect and show incorrectly sized images', 'wp-smushit' ), 'desc' => esc_html__( 'This will add functionality to your website that highlights images that are either too large or too small for their containers. Note: The highlighting will only be visible to administrators – visitors won’t see the highlighting.', 'wp-smushit' ), ), 'original' => array( 'label' => esc_html__( 'Smush my original full size images', 'wp-smushit' ), 'short_label' => esc_html__( 'Original images', 'wp-smushit' ), 'desc' => esc_html__( 'Choose how you want Smush to handle the original image file when you run a bulk smush.', 'wp-smushit' ), ), 'backup' => array( 'label' => esc_html__( 'Store a copy of my full size images', 'wp-smushit' ), 'short_label' => esc_html__( 'Original images', 'wp-smushit' ), 'desc' => esc_html__( 'Save a copy of your original full-size images separately so you can restore them at any point. Note: Keeping a copy of your original files can significantly increase the size of your uploads folder by nearly twice as much.', 'wp-smushit' ), ), 'png_to_jpg' => array( 'label' => esc_html__( 'Auto-convert PNGs to JPEGs (lossy)', 'wp-smushit' ), 'short_label' => esc_html__( 'PNG to JPEG conversion', 'wp-smushit' ), 'desc' => esc_html__( 'When you compress a PNG, Smush will check if converting it to JPEG could further reduce its size.', 'wp-smushit' ), ), ); /** * Allow to add other settings via filtering the variable * * Like Nextgen and S3 integration */ $this->settings = apply_filters( 'wp_smush_settings', $this->settings ); // Initialize Image dimensions. $this->image_sizes = $this->image_dimensions(); } /** * Add Bulk option settings page. */ function screen() { global $wpsmush_bulkui; $title = $this->validate_install() ? esc_html__( 'Smush Pro', 'wp-smushit' ) : esc_html__( 'Smush', 'wp-smushit' ); add_menu_page( $title, $title, 'manage_options', 'smush', array( $wpsmush_bulkui, 'ui', ), $this->get_menu_icon() ); // For Nextgen gallery Pages, check later in enqueue function. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); } /** * Register JS and CSS. */ function register() { // Share UI JS. wp_register_script( 'smush-wpmudev-sui', WP_SMUSH_URL . 'assets/js/shared-ui.min.js', array( 'jquery' ), WP_SHARED_UI_VERSION, true ); // Main JS. wp_register_script( 'smush-admin', WP_SMUSH_URL . 'assets/js/admin.min.js', array( 'jquery', 'smush-wpmudev-sui' ), WP_SMUSH_VERSION, true ); // Main CSS. wp_register_style( 'smush-admin', WP_SMUSH_URL . 'assets/css/admin.min.css', array(), WP_SMUSH_VERSION ); // Styles that can be used on all pages in the WP backend. wp_register_style( 'smush-admin-common', WP_SMUSH_URL . 'assets/css/common.min.css', array(), WP_SMUSH_VERSION ); // Dismiss update info. $this->dismiss_update_info(); } /** * Enqueue JS and CSS. */ function enqueue() { $current_page = $current_screen = ''; if ( function_exists( 'get_current_screen' ) ) { $current_screen = get_current_screen(); $current_page = ! empty( $current_screen ) ? $current_screen->base : $current_page; } /** * If this is called by wp_enqueue_media action, check if we are on one of the * required screen to avoid duplicate queries. * We have already enqueued scripts using admin_enqueue_scripts on required pages. */ if ( in_array( $current_page, $this->pages, true ) && doing_action( 'wp_enqueue_media' ) ) { return; } /** * Load js and css on all admin pages, in order to display install/upgrade notice. * And If upgrade/install message is dismissed or for pro users, Do not enqueue script. */ if ( get_option( 'wp-smush-hide_upgrade_notice' ) || get_site_option( 'wp-smush-hide_upgrade_notice' ) || $this->validate_install() ) { /** * Do not enqueue, unless it is one of the required screen, or not in WordPress backend. * * @var array $pages List of screens where script needs to be loaded. */ if ( empty( $current_page ) || ! is_admin() || ( ! in_array( $current_page, $this->pages, true ) && ! did_action( 'wp_enqueue_media' ) ) ) { return; } } // Allows to disable enqueuing smush files on a particular page. $enqueue_smush = apply_filters( 'wp_smush_enqueue', true ); if ( ! $enqueue_smush ) { return; } // We need it on media pages and Smush pages. wp_enqueue_script( 'smush-admin' ); wp_enqueue_style( 'smush-admin-common' ); // Load on all Smush page only. if ( in_array( $current_screen->id, $this->plugin_pages, true ) ) { // Smush admin (smush-admin) includes the Shared UI. wp_enqueue_style( 'smush-admin' ); } // Localize translatable strings for js. $this->localize(); $this->extend_media_modal(); } /** * Localize Translations */ function localize() { global $current_screen, $wpsmush_settings, $wpsmush_db, $wpsmush_dir; $current_page = ! empty( $current_screen ) ? $current_screen->base : ''; $handle = 'smush-admin'; $wp_smush_msgs = array( 'resmush' => esc_html__( 'Super-Smush', 'wp-smushit' ), 'smush_now' => esc_html__( 'Smush Now', 'wp-smushit' ), 'error_in_bulk' => esc_html__( '{{smushed}}/{{total}} images were successfully compressed, {{errors}} encountered issues.', 'wp-smushit' ), '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' ), 'checking' => esc_html__( 'Checking images..', 'wp-smushit' ), 'membership_valid' => esc_html__( 'We successfully verified your membership, all the Pro features should work completely. ', 'wp-smushit' ), 'membership_invalid' => esc_html__( "Your membership couldn't be verified.", 'wp-smushit' ), 'missing_path' => esc_html__( 'Missing file path.', 'wp-smushit' ), // Used by Directory Smush. 'unfinished_smush_single' => esc_html__( 'image could not be smushed.', 'wp-smushit' ), 'unfinished_smush' => esc_html__( 'images could not be smushed.', 'wp-smushit' ), 'already_optimised' => esc_html__( 'Already Optimized', 'wp-smushit' ), 'ajax_error' => esc_html__( 'Ajax Error', 'wp-smushit' ), 'all_done' => esc_html__( 'All Done!', 'wp-smushit' ), 'quick_setup_title' => __( 'QUICK SETUP', 'wp-smushit' ) . '
', 'sync_stats' => esc_html__( 'Give us a moment while we sync the stats.', 'wp-smushit' ), // Button text. 'resmush_check' => esc_html__( 'RE-CHECK IMAGES', 'wp-smushit' ), 'resmush_complete' => esc_html__( 'CHECK COMPLETE', 'wp-smushit' ), // Progress bar text. 'progress_smushed' => esc_html__( 'images optimized', 'wp-smushit' ), 'directory_url' => admin_url( 'admin.php?page=smush&tab=directory' ), 'bulk_resume' => esc_html__( 'Resume scan', 'wp-smushit' ), 'bulk_stop' => esc_html__( 'Stop current bulk smush process.', 'wp-smushit' ), ); wp_localize_script( $handle, 'wp_smush_msgs', $wp_smush_msgs ); // Load the stats on selected screens only. if ( 'toplevel_page_smush' === $current_page ) { // Get resmush list, If we have a resmush list already, localize those IDs. if ( $resmush_ids = get_option( 'wp-smush-resmush-list' ) ) { // Get the attachments, and get lossless count. $this->resmush_ids = $resmush_ids; } // Setup all the stats. $this->setup_global_stats( true ); // Localize smushit_IDs variable, if there are fix number of IDs. $this->unsmushed_attachments = ! empty( $_REQUEST['ids'] ) ? array_map( 'intval', explode( ',', $_REQUEST['ids'] ) ) : array(); if ( empty( $this->unsmushed_attachments ) ) { // Get attachments if all the images are not smushed. $this->unsmushed_attachments = $this->remaining_count > 0 ? $wpsmush_db->get_unsmushed_attachments() : array(); $this->unsmushed_attachments = ! empty( $this->unsmushed_attachments ) && is_array( $this->unsmushed_attachments ) ? array_values( $this->unsmushed_attachments ) : $this->unsmushed_attachments; } // Array of all smushed, unsmushed and lossless IDs. $data = array( 'count_supersmushed' => $this->super_smushed, 'count_smushed' => $this->smushed_count, 'count_total' => $this->total_count, 'count_images' => $this->stats['total_images'], 'count_resize' => $this->stats['resize_count'], 'unsmushed' => $this->unsmushed_attachments, 'resmush' => $this->resmush_ids, 'size_before' => $this->stats['size_before'], 'size_after' => $this->stats['size_after'], 'savings_bytes' => $this->stats['bytes'], 'savings_resize' => $this->stats['resize_savings'], 'savings_conversion' => $this->stats['conversion_savings'], 'savings_dir_smush' => $this->dir_stats, ); } else { $data = array( 'count_supersmushed' => '', 'count_smushed' => '', 'count_total' => '', 'count_images' => '', 'unsmushed' => '', 'resmush' => '', 'savings_bytes' => '', 'savings_resize' => '', 'savings_conversion' => '', 'savings_supersmush' => '', 'pro_savings' => '', ); } // End if(). // Check if scanner class is available. $scanner_ready = isset( $wpsmush_dir->scanner ); $data['dir_smush'] = array( 'currentScanStep' => $scanner_ready ? $wpsmush_dir->scanner->get_current_scan_step() : 0, 'totalSteps' => $scanner_ready ? $wpsmush_dir->scanner->get_scan_steps() : 0, ); $data['resize_sizes'] = $this->get_max_image_dimensions(); // Convert it into ms. $data['timeout'] = WP_SMUSH_TIMEOUT * 1000; wp_localize_script( $handle, 'wp_smushit_data', $data ); // Check if settings were changed for a multisite, and localize whether to run re-check on page load. if ( is_multisite() && $wpsmush_settings->settings['networkwide'] && ! is_network_admin() ) { // If not same, Set a variable to run re-check on page load. if ( get_site_option( WP_SMUSH_PREFIX . 'run_recheck', false ) ) { wp_localize_script( $handle, 'wp_smush_run_re_check', array( 1 ) ); } } } /** * Runs the expensive queries to get our global smush stats * * @param bool $force_update Whether to force update the global stats or not. */ public function setup_global_stats( $force_update = false ) { global $wpsmush_db, $wpsmush_dir; // Set directory smush status. $this->dir_stats = $wpsmush_dir->should_continue() ? $wpsmush_dir->total_stats() : array(); // Setup Attachments and total count. $wpsmush_db->total_count( true ); $this->stats = $this->global_stats( $force_update ); if ( empty( $this->smushed_attachments ) ) { // Get smushed attachments. $this->smushed_attachments = $wpsmush_db->smushed_count( true ); } // Get supersmushed iamges count. if ( empty( $this->super_smushed ) ) { $this->super_smushed = $wpsmush_db->super_smushed_count(); } // Set pro savings. $this->set_pro_savings(); // Set smushed count. $this->smushed_count = ! empty( $this->smushed_attachments ) ? count( $this->smushed_attachments ) : 0; $this->remaining_count = $this->remaining_count(); } /** * Set pro savings stats if not premium user. * * For non-premium users, show expected avarage savings based * on the free version savings. */ function set_pro_savings() { global $wp_smush; // No need this already premium. if ( $wp_smush->validate_install() ) { return; } // Initialize $this->stats['pro_savings'] = array( 'percent' => 0, 'savings' => 0, ); // Default values. $savings = $this->stats['percent'] > 0 ? $this->stats['percent'] : 0; $savings_bytes = $this->stats['human'] > 0 ? $this->stats['bytes'] : '0'; $orig_diff = 2.22058824; if ( ! empty( $savings ) && $savings > 49 ) { $orig_diff = 1.22054412; } // Calculate Pro savings if ( ! empty( $savings ) ) { $savings = $orig_diff * $savings; $savings_bytes = $orig_diff * $savings_bytes; } // Set pro savings in global stats. if ( $savings > 0 ) { $this->stats['pro_savings'] = array( 'percent' => number_format_i18n( $savings, 1 ), 'savings' => size_format( $savings_bytes, 1 ), ); } } /** * Bulk Smushing Handler. * * Processes the Smush request and sends back the next id for smushing. */ public function process_smush_request() { global $wp_smush, $wpsmush_helper; // Turn off errors for ajax result. @error_reporting( 0 ); if ( empty( $_REQUEST['attachment_id'] ) ) { wp_send_json_error( array( 'error' => 'missing_id', 'error_message' => $this->filter_error( esc_html__( 'No attachment ID was received', 'wp-smushit' ) ), 'file_name' => 'undefined', 'show_warning' => intval( $this->show_warning() ), ) ); } // If the bulk smush needs to be stopped. if ( ! $this->validate_install() && ! $this->check_bulk_limit() ) { wp_send_json_error( array( 'error' => 'limit_exceeded', 'error_message' => sprintf( esc_html__( "You've reached the %1\$d attachment limit for bulk smushing in the free version. Upgrade to Pro to smush unlimited images, or click resume to smush another %2\$d attachments.", 'wp-smushit' ), $this->max_free_bulk, $this->max_free_bulk ), 'continue' => false, ) ); } $attachment_id = (int) $_REQUEST['attachment_id']; $original_meta = wp_get_attachment_metadata( $attachment_id, true ); // Try to get the file name from path. $file_name = explode( '/', $original_meta['file'] ); if ( is_array( $file_name ) ) { $file_name = array_pop( $file_name ); } else { $file_name = $original_meta['file']; } /** * Filter: wp_smush_image * * Whether to smush the given attachment id or not * * @param bool $skip Whether to Smush image or not. * @param int $attachment_id Attachment ID of the image being processed. */ if ( ! apply_filters( 'wp_smush_image', true, $attachment_id ) ) { wp_send_json_error( array( 'error' => 'skipped', 'error_message' => $this->filter_error( esc_html__( 'Skipped with wp_smush_image filter', 'wp-smushit' ) ), 'show_warning' => intval( $this->show_warning() ), 'file_name' => $wpsmush_helper->get_image_media_link( $attachment_id, $file_name ), 'thumbnail' => wp_get_attachment_image( $attachment_id ), ) ); } /** * Get the file path for backup. * * @var WpSmushHelper $wpsmush_helper */ $attachment_file_path = $wpsmush_helper->get_attached_file( $attachment_id ); // Download if not exists. do_action( 'smush_file_exists', $attachment_file_path, $attachment_id ); /** * Take backup. * * @var WpSmushBackup $wpsmush_backup */ global $wpsmush_backup; $wpsmush_backup->create_backup( $attachment_file_path, '', $attachment_id ); // Proceed only if Smushing Transient is not set for the given attachment id. if ( ! get_option( 'smush-in-progress-' . $attachment_id, false ) ) { // Set a transient to avoid multiple request. update_option( 'smush-in-progress-' . $attachment_id, true ); /** * Resize the dimensions of the image. * * Filter whether the existing image should be resized or not * * @since 2.3 * * @param bool $should_resize Set to True by default. * @param int $attachment_id Image Attachment ID. */ if ( $should_resize = apply_filters( 'wp_smush_resize_media_image', true, $attachment_id ) ) { $updated_meta = $this->resize_image( $attachment_id, $original_meta ); $original_meta = ! empty( $updated_meta ) ? $updated_meta : $original_meta; } /** * Convert PNGs to JPG. * * @var WpSmushPngtoJpg $wpsmush_pngjpg */ global $wpsmush_pngjpg; $original_meta = $wpsmush_pngjpg->png_to_jpg( $attachment_id, $original_meta ); $smush = $wp_smush->resize_from_meta_data( $original_meta, $attachment_id ); wp_update_attachment_metadata( $attachment_id, $original_meta ); } // Delete transient. delete_option( 'smush-in-progress-' . $attachment_id ); $smush_data = get_post_meta( $attachment_id, $this->smushed_meta_key, true ); $resize_savings = get_post_meta( $attachment_id, WP_SMUSH_PREFIX . 'resize_savings', true ); $conversion_savings = $wpsmush_helper->get_pngjpg_savings( $attachment_id ); $stats = array( 'count' => ! empty( $smush_data['sizes'] ) ? count( $smush_data['sizes'] ) : 0, 'size_before' => ! empty( $smush_data['stats'] ) ? $smush_data['stats']['size_before'] : 0, 'size_after' => ! empty( $smush_data['stats'] ) ? $smush_data['stats']['size_after'] : 0, 'savings_resize' => $resize_savings > 0 ? $resize_savings : 0, 'savings_conversion' => $conversion_savings['bytes'] > 0 ? $conversion_savings : 0, 'is_lossy' => ! empty( $smush_data ['stats'] ) ? $smush_data['stats']['lossy'] : false, ); if ( isset( $smush ) && is_wp_error( $smush ) ) { $error_message = $smush->get_error_message(); // Check for timeout error and suggest to filter timeout. if ( strpos( $error_message, 'timed out' ) ) { $error = 'timeout'; $error_message = esc_html__( "Timeout error. You can increase the request timeout to make sure Smush has enough time to process larger files. `define('WP_SMUSH_API_TIMEOUT', 150);`", 'wp-smushit' ); } $error = isset( $error ) ? $error : 'other'; if ( ! empty( $error_message ) ) { // Used internally to modify the error message. $error_message = $this->filter_error( $error_message, $attachment_id, $error ); } wp_send_json_error( array( 'stats' => $stats, 'error' => $error, 'error_message' => $error_message, 'show_warning' => intval( $this->show_warning() ), 'error_class' => isset( $error_class ) ? $error_class : '', 'file_name' => $wpsmush_helper->get_image_media_link( $attachment_id, $file_name ), ) ); } // Check if a resmush request, update the resmush list. if ( ! empty( $_REQUEST['is_bulk_resmush'] ) && 'false' !== $_REQUEST['is_bulk_resmush'] && $_REQUEST['is_bulk_resmush'] ) { $this->update_resmush_list( $attachment_id ); } // Runs after a image is successfully smushed. do_action( 'image_smushed', $attachment_id, $stats ); // Update the bulk Limit count. $this->update_smush_count(); // Send ajax response. wp_send_json_success( array( 'stats' => $stats, 'show_warning' => intval( $this->show_warning() ), ) ); } /** * Handle the Ajax request for smushing single image * * @uses smush_single() */ public function smush_manual() { // Turn off errors for ajax result. @error_reporting( 0 ); if ( ! current_user_can( 'upload_files' ) ) { wp_die( esc_html__( "You don't have permission to work with uploaded files.", 'wp-smushit' ) ); } if ( ! isset( $_GET['attachment_id'] ) ) { wp_die( esc_html__( 'No attachment ID was provided.', 'wp-smushit' ) ); } $attachment_id = intval( $_GET['attachment_id'] ); /** * Filter: wp_smush_image. * * Whether to smush the given attachment ID or not. * * @param bool $status Smush all attachments by default. * @param int $attachment_id Attachment ID. */ if ( ! apply_filters( 'wp_smush_image', true, $attachment_id ) ) { $error = $this->filter_error( esc_html__( 'Attachment Skipped - Check `wp_smush_image` filter.', 'wp-smushit' ), $attachment_id ); wp_send_json_error( array( 'error_msg' => sprintf( ' ', $error ), 'show_warning' => intval( $this->show_warning() ), ) ); } $this->initialise(); // Pass on the attachment id to smush single function. $this->smush_single( $attachment_id ); } /** * Smush single images * * @param int $attachment_id Attachment ID. * @param bool $return Return/echo the stats. * * @return array|string */ public function smush_single( $attachment_id, $return = false ) { // If the smushing option is already set, return the status. if ( get_option( "smush-in-progress-{$attachment_id}", false ) || get_option( "wp-smush-restore-{$attachment_id}", false ) ) { // Get the button status. $status = $this->set_status( $attachment_id, false, true ); if ( $return ) { return $status; } wp_send_json_success( $status ); } // Set a transient to avoid multiple request. update_option( "smush-in-progress-{$attachment_id}", true ); global $wpsmush_pngjpg, $wpsmush_helper; $attachment_id = absint( (int) ( $attachment_id ) ); // Get the file path for backup. $attachment_file_path = $wpsmush_helper->get_attached_file( $attachment_id ); // Download file if not exists. do_action( 'smush_file_exists', $attachment_file_path, $attachment_id ); // Take backup. global $wpsmush_backup; $wpsmush_backup->create_backup( $attachment_file_path, '', $attachment_id ); // Get the image metadata from $_POST. $original_meta = ! empty( $_POST['metadata'] ) ? $this->format_meta_from_post( $_POST['metadata'] ) : ''; $original_meta = empty( $original_meta ) ? wp_get_attachment_metadata( $attachment_id ) : $original_meta; // Send image for resizing, if enabled resize first before any other operation. $updated_meta = $this->resize_image( $attachment_id, $original_meta ); // Convert PNGs to JPG. $updated_meta = $wpsmush_pngjpg->png_to_jpg( $attachment_id, $updated_meta ); $original_meta = ! empty( $updated_meta ) ? $updated_meta : $original_meta; // Smush the image. $smush = $this->resize_from_meta_data( $original_meta, $attachment_id ); // Update the details, after smushing, so that latest image is used in hook. wp_update_attachment_metadata( $attachment_id, $original_meta ); // Get the button status. $status = $this->set_status( $attachment_id, false, true ); // Delete the transient after attachment meta is updated. delete_option( 'smush-in-progress-' . $attachment_id ); // Send Json response if we are not suppose to return the results. if ( is_wp_error( $smush ) ) { if ( $return ) { return array( 'error' => $smush->get_error_message() ); } wp_send_json_error( array( 'error_msg' => ' ', 'show_warning' => intval( $this->show_warning() ), ) ); } $this->update_resmush_list( $attachment_id ); if ( $return ) { return $status; } wp_send_json_success( $status ); } /** * Format meta data from $_POST request. * * Post request in WordPress will convert all values * to string. Make sure image height and width are int. * This is required only when Async requests are used. * See - https://wordpress.org/support/topic/smushit-overwrites-image-meta-crop-sizes-as-string-instead-of-int/ * * @since 2.8.0 * * @param array $meta Meta data of attachment. * * @return array */ public function format_meta_from_post( $meta = array() ) { // Do not continue in case meta is empty. if ( empty( $meta ) ) { return $meta; } // If meta data is array proceed. if ( is_array( $meta ) ) { // Walk through each items and format. array_walk_recursive( $meta, array( $this, 'format_attachment_meta_item' ) ); } return $meta; } /** * If current item is width or height, make sure it is int. * * @since 2.8.0 * * @param mixed $value Meta item value. * @param string $key Meta item key. */ public function format_attachment_meta_item( &$value, $key ) { if ( 'height' === $key || 'width' === $key ) { $value = (int) $value; } /** * Allows to format single item in meta. * * This filter will be used only for Async, post requests. * * @param mixed $value Meta item value. * @param string $key Meta item key. */ $value = apply_filters( 'wp_smush_format_attachment_meta_item', $value, $key ); } /** * Check bulk sent count, whether to allow further smushing or not * * @param bool $reset To hard reset the transient. * @param string $key Transient Key - bulk_sent_count/dir_sent_count. * * @return bool */ public function check_bulk_limit( $reset = false, $key = 'bulk_sent_count' ) { $transient_name = WP_SMUSH_PREFIX . $key; $bulk_sent_count = (int) get_transient( $transient_name ); // Check if bulk smush limit is less than limit. if ( ! $bulk_sent_count || $bulk_sent_count < $this->max_free_bulk ) { $continue = true; } elseif ( $bulk_sent_count === $this->max_free_bulk ) { // If user has reached the limit, reset the transient. $continue = false; $reset = true; } else { $continue = false; } // If we need to reset the transient. if ( $reset ) { set_transient( $transient_name, 0, 60 ); } return $continue; } /** * Update the image smushed count in transient * * @param string $key Database key. */ public function update_smush_count( $key = 'bulk_sent_count' ) { $transient_name = WP_SMUSH_PREFIX . $key; $bulk_sent_count = get_transient( $transient_name ); // If bulk sent count is not set. if ( false === $bulk_sent_count ) { // Start transient at 0. set_transient( $transient_name, 1, 200 ); } elseif ( $bulk_sent_count < $this->max_free_bulk ) { // If lte $this->max_free_bulk images are sent, increment. set_transient( $transient_name, $bulk_sent_count + 1, 200 ); } } /** * Returns remaining count * * @return int */ function remaining_count() { // Check if the resmush count is equal to remaining count. $resmush_count = count( $this->resmush_ids ); $remaining_count = $this->total_count - $this->smushed_count; if ( $resmush_count > 0 && $resmush_count == $this->smushed_count ) { return $resmush_count + $remaining_count; } return $remaining_count; } /** * Display Thumbnails, if bulk action is choosen * * @Note: Not in use right now, Will use it in future for Media Bulk action */ function selected_ui( $send_ids, $received_ids ) { if ( empty( $received_ids ) ) { return; } ?>%1$d of %2$d images were sent for smushing:', 'wp-smushit' ), count( $send_ids ), count( $received_ids ) ); ?>
' . __( 'Smushing in progress..', 'wp-smushit' ) . '
'; // we need to show the smush button $show_button = false; // the button text $button_txt = __( 'Smush Now!', 'wp-smushit' ); return $this->column_html( $id, $status_txt, $button_txt, $show_button, true, false, true ); } // Else Return the normal status $response = trim( $this->set_status( $id, false ) ); return $response; } /** * Adds a smushit pro settings link on plugin page * * @param $links * * @return array */ function settings_link( $links, $url_only = false, $networkwide = false ) { $settings_page = is_multisite() && is_network_admin() ? network_admin_url( 'admin.php?page=smush' ) : menu_page_url( 'smush', false ); // If networkwide setting url is needed. $settings_page = $url_only && $networkwide && is_multisite() ? network_admin_url( 'admin.php?page=smush' ) : $settings_page; $settings = '' . __( 'Settings', 'wp-smushit' ) . ''; // Return Only settings page link if ( $url_only ) { return $settings_page; } // Added a fix for weird warning in multisite, "array_unshift() expects parameter 1 to be array, null given" if ( ! empty( $links ) ) { array_unshift( $links, $settings ); } else { $links = array( $settings ); } return $links; } /** * Shows Notice for free users, displays a discount coupon */ function smush_upgrade() { global $wpsmush_bulkui; // Return, If a pro user, or not super admin, or don't have the admin privilleges if ( $this->validate_install() || ! current_user_can( 'edit_others_posts' ) || ! is_super_admin() ) { return false; } // No need to show it on bulk smush if ( isset( $_GET['page'] ) && 'smush' == $_GET['page'] ) { return false; } // Return if notice is already dismissed if ( get_option( 'wp-smush-hide_upgrade_notice' ) || get_site_option( 'wp-smush-hide_upgrade_notice' ) ) { return false; } $install_type = get_site_option( 'wp-smush-install-type', false ); if ( ! $install_type ) { if ( $this->smushed_count > 0 ) { $install_type = 'existing'; } else { $install_type = 'new'; } update_site_option( 'wp-smush-install-type', $install_type ); } // Container Header echo $wpsmush_bulkui->installation_notice(); } /** * Get the smushed attachments from the database, except gif * * @global object $wpdb * * @return object query results */ function get_smushed_attachments() { global $wpdb; $allowed_images = "( 'image/jpeg', 'image/jpg', 'image/x-citrix-jpeg', 'image/png', 'image/x-png' )"; $limit = $this->query_limit(); $offset = 0; $query_next = true; while ( $query_next ) { // get the attachment id, smush data $sql = 'SELECT p.ID as attachment_id, p.post_mime_type as type, ms.meta_value as smush_data' . " FROM $wpdb->posts as p" . " LEFT JOIN $wpdb->postmeta as ms" . " ON (p.ID= ms.post_id AND ms.meta_key='wp-smpro-smush-data')" . ' WHERE' . " p.post_type='attachment'" . ' AND p.post_mime_type IN ' . $allowed_images . ' ORDER BY p . ID DESC' // add a limit . ' LIMIT ' . $limit; $results = $wpdb->get_results( $sql ); // Update the offset $offset += $limit; if ( ! empty( $this->total_count ) && $this->total_count <= $offset ) { $query_next = false; } elseif ( ! $results || empty( $results ) ) { $query_next = false; } } return $results; } /** * Store a key/value to hide the smush features on bulk page */ function dismiss_welcome_notice() { update_site_option( 'wp-smush-hide_smush_welcome', 1 ); wp_send_json_success(); } /** * Store a key/value to hide the smush features on bulk page */ function dismiss_upgrade_notice( $ajax = true ) { update_site_option( 'wp-smush-hide_upgrade_notice', 1 ); // No Need to send json response for other requests if ( $ajax ) { wp_send_json_success(); } } /** * Remove the Update info * * @param bool $remove_notice */ function dismiss_update_info( $remove_notice = false ) { // From URL arg if ( isset( $_GET['dismiss_smush_update_info'] ) && 1 == $_GET['dismiss_smush_update_info'] ) { $remove_notice = true; } // From Ajax if ( ! empty( $_REQUEST['action'] ) && 'dismiss_update_info' == $_REQUEST['action'] ) { $remove_notice = true; } // Update Db if ( $remove_notice ) { update_site_option( 'wp-smush-hide_update_info', 1 ); } } /** * Hide S3 support alert by setting a flag. */ function dismiss_s3support_alert() { // Just set a flag. update_site_option( 'wp-smush-hide_s3support_alert', 1 ); wp_send_json_success(); } /** * Resmush the image * * @uses smush_single() */ function resmush_image() { // Check empty fields. if ( empty( $_POST['attachment_id'] ) || empty( $_POST['_nonce'] ) ) { wp_send_json_error( array( 'error' => 'empty_fields', 'message' => '' . __( 'Resize, compress and optimize your images here.', 'wp-smushit' ) . '
'; ?> 'wp-smush-ignore-bulk', 'value' => 'true', 'compare' => 'EXISTS', ), ); } return $query; } } global $wpsmushit_admin; $wpsmushit_admin = new WpSmushitAdmin(); }