/** * 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 : 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 <strong>{$_POST['sizes_to_check']}</strong> 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]) || !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']))) { $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'][] = '<strong>WOULD CREATE:</strong> "' . $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'][] = '<strong>CREATED:</strong> "' . $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 "<br />metadata before<pre>"; print_r($metadata); echo "</pre><br />"; */ 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'][] = "<strong>WOULD DELETE:</strong> {$defunct_size} {$delete_current_file}"; imagedestroy($image); // Free up memory } else { $messages['errors'][] = "<strong>Can't find:</strong> {$delete_current_file}<br /><em>The attachment metadata would be deleted</em>"; $messages['success'][] = "WOULD DELETE METADATA: for {$delete_current_file}"; } } else { if (@unlink($delete_current_file)) { $messages['success'][] = "<strong>DELETED:</strong> {$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 "<br />metadata after<pre>"; print_r($metadata_after); echo "</pre><br />"; */ } $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 = "<a id='continue_deleting'>Continue deleting where we left off with the same settings</a>"; } else { $continue_link = "<a id='continue_creating'>Continue creating where we left off with the same settings</a>"; } // 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'][] = '<strong>All done!</strong>'; $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}.<br />Consider increasing the number of images per batch until you start seeing the friendly 'we had to stop the script message'."; $messages['success'][] = "<strong>But...we're not quite finished yet. {$continue_link} or use the form to adjust settings and continue.</strong>"; 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'][] = "<strong>Not quite finished yet. {$continue_link} or use the form to adjust settings and continue.</strong>"; self::$_images_regenerate_from_offset = $offset; } } } } return $messages; }
/** * Callback for viewing this plugin admin page * * @since 0.1 * @uses ZUI_WpAdditionalImageSizes::viewOptionsPage() */ function aiszViewOptionsPage() { echo "<div class='wrap'>"; echo "<h2>Additional image sizes</h2>"; ZUI_WpAdditionalImageSizes::viewOptionsPage(); echo "</div>"; }