ShortPixel Image Optimizer - Version 1.3.1

Version Description

  • possible fix for API key validation failing
  • added backup and restore for images that are processed with shortpixel
  • optimize now feature on Media Library
Download this release

Release Info

Developer ShortPixel
Plugin Icon 128x128 ShortPixel Image Optimizer
Version 1.3.1
Comparing to
See all releases

Code changes from version 1.2.0 to 1.3.1

Files changed (3) hide show
  1. readme.txt +7 -1
  2. shortpixel_api.php +17 -1
  3. wp-shortpixel.php +695 -491
readme.txt CHANGED
@@ -4,7 +4,7 @@ Contributors: AlexSP
4
  Tags: picture, optimization, image editor, pngout, upload speed, shortpixel, compression, jpegmini, webp, lossless, cwebp, media, tinypng, jpegtran,image, image optimisation, shrink, picture, photo, optimize photos, compress, performance, tinypng, crunch, pngquant, attachment, optimize, pictures,fast, images, image files, image quality, lossy, upload, kraken, resize, seo, smushit, optipng, kraken image optimizer, ewww, photo optimization, gifsicle, image optimizer, images, krakenio, png, gmagick, image optimize
5
  Requires at least: 3.0.0 or higher
6
  Tested up to: 4.0
7
- Stable tag: 1.2.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -92,6 +92,12 @@ ShortPixel team is here to help. <a href="https://shortpixel.com/contact">Contac
92
 
93
  == Changelog ==
94
 
 
 
 
 
 
 
95
  = 1.0.6 =
96
 
97
  * bulk processing runs in background now.
4
  Tags: picture, optimization, image editor, pngout, upload speed, shortpixel, compression, jpegmini, webp, lossless, cwebp, media, tinypng, jpegtran,image, image optimisation, shrink, picture, photo, optimize photos, compress, performance, tinypng, crunch, pngquant, attachment, optimize, pictures,fast, images, image files, image quality, lossy, upload, kraken, resize, seo, smushit, optipng, kraken image optimizer, ewww, photo optimization, gifsicle, image optimizer, images, krakenio, png, gmagick, image optimize
5
  Requires at least: 3.0.0 or higher
6
  Tested up to: 4.0
7
+ Stable tag: 1.3.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
92
 
93
  == Changelog ==
94
 
95
+ = 1.3.1 =
96
+
97
+ * possible fix for API key validation failing
98
+ * added backup and restore for images that are processed with shortpixel
99
+ * optimize now feature on Media Library
100
+
101
  = 1.0.6 =
102
 
103
  * bulk processing runs in background now.
shortpixel_api.php CHANGED
@@ -46,7 +46,7 @@ class shortpixel_api {
46
  $requestURL = $this->_apiEndPoint . '?key=' . $this->_apiKey . '&lossy=' . $this->_compressionType . '&url=';
47
  $requestURL = $requestURL . urlencode($url);
48
 
49
- $args = array('timeout'=> SP_MAX_TIMEOUT);
50
 
51
  $response = wp_remote_get($requestURL, $args);
52
 
@@ -128,6 +128,22 @@ class shortpixel_api {
128
  return printf("Unable to locate downloaded file (%s)", $tempFile);
129
  }
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  @unlink( $filePath );
132
  $success = @rename( $tempFile, $filePath );
133
 
46
  $requestURL = $this->_apiEndPoint . '?key=' . $this->_apiKey . '&lossy=' . $this->_compressionType . '&url=';
47
  $requestURL = $requestURL . urlencode($url);
48
 
49
+ $args = array('timeout'=> SP_MAX_TIMEOUT, 'sslverify' => false);
50
 
51
  $response = wp_remote_get($requestURL, $args);
52
 
128
  return printf("Unable to locate downloaded file (%s)", $tempFile);
129
  }
130
 
131
+ //if backup is enabled
132
+ if(get_option('wp-short-backup_images')) {
133
+
134
+ if(!file_exists(SP_BACKUP_FOLDER) && !mkdir(SP_BACKUP_FOLDER, 0777, true)) {
135
+ return printf("Backup folder does not exist and it could not be created");
136
+ }
137
+
138
+ if(is_writable(SP_BACKUP_FOLDER)) {
139
+ $source = $filePath;
140
+ $destination = SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . basename($source);
141
+ @copy($source, $destination);
142
+ } else {
143
+ return printf("Backup folder exists but is not writable");
144
+ }
145
+ }
146
+
147
  @unlink( $filePath );
148
  $success = @rename( $tempFile, $filePath );
149
 
