Settings has been edited.
*
* This plugin is a fork/rewrite of Additional image sizes created by Walter Vos
* The last version from Walter was Version: 1.0.2 and is still available on
* the WordPress plugin directory.
*
* The new version eliminiates a variety of bugs in the original
* and does everything the original did plus...
* -resizes predefined WordPress sizes if they have been changed in the admin
* -deletes images for deleted sizes and cleans up attachment metadata
* -choose which sizes to do
* -choose an extended time limit for the script to run
* -optionally show a list of images that are skipped during image creation
*
* @package ZUI
* @subpackage WordPress
* @version 0.1.7
* @since 0.1
* @author Walter Vos
* @link http://www.waltervos.com/
* @author zoe somebody
* @link http://beingzoe.com/
* @link http://www.waltervos.com/wordpress-plugins/additional-image-sizes/
* @license http://en.wikipedia.org/wiki/MIT_License The MIT License
* @todo Add ability to delete UNUSED images (not used in posts/pages/custom)
* @todo Add ability to edit existing custom sizes
* @todo Ajaxify add new image sizes
* @todo Ajaxify create new image sizes
* @todo Attempt to auto continue (via ajax) creating new image sizes after a delay
* @todo While functional some of this code is not very elegant - optimize and cleanup
* @todo There are already too many variables - attempt to simplify logic
* @todo Get in on the core 3.2 image size discussion and talk to filosofo
*/
if ( !is_admin() )
return; // not needed in front end ever - speed things up
/**
* Add hooks for static methods in ZUI_WpAdditionalImageSizes
* The init file creates the menu/page that uses this class
*
* @since 0.1
*/
add_action('admin_init', array('ZUI_WpAdditionalImageSizes', 'registerSettings') ); // Register our serialized option
add_filter('intermediate_image_sizes', array('ZUI_WpAdditionalImageSizes', 'addSizesToWpIntermediateSizes'), 1); // Tell WP there are more image sizes
add_filter('attachment_fields_to_edit', array('ZUI_WpAdditionalImageSizes', 'appendAttachmentFieldsWithAdditionalSizes'), 11, 2); // Add our sizes to media forms
/**
* Static methods for creating and maintaining additional images sizes
* Also resizes WordPress predefined image sizes (thumbnail, medium, large)
* if the size has changed.
*
* @version 0.1
* @uses ZUI_WpAdminPages
*/
class ZUI_WpAdditionalImageSizes {
/**#@+
* @since 0.1.2
* @access protected
*/
protected static $_images_regenerated_this_attempt = 0;
protected static $_images_regenerate_from_offset = 0;
/**#@-*/
/**
* This function gets the user defined image sizes from the blog options
*
* @since 0.1
* @uses get_option() WP function
* @param optional boolean $force
* @return array
*/
public static function getSizesCustom($force = FALSE) {
static $sizes_custom = NULL; // cache this - speed things up
if ( NULL === $sizes_custom || TRUE === $force) { // Allow forcing a new get if option might have been updated
$sizes_custom = get_option('aisz_sizes');
if (!is_array($sizes_custom))
$sizes_custom = array();
}
return $sizes_custom;
}
/**
* This function returns WordPress' predefined image sizes
*
* @since 0.1
* @return array
* @uses get_option() WP function
*/
public static function getSizesWp() {
static $sizes_wp = NULL; // cache this - speed things up
if ( NULL === $sizes_wp) {
$wp_size_names = array('thumbnail', 'medium', 'large' );
$sizes_wp = array();
foreach ( $wp_size_names as $size_name ) {
$sizes_wp[$size_name] = array(
'size_w' => get_option("{$size_name}_size_w"),
'size_h' => get_option("{$size_name}_size_h"),
'crop' => get_option("{$size_name}_crop")
);
}
}
return $sizes_wp;
}
/**
* This function returns ALL image sizes (name, size_w, size_h, crop)
* for all images (predefined WP and User defined)
*
* @since 0.1
* @return array
* @uses ZUI_WpAdditionalImageSizes::getSizesWp()
* @uses ZUI_WpAdditionalImageSizes::getSizesCustom()
*/
public static function getAllImageSizes() {
static $sizes_all = NULL; // cache this - speed things up
if ( NULL === $sizes_all) {
$sizes_all = array_merge(self::getSizesWp(), self::getSizesCustom());
}
return $sizes_all;
}
/**
* Method for updating ALL custom sizes in the WP options table
* Also used to optionally delete individual sizes at the same time
* but must still update ALL remaining sizes
*
* @since 0.1.5
* @uses update_option() WP function
* @param optional array $sizes_update
* @param optional array $sizes_delete only the first dimension of size keys is necessary
*/
public static function updateOptionAiszSizes($sizes_update = NULL, $sizes_delete = NULL) {
// Set the messages array to return
$messages = array();
// Get the current custom sizes
$sizes_custom = self::getSizesCustom();
// Check to see if we are deleting any first
if ( NULL !== $sizes_delete ) {
foreach ($sizes_delete as $delete) {
// $delete holds the size name
unset($sizes_custom[$delete]); // Remove that size
delete_option("{$delete}_size_w");
delete_option("{$delete}_size_h");
delete_option("{$delete}_crop");
$messages['success'][] = "The size named $delete was deleted";
}
$messages['success'][] = "You should delete copies of old size(s) now.";
}
if ( NULL !== $sizes_update ) {
foreach ($sizes_update as $size => $values) {
$sizes_custom[$size] = array(
'size_w' => $values['size_w'],
'size_h' => $values['size_h'],
'crop' => $values['crop']
);
// WP needs individual entries for each size as well
update_option("{$size}_size_w", $values['size_w']);
update_option("{$size}_size_h", $values['size_h']);
update_option("{$size}_crop", $values['crop']);
$messages['success'][] = "An additional image size named {$size} was added.";
}
$messages['success'][] = "You should generate copies of new size(s) now.";
}
update_option('aisz_sizes', $sizes_custom); // Update OUR option
self::getSizesCustom(TRUE); // Update the cached $sizes_custom
return $messages;
}
/**
* This function is hooked to 'admin_init' and registers the necessary
* settings/options for this plugin to work with WordPress Settings API
*
* @since 0.1
* @uses register_setting() WP function
*/
public static function registerSettings() {
register_setting('aisz_options', 'aisz_sizes');
}
/**
* This function is hooked to intermediate_image_sizes and makes sure that the sizes defined
* by this plugin get added to the available intermediate_image_sizes. This is how WP will
* use the sizes defined by this plugin in various situations (i.e. media uploader insert size into post)
* The actual options are added to the database when a new option is saved during managePost()
*
* @since 0.1
* @uses ZUI_WpAdditionalImageSizes::getSizesCustom()
* @uses update_option() WP function
* @param array $sizes
* @return array
*/
public static function addSizesToWpIntermediateSizes($sizes) {
$sizes_custom = self::getSizesCustom();
$sizes_custom = array_merge($sizes, array_keys($sizes_custom) );
return $sizes_custom;
}
/**
* This function is almost a replica of image_size_input_fields() which is found in
* wp-admin/includes/media.php. It is hooked onto attachment_fields_to_edit. Unfortunately
* WordPress doesn't provide an easy way to filter the data in the function
* image_attachment_fields_to_edit itself, so we append the html from image_size_input_field().
*
* @since 0.1
* @uses ZUI_WpAdditionalImageSizes::getSizesCustom()
* @uses image_downsize() WP Function
* @param required string $form_fields
* @param required object $post
* @return string
*/
public static function appendAttachmentFieldsWithAdditionalSizes($form_fields, $post) {
// Protect from being view in Media editor where there are no sizes
if ( isset($form_fields['image-size']) ) {
$out = NULL;
$size_names = array();
$sizes_custom = self::getSizesCustom();
if (is_array($sizes_custom)) {
foreach($sizes_custom as $key => $value) {
$size_names[$key] = $key;
}
}
foreach ( $size_names as $size => $label ) {
$downsize = image_downsize($post->ID, $size);
// is this size selectable?
$enabled = ( $downsize[3] || 'full' == $size );
$css_id = "image-size-{$size}-{$post->ID}";
// We must do a clumsy search of the existing html to determine is something has been checked yet
if ( FALSE === strpos('checked="checked"', $form_fields['image-size']['html']) ) {
if ( empty($check) )
$check = get_user_setting('imgsize'); // See if they checked a custom size last time
$checked = '';
// if this size is the default but that's not available, don't select it
if ( $size == $check || str_replace(" ", "", $size) == $check ) {
if ( $enabled )
$checked = " checked='checked'";
else
$check = '';
} elseif ( !$check && $enabled && 'thumbnail' != $size ) {
// if $check is not enabled, default to the first available size that's bigger than a thumbnail
$check = $size;
$checked = " checked='checked'";
}
}
$html = "
ID][image-size]' id='{$css_id}' value='{$size}'".( $checked == $size ? " checked='checked'" : '') ." />";
$html .= "";
// only show the dimensions if that choice is available
if ( $enabled )
$html .= " ";
$html .= '
';
$out .= $html;
}
$form_fields['image-size']['html'] .= $out;
} // End protect from Media editor
return $form_fields;
}
/**
* Manage variables and cookies for options page
*
* @since 0.1.5
* @uses ZUI_WpAdditionalImageSizes::getAllImageSizes()
* @uses ZUI_WpAdditionalImageSizes::getSizesCustom()
* @return array of variables to be extracted and used to fill form values
*/
public static function getAndSetOptionsPageVariables() {
$variables = array();
// Manage custom sizes
if (isset($_COOKIE['aisz_generate_form'])) {
$have_cookie_aisz_generate_form = TRUE;
extract( unserialize(stripcslashes($_COOKIE['aisz_generate_form'])) );
} else {
$have_cookie_aisz_generate_form = FALSE;
}
// Create new/missing sizes
$variables['sizes_to_check'] = ( isset($_POST['sizes_to_check'])) ? $_POST['sizes_to_check']
: (( $have_cookie_aisz_generate_form ) ? $sizes_to_check : 'all');
$variables['numberposts'] = ( isset($_POST['numberposts'])) ? $_POST['numberposts']
: (( $have_cookie_aisz_generate_form ) ? $numberposts : 50); // Shared by create/delete for now
$variables['set_time_limit'] = ( isset($_POST['set_time_limit'])) ? $_POST['set_time_limit']
: (( $have_cookie_aisz_generate_form ) ? $set_time_limit : 30); // Shared by create/delete for now
$variables['simulate_resize'] = ( isset($_POST['simulate_resize'])) ? $_POST['simulate_resize']
: (( $have_cookie_aisz_generate_form && !isset($_POST['sizes_to_check']) )
? $simulate_resize : FALSE);
$variables['show_skipped'] = ( isset($_POST['show_skipped'])) ? $_POST['show_skipped']
: (( $have_cookie_aisz_generate_form && !isset($_POST['sizes_to_check']) )
? $show_skipped : FALSE);
// All the checkboxes with cookies should be like this ;)
if ( isset($_POST['replace_sizes']) ) { // Just keep using it
$variables['replace_sizes'] = $_POST['replace_sizes'];
} else if ( isset($_POST['sizes_to_check']) ) { // They said NO
$variables['replace_sizes'] = FALSE;
} else if ( $have_cookie_aisz_generate_form ) {
$variables['replace_sizes'] = $replace_sizes;
} else {
$variables['replace_sizes'] = TRUE;
}
$variables['advanced_create_sizes'] = ( isset($_POST['advanced_create_sizes'])) ? $_POST['advanced_create_sizes']
: (( $have_cookie_aisz_generate_form && !isset($_POST['advanced_create_sizes']) && !isset($_POST['delete_images_for_deleted_sizes']) )
? $advanced_create_sizes : 'hide'); // This is getting stupid and I should just use if's or find a better way
// Delete images for deleted sizes
// No cookies for delete form
// Save those settings - must check to see if headers have been sent since 0.1.7
if ( !headers_sent() ) {
$serialized_variables = serialize($variables);
$expiry = 60 * 60 * 24 * 60 + time();
setcookie('aisz_generate_form', $serialized_variables, $expiry);
}
// Add a couple more things we don't want to save for Delete images for deleted sizes
$variables['simulate_delete'] = ( !isset($_POST['delete_images_for_deleted_sizes'])) ? 1
: (( isset($_POST['simulate_delete']) ) ? 1 : 0);
$variables['advanced_delete'] = ( isset($_POST['advanced_delete'])) ? $_POST['advanced_delete']
: 'hide';
// Necessary for all
$variables['all_image_sizes'] = self::getAllImageSizes();
$variables['sizes_custom'] = self::getSizesCustom();
// Give it back
return $variables;
}
/**
* This echoes the HTML for the admin page
*
* @since 0.1
* @uses global $_wp_additional_image_sizes WP since 0.1.5
* @uses ZUI_WpAdditionalImageSizes::managePost()
* @uses ZUI_WpAdditionalImageSizes::getAndSetOptionsPageVariables()
* @uses settings_fields() WP function
*/
public static function viewOptionsPage() {
global $_wp_additional_image_sizes;
$messages = self::managePost();
extract(self::getAndSetOptionsPageVariables());
?>
Something(s) went wrong:
That went quite well:
Create missing/changed image sizes
When you add an additional image size that size is automatically created for all NEW images you upload.
After creating new image sizes above you need to create the missing image sizes.
This feature will also create new sizes for the predefined WordPress image sizes (thumbnail, medium, large) if you have edited those.
Delete images for deleted sizes
When you delete an image size that size will not be created for all NEW images you upload.
However, images created for deleted sizes still exist on the server as well as the image attachment metadata for those sizes.
This feature will physically delete those images from the server as well as the image attachment meta data for those sizes. Use this feature at your own risk. There is no undo.
";
for ($i=0; $i < $update_count; $i++) {
$size_crop = ( isset($_POST['ais_size_crop'][$i]) ) ? $_POST['ais_size_crop'][$i]
: FALSE;
// We need a name for that size you know
if (!isset($_POST['ais_size_name'][$i]) || $_POST['ais_size_name'][$i] == '') {
$messages['errors']['size_name'] = __('Please enter a name for this size');
}
// Sizes must be an integer
if ( !is_numeric($_POST['ais_size_w'][$i]) ) { //!absint($_POST['ais_size_w'][$i])
// Blank or Zero is fine as well
if ( !empty($_POST['ais_size_w'][$i]) ) {
$messages['errors']['width'] = __('The width you entered is not valid.');
}
}
// Sizes must be an integer
if ( !is_numeric($_POST['ais_size_h'][$i]) ) { //!absint($_POST['ais_size_h'][$i])
// Blank or Zero is fine as well
if ( !empty($_POST['ais_size_h'][$i]) ) {
$messages['errors']['height'] = __('The height you entered is not valid.');
}
}
// At least one of width and height must be set
if ( empty($_POST['ais_size_w'][$i]) && empty($_POST['ais_size_h'][$i]) ) {
$messages['errors']['width_height'] = __('Width and height can\'t both be blank or "0". One or the other may be left blank to indicate proportional resize/crop.');
}
// Is it a name they already added? Or a WP reserved name?
if (isset($sizes_custom[$_POST['ais_size_name'][$i]])) {
$messages['errors'][] = __('A size with this name already exists');
} else if (isset($sizes_wp[$_POST['ais_size_name'][$i]])) {
$messages['errors'][] = __('This size name is reserved, please choose another one');
}
// There were no errors, so add it to the $sizes_update array and update it
if (empty($messages['errors'])) {
$sizes_update = array();
$sizes_update[$_POST['ais_size_name'][$i]] = array(
'size_w' => $_POST['ais_size_w'][$i],
'size_h' => $_POST['ais_size_h'][$i],
'crop' => $size_crop
);
$messages += self::updateOptionAiszSizes($sizes_update);
} // End if empty($messages['errors'])
} // End loop of custom sizes to update
}
// Remove a size
if (isset($_POST['ais_size_delete'])) {
// If none of size_name, height and width were set, but delete IS set, the
// user just wanted to delete something. No need to bug him with validation errors.
if (isset($messages['errors']['width_height']) && isset($messages['errors']['size_name'])) {
// But if crop was set, maybe the user wanted to add a size anyway?
if ( $size_crop && 1 == $size_crop) {
/* Do nothing */
} else {
unset($messages['errors']);
}
}
$messages += self::updateOptionAiszSizes(NULL, $_POST['ais_size_delete']);
}
// When no new size was entered and no existing size was deleted we
// don't have to send any validation errors.
// Crop is ignored for obvious reasons
if (isset($messages['errors']['width_height']) && isset($messages['errors']['size_name'])) { // && !isset($_POST['ais_size_delete'][0])
unset($messages['errors']);
$messages['errors'][] = 'Nothing added, nothing deleted.';
}
return $messages;
}
/**
* This method looks for images that don't have copies of the sizes defined by this plugin, and
* then tries to make those copies. It returns an array of messages divided into error messages and
* success messages. It only makes copies for X images at a time based on form selection.
*
* Also the same routine (for now) for deleting images and cleaning the attachment metadata
*
* @since 0.1
* @uses global $_wp_additional_image_sizes WP since 0.1.5
* @uses ZUI_WpAdditionalImageSizes::getAllImageAttachments()
* @uses ZUI_WpAdditionalImageSizes::getSizesWp()
* @uses ZUI_WpAdditionalImageSizes::getSizesCustom() since 0.1.3
* @uses ZUI_WpAdditionalImageSizes::getAllImageSizes() since 0.1.3
* @uses ZUI_WpAdditionalImageSizes::mightBreakScriptToAvoidTimeout()
* @uses wp_upload_dir() WP function
* @uses wp_get_attachment_metadata() WP function
* @uses wp_update_attachment_metadata() WP function
* @uses get_post_meta() WP function
* @uses image_make_intermediate_size() WP function
* @uses get_option() WP function
* @return array
* @todo abstract create/delete better
* @todo minimize the number of variables, especially the flags
*/
function regenerateImages() {
global $_wp_additional_image_sizes; // Need this to prevent deleting legit set_post_thumbnail_size() and add_image_size() images
// Set it up
$start = strtotime('now');
$max_execution_time = ini_get('max_execution_time');
$max_execution_time = round($max_execution_time / 1.25); // Let's divide that max_execution_time by 1.25 just to play it a little bit safe (we don't know when WordPress started to run so $now is off as well)
$did_increase_time_limit = FALSE; // Flag to be able to let them know we had to increase the time limit and still tell them we succeeded - clumsy as hell
$did_finish_batch = TRUE; // Flag to know if we finished the batch or aborted - set to FALSE if we break the processing loop
$did_delete_an_image = FALSE; // Flag to know if we deleted (or would have) any images or attacment metadata
$did_resize_an_image = FALSE; // Flag to know if we resized (or would have) any images to customize success message in conjunction with $did_finish_all
$did_finish_all = FALSE; // Flag to know if we finished them ALL - set to TRUE if a query for images comes up empty
$messages = array(); // Sent back to managePost() and viewOptionsPage() to print results
$i = 0; // How many images we managed to process before we stopped the script to prevent a timeout
$offset = ( isset($_POST['offset']) && 0 < $_POST['offset'] ) ? ($_POST['offset'] - 1) // Back up one in case we didn't get to all the sizes for that one
: 0; // Where we should start from; for get_post() in getAllImageAttachments()
$numberposts = ( isset($_POST['numberposts']) && 0 < $_POST['numberposts'] ) ? $_POST['numberposts'] //
: 50;
$basedir = wp_upload_dir();
$basedir = $basedir['basedir'];
$sizes_wp = self::getSizesWp();
// Set the sizes we are going to do based on site/blog owner choice
// For delete this defaults to all
if ( isset($_POST['sizes_to_check']) && 'all' != $_POST['sizes_to_check'] ) {
switch ($_POST['sizes_to_check']) {
case "custom_only":
$sizes_to_check = self::getSizesCustom();
$messages['success'][] = 'Checked custom image sizes only.';
break;
case "wordpress_only":
$sizes_to_check = $sizes_wp;
$messages['success'][] = 'Checked WordPress sizes (thumbnail, medium, large) only.';
break;
default:
$sizes_to_check = array_intersect_key(self::getSizesCustom(), array($_POST['sizes_to_check'] => array())); // This is super sloppy but for now there can be only one
$messages['success'][] = "Checked {$_POST['sizes_to_check']} image size only.";
break;
}
} else if ( isset($_POST['sizes_to_check']) && 'all' == $_POST['sizes_to_check'] ) {
$sizes_to_check = self::getAllImageSizes();
$messages['success'][] = 'Checked all custom and WordPress (thumbnail, medium, large) image sizes.';
} else if ( isset($_POST['delete_images_for_deleted_sizes']) ) {
$sizes_to_check = self::getAllImageSizes();
$messages['success'][] = 'Checked all images metadata.';
}
// Get an image batch with the quantity they requested
$images = self::getAllImageAttachments( array('offset' => $offset, 'numberposts' => $numberposts) ); // Get image attachements starting at the offset
// Loop the batch and resize as necessary
if (!empty($images)) {
foreach ($images as $image) {
$metadata = wp_get_attachment_metadata($image->ID);
$file = get_post_meta($image->ID, '_wp_attached_file', true);
if ( isset($_POST['regenerate_images']) ) { // We can skip this if not regenerating - abstract and separate regenerate/delete
foreach ($sizes_to_check as $size => $values) {
// Check to see if we are close to timing out - GRRR redundancy - we have to check this in the image loop as well if this is a delete - we need to abstract and separate regenerate/delete
$do_break_script = self::mightBreakScriptToAvoidTimeout($start, $max_execution_time);
if ( TRUE === $do_break_script) {
$did_finish_batch = FALSE;
$did_finish_all = FALSE;
break 2;
} else if ( 'extended' === $do_break_script ) {
$messages['success'][] = 'We increased the time limit at your request.';
$did_increase_time_limit = TRUE;
}
$size_width = $sizes_to_check[$size]['size_w'];
$size_height = $sizes_to_check[$size]['size_h'];
$size_crop = $sizes_to_check[$size]['crop'];
// We will try to make the size if:
// size does not exist in the image meta data yet
// OR if the size DOES exist in the image meta data but has changed (new size has a width AND metadata width doesn't match new width AND metadata height doesn't match new height)
if ( isset($_POST['regenerate_images'])
&& ( !isset($metadata['sizes'][$size]) // && !array_key_exists($size, $sizes_wp)
|| ( !empty($sizes_to_check[$size]['size_w']) && ($metadata['sizes'][$size]['width'] != $sizes_to_check[$size]['size_w'] && $metadata['sizes'][$size]['height'] != $sizes_to_check[$size]['size_h']) ) //array_key_exists($size, $sizes_wp) && isset($metadata['sizes'][$size]) &&
)
) {
$image_path = $basedir . '/' . $file;
// Simulate resize or do it for real?
if ( isset($_POST['simulate_resize'])) {
$result = self::simulate_image_make_intermediate_size(
$image_path,
$size_width,
$size_height,
$size_crop
);
} else {
$result = image_make_intermediate_size(
$image_path,
$size_width,
$size_height,
$size_crop
);
}
// If the image was (or would be) resized
if ($result) {
if ( isset($_POST['simulate_resize'])) {
// Just a simulation
if ( isset($_POST['replace_sizes']) && array_key_exists($size, $sizes_wp) ) {
$messages['success'][] = "WOULD REPLACE: {$metadata['sizes'][$size]['file']} for new {$size} size";
}
$messages['success'][] = 'WOULD CREATE: "' . $image->post_title . '" to size "' . $size . '"';
} else {
// WP sizes cannot be deleted later so clean up as we go is in order
// If they want us to and this size is a WP size
if ( isset($_POST['replace_sizes']) && array_key_exists($size, $sizes_wp) ) {
$path_parts = pathinfo($basedir . '/' . $file);
$delete_wp_file = $path_parts['dirname'] . "/" . $metadata['sizes'][$size]['file'];
//$delete_wp_file = $basedir . '/' . $metadata['sizes'][$size]['file'];
$delete_wp_file = str_replace("\\", "/", $delete_wp_file);
// Attempt to delete this old WP size
if ( @unlink($delete_wp_file) ) {
$messages['success'][] = "REPLACED: {$metadata['sizes'][$size]['file']} for new {$size} size";
} // No alternate message on fail for now
}
// Update the metadata - if a wp replaced size named key is overwritten anyway
$metadata['sizes'][$size] = array(
'file' => $result['file'], 'width' => $result['width'], 'height' => $result['height']
);
wp_update_attachment_metadata($image->ID, $metadata);
$messages['success'][] = 'CREATED: "' . $image->post_title . '" to size "' . $size . '"';
}
$did_resize_an_image = TRUE;
} else {
// Sick of looking at the skipped messages
if ( isset($_POST['show_skipped']) ) {
// Assumed the image was too small to be created/resized so just send a tentative success message
$messages['success'][] = 'SKIPPED: "' . $image->post_title . '" is already smaller than the requested size "' . $size . '"';
}
}
} else {
// Sick of looking at the skipped messages and we all know the predefined sizes exist anyway
if ( isset( $_POST['show_skipped']) && isset($_POST['simulate_resize']) ) {
$messages['success'][] = 'WOULD SKIP: "' . $size . '" already exists for "' . $image->post_title . '"';
} else if ( isset($_POST['show_skipped']) ) {
$messages['success'][] = 'SKIPPED: "' . $size . '" already exists for "' . $image->post_title . '"';
}
} // End if we resized or not
} // End sizes loop
}
if ( !empty($_wp_additional_image_sizes)) {
$sizes_to_check_plus_additional = array_merge(array_keys($sizes_to_check), array_keys($_wp_additional_image_sizes));
} else {
$sizes_to_check_plus_additional = array_keys($sizes_to_check);
}
$metadata_sizes_not_in_current_all_sizes = array_diff( array_keys($metadata['sizes']), $sizes_to_check_plus_additional ); //sizes_to_check
if ( isset($_POST['delete_images_for_deleted_sizes']) ) {
$metadata_after = $metadata;
// Leaving in some of this testing stuff for a couple of versions
/*
echo " metadata before
";
print_r($metadata);
echo "
";
*/
if ( 0 < count($metadata_sizes_not_in_current_all_sizes) ) {
foreach ($metadata_sizes_not_in_current_all_sizes as $defunct_size) {
if ( 'post-thumbnail' == $defunct_size)
continue; // kludge to protect known named size created by set_post_thumbnail_size()
$path_parts = pathinfo($basedir . '/' . $file);
$delete_current_file = $path_parts['dirname'] . "/" . $metadata_after['sizes'][$defunct_size]['file'];
$delete_current_file = str_replace("\\", "/", $delete_current_file);
if ( isset($_POST['simulate_delete']) ) {
$image = wp_load_image( $delete_current_file );
if ( is_resource( $image ) ) {
$messages['success'][] = "WOULD DELETE: {$defunct_size} {$delete_current_file}";
imagedestroy( $image ); // Free up memory
} else {
$messages['errors'][] = "Can't find: {$delete_current_file} The attachment metadata would be deleted";
$messages['success'][] = "WOULD DELETE METADATA: for {$delete_current_file}";
}
} else {
if ( @unlink($delete_current_file) ) {
$messages['success'][] = "DELETED: {$defunct_size} {$delete_current_file}";
unset($metadata_after['sizes'][$defunct_size]); // We unset this in a copy of the metadata array to be sure (and for testing)
} else {
$messages['success'][] = "Deleted metadata only: {$defunct_size} {$delete_current_file}";
unset($metadata_after['sizes'][$defunct_size]); // We unset this in a copy of the metadata array to be sure (and for testing)
}
}
$did_delete_an_image = TRUE;
// Check to see if we are close to timing out - GRRR redundancy - we have to check this in the size loop as well
$do_break_script = self::mightBreakScriptToAvoidTimeout($start, $max_execution_time);
if ( TRUE === $do_break_script) {
$did_finish_batch = FALSE;
$did_finish_all = FALSE;
break 2;
} else if ( 'extended' === $do_break_script ) {
$messages['success'][] = 'We increased the time limit at your request.';
$did_increase_time_limit = TRUE;
}
}
} else {
$messages['success'][] = "No sizes to delete for {$image->post_title}";
}
// UPDATE THE METADATA with the removed/deleted sizes
if ( !isset($_POST['simulate_delete']) ) {
wp_update_attachment_metadata($image->ID, $metadata_after);
}
/* // Leaving in some of this testing stuff for a couple of versions
echo " metadata after
";
print_r($metadata_after);
echo "
";
*/
}
$i++;
$offset++;
} // End images loop
} else {
$did_finish_all = TRUE;
}// End if we have images
// Since we finished the batch we should did a quick check for one more
if ( $did_finish_batch ) {
$images = self::getAllImageAttachments( array('offset' => $offset, 'numberposts' => 1) ); // we might have finished with this batch
if (empty($images))
$did_finish_all = TRUE; // Yay we are done!
}
// Set the number of images we got to this attempt
self::$_images_regenerated_this_attempt = $i;
// Set the continue form "submit/click" link
if ( isset($_POST['delete_images_for_deleted_sizes']) )
$continue_link = "Continue deleting where we left off with the same settings";
else
$continue_link = "Continue creating where we left off with the same settings";
// Give them a final status message for this batch
if ( !$did_resize_an_image && $did_finish_all && !isset($_POST['delete_images_for_deleted_sizes']) ) {
$messages['success'][] = 'All is well, no new copies created.';
$i = 0; // Reset because we are done!
} else if (!$did_delete_an_image && $did_finish_all && isset($_POST['delete_images_for_deleted_sizes']) ) {
$messages['success'][] = 'All is well, no images to delete.';
$i = 0; // Reset because we are done!
} else if ( $did_finish_all ) {
$messages['success'][] = 'All done!';
$i = 0; // Reset because we finished!
} else if ( $did_finish_batch ) { // We finished the request batch quantity but not all of the images
$messages['success'][] = "We finished checking a whole batch of {$i}. Consider increasing the number of images per batch until you start seeing the friendly 'we had to stop the script message'.";
$messages['success'][] = "But...we're not quite finished yet. {$continue_link} or use the form to adjust settings and continue.";
self::$_images_regenerate_from_offset = $offset;
} else { // We aborted the script to avoid timing out
$messages['success'][] = "We checked {$i} images out of an attempted {$numberposts}.";
$messages['success'][] = "Not quite finished yet. {$continue_link} or use the form to adjust settings and continue.";
self::$_images_regenerate_from_offset = $offset;
}
return $messages;
}
/**
* Method for testing if an image WOULD be resized
* Simulates the validation that occurs prior to actually beginning the resize
* process that would negate the need for or prevent the image from being resized.
*
* We use image_make_intermediate_size() to create our images
* This method recreates that. See media.php
*
* @since 0.1.5
* @uses wp_load_image() WP function
* @uses image_resize_dimensions() WP function
*/
public static function simulate_image_make_intermediate_size($file, $width, $height, $crop=false) {
// Begin image_make_intermediate_size($file, $width, $height, $crop=false)
if ( $width || $height ) {
// Begin image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 )
$image = wp_load_image( $file );
if ( !is_resource( $image ) )
return FALSE; //return new WP_Error( 'error_loading_image', $image, $file );
$size = @getimagesize( $file );
if ( !$size )
return FALSE; //return new WP_Error('invalid_image', __('Could not read image size'), $file);
list($orig_w, $orig_h, $orig_type) = $size;
$dims = image_resize_dimensions($orig_w, $orig_h, $width, $height, $crop); // $max_w, $max_h
if ( !$dims )
return FALSE; //return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
list($dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) = $dims;
}
imagedestroy( $image ); // Free up memory
// Return value of image_make_intermediate_size()
return array(
'file' => basename( $file ),
'width' => $dst_w,
'height' => $dst_h,
);
}
/**
* Determine if we should stop running this script before we timeout
* Attempt to increase the php time limit if not in safe mode
*
* @since 0.1.2
* @param required timestamp $start strtotime time the script started running
* @param required int $max_execution_time
*/
public static function mightBreakScriptToAvoidTimeout($start, $max_execution_time) {
static $set_time_limit = FALSE; // By default just don't do it
static $_do_try_to_extend_time_limit = TRUE; // When creating new sizes we try to increase the time limit if not in safe mode; TRUE if not in safe_mode; I was going to just name this is_safe_mode but other criteria might come up for not extending
static $_do_extend_time_limit = 'maybe'; // If not in safe_mode this tracks whether we 'maybe', 'can', or did (which becomes an int value of the new execution time)
static $_new_start = FALSE; // Used to set a new start time if we make it to 'can' $_do_extend_time_limit
// If this server isn't in safe_mode and they didn't disable extended time limit then we can try to extend the time limit at the last second
if ( TRUE === $_do_try_to_extend_time_limit && (ini_get('safe_mode') || 0 == $_POST['set_time_limit']) ) {
$_do_try_to_extend_time_limit = FALSE;
$_do_extend_time_limit = FALSE;
}
// If we haven't checked yet and we aren't in safe_mode
if ( 'maybe' == $_do_extend_time_limit && $_do_try_to_extend_time_limit ) {
$_do_extend_time_limit = 'can'; // We have a tenative yes we can
// Set the actual time out if we get to use it - keep it reasonable
if ( FALSE === $set_time_limit && isset($_POST['set_time_limit']) && 0 == $_POST['set_time_limit'] ) {
// can't or disabled
$_do_try_to_extend_time_limit = FALSE;
$set_time_limit = 0;
} else if ( FALSE === $set_time_limit && isset($_POST['set_time_limit']) && is_numeric($_POST['set_time_limit']) && 60 >= $_POST['set_time_limit'] ) {
// Extend time limit by site/blog owner choice
$set_time_limit = $_POST['set_time_limit'];
} else if ( FALSE === $set_time_limit ) {
// Cap their asses at 60 seconds
$set_time_limit = 60;
}
}
// On subsequent passes if we have an integer that is our new time limit - set new times
if ( is_int($_do_extend_time_limit )) {
$start = $_new_start;
$max_execution_time = $_do_extend_time_limit; // That is an integer when we have a $new_start
}
$now = strtotime('now');
$current_execution_time = $now - $start;
// We check against the original max_execution_time first
if ($max_execution_time - $current_execution_time < 2) {
// We are about to time out but can we extend the time limit?
if ( 'can' == $_do_extend_time_limit ) {
$_new_start = strtotime('now'); // We can so we will
$_do_extend_time_limit = ($set_time_limit - 2); // Keep our original 2 second cushion
set_time_limit($set_time_limit);
return 'extended'; // Keep on rockin the free world
}
return TRUE; // Nope about to expire
}
return FALSE; // Keep on rockin the free world
}
/**
* Gets an array of images uploaded on the blog. Returns false when no result was found
*
* @since 0.1
* @uses get_posts() WP function
* @return array|boolean
*/
public static function getAllImageAttachments($args = array()) {
$defaults = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => -1,
'offset' => 0,
'post_status' => null,
'post_parent' => null, // any parent
);
$args = array_merge($defaults, $args);
$attachments = get_posts($args);
if (empty($attachments))
return false;
else
return $attachments;
}
}