core = $core; $this->array_utils = new Array_Utils(); } /** * Init functionality that is related to the UI. */ public function init_ui() { if ( Membership::get_instance()->is_api_hub_access_required() ) { add_action( 'all_admin_notices', array( $this, 'smush_media_hub_connect_notice' ), 5 ); return false; } // Media library columns. add_filter( 'manage_media_columns', array( $this, 'columns' ) ); add_filter( 'manage_upload_sortable_columns', array( $this, 'sortable_column' ) ); add_action( 'manage_media_custom_column', array( $this, 'custom_column' ), 10, 2 ); // Manage column sorting. add_action( 'pre_get_posts', array( $this, 'smushit_orderby' ) ); // Smush image filter from Media Library. add_filter( 'ajax_query_attachments_args', array( $this, 'filter_media_query' ) ); // Smush image filter from Media Library (list view). add_action( 'restrict_manage_posts', array( $this, 'add_filter_dropdown' ) ); // Add pre WordPress 5.0 compatibility. add_filter( 'wp_kses_allowed_html', array( $this, 'filter_html_attributes' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'extend_media_modal' ), 15 ); add_filter( 'wp_prepare_attachment_for_js', array( $this, 'smush_send_status' ), 99, 3 ); // Add bulk restore action. add_filter( 'bulk_actions-upload', array( $this, 'add_bulk_restore_action' ) ); add_filter( 'handle_bulk_actions-upload', array( $this, 'bulk_restore_media' ), 10, 3 ); add_action( 'all_admin_notices', array( $this, 'show_bulk_restore_notice' ) ); } /** * Add bulk restore action to media lib page. * * @param array $actions List actions. * @return array */ public function add_bulk_restore_action( $actions ) { $actions['smush-bulk-restore'] = esc_html__( 'Smush restore', 'wp-smushit' ); return $actions; } /** * Bulk restore images. * * @param string $sendback The redirect URL. * @param string $doaction Bulk action name. * @param array $attachment_ids List image ids. * * @return string */ public function bulk_restore_media( $sendback, $doaction, $attachment_ids ) { // If there is not bulk restore images, return. if ( 'smush-bulk-restore' !== $doaction || empty( $attachment_ids ) ) { return $sendback; } $bulk_restore = new Bulk_Restore( $attachment_ids ); $bulk_restore->bulk_restore(); $restored_count = $bulk_restore->get_restored_count(); $total_count = $bulk_restore->get_total_count(); $missing_backup_count = $bulk_restore->get_error_count( 'missing_backup' ); $error_copy_count = $bulk_restore->get_error_count( 'copy_failed' ); $sendback = add_query_arg( array( 'smush_total' => $total_count, 'smush_restored' => $restored_count, 'smush_missing_backup_count' => $missing_backup_count, 'smush_copy_failed_count' => $error_copy_count, ), $sendback ); // Return original location. return $sendback; } /** * Show bulk restore notice. * * @return void */ public function show_bulk_restore_notice() { if ( ! isset( $_GET['smush_restored'], $_GET['smush_total'] ) ) { return; } $total = (int) $this->array_utils->get_array_value( $_GET, 'smush_total', 0 ); $restored = (int) $this->array_utils->get_array_value( $_GET, 'smush_restored', 0 ); $missing_backup_count = (int) $this->array_utils->get_array_value( $_GET, 'smush_missing_backup_count', 0 ); $error_copy_count = (int) $this->array_utils->get_array_value( $_GET, 'smush_copy_failed_count', 0 ); $failed = $total - $restored; if ( $total <= 0 || $restored > $total ) { return; } $classes = array( 'notice', 'is-dismissible', ); if ( $failed > 0 ) { $classes[] = 'notice-warning'; } else { $classes[] = 'notice-success'; } ?>

get_bulk_restore_message( $restored, $total, $missing_backup_count, $error_copy_count ) ); ?>