wp-shortpixel.php CHANGED
@@ -1,491 +1,695 @@
1
- <?php
2
- /**
3
- * Plugin Name: ShortPixel Image Optimiser
4
- * Plugin URI: https://shortpixel.com/
5
- * Description: ShortPixel is an image compression tool that helps improve your website performance. The plugin optimises images automatically using both lossy and lossless compression. Resulting, smaller, images are no different in quality from the original. To install: 1) Click the "Activate" link to the left of this description. 2) <a href="https://shortpixel.com/free-sign-up" target="_blank">Free Sign up</a> for your unique API Key . 3) Check your email for your API key. 4) Use your API key to activate ShortPixel plugin in the 'Plugins' menu in WordPress. 5) Done!
6
- * Version: 1.2.0
7
- * Author: ShortPixel
8
- * Author URI: https://shortpixel.com
9
- */
10
-
11
- require_once('shortpixel_api.php');
12
- require_once( ABSPATH . 'wp-admin/includes/image.php' );
13
-
14
- class WPShortPixel {
15
-
16
- private $_apiInterface = null;
17
- private $_apiKey = '';
18
- private $_compressionType = '';
19
- private $_processThumbnails = 1;
20
- private $_verifiedKey = false;
21
-
22
- public function __construct() {
23
- define('SP_DEBUG', false);
24
- define('SP_LOG', false);
25
- define('SP_MAX_TIMEOUT', 10);
26
-
27
- $this->populateOptions();
28
-
29
- $this->_apiInterface = new shortpixel_api($this->_apiKey, $this->_compressionType);
30
-
31
- //add hook for image upload processing
32
- add_filter( 'wp_generate_attachment_metadata', array( &$this, 'handleImageUpload' ), 10, 2 );
33
- add_filter( 'manage_media_columns', array( &$this, 'columns' ) );
34
- add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array(&$this, 'generatePluginLinks'));
35
-
36
- //add_action( 'admin_footer', array(&$this, 'handleImageProcessing'));
37
- add_action( 'manage_media_custom_column', array( &$this, 'generateCustomColumn' ), 10, 2 );
38
-
39
- //add settings page
40
- add_action( 'admin_menu', array( &$this, 'registerSettingsPage' ) );
41
- add_action( 'admin_menu', array( &$this, 'registerAdminPage' ) );
42
- add_action( 'admin_notices', array( &$this, 'displayNotice' ) );
43
-
44
-
45
- add_action( 'admin_footer', array( &$this, 'my_action_javascript') );
46
- add_action( 'wp_ajax_my_action', array( &$this, 'handleImageProcessing') );
47
- }
48
-
49
- public function populateOptions() {
50
-
51
- if(get_option('wp-short-pixel-apiKey') != false) {
52
- $this->_apiKey = get_option('wp-short-pixel-apiKey');
53
- }
54
-
55
- if(get_option('wp-short-pixel-verifiedKey') != false) {
56
- $this->_verifiedKey = get_option('wp-short-pixel-verifiedKey');
57
- }
58
-
59
- if(get_option('wp-short-pixel-compression') != false) {
60
- $this->_compressionType = get_option('wp-short-pixel-compression');
61
- }
62
-
63
- if(get_option('wp-short-process_thumbnails') != false) {
64
- $this->_processThumbnails = get_option('wp-short-process_thumbnails');
65
- } else {
66
- add_option('wp-short-process_thumbnails', $this->_processThumbnails, '', 'yes' );
67
- }
68
-
69
- if(get_option('wp-short-pixel-fileCount') === false) {
70
- add_option( 'wp-short-pixel-fileCount', 0, '', 'yes' );
71
- }
72
-
73
- if(get_option('wp-short-pixel-savedSpace') === false) {
74
- add_option( 'wp-short-pixel-savedSpace', 0, '', 'yes' );
75
- }
76
-
77
- if(get_option('wp-short-pixel-averageCompression') === false) {
78
- add_option( 'wp-short-pixel-averageCompression', 0, '', 'yes' );
79
- }
80
- }
81
-
82
- function my_action_javascript() { ?>
83
- <script type="text/javascript" >
84
- jQuery(document).ready(function($) {
85
- var data = {
86
- 'action': 'my_action',
87
- 'whatever': 1234
88
- };
89
- // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
90
- $.post(ajaxurl, data, function(response) {
91
- console.log('Server response: ' + response);
92
- });
93
- });
94
- </script> <?php
95
- }
96
-
97
- //handling older
98
- public function WPShortPixel() {
99
- $this->__construct();
100
- }
101
-
102
- public function handleImageUpload($meta, $ID = null) {
103
- $meta['ShortPixel']['WaitingProcessing'] = true;
104
- return $meta;
105
- }
106
-
107
- public function handleImageProcessing($ID = null) {
108
- //query database for first found entry that needs processing
109
- global $wpdb;
110
- $qry = "SELECT post_id FROM " . $wpdb->prefix . "postmeta WHERE meta_value LIKE '%\"WaitingProcessing\";b:1;%' LIMIT 1";
111
- $ID = $wpdb->get_var($qry);
112
-
113
- if(empty($ID)) { die; }
114
-
115
- $imageURL = wp_get_attachment_url($ID);
116
- $imagePath = get_attached_file($ID);
117
- $meta = wp_get_attachment_metadata($ID);
118
-
119
- $result = $this->_apiInterface->processImage($imageURL, $imagePath, $ID);
120
-
121
- if(is_string($result)) {
122
- $meta['ShortPixelImprovement'] = $result;
123
- die;
124
- }
125
-
126
- $processThumbnails = get_option('wp-short-process_thumbnails');
127
-
128
- //handle the rest of the thumbnails generated by WP
129
- if($processThumbnails && $result && !empty($meta['sizes'])) {
130
- foreach($meta['sizes'] as $thumbnailInfo) {
131
- $thumbURL = str_replace(basename($imagePath), $thumbnailInfo['file'], $imageURL);
132
- $thumbPath = str_replace(basename($imagePath), $thumbnailInfo['file'], $imagePath);
133
- $this->_apiInterface->processImage($thumbURL, $thumbPath);
134
- }
135
- }
136
-
137
- unset($meta['ShortPixel']['WaitingProcessing']);
138
-
139
- //check bulk processing
140
- $bulkLog = get_option('bulkProcessingLog');
141
- if(isset($bulkLog)) {
142
- if(array_key_exists($ID, $bulkLog['toDo'])) {
143
- unset($bulkLog['toDo'][$ID]);
144
- }
145
- }
146
-
147
- if(empty($bulkLog['toDo'])) { delete_option('bulkProcessingLog'); }
148
- else { update_option('bulkProcessingLog', $bulkLog); }
149
-
150
- $meta['ShortPixelImprovement'] = $result->PercentImprovement;
151
-
152
- wp_update_attachment_metadata($ID, $meta);
153
- echo "Processing done succesfully for image #{$ID}";
154
- die();
155
- }
156
-
157
- public function registerSettingsPage() {
158
- add_options_page( 'ShortPixel Settings', 'ShortPixel', 'manage_options', 'wp-shortpixel', array($this, 'renderSettingsMenu'));
159
- }
160
-
161
- function registerAdminPage( ) {
162
- add_media_page( 'ShortPixel Bulk Process', 'Bulk ShortPixel', 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcesss' ) );
163
- }
164
-
165
- public function bulkProcesss() {
166
- echo '<h1>ShortPixel Bulk Processing</h1>';
167
-
168
- $attachments = null;
169
- $attachments = get_posts( array(
170
- 'numberposts' => -1,
171
- 'post_type' => 'attachment',
172
- 'post_mime_type' => 'image'
173
- ));
174
-
175
- if($_POST["bulkProcess"]) {
176
- $imageLog = array();
177
- //remove all ShortPixel data from metadata
178
- foreach($attachments as $attachment) {
179
- $meta = wp_get_attachment_metadata($attachment->ID);
180
- $meta['ShortPixel']['WaitingProcessing'] = true;
181
- wp_update_attachment_metadata($attachment->ID, $meta);
182
- $imageLog[$attachment->ID] = false;
183
- }
184
- $bulkLog = array();
185
- $bulkLog['running'] = true;
186
- $bulkLog['toDo'] = $imageLog;
187
- $bulkLog['total'] = count($imageLog);
188
- update_option('bulkProcessingLog', $bulkLog);
189
- }
190
-
191
- $currentBulkProcessingStatus = get_option('bulkProcessingLog');
192
-
193
- if($currentBulkProcessingStatus && $currentBulkProcessingStatus['running']) {
194
- echo "<p>Bulk processing started and it may take a while to be completed.</p>";
195
-
196
- $imagesLeft = count($currentBulkProcessingStatus["toDo"]);
197
- $totalImages = $currentBulkProcessingStatus['total'];
198
-
199
- echo "<p>{$imagesLeft} out of {$totalImages} images left to process.</p>";
200
-
201
- echo '
202
- <a class="button button-secondary" href="' . get_admin_url() . 'upload.php?page=wp-short-pixel-bulk">Check Status<a/>
203
- <a class="button button-secondary" href="' . get_admin_url() . 'upload.php">Media Library</a>
204
- ';
205
- } else {
206
- echo $this->getBulkProcessingForm(count($attachments));
207
- }
208
-
209
-
210
-
211
- //TO DO: find a way to track when bulk processing is running and update a log for customer
212
-
213
- }
214
-
215
- public function renderSettingsMenu() {
216
- if ( !current_user_can( 'manage_options' ) ) {
217
- wp_die('You do not have sufficient permissions to access this page.');
218
- }
219
-
220
- if(isset($_POST['submit']) || isset($_POST['validate'])) {
221
- //handle API Key - common for submit and validate
222
- $validityData = $this->getQuotaInformation($_POST['key']);
223
- $this->_apiKey = $_POST['key'];
224
- $this->_apiInterface->setApiKey($this->_apiKey);
225
- update_option('wp-short-pixel-apiKey', $_POST['key']);
226
- if($validityData['APIKeyValid']) {
227
- update_option('wp-short-pixel-verifiedKey', true);
228
- $this->_verifiedKey = true;
229
- } else {
230
- update_option('wp-short-pixel-verifiedKey', false);
231
- $this->_verifiedKey = false;
232
- }
233
- //if save button - we process the rest of the form elements
234
- if(isset($_POST['submit'])) {
235
- update_option('wp-short-pixel-compression', $_POST['compressionType']);
236
- $this->_compressionType = $_POST['compressionType'];
237
- $this->_apiInterface->setCompressionType($this->_compressionType);
238
- if(isset($_POST['thumbnails'])) { $this->_processThumbnails = 1; } else { $this->_processThumbnails = 0; }
239
- update_option('wp-short-process_thumbnails', $this->_processThumbnails);
240
- }
241
- }
242
-
243
- $checked = '';
244
- if($this->_processThumbnails) { $checked = 'checked'; }
245
-
246
- echo '<h1>ShortPixel Image Optimiser Settings</h1>';
247
- echo '<p>
248
- <a href="https://shortpixel.com">ShortPixel.com</a> |
249
- <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/installation/">Installation </a> |
250
- <a href="https://wordpress.org/support/plugin/shortpixel-image-optimiser">Support </a>
251
- </p>';
252
- echo '<p>New images uploaded to the Media Library will be optimized automatically.<br/>If you have existing images you would like to optimize, you can use the <a href="/upload.php?page=wp-short-pixel-bulk">Bulk Optimize tool</a>.</p>';
253
-
254
- $formHTML = <<< HTML
255
- <form name='wp_shortpixel_options' action='' method='post' id='wp_shortpixel_options'>
256
- <table class="form-table">
257
- <tbody><tr>
258
- <th scope="row"><label for="key">API Key:</label></th>
259
- <td><input name="key" type="text" id="key" value="{$this->_apiKey}" class="regular-text">
260
- <input type="submit" name="validate" id="validate" class="button button-primary" title="Validate the provided API key" value="Validate">
261
- </td>
262
- </tr>
263
- HTML;
264
-
265
- if(!$this->_verifiedKey) {
266
- $formHTML .= '<tr><td style="padding-left: 0px;" colspan="2">Don’t have an API Key? <a href="https://shortpixel.com/wp-apikey" target="_blank">Sign up, it’s free.</a></td></tr>';
267
- }
268
-
269
- $formHTML .= <<< HTML
270
- <tr><th scope="row">
271
- <label for="compressionType">Compression type: <span title="
272
- Lossy compression: lossy has a better compression rate than lossless compression. The resulting image is not 100% identical with the original. Works well for photos taken with your camera.
273
- Lossless compression: the shrunk image will be identical with the original and smaller in size. Use this when you do not want to loose any of the original image's details. Works best for technical drawings, clip art and comics.
274
- ">?</span></label>
275
- </th><td>
276
- HTML;
277
-
278
- if($this->_compressionType == 'lossless') {
279
- $formHTML .= '<input type="radio" name="compressionType" value="lossy" >Lossy</br></br>';
280
- $formHTML .= '<input type="radio" name="compressionType" value="lossless" checked>Lossless';
281
- } else {
282
- $formHTML .= '<input type="radio" name="compressionType" value="lossy" checked>Lossy</br></br>';
283
- $formHTML .= '<input type="radio" name="compressionType" value="lossless" >Lossless';
284
- }
285
-
286
- $formHTML .= <<<HTML
287
- </td>
288
- </tr>
289
- <tr>
290
- <th scope="row"><label for="thumbnails">Compress also image thumbnails</label></th>
291
- <td><input name="thumbnails" type="checkbox" id="thumbnails" {$checked}></td>
292
- </tr>
293
- </tbody></table>
294
- <p class="submit">
295
- <input type="submit" name="submit" id="submit" class="button button-primary" title="Save Changes" value="Save Changes">
296
- <input type="submit" name="bulk-process" id="bulk-process" class="button button-primary" title="Process all the images in your Media Library" value="Bulk Process">
297
- </p>
298
- </form>
299
- <script>
300
- var rad = document.wp_shortpixel_options.compressionType;
301
- var prev = null;
302
- for(var i = 0; i < rad.length; i++) {
303
- rad[i].onclick = function() {
304
-
305
- if(this !== prev) {
306
- prev = this;
307
- }
308
- alert('Select Media/Bulk Short Pixel to reprocess all the images');
309
- };
310
- }
311
- </script>
312
- HTML;
313
- echo $formHTML;
314
-
315
- $fileCount = get_option('wp-short-pixel-fileCount');
316
- $savedSpace = self::formatBytes(get_option('wp-short-pixel-savedSpace'),2);
317
- $averageCompression = round(get_option('wp-short-pixel-averageCompression'),2);
318
- $savedBandwidth = self::formatBytes(get_option('wp-short-pixel-savedSpace') * 1000,2);
319
-
320
- $quotaData = $this->getQuotaInformation();
321
-
322
- $statHTML = <<< HTML
323
- <h3>ShortPixlel Facts & Figures</h3>
324
- <table class="form-table">
325
- <tbody><tr>
326
- <th scope="row"><label for="totalFiles">Your total number of processed files:</label></th>
327
- <td>$fileCount</td>
328
- </tr>
329
- <tr>
330
- <th scope="row"><label for="savedSpace">Saved space by ShortPixel</label></th>
331
- <td>$savedSpace</td>
332
- </tr>
333
- <tr>
334
- <th scope="row"><label for="savedBandwidth">Bandwith* saved with ShortPixel:</label></th>
335
- <td>$savedBandwidth</td>
336
- </tr>
337
- <tr>
338
- <th scope="row"><label for="apiQuota">Your ShortPixel plan</label></th>
339
- <td>{$quotaData['APICallsQuota']}</td>
340
- </tr>
341
- <tr>
342
- <th scope="row"><label for="usedQUota">Used Quota:</label></th>
343
- <td>{$quotaData['APICallsMade']}</td>
344
- </tr>
345
- <tr>
346
- <th scope="row"><label for="averagCompression">Average file size compression:</label></th>
347
- <td>$averageCompression%</td>
348
- </tr>
349
- </tbody></table>
350
- <p>* Saved bandwidth is calculated at 100,000 impressions/image</p>
351
- HTML;
352
- echo $statHTML;
353
- }
354
-
355
- public function getBulkProcessingForm($imageCount) {
356
- return <<< HTML
357
- </br>
358
- Currently, you have {$imageCount} images in your library. </br>
359
- </br>
360
- <form action='' method="POST" >
361
- <input type="submit" name="bulkProcess" id="bulkProcess" class="button button-primary" value="Compress all your images">
362
- </form>
363
- HTML;
364
- }
365
-
366
- public function displayNotice() {
367
- global $hook_suffix;
368
-
369
- $divHeader = '<div class="updated">';
370
- $divWarningHeader = '<div class="update-nag">';
371
- $divFooter = '</div>';
372
-
373
- $noticeActivationContent = 'ShortPixel plugin activated! Get an API key <a href="https://shortpixel.com/wp-apikey" target="_blank">here</a>. Sign up, it’s free.';
374
- $noticeWrongAPIKeyContent = 'API Key invalid!';
375
- $noticeCorrectAPIKeyContent = 'API Key valid!';
376
-
377
- if($hook_suffix == 'settings_page_wp-shortpixel' && !empty($_POST)) {
378
- $keyCheckData = $this->getQuotaInformation($_POST['key']);
379
- if($keyCheckData['APIKeyValid']) {
380
- echo $divHeader . $noticeCorrectAPIKeyContent . $divFooter;
381
- } else {
382
- echo $divWarningHeader . $noticeWrongAPIKeyContent . $divFooter;
383
- }
384
- }
385
- if($hook_suffix == 'plugins.php' && $_GET['activate']) { echo $divHeader . $noticeActivationContent . $divFooter; }
386
- }
387
-
388
- static public function formatBytes($bytes, $precision = 2) {
389
- $units = array('B', 'KB', 'MB', 'GB', 'TB');
390
-
391
- $bytes = max($bytes, 0);
392
- $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
393
- $pow = min($pow, count($units) - 1);
394
-
395
- $bytes /= pow(1024, $pow);
396
-
397
- return round($bytes, $precision) . ' ' . $units[$pow];
398
- }
399
-
400
- public function getQuotaInformation($apiKey = null) {
401
-
402
- if(is_null($apiKey)) { $apiKey = $this->_apiKey; }
403
-
404
- $requestURL = 'https://api.shortpixel.com/api-status.php?key='.$apiKey;
405
- $args = array('timeout'=> SP_MAX_TIMEOUT);
406
- $response = wp_remote_get($requestURL, $args);
407
-
408
- $defaultData = array(
409
- "APIKeyValid" => false,
410
- "APICallsMade" => 'Information unavailable. Please check your API key.',
411
- "APICallsQuota" => 'Information unavailable. Please check your API key.');
412
-
413
- if(is_object($response) && get_class($response) == 'WP_Error') {
414
- return $defaultData;
415
- }
416
-
417
- if($response['response']['code'] != 200) {
418
- return $defaultData;
419
- }
420
-
421
- $data = $response['body'];
422
- $data = $this->parseJSON($data);
423
-
424
- if(empty($data)) { return $defaultData; }
425
-
426
- if($data->Status->Code == '-401') { return $defaultData; }
427
-
428
- return array(
429
- "APIKeyValid" => true,
430
- "APICallsMade" => number_format($data->APICallsMade) . ' images',
431
- "APICallsQuota" => number_format($data->APICallsQuota) . ' images'
432
- );
433
-
434
-
435
- }
436
-
437
- public function generateCustomColumn( $column_name, $id ) {
438
- if( 'wp-shortPixel' == $column_name ) {
439
- $data = wp_get_attachment_metadata($id);
440
-
441
- if ( isset( $data['ShortPixelImprovement'] ) ) {
442
-
443
- if(isset($data['ShortPixel']['WaitingProcessing']) && $data['ShortPixel']['WaitingProcessing']) {
444
- print 'Waiting for bulk processing';
445
- return;
446
- }
447
-
448
- print $data['ShortPixelImprovement'];
449
- if(is_numeric($data['ShortPixelImprovement'])) print '%';
450
- return;
451
- }
452
-
453
- if(isset($data['ShortPixel']['WaitingProcessing']) && $data['ShortPixel']['WaitingProcessing']) {
454
- print 'Image waiting to be processed';
455
- return;
456
- } else {
457
- if ( wp_attachment_is_image( $id ) ) {
458
- print 'Image not processed';
459
- return;
460
- }
461
- }
462
- }
463
- }
464
-
465
- public function columns( $defaults ) {
466
- $defaults['wp-shortPixel'] = 'Short Pixel Compression';
467
- return $defaults;
468
- }
469
-
470
- public function generatePluginLinks($links) {
471
- $in = '<a href="options-general.php?page=wp-shortpixel">Settings</a>';
472
- array_unshift($links, $in);
473
- return $links;
474
- }
475
-
476
- public function parseJSON($data) {
477
- if ( function_exists('json_decode') ) {
478
- $data = json_decode( $data );
479
- } else {
480
- require_once( 'JSON/JSON.php' );
481
- $json = new Services_JSON( );
482
- $data = $json->decode( $data );
483
- }
484
- return $data;
485
- }
486
- }
487
-
488
- $pluginInstance = new WPShortPixel();
489
- global $pluginInstance;
490
-
491
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: ShortPixel Image Optimiser
4
+ * Plugin URI: https://shortpixel.com/
5
+ * Description: ShortPixel is an image compression tool that helps improve your website performance. The plugin optimises images automatically using both lossy and lossless compression. Resulting, smaller, images are no different in quality from the original. To install: 1) Click the "Activate" link to the left of this description. 2) <a href="https://shortpixel.com/free-sign-up" target="_blank">Free Sign up</a> for your unique API Key . 3) Check your email for your API key. 4) Use your API key to activate ShortPixel plugin in the 'Plugins' menu in WordPress. 5) Done!
6
+ * Version: 1.3.1
7
+ * Author: ShortPixel
8
+ * Author URI: https://shortpixel.com
9
+ */
10
+
11
+ require_once('shortpixel_api.php');
12
+ require_once( ABSPATH . 'wp-admin/includes/image.php' );
13
+
14
+ define('SP_DEBUG', false);
15
+ define('SP_LOG', false);
16
+ define('SP_MAX_TIMEOUT', 10);
17
+ define('SP_BACKUP_FOLDER', WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'ShortpixelBackups');
18
+ define('MUST_HAVE_KEY', true);
19
+
20
+
21
+ class WPShortPixel {
22
+
23
+ private $_apiInterface = null;
24
+ private $_apiKey = '';
25
+ private $_compressionType = '';
26
+ private $_processThumbnails = 1;
27
+ private $_backupImages = 1;
28
+ private $_verifiedKey = false;
29
+
30
+ public function __construct() {
31
+ $this->populateOptions();
32
+
33
+ $this->_apiInterface = new shortpixel_api($this->_apiKey, $this->_compressionType);
34
+
35
+ //add hook for image upload processing
36
+ add_filter( 'wp_generate_attachment_metadata', array( &$this, 'handleImageUpload' ), 10, 2 );
37
+ add_filter( 'manage_media_columns', array( &$this, 'columns' ) );
38
+ add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array(&$this, 'generatePluginLinks'));
39
+
40
+ //add_action( 'admin_footer', array(&$this, 'handleImageProcessing'));
41
+ add_action( 'manage_media_custom_column', array( &$this, 'generateCustomColumn' ), 10, 2 );
42
+
43
+ //add settings page
44
+ add_action( 'admin_menu', array( &$this, 'registerSettingsPage' ) );
45
+ add_action( 'admin_menu', array( &$this, 'registerAdminPage' ) );
46
+ add_action( 'admin_notices', array( &$this, 'displayNotice' ) );
47
+
48
+ //automatic optimization
49
+ add_action( 'admin_footer', array( &$this, 'my_action_javascript') );
50
+ add_action( 'wp_ajax_my_action', array( &$this, 'handleImageProcessing') );
51
+
52
+ //manual optimization
53
+ add_action('admin_action_shortpixel_manual_optimize', array(&$this, 'handleManualOptimization'));
54
+ //backup restore
55
+ add_action('admin_action_shortpixel_restore_backup', array(&$this, 'handleRestoreBackup'));
56
+
57
+ //bulk processing in media library
58
+ //add_action('load-upload.php', array(&$this, 'wp_load_admin_js'));
59
+ //add_action('admin_enqueue_scripts', array(&$this, 'bulkOptimizeActionHandler'));
60
+ }
61
+
62
+ public function populateOptions() {
63
+
64
+ if(get_option('wp-short-pixel-apiKey') != false) {
65
+ $this->_apiKey = get_option('wp-short-pixel-apiKey');
66
+ }
67
+
68
+ if(get_option('wp-short-pixel-verifiedKey') != false) {
69
+ $this->_verifiedKey = get_option('wp-short-pixel-verifiedKey');
70
+ }
71
+
72
+ if(get_option('wp-short-pixel-compression') != false) {
73
+ $this->_compressionType = get_option('wp-short-pixel-compression');
74
+ }
75
+
76
+ if(get_option('wp-short-process_thumbnails') != false) {
77
+ $this->_processThumbnails = get_option('wp-short-process_thumbnails');
78
+ } else {
79
+ add_option('wp-short-process_thumbnails', $this->_processThumbnails, '', 'yes' );
80
+ }
81
+
82
+ if(get_option('wp-short-backup_images') != false) {
83
+ $this->_backupImages = get_option('wp-short-backup_images');
84
+ } else {
85
+ add_option('wp-short-backup_images', $this->_backupImages, '', 'yes' );
86
+ }
87
+
88
+ if(get_option('wp-short-pixel-fileCount') === false) {
89
+ add_option( 'wp-short-pixel-fileCount', 0, '', 'yes' );
90
+ }
91
+
92
+ if(get_option('wp-short-pixel-savedSpace') === false) {
93
+ add_option( 'wp-short-pixel-savedSpace', 0, '', 'yes' );
94
+ }
95
+
96
+ if(get_option('wp-short-pixel-averageCompression') === false) {
97
+ add_option( 'wp-short-pixel-averageCompression', 0, '', 'yes' );
98
+ }
99
+ }
100
+
101
+ function my_action_javascript() { ?>
102
+ <script type="text/javascript" >
103
+ jQuery(document).ready(function($) {
104
+ var data = {
105
+ 'action': 'my_action'
106
+ };
107
+ // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
108
+ $.post(ajaxurl, data, function(response) {
109
+ console.log('Server response: ' + response);
110
+ });
111
+ });
112
+ </script> <?php
113
+ }
114
+
115
+ function wp_load_admin_js() {
116
+ add_action('admin_print_footer_scripts', array(&$this, 'add_bulk_actions_via_javascript'));
117
+ }
118
+
119
+ function add_bulk_actions_via_javascript() {
120
+ ?>
121
+ <script type="text/javascript">
122
+ jQuery(document).ready(function($){
123
+ $('select[name^="action"] option:last-child').before('<option value="2">Bulk Optimize</option>');
124
+ });
125
+ </script>
126
+ <?php }
127
+
128
+ //handling older
129
+ public function WPShortPixel() {
130
+ $this->__construct();
131
+ }
132
+
133
+ public function handleImageUpload($meta, $ID = null) {
134
+ if(MUST_HAVE_KEY && $this->_verifiedKey) {
135
+ $url = wp_get_attachment_url($ID);
136
+ $path = get_attached_file($ID);
137
+ $this->_apiInterface->doRequests($url, $path, $ID);
138
+ }
139
+ $meta['ShortPixel']['WaitingProcessing'] = true;
140
+ return $meta;
141
+ }
142
+
143
+ public function handleImageProcessing($ID = null) {
144
+ if(MUST_HAVE_KEY && $this->_verifiedKey == false) {
145
+ echo "Missing API Key";
146
+ die();
147
+ }
148
+
149
+ //query database for first found entry that needs processing
150
+ global $wpdb;
151
+ $qry = "SELECT post_id FROM " . $wpdb->prefix . "postmeta WHERE meta_value LIKE '%\"WaitingProcessing\";b:1;%' LIMIT 1";
152
+ $ID = $wpdb->get_var($qry);
153
+
154
+ if(empty($ID)) { die; }
155
+
156
+ $imageURL = wp_get_attachment_url($ID);
157
+ $imagePath = get_attached_file($ID);
158
+ $meta = wp_get_attachment_metadata($ID);
159
+
160
+ $result = $this->_apiInterface->processImage($imageURL, $imagePath, $ID);
161
+
162
+ if(is_string($result)) {
163
+ $meta['ShortPixelImprovement'] = $result;
164
+ die;
165
+ }
166
+
167
+ $processThumbnails = get_option('wp-short-process_thumbnails');
168
+
169
+ //handle the rest of the thumbnails generated by WP
170
+ if($processThumbnails && $result && !empty($meta['sizes'])) {
171
+ foreach($meta['sizes'] as $thumbnailInfo) {
172
+ $thumbURL = str_replace(basename($imagePath), $thumbnailInfo['file'], $imageURL);
173
+ $thumbPath = str_replace(basename($imagePath), $thumbnailInfo['file'], $imagePath);
174
+ $this->_apiInterface->processImage($thumbURL, $thumbPath);
175
+ }
176
+ }
177
+
178
+ unset($meta['ShortPixel']['WaitingProcessing']);
179
+ if(isset($meta['ShortPixel']['BulkProcessing'])) {
180
+ unset($meta['ShortPixel']['BulkProcessing']);
181
+ }
182
+
183
+ //check bulk processing
184
+ $bulkLog = get_option('bulkProcessingLog');
185
+ if(isset($bulkLog['toDo'])) {
186
+ if(array_key_exists($ID, $bulkLog['toDo'])) {
187
+ unset($bulkLog['toDo'][$ID]);
188
+ }
189
+ }
190
+
191
+ if(empty($bulkLog['toDo'])) { delete_option('bulkProcessingLog'); }
192
+ else { update_option('bulkProcessingLog', $bulkLog); }
193
+
194
+ $meta['ShortPixelImprovement'] = $result->PercentImprovement;
195
+
196
+ wp_update_attachment_metadata($ID, $meta);
197
+ echo "Processing done succesfully for image #{$ID}";
198
+ die();
199
+ }
200
+
201
+ public function handleManualOptimization() {
202
+ $attachmentID = intval($_GET['attachment_ID']);
203
+
204
+ $url = wp_get_attachment_url($attachmentID);
205
+ $filePath = get_attached_file($attachmentID);
206
+
207
+ $this->_apiInterface->processImage($url, $filePath, $attachmentID);
208
+
209
+ // store the referring webpage location
210
+ $sendback = wp_get_referer();
211
+ // sanitize the referring webpage location
212
+ $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
213
+ // send the user back where they came from
214
+ wp_redirect($sendback);
215
+ // we are done,
216
+ }
217
+
218
+ public function handleRestoreBackup() {
219
+ $attachmentID = intval($_GET['attachment_ID']);
220
+
221
+ $uploadFilePath = get_attached_file($attachmentID);
222
+ $meta = wp_get_attachment_metadata($attachmentID);
223
+ $pathInfo = pathinfo($uploadFilePath);
224
+
225
+ try {
226
+ //main file
227
+ @rename(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . basename($uploadFilePath), $uploadFilePath);
228
+ //overwriting thumbnails
229
+ foreach($meta["sizes"] as $size => $imageData) {
230
+ $source = SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $imageData['file'];
231
+ $destination = $pathInfo['dirname'] . DIRECTORY_SEPARATOR . $imageData['file'];
232
+ @rename($source, $destination);
233
+ }
234
+
235
+ unset($meta["ShortPixelImprovement"]);
236
+ wp_update_attachment_metadata($attachmentID, $meta);
237
+
238
+ } catch(Exception $e) {
239
+ //what to do, what to do?
240
+ }
241
+ // store the referring webpage location
242
+ $sendback = wp_get_referer();
243
+ // sanitize the referring webpage location
244
+ $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
245
+ // send the user back where they came from
246
+ wp_redirect($sendback);
247
+ // we are done
248
+ }
249
+
250
+ public function bulkOptimizeActionHandler($hook) {
251
+ if($hook == 'upload.php') {
252
+ if($_GET['action'] == 2) {
253
+ if(!empty($_GET['media'])) {
254
+ $imageLog = array();
255
+ //remove all ShortPixel data from metadata
256
+ foreach($_GET['media'] as $attachmentID) {
257
+ $meta = wp_get_attachment_metadata($attachmentID);
258
+ $meta['ShortPixel']['WaitingProcessing'] = true;
259
+ $meta['ShortPixel']['BulkProcessing'] = true;
260
+ unset($meta['ShortPixelImprovement']);
261
+ wp_update_attachment_metadata($attachmentID, $meta);
262
+ $imageLog[$attachmentID] = false;
263
+ }
264
+ $bulkLog = array();
265
+ $bulkLog['running'] = true;
266
+ $bulkLog['toDo'] = $imageLog;
267
+ $bulkLog['total'] = count($imageLog);
268
+ update_option('bulkProcessingLog', $bulkLog);
269
+ }
270
+ }
271
+ }
272
+ }
273
+
274
+ public function registerSettingsPage() {
275
+ add_options_page( 'ShortPixel Settings', 'ShortPixel', 'manage_options', 'wp-shortpixel', array($this, 'renderSettingsMenu'));
276
+ }
277
+
278
+ function registerAdminPage( ) {
279
+ add_media_page( 'ShortPixel Bulk Process', 'Bulk ShortPixel', 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcesss' ) );
280
+ }
281
+
282
+ public function bulkProcesss() {
283
+ echo '<h1>ShortPixel Bulk Processing</h1>';
284
+
285
+ if(MUST_HAVE_KEY && $this->_verifiedKey == false) {
286
+ echo "<p>You do not have an API Key set. Bulk processing cannot be used. </p>";
287
+ echo "<p>Don’t have an API Key? <a href=\"https://shortpixel.com/wp-apikey\" target=\"_blank\">Sign up, it’s free.</a> </p>";
288
+ return;
289
+ }
290
+
291
+ $attachments = null;
292
+ $attachments = get_posts( array(
293
+ 'numberposts' => -1,
294
+ 'post_type' => 'attachment',
295
+ 'post_mime_type' => 'image'
296
+ ));
297
+
298
+ if($_POST["bulkProcess"]) {
299
+ $imageLog = array();
300
+ //remove all ShortPixel data from metadata
301
+ foreach($attachments as $attachment) {
302
+ $meta = wp_get_attachment_metadata($attachment->ID);
303
+ $meta['ShortPixel']['WaitingProcessing'] = true;
304
+ $meta['ShortPixel']['BulkProcessing'] = true;
305
+ wp_update_attachment_metadata($attachment->ID, $meta);
306
+ $imageLog[$attachment->ID] = false;
307
+ }
308
+ $bulkLog = array();
309
+ $bulkLog['running'] = true;
310
+ $bulkLog['toDo'] = $imageLog;
311
+ $bulkLog['total'] = count($imageLog);
312
+ update_option('bulkProcessingLog', $bulkLog);
313
+ }
314
+
315
+ $currentBulkProcessingStatus = get_option('bulkProcessingLog');
316
+
317
+ if($currentBulkProcessingStatus && $currentBulkProcessingStatus['running']) {
318
+ echo "<p>Bulk processing started and it may take a while to be completed.</p>";
319
+
320
+ $imagesLeft = count($currentBulkProcessingStatus["toDo"]);
321
+ $totalImages = $currentBulkProcessingStatus['total'];
322
+
323
+ echo "<p>{$imagesLeft} out of {$totalImages} images left to process.</p>";
324
+
325
+ echo '
326
+ <a class="button button-secondary" href="' . get_admin_url() . 'upload.php?page=wp-short-pixel-bulk">Check Status<a/>
327
+ <a class="button button-secondary" href="' . get_admin_url() . 'upload.php">Media Library</a>
328
+ ';
329
+ } else {
330
+ echo $this->getBulkProcessingForm(count($attachments));
331
+ }
332
+ }
333
+
334
+ public function renderSettingsMenu() {
335
+ if ( !current_user_can( 'manage_options' ) ) {
336
+ wp_die('You do not have sufficient permissions to access this page.');
337
+ }
338
+
339
+ if(isset($_POST['submit']) || isset($_POST['validate'])) {
340
+ //handle API Key - common for submit and validate
341
+ $validityData = $this->getQuotaInformation($_POST['key']);
342
+ $this->_apiKey = $_POST['key'];
343
+ $this->_apiInterface->setApiKey($this->_apiKey);
344
+ update_option('wp-short-pixel-apiKey', $_POST['key']);
345
+ if($validityData['APIKeyValid']) {
346
+ update_option('wp-short-pixel-verifiedKey', true);
347
+ $this->_verifiedKey = true;
348
+ } else {
349
+ update_option('wp-short-pixel-verifiedKey', false);
350
+ $this->_verifiedKey = false;
351
+ }
352
+ //if save button - we process the rest of the form elements
353
+ if(isset($_POST['submit'])) {
354
+ update_option('wp-short-pixel-compression', $_POST['compressionType']);
355
+ $this->_compressionType = $_POST['compressionType'];
356
+ $this->_apiInterface->setCompressionType($this->_compressionType);
357
+ if(isset($_POST['thumbnails'])) { $this->_processThumbnails = 1; } else { $this->_processThumbnails = 0; }
358
+ if(isset($_POST['backupImages'])) { $this->_backupImages = 1; } else { $this->_backupImages = 0; }
359
+ update_option('wp-short-process_thumbnails', $this->_processThumbnails);
360
+ update_option('wp-short-backup_images', $this->_backupImages);
361
+ }
362
+ }
363
+
364
+ if(isset($_POST['emptyBackup'])) {
365
+ if(file_exists(SP_BACKUP_FOLDER)) {
366
+ $files = scandir(SP_BACKUP_FOLDER);
367
+ $cleanPath = rtrim(SP_BACKUP_FOLDER, '/'). '/';
368
+ foreach($files as $t) {
369
+ if ( $t != "." && $t != "..") {
370
+ unlink(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $t);
371
+ }
372
+ }
373
+ }
374
+ }
375
+
376
+ $checked = '';
377
+ if($this->_processThumbnails) { $checked = 'checked'; }
378
+
379
+ $checkedBackupImages = '';
380
+ if($this->_backupImages) { $checkedBackupImages = 'checked'; }
381
+
382
+ echo '<h1>ShortPixel Image Optimiser Settings</h1>';
383
+ echo '<p>
384
+ <a href="https://shortpixel.com">ShortPixel.com</a> |
385
+ <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/installation/">Installation </a> |
386
+ <a href="https://wordpress.org/support/plugin/shortpixel-image-optimiser">Support </a>
387
+ </p>';
388
+ echo '<p>New images uploaded to the Media Library will be optimized automatically.<br/>If you have existing images you would like to optimize, you can use the <a href="/upload.php?page=wp-short-pixel-bulk">Bulk Optimize tool</a>.</p>';
389
+
390
+ $formHTML = <<< HTML
391
+ <form name='wp_shortpixel_options' action='' method='post' id='wp_shortpixel_options'>
392
+ <table class="form-table">
393
+ <tbody><tr>
394
+ <th scope="row"><label for="key">API Key:</label></th>
395
+ <td><input name="key" type="text" id="key" value="{$this->_apiKey}" class="regular-text">
396
+ <input type="submit" name="validate" id="validate" class="button button-primary" title="Validate the provided API key" value="Validate">
397
+ </td>
398
+ </tr>
399
+ HTML;
400
+
401
+ if(!$this->_verifiedKey) {
402
+ //if invalid key we display the link to the API Key
403
+ $formHTML .= '<tr><td style="padding-left: 0px;" colspan="2">Don’t have an API Key? <a href="https://shortpixel.com/wp-apikey" target="_blank">Sign up, it’s free.</a></td></tr>';
404
+ $formHTML .= '</form>';
405
+ } else {
406
+ //if valid key we display the rest of the options
407
+ $formHTML .= <<< HTML
408
+ <tr><th scope="row">
409
+ <label for="compressionType">Compression type: <span title="
410
+ Lossy compression: lossy has a better compression rate than lossless compression. The resulting image is not 100% identical with the original. Works well for photos taken with your camera.
411
+ Lossless compression: the shrunk image will be identical with the original and smaller in size. Use this when you do not want to loose any of the original image's details. Works best for technical drawings, clip art and comics.
412
+ ">?</span></label>
413
+ </th><td>
414
+ HTML;
415
+
416
+ if($this->_compressionType == 'lossless') {
417
+ $formHTML .= '<input type="radio" name="compressionType" value="lossy" >Lossy</br></br>';
418
+ $formHTML .= '<input type="radio" name="compressionType" value="lossless" checked>Lossless';
419
+ } else {
420
+ $formHTML .= '<input type="radio" name="compressionType" value="lossy" checked>Lossy</br></br>';
421
+ $formHTML .= '<input type="radio" name="compressionType" value="lossless" >Lossless';
422
+ }
423
+
424
+ $formHTML .= <<<HTML
425
+ </td>
426
+ </tr>
427
+ <tr>
428
+ <th scope="row"><label for="thumbnails">Compress also image thumbnails:</label></th>
429
+ <td><input name="thumbnails" type="checkbox" id="thumbnails" {$checked}></td>
430
+ </tr>
431
+ <tr>
432
+ <th scope="row"><label for="backupImages">Back up all images
433
+ <span title="If selected all images will be backed up in the ShortpixelBackups folder located in your wp-content folder">?</span>
434
+ </label></th>
435
+ <td>
436
+ <input name="backupImages" type="checkbox" id="backupImages" {$checkedBackupImages}>
437
+ </td>
438
+ </tr>
439
+ </tbody></table>
440
+ <p class="submit">
441
+ <input type="submit" name="submit" id="submit" class="button button-primary" title="Save Changes" value="Save Changes">
442
+ <input type="submit" name="bulk-process" id="bulk-process" class="button button-primary" title="Process all the images in your Media Library" value="Bulk Process">
443
+ </p>
444
+ </form>
445
+ <script>
446
+ var rad = document.wp_shortpixel_options.compressionType;
447
+ var prev = null;
448
+ for(var i = 0; i < rad.length; i++) {
449
+ rad[i].onclick = function() {
450
+
451
+ if(this !== prev) {
452
+ prev = this;
453
+ }
454
+ alert('Select Media/Bulk Short Pixel to reprocess all the images');
455
+ };
456
+ }
457
+ </script>
458
+ HTML;
459
+ }
460
+
461
+ echo $formHTML;
462
+
463
+ if($this->_verifiedKey) {
464
+ $fileCount = get_option('wp-short-pixel-fileCount');
465
+ $savedSpace = self::formatBytes(get_option('wp-short-pixel-savedSpace'),2);
466
+ $averageCompression = round(get_option('wp-short-pixel-averageCompression'),2);
467
+ $savedBandwidth = self::formatBytes(get_option('wp-short-pixel-savedSpace') * 1000,2);
468
+ $quotaData = $this->getQuotaInformation();
469
+ $backupFolderSize = self::formatBytes(self::folderSize(SP_BACKUP_FOLDER));
470
+
471
+ $statHTML = <<< HTML
472
+ <h3>ShortPixlel Facts & Figures</h3>
473
+ <table class="form-table">
474
+ <tbody><tr>
475
+ <th scope="row"><label for="totalFiles">Your total number of processed files:</label></th>
476
+ <td>$fileCount</td>
477
+ </tr>
478
+ <tr>
479
+ <th scope="row"><label for="savedSpace">Saved space by ShortPixel</label></th>
480
+ <td>$savedSpace</td>
481
+ </tr>
482
+ <tr>
483
+ <th scope="row"><label for="savedBandwidth">Bandwith* saved with ShortPixel:</label></th>
484
+ <td>$savedBandwidth</td>
485
+ </tr>
486
+ <tr>
487
+ <th scope="row"><label for="apiQuota">Your ShortPixel plan</label></th>
488
+ <td>{$quotaData['APICallsQuota']}</td>
489
+ </tr>
490
+ <tr>
491
+ <th scope="row"><label for="usedQUota">Used Quota:</label></th>
492
+ <td>{$quotaData['APICallsMade']}</td>
493
+ </tr>
494
+ <tr>
495
+ <th scope="row"><label for="averagCompression">Average file size compression:</label></th>
496
+ <td>$averageCompression%</td>
497
+ </tr>
498
+ HTML;
499
+ if($this->_backupImages) {
500
+ $statHTML .= <<< HTML
501
+ <form action="" method="POST">
502
+ <tr>
503
+ <th scope="row"><label for="sizeBackup">Backup folder size:</label></th>
504
+ <td>
505
+ {$backupFolderSize}
506
+ <input type="submit" style="margin-left: 15px; vertical-align: middle;" class="button button-secondary" name="emptyBackup" value="Empty backups"/>
507
+ </td>
508
+ </tr>
509
+ </form>
510
+ HTML;
511
+ }
512
+
513
+ $statHTML .= <<< HTML
514
+ </tbody></table>
515
+ <p>* Saved bandwidth is calculated at 100,000 impressions/image</p>
516
+ HTML;
517
+ echo $statHTML;
518
+ }
519
+ }
520
+
521
+ public function getBulkProcessingForm($imageCount) {
522
+ return <<< HTML
523
+ </br>
524
+ Currently, you have {$imageCount} images in your library. </br>
525
+ </br>
526
+ <form action='' method="POST" >
527
+ <input type="submit" name="bulkProcess" id="bulkProcess" class="button button-primary" value="Compress all your images">
528
+ </form>
529
+ HTML;
530
+ }
531
+
532
+ public function displayNotice() {
533
+ global $hook_suffix;
534
+
535
+ $divHeader = '<br/><div class="updated">';
536
+ $divWarningHeader = '<br/><div class="update-nag">';
537
+ $divFooter = '</div>';
538
+
539
+ $noticeInvalidKeyContent = '
540
+ <div style="background-color: #FFFFFF; box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1); padding: 1px 12px; margin: 15px 0; ">
541
+ <p>
542
+ <a href="options-general.php?page=wp-shortpixel">Activate your ShortPixel plugin.</a>
543
+ Get an API key <a href="https://shortpixel.com/wp-apikey" target="_blank">here</a>. Sign up, it’s free.
544
+ </p>
545
+ </div>
546
+ ';
547
+ $noticeWrongAPIKeyContent = '<p>API Key invalid!</p>';
548
+ $noticeCorrectAPIKeyContent = '<p>API Key valid!</p>';
549
+
550
+ if($hook_suffix == 'settings_page_wp-shortpixel' && !empty($_POST)) {
551
+ $keyCheckData = $this->getQuotaInformation($_POST['key']);
552
+ if($keyCheckData['APIKeyValid']) {
553
+ echo $divHeader . $noticeCorrectAPIKeyContent . $divFooter;
554
+ } else {
555
+ echo $divWarningHeader . $noticeWrongAPIKeyContent . $divFooter;
556
+ }
557
+ }
558
+ if($hook_suffix == 'plugins.php' && !$this->_verifiedKey) { echo $noticeInvalidKeyContent; }
559
+ }
560
+
561
+ public function getQuotaInformation($apiKey = null) {
562
+
563
+ if(is_null($apiKey)) { $apiKey = $this->_apiKey; }
564
+
565
+ $requestURL = 'https://api.shortpixel.com/api-status.php?key='.$apiKey;
566
+ $args = array('timeout'=> SP_MAX_TIMEOUT, 'sslverify' => false);
567
+ $response = wp_remote_get($requestURL, $args);
568
+
569
+ $defaultData = array(
570
+ "APIKeyValid" => false,
571
+ "APICallsMade" => 'Information unavailable. Please check your API key.',
572
+ "APICallsQuota" => 'Information unavailable. Please check your API key.');
573
+
574
+ if(is_object($response) && get_class($response) == 'WP_Error') {
575
+ return $defaultData;
576
+ }
577
+
578
+ if($response['response']['code'] != 200) {
579
+ return $defaultData;
580
+ }
581
+
582
+ $data = $response['body'];
583
+ $data = $this->parseJSON($data);
584
+
585
+ if(empty($data)) { return $defaultData; }
586
+
587
+ if($data->Status->Code == '-401') { return $defaultData; }
588
+
589
+ return array(
590
+ "APIKeyValid" => true,
591
+ "APICallsMade" => number_format($data->APICallsMade) . ' images',
592
+ "APICallsQuota" => number_format($data->APICallsQuota) . ' images'
593
+ );
594
+
595
+
596
+ }
597
+
598
+ public function generateCustomColumn( $column_name, $id ) {
599
+ if( 'wp-shortPixel' == $column_name ) {
600
+
601
+ $data = wp_get_attachment_metadata($id);
602
+
603
+ if ( isset( $data['ShortPixelImprovement'] ) ) {
604
+
605
+ if(isset($meta['ShortPixel']['BulkProcessing'])) {
606
+ print 'Waiting for bulk processing';
607
+ return;
608
+ }
609
+
610
+ print $data['ShortPixelImprovement'];
611
+ if(is_numeric($data['ShortPixelImprovement'])) print '%';
612
+ print " | <a href=\"admin.php?action=shortpixel_manual_optimize&amp;attachment_ID={$id}\">Optimize now</a>";
613
+ print " | <a href=\"admin.php?action=shortpixel_restore_backup&amp;attachment_ID={$id}\">Restore backup</a>";
614
+ return;
615
+ }
616
+
617
+ if(isset($data['ShortPixel']['WaitingProcessing'])) {
618
+ print 'Image waiting to be processed';
619
+ return;
620
+ } else {
621
+ if ( wp_attachment_is_image( $id ) ) {
622
+ print 'Image not processed';
623
+ print " | <a href=\"admin.php?action=shortpixel_manual_optimize&amp;attachment_ID={$id}\">Optimize now</a>";
624
+ return;
625
+ }
626
+ }
627
+ }
628
+ }
629
+
630
+ public function columns( $defaults ) {
631
+ $defaults['wp-shortPixel'] = 'Short Pixel Compression';
632
+ return $defaults;
633
+ }
634
+
635
+ public function generatePluginLinks($links) {
636
+ $in = '<a href="options-general.php?page=wp-shortpixel">Settings</a>';
637
+ array_unshift($links, $in);
638
+ return $links;
639
+ }
640
+
641
+ public function parseJSON($data) {
642
+ if ( function_exists('json_decode') ) {
643
+ $data = json_decode( $data );
644
+ } else {
645
+ require_once( 'JSON/JSON.php' );
646
+ $json = new Services_JSON( );
647
+ $data = $json->decode( $data );
648
+ }
649
+ return $data;
650
+ }
651
+
652
+
653
+ static public function formatBytes($bytes, $precision = 2) {
654
+ $units = array('B', 'KB', 'MB', 'GB', 'TB');
655
+
656
+ $bytes = max($bytes, 0);
657
+ $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
658
+ $pow = min($pow, count($units) - 1);
659
+
660
+ $bytes /= pow(1024, $pow);
661
+
662
+ return round($bytes, $precision) . ' ' . $units[$pow];
663
+ }
664
+
665
+ static public function folderSize($path) {
666
+ $total_size = 0;
667
+ if(file_exists($path)) {
668
+ $files = scandir($path);
669
+ } else {
670
+ return $total_size;
671
+ }
672
+ $cleanPath = rtrim($path, '/'). '/';
673
+ foreach($files as $t) {
674
+ if ($t<>"." && $t<>"..") {
675
+ $currentFile = $cleanPath . $t;
676
+ if (is_dir($currentFile)) {
677
+ $size = foldersize($currentFile);
678
+ $total_size += $size;
679
+ }
680
+ else {
681
+ $size = filesize($currentFile);
682
+ $total_size += $size;
683
+ }
684
+ }
685
+ }
686
+ return $total_size;
687
+ }
688
+
689
+
690
+ }
691
+
692
+ $pluginInstance = new WPShortPixel();
693
+ global $pluginInstance;
694
+
695
+ ?>