get_data(); $progress = get_transient( 'imagify_bulk_optimization_result' ); if ( $data->is_optimized() ) { $size_data = $data->get_size_data(); if ( false === $progress ) { $progress = [ 'total' => 0, 'original_size' => 0, 'optimized_size' => 0, ]; } $progress['total']++; $progress['original_size'] += $size_data['original_size']; $progress['optimized_size'] += $size_data['optimized_size']; set_transient( 'imagify_bulk_optimization_result', $progress, DAY_IN_SECONDS ); } $remaining = 0; if ( false !== $custom_folders ) { if ( false !== strpos( $item['process_class'], 'CustomFolders' ) ) { $custom_folders['remaining']--; set_transient( 'imagify_custom-folders_optimize_running', $custom_folders, DAY_IN_SECONDS ); $remaining += $custom_folders['remaining']; } } if ( false !== $library_wp ) { if ( false !== strpos( $item['process_class'], 'WP' ) ) { $library_wp['remaining']--; set_transient( 'imagify_wp_optimize_running', $library_wp, DAY_IN_SECONDS ); $remaining += $library_wp['remaining']; } } if ( 0 >= $remaining ) { delete_transient( 'imagify_custom-folders_optimize_running' ); delete_transient( 'imagify_wp_optimize_running' ); set_transient( 'imagify_bulk_optimization_complete', 1, DAY_IN_SECONDS ); } } /** * Decrease optimization running counter for the given context * * @param string $context Context to update. * * @return void */ private function decrease_counter( string $context ) { $counter = get_transient( "imagify_{$context}_optimize_running" ); if ( false === $counter ) { return; } $counter['total'] = $counter['total'] - 1; $counter['remaining'] = $counter['remaining'] - 1; if ( 0 === $counter['total'] && 0 >= $counter['remaining'] ) { delete_transient( "imagify_{$context}_optimize_running" ); } set_transient( "imagify_{$context}_optimize_running", $counter, DAY_IN_SECONDS ); } /** * Process a media with the requested imagify bulk action. * * @since 2.1 * * @param int $media_id Media ID. * @param string $context Current context. * @param int $optimization_level Optimization level. */ public function optimize_media( int $media_id, string $context, int $optimization_level ) { if ( ! $media_id || ! $context || ! is_numeric( $optimization_level ) ) { $this->decrease_counter( $context ); return; } $this->force_optimize( $media_id, $context, $optimization_level ); } /** * Runs the bulk optimization * * @param string $context Current context (WP/Custom folders). * @param int $optimization_level Optimization level. * * @return array */ public function run_optimize( string $context, int $optimization_level ) { if ( ! $this->can_optimize() ) { return [ 'success' => false, 'message' => 'over-quota', ]; } $media_ids = $this->get_bulk_instance( $context )->get_unoptimized_media_ids( $optimization_level ); if ( empty( $media_ids ) ) { return [ 'success' => false, 'message' => 'no-images', ]; } foreach ( $media_ids as $media_id ) { try { as_enqueue_async_action( 'imagify_optimize_media', [ 'id' => $media_id, 'context' => $context, 'level' => $optimization_level, ], "imagify-{$context}-optimize-media" ); } catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch // nothing to do. } } $data = [ 'total' => count( $media_ids ), 'remaining' => count( $media_ids ), ]; set_transient( "imagify_{$context}_optimize_running", $data, DAY_IN_SECONDS ); return [ 'success' => true, 'message' => 'success', ]; } /** * Runs the WebP generation * * @param array $contexts An array of contexts (WP/Custom folders). * * @return array */ public function run_generate_webp( array $contexts ) { if ( ! $this->can_optimize() ) { return [ 'success' => false, 'message' => 'over-quota', ]; } delete_transient( 'imagify_stat_without_webp' ); $medias = []; foreach ( $contexts as $context ) { $media = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_webp(); if ( ! $media['ids'] && $media['errors']['no_backup'] ) { // No backup, no WebP. return [ 'success' => false, 'message' => 'no-backup', ]; } elseif ( ! $media['ids'] && $media['errors']['no_file_path'] ) { // Error. return [ 'success' => false, 'message' => __( 'The path to the selected files could not be retrieved.', 'imagify' ), ]; } $medias[ $context ] = $media['ids']; } if ( empty( $medias ) ) { return [ 'success' => false, 'message' => 'no-images', ]; } $total = 0; foreach ( $medias as $context => $media_ids ) { $total += count( $media_ids ); foreach ( $media_ids as $media_id ) { try { as_enqueue_async_action( 'imagify_convert_webp', [ 'id' => $media_id, 'context' => $context, ], "imagify-{$context}-convert-webp" ); } catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch // nothing to do. } } } set_transient( 'imagify_missing_webp_total', $total, HOUR_IN_SECONDS ); return [ 'success' => true, 'message' => $total, ]; } /** * Get the Bulk class name depending on a context. * * @since 2.1 * * @param string $context The context name. Default values are 'wp' and 'custom-folders'. * @return string The Bulk class name. */ private function get_bulk_class_name( string $context ): string { switch ( $context ) { case 'wp': $class_name = WP::class; break; case 'custom-folders': $class_name = CustomFolders::class; break; default: $class_name = Noop::class; } /** * Filter the name of the class to use for bulk process. * * @since 1.9 * * @param int $class_name The class name. * @param string $context The context name. */ $class_name = apply_filters( 'imagify_bulk_class_name', $class_name, $context ); return '\\' . ltrim( $class_name, '\\' ); } /** * Get the Bulk instance depending on a context. * * @since 2.1 * * @param string $context The context name. Default values are 'wp' and 'custom-folders'. * * @return BulkInterface The optimization process instance. */ public function get_bulk_instance( string $context ): BulkInterface { $class_name = $this->get_bulk_class_name( $context ); return new $class_name(); } /** * Optimize all files from a media, whatever this media’s previous optimization status (will be restored if needed). * This is used by the bulk optimization page. * * @since 1.9 * * @param int $media_id The media ID. * @param string $context The context. * @param int $level The optimization level. * * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. */ private function force_optimize( int $media_id, string $context, int $level ) { if ( ! $this->can_optimize() ) { $this->decrease_counter( $context ); return false; } $process = imagify_get_optimization_process( $media_id, $context ); $data = $process->get_data(); // Restore before re-optimizing. if ( $data->is_optimized() ) { $result = $process->restore(); if ( is_wp_error( $result ) ) { $this->decrease_counter( $context ); // Return an error message. return $result; } } return $process->optimize( $level ); } /** * Generate WebP images if they are missing. * * @since 2.1 * * @param int $media_id Media ID. * @param string $context Current context. * * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. */ public function generate_webp_versions( int $media_id, string $context ) { if ( ! $this->can_optimize() ) { return false; } return imagify_get_optimization_process( $media_id, $context )->generate_webp_versions(); } /** * Check if the user has a valid account and has quota. Die on failure. * * @since 2.1 */ public function can_optimize() { if ( ! \Imagify_Requirements::is_api_key_valid() ) { return false; } if ( \Imagify_Requirements::is_over_quota() ) { return false; } return true; } /** * Get the submitted context. * * @since 1.9 * * @param string $method The method used: 'GET' (default), or 'POST'. * @param string $parameter The name of the parameter to look for. * @return string */ public function get_context( $method = 'GET', $parameter = 'context' ) { $method = 'POST' === $method ? INPUT_POST : INPUT_GET; $context = filter_input( $method, $parameter, FILTER_SANITIZE_STRING ); return imagify_sanitize_context( $context ); } /** * Get the submitted optimization level. * * @since 1.7 * @since 1.9 Added $method and $parameter parameters. * @author Grégory Viguier * * @param string $method The method used: 'GET' (default), or 'POST'. * @param string $parameter The name of the parameter to look for. * @return int */ public function get_optimization_level( $method = 'GET', $parameter = 'optimization_level' ) { $method = 'POST' === $method ? INPUT_POST : INPUT_GET; $level = filter_input( $method, $parameter ); if ( ! is_numeric( $level ) || $level < 0 || $level > 2 ) { if ( get_imagify_option( 'lossless' ) ) { return 0; } return get_imagify_option( 'optimization_level' ); } return (int) $level; } /** ----------------------------------------------------------------------------------------- */ /** BULK OPTIMIZATION CALLBACKS ============================================================= */ /** ----------------------------------------------------------------------------------------- */ /** * Launch the bulk optimization action * * @return void */ public function bulk_optimize_callback() { imagify_check_nonce( 'imagify-bulk-optimize' ); $context = $this->get_context(); $level = $this->get_optimization_level(); if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) { imagify_die(); } $data = $this->run_optimize( $context, $level ); if ( false === $data['success'] ) { wp_send_json_error( [ 'message' => $data['message'] ] ); } wp_send_json_success( [ 'total' => $data['message'] ] ); } /** * Launch the missing WebP versions generation * * @return void */ public function missing_webp_callback() { imagify_check_nonce( 'imagify-bulk-optimize' ); $contexts = explode( '_', sanitize_key( wp_unslash( $_GET['context'] ) ) ); foreach ( $contexts as $context ) { if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) { imagify_die(); } } $data = $this->run_generate_webp( $contexts ); if ( false === $data['success'] ) { wp_send_json_error( [ 'message' => $data['message'] ] ); } wp_send_json_success( [ 'total' => $data['message'] ] ); } /** * Get stats data for a specific folder type. * * @since 1.7 */ public function get_folder_type_data_callback() { imagify_check_nonce( 'imagify-bulk-optimize' ); $context = $this->get_context(); if ( ! $context ) { imagify_die( __( 'Invalid request', 'imagify' ) ); } if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) { imagify_die(); } $bulk = $this->get_bulk_instance( $context ); wp_send_json_success( $bulk->get_context_data() ); } /** * Set the "bulk info" popup state as "seen". * * @since 1.7 */ public function bulk_info_seen_callback() { imagify_check_nonce( 'imagify-bulk-optimize' ); $context = $this->get_context(); if ( ! $context ) { imagify_die( __( 'Invalid request', 'imagify' ) ); } if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) { imagify_die(); } set_transient( 'imagify_bulk_optimization_infos', 1, WEEK_IN_SECONDS ); wp_send_json_success(); } /** * Get generic stats to display in the bulk page. * * @since 1.7.1 */ public function bulk_get_stats_callback() { imagify_check_nonce( 'imagify-bulk-optimize' ); $folder_types = filter_input( INPUT_GET, 'types', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY ); $folder_types = is_array( $folder_types ) ? array_filter( $folder_types, 'is_string' ) : []; if ( ! $folder_types ) { imagify_die( __( 'Invalid request', 'imagify' ) ); } foreach ( $folder_types as $folder_type_data ) { $context = ! empty( $folder_type_data['context'] ) ? $folder_type_data['context'] : 'noop'; if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) { imagify_die(); } } wp_send_json_success( imagify_get_bulk_stats( array_flip( $folder_types ) ) ); } }