'; $backup_link_close = ''; if ( $missing_backup_count > 0 && $error_copy_count > 0 ) { // Mixed message. /* translators: %1$d: restored count, %2$d: total count, %3$d: no backup count, %4$d: copy error count, %5$s: link start tag, %6$s: link end tag */ return sprintf( esc_html__( '%1$s%2$d/%3$d images were restored successfully%4$s. %5$d couldn\'t be restored as no backup exists, and %6$d due to a backup copy error. Ensure %7$sBackup original images%8$s is enabled to keep copies of your originals.', 'wp-smushit' ), '', (int) $restored, (int) $total, '', (int) $missing_backup_count, (int) $error_copy_count, $backup_link, $backup_link_close ); } elseif ( $missing_backup_count > 0 ) { /* translators: %1$d: restored count, %2$d: total count, %3$d: failed count, %4$s: link start tag, %5$s: link end tag */ return sprintf( esc_html__( '%1$s%2$d/%3$d images were restored successfully%4$s. %5$d couldn\'t be restored as no backup exists. Ensure %6$sBackup original images%7$s is enabled to keep copies of your originals.', 'wp-smushit' ), '', (int) $restored, (int) $total, '', (int) $missing_backup_count, $backup_link, $backup_link_close ); } elseif ( $error_copy_count > 0 ) { /* translators: %1$d: restored count, %2$d: total count, %3$d: failed count, %4$s: link start tag, %5$s: link end tag */ return sprintf( esc_html__( '%1$s%2$d/%3$d images were restored successfully%4$s. %5$d couldn\'t be restored due to a backup copy error. Ensure %6$sBackup original images%7$s is enabled to keep copies of your originals.', 'wp-smushit' ), '', (int) $restored, (int) $total, '', (int) $error_copy_count, $backup_link, $backup_link_close ); } return sprintf( /* translators: %1$d: restored count, %2$d: total count */ esc_html__( 'All selected images were restored successfully (%1$d/%2$d).', 'wp-smushit' ), (int) $restored, (int) $total ); } /** * Print column header for Smush results in the media library using the `manage_media_columns` hook. * * @param array $defaults Defaults array. * * @return array */ public function columns( $defaults ) { $defaults['smushit'] = 'Smush'; return $defaults; } /** * Add the Smushit Column to sortable list * * @param array $columns Columns array. * * @return array */ public function sortable_column( $columns ) { $columns['smushit'] = 'smushit'; return $columns; } /** * Print column data for Smush results in the media library using * the `manage_media_custom_column` hook. * * @param string $column_name Column name. * @param int $id Attachment ID. */ public function custom_column( $column_name, $id ) { if ( 'smushit' === $column_name ) { $escaped_text = wp_kses_post( $this->generate_markup( $id ) ); if ( $this->is_failed_processing_page() ) { $escaped_text = sprintf( '
%s
', $escaped_text ); } echo $escaped_text; } } /** * Detect failed processing page. * * @since 3.12.0 * * @return boolean */ private function is_failed_processing_page() { static $is_failed_processing_page; if ( null === $is_failed_processing_page ) { $filter = filter_input( INPUT_GET, 'smush-filter', FILTER_SANITIZE_SPECIAL_CHARS ); $is_failed_processing_page = 'failed_processing' === $filter; } return $is_failed_processing_page; } /** * Order by query for smush columns. * * @param WP_Query $query Query. * * @return WP_Query */ public function smushit_orderby( $query ) { global $current_screen; // Filter only media screen. if ( ! is_admin() || ( ! empty( $current_screen ) && 'upload' !== $current_screen->base ) || 'attachment' !== $query->get( 'post_type' ) ) { return $query; } $filter = filter_input( INPUT_GET, 'smush-filter', FILTER_SANITIZE_SPECIAL_CHARS ); // Ignored. if ( 'ignored' === $filter ) { $query->set( 'meta_query', $this->query_ignored() ); return $query; } elseif ( 'unsmushed' === $filter ) { // Not processed. $query->set( 'meta_query', $this->query_unsmushed() ); return $query; } elseif ( 'failed_processing' === $filter ) { // Failed processing. $query->set( 'meta_query', $this->query_failed_processing() ); return $query; } // TODO: do we need this? $orderby = $query->get( 'orderby' ); if ( isset( $orderby ) && 'smushit' === $orderby ) { $query->set( 'meta_query', array( 'relation' => 'OR', array( 'key' => Smush::$smushed_meta_key, 'compare' => 'EXISTS', ), array( 'key' => Smush::$smushed_meta_key, 'compare' => 'NOT EXISTS', ), ) ); $query->set( 'orderby', 'meta_value_num' ); } return $query; } /** * Add our filter to the media query filter in Media Library. * * @since 2.9.0 * * @see wp_ajax_query_attachments() * * @param array $query Query. * * @return mixed */ public function filter_media_query( $query ) { $post_query = filter_input( INPUT_POST, 'query', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_REQUIRE_ARRAY ); if ( ! isset( $post_query['stats'] ) ) { return $query; } $filter_name = $post_query['stats']; // Excluded. if ( 'excluded' === $filter_name ) { $query['meta_query'] = $this->query_ignored(); } elseif ( 'unsmushed' === $filter_name ) { // Unsmushed. $query['meta_query'] = $this->query_unsmushed(); } elseif ( 'failed_processing' === $filter_name ) { // Failed processing. $query['meta_query'] = $this->query_failed_processing(); } return $query; } /** * Meta query for images skipped from bulk smush. * * @return array */ private function query_failed_processing() { // Custom query to add error items. add_filter( 'posts_where_request', array( $this, 'filter_query_to_add_media_item_errors' ) ); // Custom query for failed on optimization. $meta_query = array( 'relation' => 'AND', array( 'key' => Media_Item_Optimizer::get_error_meta_key(), 'compare' => 'EXISTS', ), array( 'key' => Media_Item::get_ignored_meta_key(), 'compare' => 'NOT EXISTS', ), ); return $meta_query; } public function filter_query_to_add_media_item_errors( $where ) { global $wpdb; remove_filter( 'posts_where_request', array( $this, 'filter_query_to_add_media_item_errors' ) ); $media_error_ids = Global_Stats::get()->get_error_list()->get_ids(); if ( empty( $media_error_ids ) ) { return $where; } $where .= sprintf( " OR {$wpdb->posts}.ID IN (%s)", join( ',', $media_error_ids ) ); return $where; } /** * Meta query for images skipped from bulk smush. * * @return array */ private function query_ignored() { return array( array( 'key' => Media_Item::get_ignored_meta_key(), 'compare' => 'EXISTS', ), ); } /** * Meta query for uncompressed images. * * @return array */ private function query_unsmushed() { return Core::get_unsmushed_meta_query(); } /** * Adds a search dropdown in Media Library list view to filter out images that have been * ignored with bulk Smush. * * @since 3.2.0 */ public function add_filter_dropdown() { $scr = get_current_screen(); if ( 'upload' !== $scr->base ) { return; } $ignored = filter_input( INPUT_GET, 'smush-filter', FILTER_SANITIZE_SPECIAL_CHARS ); ?> elements on WordPress before 5.0.0. * Add backward compatibility. * * @since 3.5.0 * @see https://github.com/WordPress/WordPress/commit/a0309e80b6a4d805e4f230649be07b4bfb1a56a5#diff-a0e0d196dd71dde453474b0f791828fe * @param array $context Context. * * @return mixed */ public function filter_html_attributes( $context ) { global $wp_version; if ( version_compare( '5.0.0', $wp_version, '<' ) ) { return $context; } $context['a']['data-tooltip'] = true; $context['a']['data-id'] = true; $context['a']['data-nonce'] = true; return $context; } /** * Load media assets. * * Localization also used in Gutenberg integration. */ public function extend_media_modal() { // Get current screen. $current_screen = get_current_screen(); // Only run on required pages. if ( ! empty( $current_screen ) && ! in_array( $current_screen->id, Core::$external_pages, true ) && empty( $current_screen->is_block_editor ) ) { return; } if ( wp_script_is( 'smush-backbone-extension', 'enqueued' ) ) { return; } wp_enqueue_script( 'smush-backbone-extension', WP_SMUSH_URL . 'app/assets/js/smush-media.min.js', array( 'jquery', 'media-editor', // Used in image filters. 'media-views', 'media-grid', 'wp-util', 'wp-api', ), WP_SMUSH_VERSION, true ); wp_localize_script( 'smush-backbone-extension', 'smush_vars', array( 'strings' => array( 'stats_label' => esc_html__( 'Smush', 'wp-smushit' ), 'filter_all' => esc_html__( 'Smush: All images', 'wp-smushit' ), 'filter_not_processed' => esc_html__( 'Smush: Not processed', 'wp-smushit' ), 'filter_excl' => esc_html__( 'Smush: Bulk ignored', 'wp-smushit' ), 'filter_failed' => esc_html__( 'Smush: Failed Processing', 'wp-smushit' ), 'gb' => array( 'stats' => esc_html__( 'Smush Stats', 'wp-smushit' ), 'select_image' => esc_html__( 'Select an image to view Smush stats.', 'wp-smushit' ), 'size' => esc_html__( 'Image size', 'wp-smushit' ), 'savings' => esc_html__( 'Savings', 'wp-smushit' ), ), ), ) ); } /** * Send smush status for attachment. * * @param array $response Response array. * @param WP_Post $attachment Attachment object. * * @return mixed */ public function smush_send_status( $response, $attachment ) { if ( ! isset( $attachment->ID ) ) { return $response; } // Validate nonce. $status = $this->smush_status( $attachment->ID ); $response['smush'] = $status; return $response; } /** * Get the smush button text for attachment. * * @param int $id Attachment ID for which the Status has to be set. * * @return string */ private function smush_status( $id ) { $action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE ); // Show Temporary Status, For Async Optimisation, No Good workaround. if ( ! get_transient( 'wp-smush-restore-' . $id ) && 'upload-attachment' === $action && $this->settings->get( 'auto' ) ) { $status_txt = '

' . __( 'Smushing in progress...', 'wp-smushit' ) . '

'; $button_txt = __( 'Smush Now!', 'wp-smushit' ); return $this->column_html( $id, $status_txt, $button_txt, false ); } // Else return the normal status. return trim( $this->generate_markup( $id ) ); } /** * Display the Smush Hub Connect notice in Media Library. * * Callback for the 'admin_notices' action. * * @return void */ public function smush_media_hub_connect_notice() { if ( ! $this->should_display_media_notice() ) { return; } $notice_hidden = WP_Smush::get_instance()->admin()->is_notice_dismissed( 'media-hub-connect-notice' ); if ( $notice_hidden ) { return; } $hub_connect_url = Hub_Connector::get_connect_site_url( 'smush-bulk', 'smush_wpadmin_media_library' ); if ( is_multisite() ) { $hub_connect_url = str_replace( '/wp-admin/', '/wp-admin/network/', $hub_connect_url ); } ?> $smush_orgnl_txt, 'size_limit' => esc_html__( "Image couldn't be smushed as it exceeded the 5Mb size limit, Pro users can smush images without any size restriction.", 'wp-smushit' ), ); $skip_rsn = ''; if ( ! empty( $skip_msg[ $msg_id ] ) ) { $skip_rsn = ' ' . esc_html__( 'PRO', 'wp-smushit' ) . ''; } return $skip_rsn; } /** * Generate HTML for image status on the media library page. * * @since 3.5.0 Refactored from set_status(). * * @param int $id Attachment ID. * * @return string HTML content or array of results. */ public function generate_markup( $id ) { $media_lib_item = Media_Library_Row::get_instance( $id ); return $media_lib_item->generate_markup(); } /** * Print the column html. * * @param string $id Media id. * @param string $html Status text. * @param string $button_txt Button label. * @param boolean $show_button Whether to shoe the button. * * @return string */ private function column_html( $id, $html = '', $button_txt = '', $show_button = true ) { // Don't proceed if attachment is not image, or if image is not a jpg, png or gif, or if is not found. $is_smushable = Helper::is_smushable( $id ); if ( ! $is_smushable ) { return false === $is_smushable ? esc_html__( 'Image not found!', 'wp-smushit' ) : esc_html__( 'Not processed', 'wp-smushit' ); } // If we aren't showing the button. if ( ! $show_button ) { return $html; } if ( 'Super-Smush' === $button_txt ) { $html .= ' | '; } $html .= "{$button_txt}"; if ( get_post_meta( $id, 'wp-smush-ignore-bulk', true ) ) { $nonce = wp_create_nonce( 'wp-smush-remove-skipped' ); $html .= " | " . esc_html__( 'Show in bulk Smush', 'wp-smushit' ) . ''; } else { $html .= " | " . esc_html__( 'Ignore', 'wp-smushit' ) . ''; } $html .= self::progress_bar(); return $html; } /** * Check whether the Smush media notice should be displayed. * * @return bool */ private function should_display_media_notice() { if( ! Helper::is_user_allowed( 'manage_options' ) || ( is_multisite() && ! Helper::is_user_allowed( 'manage_network' ) ) ){ return false; } if ( ! function_exists( 'get_current_screen' ) ) { return false; } $current_screen = get_current_screen(); // Show only on Media Library (upload) page. if ( false === strpos( $current_screen->id, 'upload' ) ) { return false; } return true; } /** * Returns the HTML for progress bar * * @return string */ public static function progress_bar() { return ''; } }