OwlCyberSecurity - MANAGER
Edit File: class-forceregeneratethumbnails.php
<?php /** * Main class for Force Regenerate Thumbnails. * * @link https://wordpress.org/plugins/force-regenerate-thumbnails/ * @package ForceRegenerateThumbnails */ /** * Force Regenerate Thumbnails * * @since 1.0 */ class ForceRegenerateThumbnails { /** * Register ID of management page, or false if the user has no permissions. * * @access public * @var string|bool $menu_id * @since 1.0 */ public $menu_id; /** * User capability required to use this plugin. * * @access public * @var string $capability * @since 1.0 */ public $capability; /** * Number of images to regenerate. * * @access protected * @var int $image_count * @since 2.1.0 */ protected $image_count; /** * Primary admin color. * * @access protected * @var string $admin_color * @since 2.1.0 */ protected $admin_color; /** * Version of the plugin. * * @access public * @var float VERSION * @since 2.1.0 */ const VERSION = 221; /** * Plugin initialization * * @access public * @since 1.0 */ public function __construct() { add_action( 'admin_menu', array( &$this, 'add_admin_menu' ) ); add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueues' ) ); add_action( 'wp_ajax_regeneratethumbnail', array( &$this, 'ajax_process_image' ) ); add_action( 'wp_ajax_frt_finish_regen', array( &$this, 'ajax_finish_regen' ) ); add_filter( 'media_row_actions', array( &$this, 'add_media_row_action' ), 10, 2 ); add_filter( 'bulk_actions-upload', array( &$this, 'add_bulk_actions' ) ); add_filter( 'handle_bulk_actions-upload', array( &$this, 'bulk_action_handler' ), 10, 3 ); // Allow people to change what capability is required to use this plugin. $this->capability = apply_filters( 'regenerate_thumbs_cap', 'manage_options' ); } /** * Register the management page * * @access public * @since 1.0 */ public function add_admin_menu() { $this->menu_id = add_management_page( _x( 'Force Regenerate Thumbnails', 'Admin menu page title tag', 'force-regenerate-thumbnails' ), _x( 'Force Regenerate Thumbnails', 'Admin menu text', 'force-regenerate-thumbnails' ), $this->capability, 'force-regenerate-thumbnails', array( &$this, 'force_regenerate_interface' ) ); } /** * Enqueue the needed Javascript and CSS * * @param string $hook_suffix The hook suffix for the current admin page. * @access public * @since 1.0 */ public function admin_enqueues( $hook_suffix ) { if ( $hook_suffix !== $this->menu_id ) { return; } global $wpdb; wp_enqueue_style( 'jquery-ui-force-regen', plugins_url( 'assets/jquery-ui-1.10.1.custom.css', __FILE__ ), array(), self::VERSION ); wp_enqueue_script( 'force-regen-script', plugins_url( 'assets/regen.js', __FILE__ ), array( 'jquery-ui-progressbar' ), self::VERSION ); if ( ! empty( $_POST['reset-regenerate-thumbnails'] ) ) { // Capability check. if ( ! current_user_can( $this->capability ) ) { wp_die( esc_html__( 'Access denied.', 'force-regenerate-thumbnails' ) ); } // Form nonce check. check_admin_referer( 'force-regenerate-thumbnails' ); delete_option( 'frt_last_regenerated' ); } // If the button was clicked. if ( ! empty( $_POST['force-regenerate-thumbnails'] ) || ! empty( $_REQUEST['ids'] ) ) { // Capability check. if ( ! current_user_can( $this->capability ) ) { wp_die( esc_html__( 'Access denied.', 'force-regenerate-thumbnails' ) ); } // Form nonce check. check_admin_referer( 'force-regenerate-thumbnails' ); // Create the list of image IDs. $images = array(); if ( ! empty( $_REQUEST['ids'] ) && ( preg_match( '/^[\d,]+$/', trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' ), $request_ids ) || is_numeric( trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' ) ) ) ) { if ( is_numeric( trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' ) ) ) { $images[] = (int) trim( sanitize_text_field( wp_unslash( $_REQUEST['ids'] ) ), ',' ); } else { $images = explode( ',', $request_ids[0] ); array_walk( $images, 'intval' ); } $ids = implode( ',', $images ); delete_option( 'frt_last_regenerated' ); } else { $resume_position = get_option( 'frt_last_regenerated' ); if ( empty( $resume_position ) ) { $resume_position = PHP_INT_MAX; } // Directly querying the database is normally frowned upon, but all // of the API functions will return the full post objects which will // suck up lots of memory. This is best, just not as future proof. if ( extension_loaded( 'imagick' ) ) { $images = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE ID < %d AND post_type = 'attachment' AND (post_mime_type LIKE %s OR post_mime_type LIKE %s) ORDER BY ID DESC", $resume_position, '%image%', '%pdf%' ) ); } else { $images = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE ID < %d AND post_type = 'attachment' AND post_mime_type LIKE %s ORDER BY ID DESC", $resume_position, '%image%' ) ); } if ( empty( $images ) ) { delete_option( 'frt_last_regenerated' ); return; } $ids = implode( ',', $images ); } $this->image_count = count( $images ); wp_localize_script( 'force-regen-script', 'regen_thumbs', array( '_wpnonce' => wp_create_nonce( 'force-regenerate-attachment' ), 'image_ids' => $images, 'image_count' => $this->image_count, 'stopping' => esc_html__( 'Stopping...', 'force-regenerate-thumbnails' ), 'unknown_error' => esc_html__( 'Unknown error occured.', 'force-regenerate-thumbnails' ), ) ); $this->get_admin_colors(); wp_add_inline_style( 'jquery-ui-force-regen', '.ui-widget-header { background-color: ' . $this->admin_color . '; }' ); } } /** * Grabs the color scheme information from the current admin theme. * * @global array $_wp_admin_css_colors An array of available admin color/theme objects. */ public function get_admin_colors() { if ( ! empty( $this->admin_color ) && preg_match( '/^\#([0-9a-fA-F]){3,6}$/', $this->admin_color ) ) { return; } global $_wp_admin_css_colors; if ( function_exists( 'wp_add_inline_style' ) ) { $user_info = wp_get_current_user(); if ( is_array( $_wp_admin_css_colors ) && ! empty( $user_info->admin_color ) && isset( $_wp_admin_css_colors[ $user_info->admin_color ] ) && is_object( $_wp_admin_css_colors[ $user_info->admin_color ] ) && is_array( $_wp_admin_css_colors[ $user_info->admin_color ]->colors ) && ! empty( $_wp_admin_css_colors[ $user_info->admin_color ]->colors[2] ) && preg_match( '/^\#([0-9a-fA-F]){3,6}$/', $_wp_admin_css_colors[ $user_info->admin_color ]->colors[2] ) ) { $this->admin_color = $_wp_admin_css_colors[ $user_info->admin_color ]->colors[2]; return; } switch ( $user_info->admin_color ) { case 'midnight': $this->admin_color = '#e14d43'; break; case 'blue': $this->admin_color = '#096484'; break; case 'light': $this->admin_color = '#04a4cc'; break; case 'ectoplasm': $this->admin_color = '#a3b745'; break; case 'coffee': $this->admin_color = '#c7a589'; break; case 'ocean': $this->admin_color = '#9ebaa0'; break; case 'sunrise': $this->admin_color = '#dd823b'; break; default: $this->admin_color = '#0073aa'; } } if ( empty( $this->admin_color ) ) { $this->admin_color = '#0073aa'; } } /** * Add a "Force Regenerate Thumbnails" link to the media row actions * * @param array $actions A list of existing Media row actions. * @param WP_Post $post The current attachment/post object. * @return array * @access public * @since 1.0 */ public function add_media_row_action( $actions, $post ) { if ( 'application/pdf' === $post->post_mime_type && ! extension_loaded( 'imagick' ) ) { return $actions; } if ( 'image/svg+xml' === $post->post_mime_type ) { return $actions; } if ( ( 'application/pdf' === $post->post_mime_type || 'image/' === substr( $post->post_mime_type, 0, 6 ) ) && current_user_can( $this->capability ) ) { $url = wp_nonce_url( add_query_arg( array( 'page' => 'force-regenerate-thumbnails', 'goback' => add_query_arg( null, null ), 'ids' => (int) $post->ID, ), admin_url( 'tools.php' ) ), 'force-regenerate-thumbnails' ); $actions['regenerate_thumbnails'] = '<a href="' . esc_url( $url ) . '" title="' . esc_attr__( 'Regenerate the thumbnails for this single image', 'force-regenerate-thumbnails' ) . '">' . _x( 'Force Regenerate Thumbnails', 'Media row action', 'force-regenerate-thumbnails' ) . '</a>'; } return $actions; } /** * Add "Force Regenerate Thumbnails" to the Bulk Actions media dropdown * * @access public * @since 1.0 * * @param array $actions Bulk actions list. * @return array */ public function add_bulk_actions( $actions ) { $delete = false; if ( ! empty( $actions['delete'] ) ) { $delete = $actions['delete']; unset( $actions['delete'] ); } $actions['bulk_force_regenerate_thumbnails'] = _x( 'Force Regenerate Thumbnails', 'Media Library bulk actions drop-down', 'force-regenerate-thumbnails' ); if ( $delete ) { $actions['delete'] = $delete; } return $actions; } /** * Get a list of currently active/registered sizes. * * @access public * @since 2.2.1 * * @return array A list of registered sizes with applicable dimensions. */ public function get_active_image_sizes() { $sizes = wp_get_registered_image_subsizes(); global $wpdb; $latest_upload = (int) $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type LIKE '%%image%%' ORDER BY ID DESC LIMIT 1" ); if ( $latest_upload ) { $image_meta = wp_get_attachment_metadata( $latest_upload ); $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes, $image_meta, $latest_upload ); } elseif ( function_exists( 'ewww_image_optimizer_image_sizes_advanced' ) ) { $sizes = ewww_image_optimizer_image_sizes_advanced( $sizes ); } return $sizes; } /** * Handles the bulk actions POST * * @access public * @since 1.0 * * @param string $redirect_to The URL from whence we came. * @param string $doaction The action requested. * @param array $post_ids An array of attachment ID numbers. * @return string The URL to go back to when we are done handling the action. */ public function bulk_action_handler( $redirect_to, $doaction, $post_ids ) { if ( empty( $doaction ) || 'bulk_force_regenerate_thumbnails' !== $doaction ) { return $redirect_to; } if ( empty( $post_ids ) || ! is_array( $post_ids ) ) { return $redirect_to; } check_admin_referer( 'bulk-media' ); $ids = implode( ',', array_map( 'intval', $post_ids ) ); return add_query_arg( array( '_wpnonce' => wp_create_nonce( 'force-regenerate-thumbnails' ), 'page' => 'force-regenerate-thumbnails', 'ids' => $ids, 'goback' => esc_url( wp_get_referer() ), ), admin_url( 'tools.php' ) ); } /** * The user interface plus thumbnail regenerator * * @access public * @since 1.0 */ public function force_regenerate_interface() { $retry_url = wp_nonce_url( admin_url( 'tools.php?page=force-regenerate-thumbnails' ), 'force-regenerate-thumbnails' ); ?> <div id="frt-aborted" class="notice notice-warning is-dismissible" style="display:none"> <p><strong><?php esc_html_e( 'Process aborted, refresh to resume or start again.', 'force-regenerate-thumbnails' ); ?></strong></p> </div> <div id="frt-message" class="notice notice-success is-dismissible" style="display:none"> <p><strong><?php esc_html_e( 'All done!', 'force-regenerate-thumbnails' ); ?></strong> <?php if ( ! empty( $_GET['goback'] ) ) : ?> <a href=<?php echo '"' . esc_url( sanitize_text_field( wp_unslash( $_GET['goback'] ) ) ) . '"'; ?>><?php esc_html_e( 'Go back to the previous page.', 'force-regenerate-thumbnails' ); ?></a> <?php endif; ?> </p> <p id="frt-retry-container" style="display:none"> <a id="frt-retry-images" href="<?php echo esc_url( $retry_url ); ?>" class="button-secondary"><?php esc_html_e( 'Retry Failed Images', 'force-regenerate-thumbnails' ); ?></a> </p> </div> <div class="wrap regenthumbs"> <h2><?php echo esc_html_x( 'Force Regenerate Thumbnails', 'Thumbnail regen page', 'force-regenerate-thumbnails' ); ?></h2> <?php // If the button was clicked. if ( ! empty( $_POST['force-regenerate-thumbnails'] ) || ! empty( $_REQUEST['ids'] ) ) { // Capability check. if ( ! current_user_can( $this->capability ) ) { echo '<p>' . esc_html__( 'Access denied.', 'force-regenerate-thumbnails' ) . '</p>'; return; } // Form nonce check. check_admin_referer( 'force-regenerate-thumbnails' ); if ( empty( $this->image_count ) ) { echo '<p>' . esc_html__( 'You do not appear to have uploaded any images or PDF files yet.', 'force-regenerate-thumbnails' ) . '</p></div>'; return; } ?> <p><?php esc_html_e( 'Please be patient while the thumbnails are regenerated. Details will be displayed below as each image is completed.', 'force-regenerate-thumbnails' ); ?></p> <noscript><p><em><?php esc_html_e( 'You must enable Javascript in order to proceed!', 'force-regenerate-thumbnails' ); ?></em></p></noscript> <div id="regenthumbs-bar" style="position:relative;height:25px;"> </div> <p><input type="button" class="button hide-if-no-js" name="regenthumbs-stop" id="regenthumbs-stop" value="<?php esc_html_e( 'Abort Process', 'force-regenerate-thumbnails' ); ?>" /></p> <h3 class="title"><?php esc_html_e( 'Process Information', 'force-regenerate-thumbnails' ); ?></h3> <p> <?php /* translators: %d: the total number of images */ printf( esc_html__( 'Total: %s', 'force-regenerate-thumbnails' ), '<span id="regenthumbs-debug-totalcount">' . (int) $this->image_count . '</span>' ); echo '<br>'; /* translators: %d: the number of successfully regenerated images */ printf( esc_html__( 'Success: %s', 'force-regenerate-thumbnails' ), '<span id="regenthumbs-debug-successcount">0</span>' ); echo '<br>'; /* translators: %d: the number of regeneration failures */ printf( esc_html__( 'Failure: %s', 'force-regenerate-thumbnails' ), '<span id="regenthumbs-debug-failurecount">0</span>' ); ?> </p> <ol id="regenthumbs-debuglist"> <li style="display:none"></li> </ol> <?php } else { // No button click, so display the form. $image_sizes = $this->get_active_image_sizes(); $ewwwio_localmode_url = wp_nonce_url( add_query_arg( array( 'page' => 'ewww-image-optimizer-options', 'enable-local' => 1, ), admin_url( 'options-general.php' ) ), 'ewww_image_optimizer_options-options' ); ?> <form method="post" action=""> <?php wp_nonce_field( 'force-regenerate-thumbnails' ); ?> <h3><?php esc_html_e( 'All Thumbnails', 'force-regenerate-thumbnails' ); ?></h3> <p> <?php esc_html_e( 'You may regenerate thumbnails for all images that you have uploaded to your blog.', 'force-regenerate-thumbnails' ); ?><br> <?php esc_html_e( 'Be sure to backup your site before you begin.', 'force-regenerate-thumbnails' ); ?> </p> <noscript><p><em><?php esc_html_e( 'You must enable Javascript in order to proceed!', 'force-regenerate-thumbnails' ); ?></em></p></noscript> <?php if ( ! empty( $image_sizes ) ) : ?> <p><?php esc_html_e( 'The following image sizes will be regenerated, all other thumbnails will be deleted:', 'force-regenerate-thumbnails' ); ?></p> <ul style="list-style: disc inside; margin: 1em 0 2em 0.5em;"> <?php foreach ( $image_sizes as $size => $dimensions ) : ?> <?php if ( empty( $dimensions['width'] ) && empty( $dimensions['height'] ) ) { continue; } ?> <li><?php echo esc_html( $size ) . ' - ' . (int) $dimensions['width'] . ' x ' . (int) $dimensions['height']; ?></li> <?php endforeach; ?> </ul> <?php endif; ?> <?php if ( get_option( 'frt_last_regenerated' ) ) : ?> <p><em><?php esc_html_e( 'A previous thumbnail regeneration was interrupted.', 'force-regenerate-thumbnails' ); ?></em></p> <p><input type="submit" class="button-primary hide-if-no-js" name="force-regenerate-thumbnails" id="force-regenerate-thumbnails" value="<?php esc_attr_e( 'Continue Regenerating Thumbnails', 'force-regenerate-thumbnails' ); ?>" /></p> <?php else : ?> <p><input type="submit" class="button-primary hide-if-no-js" name="force-regenerate-thumbnails" id="force-regenerate-thumbnails" value="<?php esc_attr_e( 'Regenerate All Thumbnails', 'force-regenerate-thumbnails' ); ?>" /></p> <?php endif; ?> </form> <?php if ( get_option( 'frt_last_regenerated' ) ) : ?> <form method="post" action=""> <?php wp_nonce_field( 'force-regenerate-thumbnails' ); ?> <p> <input type="submit" class="button-secondary hide-if-no-js" name="reset-regenerate-thumbnails" id="reset-regenerate-thumbnails" value="<?php esc_attr_e( 'Start Over', 'force-regenerate-thumbnails' ); ?>" /> </p> </form> <?php endif; ?> <h3><?php esc_html_e( 'Specific Thumbnails', 'force-regenerate-thumbnails' ); ?></h3> <p> <?php /* translators: %s: Media Library (link) */ ?> <?php printf( esc_html__( 'You may regenerate thumbnails for specific images from the %s in List mode.', 'force-regenerate-thumbnails' ), '<a href="' . esc_url( admin_url( 'upload.php?mode=list' ) ) . '">' . esc_html__( 'Media Library', 'force-regenerate-thumbnails' ) . '</a>' ); ?> </p> <?php } // End if button clicked to start regen process. ?> <?php if ( ! function_exists( 'ewww_image_optimizer' ) ) : ?> <p> <?php printf( /* translators: %s: link to install EWWW Image Optimizer plugin */ esc_html__( 'Install the free %s for sharper thumbnails, better compression, and to control which thumbnails are created.', 'force-regenerate-thumbnails' ), '<a href="' . esc_url( admin_url( 'plugin-install.php?s=ewww+image+optimizer&tab=search&type=term' ) ) . '">EWWW Image Optimizer</a>' ); ?> </p> <?php else : ?> <p> <?php printf( /* translators: %s: link to EWWW Image Optimizer settings */ esc_html__( 'Configure the %s to enable sharper thumbnails and control which thumbnails are created.', 'force-regenerate-thumbnails' ), '<a href="' . esc_url( $ewwwio_localmode_url ) . '">EWWW Image Optimizer</a>' ); ?> </p> <?php endif; ?> </div> <?php } /** * Mark the bulk regen process as complete, by removing the last regen option. * * @access public * @since 2.2 */ public function ajax_finish_regen() { if ( ! current_user_can( $this->capability ) ) { wp_die(); } if ( empty( $_REQUEST['frt_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['frt_wpnonce'] ), 'force-regenerate-attachment' ) ) { wp_die(); } delete_option( 'frt_last_regenerated' ); } /** * Process a single image ID via an AJAX request. * * @access public * @since 1.0 * @throws Exception Any time we find an image we can't handle: permissions, corruption, doesn't exist, etc. */ public function ajax_process_image() { if ( empty( $_REQUEST['id'] ) ) { $this->ob_clean(); wp_die( wp_json_encode( array( 'error' => esc_html__( 'No attachment ID submitted.', 'force-regenerate-thumbnails' ) ) ) ); } // No timeout limit. if ( $this->stl_check() ) { set_time_limit( 0 ); } // Don't break the JSON result. error_reporting( 0 ); $id = (int) $_REQUEST['id']; try { header( 'Content-type: application/json' ); if ( ! current_user_can( $this->capability ) ) { throw new Exception( esc_html__( 'Your user account does not have permission to regenerate thumbnails.', 'force-regenerate-thumbnails' ) ); } if ( empty( $_REQUEST['frt_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['frt_wpnonce'] ), 'force-regenerate-attachment' ) ) { throw new Exception( esc_html__( 'Access token has expired, please reload the page.', 'force-regenerate-thumbnails' ) ); } // Find out if our nonce is on it's last leg/tick. $current_nonce = sanitize_key( $_REQUEST['frt_wpnonce'] ); $tick = wp_verify_nonce( $current_nonce, 'force-regenerate-attachment' ); if ( 2 === $tick ) { $current_nonce = wp_create_nonce( 'force-regenerate-attachment' ); } if ( apply_filters( 'regenerate_thumbs_skip_image', false, $id ) ) { update_option( 'frt_last_regenerated', $id ); die( wp_json_encode( array( /* translators: %d: attachment ID number */ 'success' => '<div id="message" class="notice notice-info"><p>' . sprintf( esc_html__( 'Skipped: %d', 'force-regenerate-thumbnails' ), (int) $id ) . '</p></div>', 'current_nonce' => $current_nonce, ) ) ); } $image = get_post( $id ); if ( is_null( $image ) ) { /* translators: %d: attachment ID number */ throw new Exception( sprintf( esc_html__( 'Failed: %d is an invalid media ID.', 'force-regenerate-thumbnails' ), (int) $id ) ); } if ( 'attachment' !== $image->post_type || ( 'image/' !== substr( $image->post_mime_type, 0, 6 ) && 'application/pdf' !== $image->post_mime_type ) ) { /* translators: %d: attachment ID number */ throw new Exception( sprintf( esc_html__( 'Failed: %d is an invalid media ID.', 'force-regenerate-thumbnails' ), (int) $id ) ); } if ( 'application/pdf' === $image->post_mime_type && ! extension_loaded( 'imagick' ) ) { throw new Exception( esc_html__( 'Failed: The imagick extension is required for PDF files.', 'force-regenerate-thumbnails' ) ); } if ( 'image/svg+xml' === $image->post_mime_type ) { update_option( 'frt_last_regenerated', $id ); die( wp_json_encode( array( /* translators: %d: attachment ID number */ 'success' => '<div id="message" class="notice notice-info"><p>' . sprintf( esc_html__( 'Skipped: %d is a SVG', 'force-regenerate-thumbnails' ), (int) $id ) . '</p></div>', 'current_nonce' => $current_nonce, ) ) ); } $upload_dir = wp_get_upload_dir(); $meta = wp_get_attachment_metadata( $image->ID ); // Get full-size image. $image_fullpath = $this->get_attachment_path( $image->ID, $meta ); $debug_1 = $image_fullpath; $debug_2 = ''; $debug_3 = ''; $debug_4 = ''; // Didn't get a valid image path. if ( empty( $image_fullpath ) || false === realpath( $image_fullpath ) ) { throw new Exception( esc_html__( 'The originally uploaded image file cannot be found.', 'force-regenerate-thumbnails' ) ); } $thumb_deleted = array(); $thumb_error = array(); $thumb_regenerate = array(); $file_info = pathinfo( $image_fullpath ); /** * Try delete all thumbnails */ if ( ! empty( $meta['sizes'] ) && is_iterable( $meta['sizes'] ) ) { foreach ( $meta['sizes'] as $size_data ) { if ( empty( $size_data['file'] ) ) { continue; } $thumb_fullpath = trailingslashit( $file_info['dirname'] ) . wp_basename( $size_data['file'] ); if ( apply_filters( 'regenerate_thumbs_weak', false, $thumb_fullpath ) ) { continue; } if ( $thumb_fullpath !== $image_fullpath && is_file( $thumb_fullpath ) ) { do_action( 'regenerate_thumbs_pre_delete', $thumb_fullpath ); unlink( $thumb_fullpath ); if ( is_file( $thumb_fullpath . '.webp' ) ) { unlink( $thumb_fullpath . '.webp' ); } clearstatcache(); do_action( 'regenerate_thumbs_post_delete', $thumb_fullpath ); if ( ! is_file( $thumb_fullpath ) ) { $thumb_deleted[] = sprintf( '%dx%d', $size_data['width'], $size_data['height'] ); } else { $thumb_error[] = sprintf( '%dx%d', $size_data['width'], $size_data['height'] ); } } } } // Hack to find thumbnail. $file_stem = $this->remove_from_end( $file_info['filename'], '-scaled' ) . '-'; $files = array(); $path = opendir( $file_info['dirname'] ); if ( false !== $path ) { $thumb = readdir( $path ); while ( false !== $thumb ) { if ( 0 === strpos( $thumb, $file_stem ) && str_ends_with( $thumb, $file_info['extension'] ) ) { $files[] = $thumb; } $thumb = readdir( $path ); } closedir( $path ); sort( $files ); } foreach ( $files as $thumb ) { $thumb_fullpath = trailingslashit( $file_info['dirname'] ) . $thumb; if ( apply_filters( 'regenerate_thumbs_weak', false, $thumb_fullpath ) ) { continue; } $thumb_info = pathinfo( $thumb_fullpath ); $valid_thumb = explode( $file_stem, $thumb_info['filename'] ); if ( '' === $valid_thumb[0] && ! empty( $valid_thumb[1] ) ) { if ( 0 === strpos( $valid_thumb[1], 'scaled-' ) ) { $valid_thumb[1] = str_replace( 'scaled-', '', $valid_thumb[1] ); } $dimension_thumb = explode( 'x', $valid_thumb[1] ); if ( 2 === count( $dimension_thumb ) ) { if ( is_numeric( $dimension_thumb[0] ) && is_numeric( $dimension_thumb[1] ) ) { do_action( 'regenerate_thumbs_pre_delete', $thumb_fullpath ); unlink( $thumb_fullpath ); if ( is_file( $thumb_fullpath . '.webp' ) ) { unlink( $thumb_fullpath . '.webp' ); } clearstatcache(); do_action( 'regenerate_thumbs_post_delete', $thumb_fullpath ); if ( ! is_file( $thumb_fullpath ) ) { $thumb_deleted[] = sprintf( '%dx%d', $dimension_thumb[0], $dimension_thumb[1] ); } else { $thumb_error[] = sprintf( '%dx%d', $dimension_thumb[0], $dimension_thumb[1] ); } } } } } /** * Regenerate all thumbnails */ if ( function_exists( 'wp_get_original_image_path' ) ) { $original_path = apply_filters( 'regenerate_thumbs_original_image', wp_get_original_image_path( $image->ID, true ) ); } if ( empty( $original_path ) || ! is_file( $original_path ) ) { $regen_path = $image_fullpath; $original_path = $image_fullpath; } elseif ( preg_match( '/e\d{10,}\./', $image_fullpath ) ) { $regen_path = $image_fullpath; } else { $regen_path = $original_path; } $debug_1 = $regen_path; $metadata = wp_generate_attachment_metadata( $image->ID, $regen_path ); if ( is_wp_error( $metadata ) ) { throw new Exception( esc_html( $metadata->get_error_message() ) ); } if ( empty( $metadata ) ) { throw new Exception( esc_html__( 'Unknown failure.', 'force-regenerate-thumbnails' ) ); } if ( ! empty( $meta['original_image'] ) && is_file( $original_path ) && empty( $metadata['original_image'] ) ) { $metadata['original_image'] = $meta['original_image']; } wp_update_attachment_metadata( $image->ID, $metadata ); do_action( 'regenerate_thumbs_post_update', $image->ID, $regen_path ); /** * Verify results (deleted, errors, success) */ $files = array(); $path = opendir( $file_info['dirname'] ); if ( false !== $path ) { $thumb = readdir( $path ); while ( false !== $thumb ) { /* if ( ! ( strrpos( $thumb, $file_info['filename'] ) === false ) ) { */ if ( 0 === strpos( $thumb, $file_stem ) && str_ends_with( $thumb, $file_info['extension'] ) ) { $files[] = $thumb; } $thumb = readdir( $path ); } closedir( $path ); sort( $files ); } if ( ! empty( $metadata['sizes'] ) && is_iterable( $metadata['sizes'] ) ) { foreach ( $metadata['sizes'] as $size_data ) { if ( empty( $size_data['file'] ) ) { continue; } if ( in_array( $size_data['file'], $files, true ) ) { continue; } $thumb_regenerate[] = sprintf( '%dx%d', $size_data['width'], $size_data['height'] ); } } foreach ( $files as $thumb ) { $thumb_fullpath = trailingslashit( $file_info['dirname'] ) . $thumb; $thumb_info = pathinfo( $thumb_fullpath ); $valid_thumb = explode( $file_stem, $thumb_info['filename'] ); if ( '' === $valid_thumb[0] ) { $dimension_thumb = explode( 'x', $valid_thumb[1] ); if ( 2 === count( $dimension_thumb ) ) { if ( is_numeric( $dimension_thumb[0] ) && is_numeric( $dimension_thumb[1] ) ) { $thumb_regenerate[] = sprintf( '%dx%d', $dimension_thumb[0], $dimension_thumb[1] ); } } } } // Remove success if has in error list. foreach ( $thumb_regenerate as $key => $regenerate ) { if ( in_array( $regenerate, $thumb_error, true ) ) { unset( $thumb_regenerate[ $key ] ); } } // Remove deleted if has in success list, so that we only show those that were no longer registered. foreach ( $thumb_deleted as $key => $deleted ) { if ( in_array( $deleted, $thumb_regenerate, true ) ) { unset( $thumb_deleted[ $key ] ); } } /** * Display results */ ob_start(); // To suppress any error output. $message = '<strong>' . sprintf( /* translators: 1: attachment title 2: attachment ID number */ esc_html__( '%1$s (ID %2$d)', 'force-regenerate-thumbnails' ), esc_html( get_the_title( $id ) ), (int) $image->ID ) . '</strong>'; $message .= '<br><br>'; // The Upload dir/URL are suppressed, just extra noise that we don't need. /* translators: %s: the path to the uploads directory */ $old_message = '<code>' . sprintf( esc_html__( 'Upload Directory: %s', 'force-regenerate-thumbnails' ), esc_html( $upload_dir['basedir'] ) ) . '</code><br>'; /* translators: %s: the base URL of the uploads directory */ $old_message = '<code>' . sprintf( esc_html__( 'Upload URL: %s', 'force-regenerate-thumbnails' ), esc_html( $upload_dir['baseurl'] ) ) . '</code><br>'; /* translators: %s: the full path of the image */ $message .= '<code>' . sprintf( esc_html__( 'Image: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_1 ) ) . '</code><br>'; if ( $debug_2 ) { /* translators: %s: debug info, if populated */ $message .= '<code>' . sprintf( esc_html__( 'Image Debug 2: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_2 ) ) . '</code><br>'; } if ( $debug_3 ) { /* translators: %s: debug info, if populated */ $message .= '<code>' . sprintf( esc_html__( 'Image Debug 3: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_3 ) ) . '</code><br>'; } if ( $debug_4 ) { /* translators: %s: debug info, if populated */ $message .= '<code>' . sprintf( esc_html__( 'Image Debug 4: %s', 'force-regenerate-thumbnails' ), esc_html( $debug_4 ) ) . '</code><br>'; } $message .= '<br>'; if ( count( $thumb_deleted ) > 0 ) { /* translators: %s: list of deleted thumbs */ $message .= sprintf( esc_html__( 'Deleted: %s', 'force-regenerate-thumbnails' ), esc_html( implode( ', ', $thumb_deleted ) ) ) . '<br>'; } if ( count( $thumb_error ) > 0 ) { /* translators: %s: size of the failed thumbnail(s), like 400x300 */ $message .= '<strong><span style="color: #dd3d36;">' . sprintf( esc_html__( 'Delete failed: %s', 'force-regenerate-thumbnails' ), esc_html( implode( ', ', $thumb_error ) ) ) . '</span></strong><br>'; /* translators: %s: the path to the uploads directory */ $message .= '<span style="color: #dd3d36;">' . sprintf( esc_html__( 'Please ensure the folder has write permissions (chmod 755): %s', 'force-regenerate-thumbnails' ), esc_html( $upload_dir['basedir'] ) ) . '</span><br>'; } if ( count( $thumb_regenerate ) > 0 ) { /* translators: %s: list of regenerated thumbs/sizes */ $message .= sprintf( esc_html__( 'Regenerate: %s', 'force-regenerate-thumbnails' ), esc_html( implode( ', ', $thumb_regenerate ) ) ) . '<br>'; if ( count( $thumb_error ) <= 0 ) { /* translators: %s: time elapsed */ $message .= sprintf( esc_html__( 'Successfully regenerated in %s seconds', 'force-regenerate-thumbnails' ), esc_html( timer_stop() ) ) . '<br>'; } } $message = $this->remove_from_end( $message, '<br>' ); $this->ob_clean(); if ( count( $thumb_error ) > 0 ) { update_option( 'frt_last_regenerated', $id ); die( wp_json_encode( array( 'error' => '<div id="message" class="notice notice-error"><p>' . $message . '</p></div>', 'current_nonce' => $current_nonce, ) ) ); } else { update_option( 'frt_last_regenerated', $id ); die( wp_json_encode( array( 'success' => '<div id="message" class="notice notice-success"><p>' . $message . '</p></div>', 'current_nonce' => $current_nonce, ) ) ); } } catch ( Exception $e ) { $this->die_json_failure_msg( $id, '<b><span style="color: #DD3D36;">' . $e->getMessage() . '</span></b>' ); } exit; } /** * Retrieves the path of an attachment via the ID number. * * @param int $id The attachment ID number. * @param array $meta The attachment metadata. * @return string The full path to the image. */ public function get_attachment_path( $id, $meta ) { // Retrieve the location of the WordPress upload folder. $upload_dir = wp_upload_dir( null, false, true ); $upload_path = trailingslashit( $upload_dir['basedir'] ); // Get the file path from postmeta. $file = get_post_meta( $id, '_wp_attached_file', true ); $file_path = ( 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) ? $upload_path . $file : $file ); $filtered_file_path = apply_filters( 'get_attached_file', $file_path, $id ); if ( ( ! $this->stream_wrapped( $filtered_file_path ) || $this->stream_wrapper_exists() ) && is_file( $filtered_file_path ) ) { return $filtered_file_path; } if ( ( ! $this->stream_wrapped( $file_path ) || $this->stream_wrapper_exists() ) && is_file( $file_path ) ) { return $file_path; } if ( is_array( $meta ) && ! empty( $meta['file'] ) ) { $file_path = $meta['file']; if ( $this->stream_wrapped( $file_path ) && ! $this->stream_wrapper_exists() ) { return ''; } if ( is_file( $file_path ) ) { return $file_path; } $file_path = trailingslashit( $upload_path ) . $file_path; if ( is_file( $file_path ) ) { return $file_path; } $upload_path = trailingslashit( WP_CONTENT_DIR ) . 'uploads/'; $file_path = $upload_path . $meta['file']; if ( is_file( $file_path ) ) { return $file_path; } } return ''; } /** * Checks the existence of a cloud storage stream wrapper. * * @return bool True if a supported stream wrapper is found, false otherwise. */ public function stream_wrapper_exists() { $wrappers = stream_get_wrappers(); if ( ! is_iterable( $wrappers ) ) { return false; } foreach ( $wrappers as $wrapper ) { if ( strpos( $wrapper, 's3' ) === 0 ) { return true; } if ( strpos( $wrapper, 'gs' ) === 0 ) { return true; } } return false; } /** * Checks the filename for an S3 or GCS stream wrapper. * * @param string $filename The filename to be searched. * @return bool True if a stream wrapper is found, false otherwise. */ public function stream_wrapped( $filename ) { if ( false !== strpos( $filename, '://' ) ) { if ( strpos( $filename, 's3' ) === 0 ) { return true; } if ( strpos( $filename, 'gs' ) === 0 ) { return true; } } return false; } /** * Trims the given 'needle' from the end of the 'haystack'. * * @param string $haystack The string to be modified if it contains needle. * @param string $needle The string to remove if it is at the end of the haystack. * @return string The haystack with needle removed from the end. */ public function remove_from_end( $haystack, $needle ) { $needle_length = strlen( $needle ); if ( substr( $haystack, -$needle_length ) === $needle ) { return substr( $haystack, 0, -$needle_length ); } return $haystack; } /** * Checks if a function is disabled or does not exist. * * @param string $function_name The name of a function to test. * @param bool $debug Whether to output debugging. * @return bool True if the function is available, False if not. */ public function function_exists( $function_name, $debug = false ) { if ( extension_loaded( 'suhosin' ) && function_exists( 'ini_get' ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors $suhosin_disabled = @ini_get( 'suhosin.executor.func.blacklist' ); if ( ! empty( $suhosin_disabled ) ) { $suhosin_disabled = explode( ',', $suhosin_disabled ); $suhosin_disabled = array_map( 'trim', $suhosin_disabled ); $suhosin_disabled = array_map( 'strtolower', $suhosin_disabled ); if ( function_exists( $function_name ) && ! in_array( $function_name, $suhosin_disabled, true ) ) { return true; } return false; } } return \function_exists( $function_name ); } /** * Find out if set_time_limit() is allowed. */ public function stl_check() { if ( defined( 'FTR_DISABLE_STL' ) && FTR_DISABLE_STL ) { // set_time_limit() disabled by user. return false; } if ( function_exists( 'wp_is_ini_value_changeable' ) && ! wp_is_ini_value_changeable( 'max_execution_time' ) ) { // max_execution_time is not configurable. return false; } return $this->function_exists( 'set_time_limit' ); } /** * Clear output buffers without throwing a fit. */ public function ob_clean() { if ( ob_get_length() ) { ob_end_clean(); } } /** * Helper to make a JSON failure message * * @param integer $id An attachment ID. * @param string $message An error message. * @access public * @since 1.8 */ public function die_json_failure_msg( $id, $message ) { $this->ob_clean(); die( wp_json_encode( array( /* translators: %d: the attachment ID number */ 'error' => sprintf( esc_html__( '(ID %d)', 'force-regenerate-thumbnails' ), $id ) . '<br />' . $message, ) ) ); } }