wp-smushit
Last commit date
JSON
17 years ago
bulk.php
14 years ago
license.txt
17 years ago
readme.txt
13 years ago
screenshot-1.jpg
17 years ago
settings.php
13 years ago
wp-smushit.php
14 years ago
wp-smushit.php
391 lines
| 1 | <?php |
| 2 | /* |
| 3 | Plugin Name: WP Smush.it |
| 4 | Plugin URI: http://dialect.ca/code/wp-smushit/ |
| 5 | Description: Reduce image file sizes and improve performance using the <a href="http://smush.it/">Smush.it</a> API within WordPress. |
| 6 | Author: Dialect |
| 7 | Version: 1.6.0 |
| 8 | Author URI: http://dialect.ca/ |
| 9 | */ |
| 10 | |
| 11 | if ( !function_exists('json_encode') ) { |
| 12 | require_once('JSON/JSON.php'); |
| 13 | } |
| 14 | |
| 15 | if ( !function_exists('download_url') ) { |
| 16 | require_once(ABSPATH . 'wp-admin/includes/file.php'); |
| 17 | } |
| 18 | |
| 19 | /** |
| 20 | * Constants |
| 21 | */ |
| 22 | define('SMUSHIT_REQ_URL', 'http://www.smushit.com/ysmush.it/ws.php?img=%s'); |
| 23 | define('SMUSHIT_BASE_URL', 'http://www.smushit.com/'); |
| 24 | |
| 25 | define('WP_SMUSHIT_DOMAIN', 'wp_smushit'); |
| 26 | define('WP_SMUSHIT_UA', 'WP Smush.it/1.6.0 (+http://dialect.ca/code/wp-smushit)'); |
| 27 | define('WP_SMUSHIT_PLUGIN_DIR', dirname(plugin_basename(__FILE__))); |
| 28 | |
| 29 | define('WP_SMUSHIT_AUTO', intval(get_option('wp_smushit_smushit_auto', 0))); |
| 30 | require( dirname(__FILE__) . '/settings.php' ); |
| 31 | |
| 32 | /** |
| 33 | * Hooks |
| 34 | */ |
| 35 | |
| 36 | if (WP_SMUSHIT_AUTO == WP_SMUSHIT_AUTO_OK) { |
| 37 | add_filter('wp_generate_attachment_metadata', 'wp_smushit_resize_from_meta_data', 10, 2); |
| 38 | } |
| 39 | add_filter('manage_media_columns', 'wp_smushit_columns'); |
| 40 | add_action('manage_media_custom_column', 'wp_smushit_custom_column', 10, 2); |
| 41 | add_action('admin_init', 'wp_smushit_admin_init'); |
| 42 | add_action('admin_action_wp_smushit_manual', 'wp_smushit_manual'); |
| 43 | |
| 44 | /** |
| 45 | * Plugin admin functions |
| 46 | */ |
| 47 | function wp_smushit_admin_init() { |
| 48 | load_plugin_textdomain(WP_SMUSHIT_DOMAIN); |
| 49 | wp_enqueue_script('common'); |
| 50 | } |
| 51 | |
| 52 | function wp_smushit_admin_menu() { |
| 53 | add_media_page( 'Bulk Smush.it', 'Bulk Smush.it', 'edit_others_posts', 'wp-smushit-bulk', 'wp_smushit_bulk_preview'); |
| 54 | } |
| 55 | add_action( 'admin_menu', 'wp_smushit_admin_menu' ); |
| 56 | |
| 57 | |
| 58 | function wp_smushit_bulk_preview() { |
| 59 | if ( function_exists( 'apache_setenv' ) ) { |
| 60 | @apache_setenv('no-gzip', 1); |
| 61 | } |
| 62 | @ini_set('output_buffering','on'); |
| 63 | @ini_set('zlib.output_compression', 0); |
| 64 | @ini_set('implicit_flush', 1); |
| 65 | |
| 66 | $attachments = null; |
| 67 | $auto_start = false; |
| 68 | |
| 69 | if ( isset($_REQUEST['ids'])) { |
| 70 | $attachments = get_posts( array( |
| 71 | 'numberposts' => -1, |
| 72 | 'include' => explode(',', $_REQUEST['ids']), |
| 73 | 'post_type' => 'attachment', |
| 74 | 'post_mime_type' => 'image' |
| 75 | )); |
| 76 | $auto_start = true; |
| 77 | } else { |
| 78 | $attachments = get_posts( array( |
| 79 | 'numberposts' => -1, |
| 80 | 'post_type' => 'attachment', |
| 81 | 'post_mime_type' => 'image' |
| 82 | )); |
| 83 | } |
| 84 | |
| 85 | |
| 86 | require( dirname(__FILE__) . '/bulk.php' ); |
| 87 | } |
| 88 | |
| 89 | /** |
| 90 | * Manually process an image from the Media Library |
| 91 | */ |
| 92 | function wp_smushit_manual() { |
| 93 | if ( FALSE === current_user_can('upload_files') ) { |
| 94 | wp_die(__('You don\'t have permission to work with uploaded files.', WP_SMUSHIT_DOMAIN)); |
| 95 | } |
| 96 | |
| 97 | if ( FALSE === isset($_GET['attachment_ID'])) { |
| 98 | wp_die(__('No attachment ID was provided.', WP_SMUSHIT_DOMAIN)); |
| 99 | } |
| 100 | |
| 101 | $attachment_ID = intval($_GET['attachment_ID']); |
| 102 | |
| 103 | $original_meta = wp_get_attachment_metadata( $attachment_ID ); |
| 104 | |
| 105 | $new_meta = wp_smushit_resize_from_meta_data( $original_meta, $attachment_ID ); |
| 106 | wp_update_attachment_metadata( $attachment_ID, $new_meta ); |
| 107 | |
| 108 | $sendback = wp_get_referer(); |
| 109 | $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback); |
| 110 | wp_redirect($sendback); |
| 111 | exit(0); |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * Process an image with Smush.it. |
| 116 | * |
| 117 | * Returns an array of the $file $results. |
| 118 | * |
| 119 | * @param string $file Full absolute path to the image file |
| 120 | * @param string $file_url Optional full URL to the image file |
| 121 | * @returns array |
| 122 | */ |
| 123 | function wp_smushit($file, $file_url = null) { |
| 124 | // don't run on localhost, IPv4 and IPv6 checks |
| 125 | // if( in_array($_SERVER['SERVER_ADDR'], array('127.0.0.1', '::1')) ) |
| 126 | // return array($file, __('Not processed (local file)', WP_SMUSHIT_DOMAIN)); |
| 127 | |
| 128 | // canonicalize path - disabled 2011-02-1 troubleshooting 'Could not find...' errors. |
| 129 | // From the PHP docs: "The running script must have executable permissions on |
| 130 | // all directories in the hierarchy, otherwise realpath() will return FALSE." |
| 131 | // $file_path = realpath($file); |
| 132 | |
| 133 | $file_path = $file; |
| 134 | // check that the file exists |
| 135 | if ( FALSE === file_exists($file_path) || FALSE === is_file($file_path) ) { |
| 136 | $msg = sprintf(__("Could not find <span class='code'>%s</span>", WP_SMUSHIT_DOMAIN), $file_path); |
| 137 | return array($file, $msg); |
| 138 | } |
| 139 | |
| 140 | // check that the file is writable |
| 141 | if ( FALSE === is_writable($file_path) ) { |
| 142 | $msg = sprintf(__("<span class='code'>%s</span> is not writable", WP_SMUSHIT_DOMAIN), $file_path); |
| 143 | return array($file, $msg); |
| 144 | } |
| 145 | |
| 146 | // check that the file is within the WP_CONTENT_DIR |
| 147 | $upload_dir = wp_upload_dir(); |
| 148 | $wp_upload_dir = $upload_dir['basedir']; |
| 149 | $wp_upload_url = $upload_dir['baseurl']; |
| 150 | if ( 0 !== stripos(realpath($file_path), realpath(ABSPATH)) ) { |
| 151 | $msg = sprintf(__("<span class='code'>%s</span> must be within the content directory (<span class='code'>%s</span>)", WP_SMUSHIT_DOMAIN), htmlentities($file_path), $wp_upload_dir); |
| 152 | |
| 153 | return array($file, $msg); |
| 154 | } |
| 155 | |
| 156 | if ( !$file_url ) { |
| 157 | // determine the public URL |
| 158 | $file_url = str_replace( $wp_upload_dir, $wp_upload_url, $file ); |
| 159 | } |
| 160 | |
| 161 | $data = wp_smushit_post($file_url); |
| 162 | |
| 163 | if ( FALSE === $data ) |
| 164 | return array($file, __('Error posting to Smush.it', WP_SMUSHIT_DOMAIN)); |
| 165 | |
| 166 | // make sure the response looks like JSON -- added 2008-12-19 when |
| 167 | // Smush.it was returning PHP warnings before the JSON output |
| 168 | if ( strpos( trim($data), '{' ) != 0 ) { |
| 169 | return array($file, __('Bad response from Smush.it', WP_SMUSHIT_DOMAIN)); |
| 170 | } |
| 171 | |
| 172 | // read the JSON response |
| 173 | if ( function_exists('json_decode') ) { |
| 174 | $data = json_decode( $data ); |
| 175 | } else { |
| 176 | $json = new Services_JSON(); |
| 177 | $data = $json->decode($data); |
| 178 | } |
| 179 | |
| 180 | if ( -1 === intval($data->dest_size) ) |
| 181 | return array($file, __('No savings', WP_SMUSHIT_DOMAIN)); |
| 182 | |
| 183 | if ( !$data->dest ) { |
| 184 | $err = ($data->error ? 'Smush.it error: ' . $data->error : 'unknown error'); |
| 185 | $err .= " while processing <span class='code'>$file_url</span> (<span class='code'>$file_path</span>)"; |
| 186 | return array($file, __($err, WP_SMUSHIT_DOMAIN) ); |
| 187 | } |
| 188 | |
| 189 | $processed_url = $data->dest; |
| 190 | |
| 191 | // The smush.it web service does not append the domain; |
| 192 | // smushit.com web service does |
| 193 | if ( 0 !== stripos($processed_url, 'http://') ) { |
| 194 | $processed_url = SMUSHIT_BASE_URL . $processed_url; |
| 195 | } |
| 196 | |
| 197 | $temp_file = download_url($processed_url); |
| 198 | |
| 199 | if ( is_wp_error($temp_file) ) { |
| 200 | @unlink($tmp); |
| 201 | $results_msg = sprintf(__("Error downloading file (%s)", WP_SMUSHIT_DOMAIN), |
| 202 | $temp_file->get_error_message()); |
| 203 | return array($file, $results_msg ); |
| 204 | } |
| 205 | |
| 206 | @rename( $temp_file, $file_path ); |
| 207 | |
| 208 | $savings = intval($data->src_size) - intval($data->dest_size); |
| 209 | $savings_str = wp_smushit_format_bytes($savings, 1); |
| 210 | $savings_str = str_replace(' ', ' ', $savings_str); |
| 211 | |
| 212 | $results_msg = sprintf(__("Reduced by %01.1f%% (%s)", WP_SMUSHIT_DOMAIN), |
| 213 | $data->percent, |
| 214 | $savings_str); |
| 215 | |
| 216 | return array($file, $results_msg); |
| 217 | } |
| 218 | |
| 219 | function wp_smushit_should_resmush($previous_status) { |
| 220 | if ( !$previous_status || empty($previous_status) ) { |
| 221 | return TRUE; |
| 222 | } |
| 223 | |
| 224 | if ( stripos($previous_status, 'no savings') !== FALSE || stripos($previous_status, 'reduced') !== FALSE ) { |
| 225 | return FALSE; |
| 226 | } |
| 227 | |
| 228 | // otherwise an error |
| 229 | return TRUE; |
| 230 | } |
| 231 | |
| 232 | |
| 233 | /** |
| 234 | * Read the image paths from an attachment's meta data and process each image |
| 235 | * with wp_smushit(). |
| 236 | * |
| 237 | * This method also adds a `wp_smushit` meta key for use in the media library. |
| 238 | * |
| 239 | * Called after `wp_generate_attachment_metadata` is completed. |
| 240 | */ |
| 241 | function wp_smushit_resize_from_meta_data($meta, $ID = null, $force_resmush = true) { |
| 242 | $file_path = $meta['file']; |
| 243 | $store_absolute_path = true; |
| 244 | $upload_dir = wp_upload_dir(); |
| 245 | $upload_path = trailingslashit( $upload_dir['basedir'] ); |
| 246 | |
| 247 | // WordPress >= 2.6.2: determine the absolute $file_path (http://core.trac.wordpress.org/changeset/8796) |
| 248 | if ( FALSE === strpos($file, $upload_path) ) { |
| 249 | $store_absolute_path = false; |
| 250 | $file_path = $upload_path . $file_path; |
| 251 | } |
| 252 | |
| 253 | if ( $force_resmush || wp_smushit_should_resmush( @$meta['wp_smushit'] ) ) { |
| 254 | list($file, $msg) = wp_smushit($file_path); |
| 255 | $meta['wp_smushit'] = $msg; |
| 256 | } |
| 257 | |
| 258 | // strip absolute path for Wordpress >= 2.6.2 |
| 259 | if ( FALSE === $store_absolute_path ) { |
| 260 | $meta['file'] = str_replace($upload_path, '', $meta['file']); |
| 261 | } |
| 262 | |
| 263 | // no resized versions, so we can exit |
| 264 | if ( !isset($meta['sizes']) ) |
| 265 | return $meta; |
| 266 | |
| 267 | // meta sizes don't contain a path, so we calculate one |
| 268 | $base_dir = trailingslashit( dirname($file_path) ); |
| 269 | |
| 270 | foreach($meta['sizes'] as $size => $data) { |
| 271 | if ( !$force_resmush && wp_smushit_should_resmush( @$meta['sizes'][$size]['wp_smushit'] ) === FALSE ) { |
| 272 | continue; |
| 273 | } |
| 274 | |
| 275 | list($smushed_file, $results) = wp_smushit( $base_dir . wp_basename( $data['file'] ) ); |
| 276 | $meta['sizes'][$size]['wp_smushit'] = $results; |
| 277 | } |
| 278 | return $meta; |
| 279 | } |
| 280 | |
| 281 | |
| 282 | /** |
| 283 | * Post an image to Smush.it. |
| 284 | * |
| 285 | * @param string $file_url URL of the file to send to Smush.it |
| 286 | * @return string|boolean Returns the JSON response on success or else FALSE |
| 287 | */ |
| 288 | function wp_smushit_post($file_url) { |
| 289 | $req = sprintf( SMUSHIT_REQ_URL, urlencode( $file_url ) ); |
| 290 | |
| 291 | $data = false; |
| 292 | |
| 293 | if ( function_exists('wp_remote_get') ) { |
| 294 | $response = wp_remote_get($req, array('user-agent' => WP_SMUSHIT_UA, 'timeout' => 20)); |
| 295 | |
| 296 | if( is_wp_error( $response ) ) { |
| 297 | wp_smushit_temporarily_disable(); |
| 298 | $msg = 'Automatic smushing has been disabled temporarily due to an error. ' . $response->get_error_message(); |
| 299 | wp_die( $msg ); |
| 300 | } |
| 301 | |
| 302 | $data = wp_remote_retrieve_body($response); |
| 303 | } else { |
| 304 | wp_die( __('WP Smush.it requires WordPress 2.8 or greater', WP_SMUSHIT_DOMAIN) ); |
| 305 | } |
| 306 | |
| 307 | return $data; |
| 308 | } |
| 309 | |
| 310 | |
| 311 | /** |
| 312 | * Print column header for Smush.it results in the media library using |
| 313 | * the `manage_media_columns` hook. |
| 314 | */ |
| 315 | function wp_smushit_columns($defaults) { |
| 316 | $defaults['smushit'] = 'Smush.it'; |
| 317 | return $defaults; |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * Return the filesize in a humanly readable format. |
| 322 | * Taken from http://www.php.net/manual/en/function.filesize.php#91477 |
| 323 | */ |
| 324 | function wp_smushit_format_bytes($bytes, $precision = 2) { |
| 325 | $units = array('B', 'KB', 'MB', 'GB', 'TB'); |
| 326 | $bytes = max($bytes, 0); |
| 327 | $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); |
| 328 | $pow = min($pow, count($units) - 1); |
| 329 | $bytes /= pow(1024, $pow); |
| 330 | return round($bytes, $precision) . ' ' . $units[$pow]; |
| 331 | } |
| 332 | |
| 333 | /** |
| 334 | * Print column data for Smush.it results in the media library using |
| 335 | * the `manage_media_custom_column` hook. |
| 336 | */ |
| 337 | function wp_smushit_custom_column($column_name, $id) { |
| 338 | if( $column_name == 'smushit' ) { |
| 339 | $data = wp_get_attachment_metadata($id); |
| 340 | if ( isset($data['wp_smushit']) && !empty($data['wp_smushit']) ) { |
| 341 | print $data['wp_smushit']; |
| 342 | printf("<br><a href=\"admin.php?action=wp_smushit_manual&attachment_ID=%d\">%s</a>", |
| 343 | $id, |
| 344 | __('Re-smush', WP_SMUSHIT_DOMAIN)); |
| 345 | } else { |
| 346 | print __('Not processed', WP_SMUSHIT_DOMAIN); |
| 347 | printf("<br><a href=\"admin.php?action=wp_smushit_manual&attachment_ID=%d\">%s</a>", |
| 348 | $id, |
| 349 | __('Smush.it now!', WP_SMUSHIT_DOMAIN)); |
| 350 | } |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | add_action( 'admin_head-upload.php', 'wp_smushit_add_bulk_actions_via_javascript' ); |
| 355 | |
| 356 | // Borrowed from http://www.viper007bond.com/wordpress-plugins/regenerate-thumbnails/ |
| 357 | function wp_smushit_add_bulk_actions_via_javascript() { ?> |
| 358 | <script type="text/javascript"> |
| 359 | jQuery(document).ready(function($){ |
| 360 | $('select[name^="action"] option:last-child').before('<option value="bulk_smushit">Bulk Smush.it</option>'); |
| 361 | }); |
| 362 | </script> |
| 363 | <?php } |
| 364 | |
| 365 | |
| 366 | add_action( 'admin_action_bulk_smushit', 'wp_smushit_bulk_action_handler' ); |
| 367 | |
| 368 | // Handles the bulk actions POST |
| 369 | // Borrowed from http://www.viper007bond.com/wordpress-plugins/regenerate-thumbnails/ |
| 370 | function wp_smushit_bulk_action_handler() { |
| 371 | check_admin_referer( 'bulk-media' ); |
| 372 | |
| 373 | if ( empty( $_REQUEST['media'] ) || ! is_array( $_REQUEST['media'] ) ) |
| 374 | return; |
| 375 | |
| 376 | $ids = implode( ',', array_map( 'intval', $_REQUEST['media'] ) ); |
| 377 | |
| 378 | |
| 379 | // Can't use wp_nonce_url() as it escapes HTML entities |
| 380 | wp_redirect( add_query_arg( '_wpnonce', wp_create_nonce( 'wp-smushit-bulk' ), admin_url( 'upload.php?page=wp-smushit-bulk&goback=1&ids=' . $ids ) ) ); |
| 381 | exit(); |
| 382 | } |
| 383 | |
| 384 | if ( function_exists( 'wp_basename' ) === false ) { |
| 385 | /** |
| 386 | * Introduced in WP 3.1... this is copied verbatim from wp-includes/formatting.php. |
| 387 | */ |
| 388 | function wp_basename( $path, $suffix = '' ) { |
| 389 | return urldecode( basename( str_replace( '%2F', '/', urlencode( $path ) ), $suffix ) ); |
| 390 | } |
| 391 | } |