ShortPixel Image Optimizer - Version 3.0.5

Version Description

  • different bug fixes
Download this release

Release Info

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

Code changes from version 3.0.3 to 3.0.5

Files changed (4) hide show
  1. js/short-pixel.js +6 -1
  2. readme.txt +7 -3
  3. shortpixel_view.php +4 -1
  4. wp-shortpixel.php +1526 -1530
js/short-pixel.js CHANGED
@@ -230,7 +230,12 @@ function sliderUpdate(id, thumb, bkThumb, percent){
230
  }
231
  oldSlide.css("z-index", 1000);
232
  jQuery(".bulk-img-opt", newSlide).attr("src", thumb);
233
- jQuery(".bulk-img-orig", newSlide).attr("src", bkThumb);
 
 
 
 
 
234
  jQuery(".bulk-opt-percent", newSlide).text(percent + "%");
235
 
236
  jQuery(".bulk-slider").append(newSlide);
230
  }
231
  oldSlide.css("z-index", 1000);
232
  jQuery(".bulk-img-opt", newSlide).attr("src", thumb);
233
+ if(bkThumb.length > 0) {
234
+ jQuery(".img-original", newSlide).show();
235
+ jQuery(".bulk-img-orig", newSlide).attr("src", bkThumb);
236
+ } else {
237
+ jQuery(".img-original", newSlide).hide();
238
+ }
239
  jQuery(".bulk-opt-percent", newSlide).text(percent + "%");
240
 
241
  jQuery(".bulk-slider").append(newSlide);
readme.txt CHANGED
@@ -3,9 +3,9 @@
3
  Contributors: AlexSP
4
  Tags: picture, optimization, image editor, pngout, upload speed, shortpixel, compression, jpegmini, webp, lossless, cwebp, media, 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, pdf, pdf optimisation, pdf optimization, optimize pdf, optimise pdf, shrink pdf, jpg, jpeg, jpg optimisation, jpg optimization, optimize jpg, optimise jpg, shrink jpg, gif, animated gif, optimize gif, optimise gif, optimizer, optimiser, compresion, optimization, cruncher, image cruncher, compress png, compress jpg, compress jpeg, faster loading times, image optimiser, improve pagerank, optimise, optimize animated gif, optimise jpeg, optimize jpeg, optimize png, optimise png, tinyjpg, short pixel, shortpixel
5
 
6
- Requires at least: 3.0.1 or higher
7
  Tested up to: 4.2
8
- Stable tag: 3.0.3
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -119,7 +119,11 @@ The ShortPixel team is here to help. <a href="https://shortpixel.com/contact">Co
119
 
120
  == Changelog ==
121
 
122
- = 3.0.3 =
 
 
 
 
123
 
124
  * Progress bar improvements
125
 
3
  Contributors: AlexSP
4
  Tags: picture, optimization, image editor, pngout, upload speed, shortpixel, compression, jpegmini, webp, lossless, cwebp, media, 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, pdf, pdf optimisation, pdf optimization, optimize pdf, optimise pdf, shrink pdf, jpg, jpeg, jpg optimisation, jpg optimization, optimize jpg, optimise jpg, shrink jpg, gif, animated gif, optimize gif, optimise gif, optimizer, optimiser, compresion, optimization, cruncher, image cruncher, compress png, compress jpg, compress jpeg, faster loading times, image optimiser, improve pagerank, optimise, optimize animated gif, optimise jpeg, optimize jpeg, optimize png, optimise png, tinyjpg, short pixel, shortpixel
5
 
6
+ Requires at least: 3.0.1
7
  Tested up to: 4.2
8
+ Stable tag: 3.0.5
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
119
 
120
  == Changelog ==
121
 
122
+ = 3.0.5 =
123
+
124
+ * different bug fixes
125
+
126
+ = 3.0.2 =
127
 
128
  * Progress bar improvements
129
 
shortpixel_view.php CHANGED
@@ -71,6 +71,9 @@ class ShortPixelView {
71
 
72
  <?=$this->displayBulkStats($filesOptimized, $thumbsProcessedCount, $under5PercentCount, $averageCompression, $savedSpace)?>
73
  <p>Download your detailed <a href="https://api.shortpixel.com/v2/report.php?key=<?=$this->ctrl->getApiKey()?>">Optimization Report</a> to check your image optimization statistics for the last 40 days</p>
 
 
 
74
  <p>Restart the optimization process for new images added to your library:
75
  <form action='' method='POST' >
76
  <input type='checkbox' name='thumbnails' <?=$this->ctrl->processThumbnails() ? "checked":""?>> Include thumbnails<br><br>
@@ -151,4 +154,4 @@ class ShortPixelView {
151
  <?php
152
  }
153
 
154
- }
71
 
72
  <?=$this->displayBulkStats($filesOptimized, $thumbsProcessedCount, $under5PercentCount, $averageCompression, $savedSpace)?>
73
  <p>Download your detailed <a href="https://api.shortpixel.com/v2/report.php?key=<?=$this->ctrl->getApiKey()?>">Optimization Report</a> to check your image optimization statistics for the last 40 days</p>
74
+ <?php if(false && $imgProcessedCount['totalFiles'] < $imageCount['totalFiles']) { ?>
75
+ <p>However, <?=number_format($imageCount['mainFiles'] - $imgProcessedCount['mainFiles'])?> images and <?=number_format(($imageCount['totalFiles'] - $imageCount['mainFiles']) - ($imgProcessedCount['totalFiles'] - $imgProcessedCount['mainFiles']))?> thumbnails are not yet optimized by ShorPixel.</p>
76
+ <?php } ?>
77
  <p>Restart the optimization process for new images added to your library:
78
  <form action='' method='POST' >
79
  <input type='checkbox' name='thumbnails' <?=$this->ctrl->processThumbnails() ? "checked":""?>> Include thumbnails<br><br>
154
  <?php
155
  }
156
 
157
+ }
wp-shortpixel.php CHANGED
@@ -1,1530 +1,1526 @@
1
- <?php
2
- /**
3
- * Plugin Name: ShortPixel Image Optimizer
4
- * Plugin URI: https://shortpixel.com/
5
- * Description: ShortPixel optimizes images automatically, while guarding the quality of your images. Check your <a href="options-general.php?page=wp-shortpixel" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
6
- * Version: 3.0.3
7
- * Author: ShortPixel
8
- * Author URI: https://shortpixel.com
9
- */
10
-
11
- require_once('shortpixel_api.php');
12
- require_once('shortpixel_queue.php');
13
- require_once('shortpixel_view.php');
14
- require_once( ABSPATH . 'wp-admin/includes/image.php' );
15
- include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
16
- if ( !is_plugin_active( 'wpmandrill/wpmandrill.php' ) ) {
17
- require_once( ABSPATH . 'wp-includes/pluggable.php' );//to avoid conflict with wpmandrill plugin
18
- }
19
-
20
- define('SP_RESET_ON_ACTIVATE', false);
21
-
22
- define('PLUGIN_VERSION', "3.0.3");
23
- define('SP_MAX_TIMEOUT', 10);
24
- define('SP_BACKUP', 'ShortpixelBackups');
25
- define('SP_BACKUP_FOLDER', WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . SP_BACKUP);
26
- define('MAX_API_RETRIES', 5);
27
- $MAX_EXECUTION_TIME = ini_get('max_execution_time');
28
- if ( is_numeric($MAX_EXECUTION_TIME) )
29
- define('MAX_EXECUTION_TIME', $MAX_EXECUTION_TIME - 5 ); //in seconds
30
- else
31
- define('MAX_EXECUTION_TIME', 25 );
32
- define("SP_MAX_RESULTS_QUERY", 6);
33
-
34
- class WPShortPixel {
35
-
36
- const BULK_EMPTY_QUEUE = 0;
37
-
38
- private $_apiKey = '';
39
- private $_compressionType = 1;
40
- private $_processThumbnails = 1;
41
- private $_CMYKtoRGBconversion = 1;
42
- private $_backupImages = 1;
43
- private $_verifiedKey = false;
44
-
45
- private $_apiInterface = null;
46
- private $prioQ = null;
47
- private $view = null;
48
-
49
- //handling older
50
- public function WPShortPixel() {
51
- $this->__construct();
52
- }
53
-
54
- public function __construct() {
55
- session_start();
56
-
57
- $this->populateOptions();
58
-
59
- $this->_apiInterface = new ShortPixelAPI($this->_apiKey, $this->_compressionType, $this->_CMYKtoRGBconversion);
60
- $this->prioQ = new ShortPixelQueue($this);
61
- $this->view = new ShortPixelView($this);
62
-
63
- define('QUOTA_EXCEEDED', "Quota Exceeded. <a href='https://shortpixel.com/login/".$this->_apiKey."' target='_blank'>Extend Quota</a>");
64
-
65
- $this->setDefaultViewModeList();//set default mode as list. only @ first run
66
-
67
- //add hook for image upload processing
68
- add_filter( 'wp_generate_attachment_metadata', array( &$this, 'handleImageUpload' ), 10, 2 );
69
- add_filter( 'manage_media_columns', array( &$this, 'columns' ) );//add media library column header
70
- add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array(&$this, 'generatePluginLinks'));//for plugin settings page
71
-
72
- //add_action( 'admin_footer', array(&$this, 'handleImageProcessing'));
73
- add_action( 'manage_media_custom_column', array( &$this, 'generateCustomColumn' ), 10, 2 );//generate the media library column
74
-
75
- //add settings page
76
- add_action( 'admin_menu', array( &$this, 'registerSettingsPage' ) );//display SP in Settings menu
77
- add_action( 'admin_menu', array( &$this, 'registerAdminPage' ) );
78
- add_action( 'delete_attachment', array( &$this, 'handleDeleteAttachmentInBackup' ) );
79
- add_action( 'load-upload.php', array( &$this, 'handleCustomBulk'));
80
-
81
- //when plugin is activated run this
82
- register_activation_hook( __FILE__, array( &$this, 'shortPixelActivatePlugin' ) );
83
- register_deactivation_hook( __FILE__, array( &$this, 'shortPixelDeactivatePlugin' ) );
84
-
85
- //automatic optimization
86
- add_action( 'wp_ajax_shortpixel_image_processing', array( &$this, 'handleImageProcessing') );
87
- //manual optimization
88
- add_action( 'wp_ajax_shortpixel_manual_optimization', array(&$this, 'handleManualOptimization'));
89
- //backup restore
90
- add_action('admin_action_shortpixel_restore_backup', array(&$this, 'handleRestoreBackup'));
91
-
92
- //This adds the constants used in PHP to be available also in JS
93
- add_action( 'admin_footer', array( &$this, 'shortPixelJS') );
94
-
95
-
96
- //example toolbar by fai, to be configured
97
- add_action( 'admin_bar_menu', array( &$this, 'toolbar_shortpixel_processing'), 999 );
98
-
99
- $this->migrateBackupFolder();
100
- }
101
-
102
- public function populateOptions() {
103
-
104
- $this->_apiKey = self::getOpt('wp-short-pixel-apiKey', '');
105
- $this->_verifiedKey = self::getOpt('wp-short-pixel-verifiedKey', $this->_verifiedKey);
106
- $this->_compressionType = self::getOpt('wp-short-pixel-compression', $this->_compressionType);
107
- $this->_processThumbnails = self::getOpt('wp-short-process_thumbnails', $this->_processThumbnails);
108
- $this->_CMYKtoRGBconversion = self::getOpt('wp-short-pixel_cmyk2rgb', $this->_CMYKtoRGBconversion);
109
- $this->_backupImages = self::getOpt('wp-short-backup_images', $this->_backupImages);
110
- // the following practically set defaults for options if they're not set
111
- self::getOpt( 'wp-short-pixel-fileCount', 0);
112
- self::getOpt( 'wp-short-pixel-thumbnail-count', 0);//amount of optimized thumbnails
113
- self::getOpt( 'wp-short-pixel-files-under-5-percent', 0);//amount of optimized thumbnails
114
- self::getOpt( 'wp-short-pixel-savedSpace', 0);
115
- self::getOpt( 'wp-short-pixel-api-retries', 0);//sometimes we need to retry processing/downloading a file multiple times
116
- self::getOpt( 'wp-short-pixel-quota-exceeded', 0);
117
- self::getOpt( 'wp-short-pixel-total-original', 0);//amount of original data
118
- self::getOpt( 'wp-short-pixel-total-optimized', 0);//amount of optimized
119
- self::getOpt( 'wp-short-pixel-protocol', 'https');
120
- }
121
-
122
- public function shortPixelActivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
123
- {
124
- $this->prioQ->resetBulk();
125
- if(SP_RESET_ON_ACTIVATE === true && WP_DEBUG === true) { //force reset plugin counters, only on specific occasions and on test environments
126
- update_option( 'wp-short-pixel-fileCount', 0);
127
- update_option( 'wp-short-pixel-thumbnail-count', 0);
128
- update_option( 'wp-short-pixel-files-under-5-percent', 0);
129
- update_option( 'wp-short-pixel-savedSpace', 0);
130
- update_option( 'wp-short-pixel-api-retries', 0);//sometimes we need to retry processing/downloading a file multiple times
131
- update_option( 'wp-short-pixel-quota-exceeded', 0);
132
- update_option( 'wp-short-pixel-total-original', 0);//amount of original data
133
- update_option( 'wp-short-pixel-total-optimized', 0);//amount of optimized
134
- update_option( 'wp-short-pixel-bulk-ever-ran', 0);
135
- delete_option('wp-short-pixel-priorityQueue');
136
- unset($_SESSION["wp-short-pixel-priorityQueue"]);
137
- delete_option("wp-short-pixel-bulk-previous-percent");
138
- }
139
- }
140
-
141
- public function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
142
- {
143
- $this->prioQ->resetBulk();
144
- }
145
-
146
- //set default move as "list". only set once, it won't try to set the default mode again.
147
- public function setDefaultViewModeList()
148
- {
149
- if(get_option('wp-short-pixel-view-mode') === false)
150
- {
151
- add_option('wp-short-pixel-view-mode', 1, '', 'yes' );
152
- if ( function_exists('get_currentuserinfo') )
153
- {
154
- global $current_user;
155
- get_currentuserinfo();
156
- $currentUserID = $current_user->ID;
157
- update_user_meta($currentUserID, "wp_media_library_mode", "list");
158
- }
159
- }
160
-
161
- }
162
-
163
- static function log($message) {
164
- if (WP_DEBUG === true) {
165
- if (is_array($message) || is_object($message)) {
166
- error_log(print_r($message, true));
167
- } else {
168
- error_log($message);
169
- }
170
- }
171
- }
172
-
173
- function shortPixelJS() { ?>
174
- <script type="text/javascript" >
175
- jQuery(document).ready(function($){
176
- ShortPixel.setOptions({
177
- STATUS_SUCCESS: <?= ShortPixelAPI::STATUS_SUCCESS ?>,
178
- STATUS_EMPTY_QUEUE: <?= self::BULK_EMPTY_QUEUE ?>,
179
- STATUS_ERROR: <?= ShortPixelAPI::STATUS_ERROR ?>,
180
- STATUS_FAIL: <?= ShortPixelAPI::STATUS_FAIL ?>,
181
- STATUS_SKIP: <?= ShortPixelAPI::STATUS_SKIP ?>,
182
- STATUS_QUOTA_EXCEEDED: <?= ShortPixelAPI::STATUS_QUOTA_EXCEEDED ?>,
183
- WP_PLUGIN_URL: '<?= WP_PLUGIN_URL ?>',
184
- API_KEY: "<?= $this->_apiKey ?>"
185
- });
186
- });
187
- </script> <?php
188
- wp_enqueue_style('short-pixel.css', plugins_url('/css/short-pixel.css',__FILE__) );
189
- }
190
-
191
- function toolbar_shortpixel_processing( $wp_admin_bar ) {
192
- if ( !is_admin()) {
193
- return;
194
- }
195
-
196
- wp_enqueue_script('short-pixel.js', plugins_url('/js/short-pixel.js',__FILE__) );
197
-
198
- $extraClasses = " shortpixel-hide";
199
- $tooltip = "ShortPixel optimizing...";
200
- $icon = "shortpixel.png";
201
- $link = 'upload.php?page=wp-short-pixel-bulk';
202
- $blank = "";
203
- if($this->prioQ->processing()) {
204
- $extraClasses = " shortpixel-processing";
205
- }
206
- self::log("TOOLBAR: Quota exceeded: " . self::getOpt( 'wp-short-pixel-quota-exceeded', 0));
207
- if(self::getOpt( 'wp-short-pixel-quota-exceeded', 0)) {
208
- $extraClasses = " shortpixel-alert shortpixel-quota-exceeded";
209
- $tooltip = "ShortPixel quota exceeded. Click to top-up";
210
- $link = "http://shortpixel.com/login/" . $this->_apiKey;
211
- $blank = '_blank';
212
- //$icon = "shortpixel-alert.png";
213
- }
214
- self::log("TB: Start: " . $this->prioQ->getStartBulkId() . ", stop: " . $this->prioQ->getStopBulkId() . " PrioQ: "
215
- .json_encode($this->prioQ->get()));
216
-
217
- $args = array(
218
- 'id' => 'shortpixel_processing',
219
- 'title' => '<div title="' . $tooltip . '" ><img src="'
220
- . WP_PLUGIN_URL . '/shortpixel-image-optimiser/img/' . $icon . '"><span class="shp-alert">!</span></div>',
221
- 'href' => $link,
222
- 'meta' => array('target'=> $blank, 'class' => 'shortpixel-toolbar-processing' . $extraClasses)
223
- );
224
- $wp_admin_bar->add_node( $args );
225
- }
226
-
227
- public static function getOpt($key, $default) {
228
- if(get_option($key) === false) {
229
- add_option( $key, $default, '', 'yes' );
230
- }
231
- return get_option($key);
232
- }
233
-
234
- public function handleCustomBulk() {
235
- // 1. get the action
236
- $wp_list_table = _get_list_table('WP_Media_List_Table');
237
- $action = $wp_list_table->current_action();
238
-
239
- switch($action) {
240
- // 2. Perform the action
241
- case 'short-pixel-bulk':
242
- // security check
243
- check_admin_referer('bulk-media');
244
- if(!is_array($_GET['media'])) {
245
- break;
246
- }
247
- $mediaIds = array_reverse($_GET['media']);
248
- foreach( $mediaIds as $ID ) {
249
- $meta = wp_get_attachment_metadata($ID);
250
- if( (!isset($meta['ShortPixel']) || !isset($meta['ShortPixel']['WaitingProcessing']) || $meta['ShortPixel']['WaitingProcessing'] != true)
251
- && (!isset($meta['ShortPixelImprovement']) || $meta['ShortPixelImprovement'] != 'Optimization N/A')) {
252
- $this->prioQ->push($ID);
253
- $meta['ShortPixel']['WaitingProcessing'] = true;
254
- wp_update_attachment_metadata($ID, $meta);
255
- }
256
- }
257
- break;
258
- }
259
- }
260
-
261
- public function handleImageUpload($meta, $ID = null)
262
- {
263
- if( !$this->_verifiedKey) {// no API Key set/verified -> do nothing here, just return
264
- return $meta;
265
- }
266
- //else
267
- self::log("IMG: Auto-analyzing file ID #{$ID}");
268
-
269
- if( self::isProcessable($ID) == false )
270
- {//not a file that we can process
271
- $meta['ShortPixelImprovement'] = 'Optimization N/A';
272
- return $meta;
273
- }
274
- else
275
- {//the kind of file we can process. goody.
276
- $this->prioQ->push($ID);
277
- $URLsAndPATHs = $this->getURLsAndPATHs($ID, $meta);
278
- $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $ID);//send a processing request right after a file was uploaded, do NOT wait for response
279
- self::log("IMG: sent: " . json_encode($URLsAndPATHs));
280
- $meta['ShortPixel']['WaitingProcessing'] = true;
281
- return $meta;
282
- }
283
-
284
- }//end handleImageUpload
285
-
286
- public function getCurrentBulkItemsCount(){
287
- global $wpdb;
288
-
289
- $startQueryID = $this->prioQ->getFlagBulkId();
290
- $endQueryID = $this->prioQ->getStopBulkId();
291
-
292
- if ( $startQueryID <= $endQueryID ) {
293
- return 0;
294
- }
295
- $queryPostMeta = "SELECT COUNT(DISTINCT post_id) items FROM " . $wpdb->prefix . "postmeta
296
- WHERE ( post_id <= $startQueryID AND post_id > $endQueryID ) AND (
297
- meta_key = '_wp_attached_file'
298
- OR meta_key = '_wp_attachment_metadata' )";
299
- $res = $wpdb->get_results($queryPostMeta);
300
- return $res[0]->items;
301
- }
302
-
303
- public function getBulkItemsFromDb(){
304
- global $wpdb;
305
-
306
- $startQueryID = $this->prioQ->getStartBulkId();
307
- $endQueryID = $this->prioQ->getStopBulkId();
308
- $skippedAlreadyProcessed = 0;
309
-
310
- if ( $startQueryID <= $endQueryID ) {
311
- return false;
312
- }
313
- $idList = array();
314
- for ($sanityCheck = 0, $crtStartQueryID = $startQueryID;
315
- $crtStartQueryID > $endQueryID && count($idList) < 3; $sanityCheck++) {
316
-
317
- self::log("GETDB: current StartID: " . $crtStartQueryID);
318
-
319
- $queryPostMeta = "SELECT * FROM " . $wpdb->prefix . "postmeta
320
- WHERE ( post_id <= $crtStartQueryID AND post_id > $endQueryID )
321
- AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
322
- ORDER BY post_id DESC
323
- LIMIT " . SP_MAX_RESULTS_QUERY;
324
- $resultsPostMeta = $wpdb->get_results($queryPostMeta);
325
-
326
- if($sanityCheck > 1000) {
327
- die("oops! $crtStartQueryID -- $startQueryID -> $endQueryID");
328
- }
329
- if ( empty($resultsPostMeta) ) {
330
- $crtStartQueryID -= SP_MAX_RESULTS_QUERY;
331
- continue;
332
- }
333
-
334
- foreach ( $resultsPostMeta as $itemMetaData ) {
335
- $crtStartQueryID = $itemMetaData->post_id;
336
- if(!in_array($crtStartQueryID, $idList) && self::isProcessable($crtStartQueryID)) {
337
- $meta = wp_get_attachment_metadata($crtStartQueryID);
338
- if(!isset($meta["ShortPixelImprovement"]) || !is_numeric($meta["ShortPixelImprovement"])) {
339
- $idList[] = $crtStartQueryID;
340
- } elseif($itemMetaData->meta_key == '_wp_attachment_metadata') { //count skipped
341
- $skippedAlreadyProcessed++;
342
- }
343
- }
344
- }
345
- if(!count($idList) && $crtStartQueryID <= $startQueryID) {
346
- //daca n-am adaugat niciuna pana acum, n-are sens sa mai selectez zona asta de id-uri in bulk-ul asta.
347
- $leapStart = $this->prioQ->getStartBulkId();
348
- $crtStartQueryID = $startQueryID = $itemMetaData->post_id - 1; //decrement it so we don't select it again
349
- $res = self::countAllProcessedFiles($leapStart, $crtStartQueryID);
350
- $skippedAlreadyProcessed += $res["mainFiles"];
351
- $this->prioQ->setStartBulkId($startQueryID);
352
- } else {
353
- $crtStartQueryID--;
354
- }
355
- }
356
- return array("ids" => $idList, "skipped" => $skippedAlreadyProcessed);
357
- }
358
-
359
- /**
360
- * Get last added items from priority
361
- * @return type
362
- */
363
- public function getFromPrioAndCheck() {
364
- $ids = array();
365
- $removeIds = array();
366
-
367
- $idsPrio = $this->prioQ->get();
368
- for($i = count($idsPrio) - 1, $cnt = 0; $i>=0 && $cnt < 3; $i--) {
369
- $id = $idsPrio[$i];
370
- if(wp_get_attachment_url($id)) {
371
- $ids[] = $id; //valid ID
372
- } else {
373
- $removeIds[] = $id;//absent, to remove
374
- }
375
- }
376
- foreach($removeIds as $rId){
377
- self::log("HIP: Unfound ID $rID Remove from Priority Queue: ".json_encode(get_option($this->prioQ->get())));
378
- $this->prioQ->remove($rId);
379
- }
380
- return $ids;
381
- }
382
-
383
- public function handleImageProcessing($ID = null) {
384
- //die("bau");
385
- //0: check key
386
- if( $this->_verifiedKey == false) {
387
- echo "Missing API Key";
388
- die();
389
- }
390
-
391
- self::log("HIP: 0 Priority Queue: ".json_encode($this->prioQ->get()));
392
-
393
- //1: get 3 ids to process. Take them with priority from the queue
394
- $ids = $this->getFromPrioAndCheck();
395
- if(count($ids) < 3 ) { //take from bulk if bulk processing active
396
- $bulkStatus = $this->prioQ->bulkRunning();
397
- if($bulkStatus =='running') {
398
- $res = $this->getBulkItemsFromDb();
399
- $bulkItems = $res['ids'];
400
- if($bulkItems){
401
- $ids = array_merge ($ids, $bulkItems);
402
- }
403
- }
404
- }
405
- if ($ids === false || count( $ids ) == 0 ){
406
- $bulkEverRan = $this->prioQ->stopBulk();
407
- $avg = self::getAverageCompression();
408
- $fileCount = get_option('wp-short-pixel-fileCount');
409
- die(json_encode(array("Status" => self::BULK_EMPTY_QUEUE,
410
- "Message" => 'Empty queue ' . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
411
- "BulkStatus" => ($this->prioQ->bulkRunning()
412
- ? "1" : ($this->prioQ->bulkPaused() ? "2" : "0")),
413
- "AverageCompression" => $avg,
414
- "FileCount" => $fileCount,
415
- "BulkPercent" => $this->prioQ->getBulkPercent())));
416
- }
417
-
418
- self::log("HIP: 1 Prio Queue: ".json_encode($this->prioQ->get()));
419
-
420
- //2: Send up to 3 files to the server for processing
421
- for($i = 0; $i < min(3, count($ids)); $i++) {
422
- $ID = $ids[$i];
423
- $URLsAndPATHs = $this->sendToProcessing($ID);
424
- if($i == 0) { //save for later use
425
- $firstUrlAndPaths = $URLsAndPATHs;
426
- }
427
- }
428
-
429
- self::log("HIP: 2 Prio Queue: ".json_encode($this->prioQ->get()));
430
-
431
- //3: Retrieve the file for the first element of the list
432
- $ID = $ids[0];
433
- $result = $this->_apiInterface->processImage($firstUrlAndPaths['URLs'], $firstUrlAndPaths['PATHs'], $ID);
434
- $result["ImageID"] = $ID;
435
-
436
- self::log("HIP: 3 Prio Queue: ".json_encode($this->prioQ->get()));
437
-
438
- //4: update counters and priority list
439
- if( $result["Status"] == ShortPixelAPI::STATUS_SUCCESS) {
440
- self::log("HIP: Image ID $ID optimized successfully: ".json_encode($result));
441
- $prio = $this->prioQ->remove($ID);
442
- if(!$prio && $ID <= $this->prioQ->getStartBulkId()) {
443
- $this->prioQ->setStartBulkId($ID - 1);
444
- $this->prioQ->logBulkProgress();
445
-
446
- $deltaBulkPercent = $this->prioQ->getDeltaBulkPercent();
447
- $msg = $this->bulkProgressMessage($deltaBulkPercent, $this->prioQ->getTimeRemaining());
448
- $result["BulkPercent"] = $this->prioQ->getBulkPercent();;
449
- $result["BulkMsg"] = $msg;
450
-
451
- $thumb = $bkThumb = "";
452
- $percent = 0;
453
- $meta = wp_get_attachment_metadata($ID);
454
- if(isset($meta["ShortPixelImprovement"]) && isset($meta["file"])){
455
- $percent = $meta["ShortPixelImprovement"];
456
-
457
- $filePath = explode("/", $meta["file"]);
458
- $uploadsUrl = content_url() . "/uploads/";
459
- $urlPath = implode("/", array_slice($filePath, 0, count($filePath) - 1));
460
- $thumb = (isset($meta["sizes"]["medium"]) ? $meta["sizes"]["medium"]["file"] : (isset($meta["sizes"]["thumbnail"]) ? $meta["sizes"]["thumbnail"]["file"]: ""));
461
- if(strlen($thumb) && get_option('wp-short-backup_images')) {
462
- $bkThumb = $uploadsUrl . SP_BACKUP . "/" . $urlPath . "/" . $thumb;
463
- }
464
- if(strlen($thumb)) {
465
- $thumb = $uploadsUrl . $urlPath . "/" . $thumb;
466
- }
467
- $result["Thumb"] = $thumb;
468
- $result["BkThumb"] = $bkThumb;
469
- }
470
- }
471
- }
472
- elseif ($result["Status"] == ShortPixelAPI::STATUS_SKIP
473
- || $result["Status"] == ShortPixelAPI::STATUS_FAIL) {
474
- $prio = $this->prioQ->remove($ID);
475
- if(!$prio && $ID <= $this->prioQ->getStartBulkId()) {
476
- $this->prioQ->setStartBulkId($ID - 1);
477
- }
478
- }
479
- die(json_encode($result));
480
- }
481
-
482
- private function sendToProcessing($ID) {
483
- $URLsAndPATHs = $this->getURLsAndPATHs($ID);
484
- $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $ID);//send a request, do NOT wait for response
485
- $meta = wp_get_attachment_metadata($ID);
486
- $meta['ShortPixel']['WaitingProcessing'] = true;
487
- wp_update_attachment_metadata($ID, $meta);
488
- return $URLsAndPATHs;
489
- }
490
-
491
- public function handleManualOptimization() {
492
- $imageId = intval($_GET['image_id']);
493
-
494
- if(self::isProcessable($imageId)) {
495
- $this->prioQ->push($imageId);
496
- $this->sendToProcessing($imageId);
497
- $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "message" => "");
498
- } else {
499
- die(var_dump($pathParts));
500
- }
501
- die(json_encode($ret));
502
-
503
- $urlList[] = wp_get_attachment_url($attachmentID);
504
- $filePath[] = get_attached_file($attachmentID);
505
- $meta = wp_get_attachment_metadata($attachmentID);
506
-
507
- $processThumbnails = get_option('wp-short-process_thumbnails');
508
-
509
- //process all files (including thumbs)
510
- if($processThumbnails && !empty($meta['sizes'])) {
511
- //we generate an array with the URLs that need to be handled
512
- $SubDir = $this->_apiInterface->returnSubDir($meta['file']);
513
- foreach($meta['sizes'] as $thumbnailInfo)
514
- {
515
- $urlList[]= str_replace(ShortPixelAPI::MB_basename($filePath[0]), $thumbnailInfo['file'], $urlList[0]);
516
- $filePath[] = str_replace(ShortPixelAPI::MB_basename($filePath[0]), $thumbnailInfo['file'], $filePath[0]);
517
- }
518
- }
519
-
520
- $result = $this->_apiInterface->processImage($urlList, $filePath, $attachmentID);//request to process all the images
521
-
522
- if ( !is_array($result) )//there was an error, we save it in ShortPixelImprovement data
523
- $this->handleError($attachmentID, $result);
524
-
525
- // store the referring webpage location
526
- $sendback = wp_get_referer();
527
- // sanitize the referring webpage location
528
- $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
529
- // send the user back where they came from
530
- wp_redirect($sendback);
531
- // we are done,
532
- }
533
-
534
- //save error in file's meta data
535
- public function handleError($ID, $result)
536
- {
537
- $meta = wp_get_attachment_metadata($ID);
538
- $meta['ShortPixelImprovement'] = $result;
539
- wp_update_attachment_metadata($ID, $meta);
540
- }
541
-
542
- public function handleRestoreBackup() {
543
- $attachmentID = intval($_GET['attachment_ID']);
544
-
545
- $file = get_attached_file($attachmentID);
546
- $meta = wp_get_attachment_metadata($attachmentID);
547
- $pathInfo = pathinfo($file);
548
-
549
- $fileExtension = strtolower(substr($file,strrpos($file,".")+1));
550
- $SubDir = $this->_apiInterface->returnSubDir($file);
551
-
552
- //sometimes the month of original file and backup can differ
553
- if ( !file_exists(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . ShortPixelAPI::MB_basename($file)) )
554
- $SubDir = date("Y") . "/" . date("m") . "/";
555
-
556
- try {
557
- //main file
558
- @rename(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . ShortPixelAPI::MB_basename($file), $file);
559
-
560
- //overwriting thumbnails
561
- if( !empty($meta['file']) ) {
562
- foreach($meta["sizes"] as $size => $imageData) {
563
- $source = SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . $imageData['file'];
564
- $destination = $pathInfo['dirname'] . DIRECTORY_SEPARATOR . $imageData['file'];
565
- @rename($source, $destination);
566
- }
567
- }
568
- unset($meta["ShortPixelImprovement"]);
569
- unset($meta['ShortPixel']['WaitingProcessing']);
570
- wp_update_attachment_metadata($attachmentID, $meta);
571
-
572
- } catch(Exception $e) {
573
- //what to do, what to do?
574
- }
575
- // store the referring webpage location
576
- $sendback = wp_get_referer();
577
- // sanitize the referring webpage location
578
- $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
579
- // send the user back where they came from
580
- wp_redirect($sendback);
581
- // we are done
582
- }
583
-
584
-
585
- public function handleDeleteAttachmentInBackup($ID) {
586
- $file = get_attached_file($ID);
587
- $meta = wp_get_attachment_metadata($ID);
588
-
589
- if(self::isProcessable($ID) != false)
590
- {
591
- $SubDir = $this->_apiInterface->returnSubDir($file);
592
- try {
593
- $SubDir = $this->_apiInterface->returnSubDir($file);
594
-
595
- @unlink(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . ShortPixelAPI::MB_basename($file));
596
-
597
- if ( !empty($meta['file']) )
598
- {
599
- $filesPath = SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir;//base BACKUP path
600
- //remove thumbs thumbnails
601
- if(isset($meta["sizes"])) {
602
- foreach($meta["sizes"] as $size => $imageData) {
603
- @unlink($filesPath . ShortPixelAPI::MB_basename($imageData['file']));//remove thumbs
604
- }
605
- }
606
- }
607
-
608
- } catch(Exception $e) {
609
- //what to do, what to do?
610
- }
611
- }
612
- }
613
-
614
- public function registerSettingsPage() {
615
- add_options_page( 'ShortPixel Settings', 'ShortPixel', 'manage_options', 'wp-shortpixel', array($this, 'renderSettingsMenu'));
616
- }
617
-
618
- function registerAdminPage( ) {
619
- add_media_page( 'ShortPixel Bulk Process', 'Bulk ShortPixel', 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcess' ) );
620
- }
621
-
622
- public function checkQuotaAndAlert() {
623
- $quotaData = $this->getQuotaInformation();
624
- if ( !$quotaData['APIKeyValid']) {
625
- return $quotaData;
626
- }
627
- if($quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] > $quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric']) {
628
- update_option('wp-short-pixel-quota-exceeded','0');
629
- ?><script>var shortPixelQuotaExceeded = 0;</script><?php
630
- }
631
- else {
632
- $this->view->displayQuotaExceededAlert($quotaData);
633
- ?><script>var shortPixelQuotaExceeded = 1;</script><?php
634
- }
635
- return $quotaData;
636
- }
637
-
638
- public function bulkProcess() {
639
- global $wpdb;
640
-
641
- if( $this->_verifiedKey == false ) {//invalid API Key
642
- $this->view->displayApiKeyAlert();
643
- return;
644
- }
645
-
646
- $quotaData = $this->checkQuotaAndAlert();
647
- if(self::getOpt('wp-short-pixel-quota-exceeded', 0) != 0) return;
648
-
649
- if(isset($_POST['bulkProcessPause']))
650
- {//pause an ongoing bulk processing, it might be needed sometimes
651
- $this->prioQ->pauseBulk();
652
- }
653
-
654
- if(isset($_POST["bulkProcess"]))
655
- {
656
- //set the thumbnails option
657
- if ( isset($_POST['thumbnails']) ) {
658
- update_option('wp-short-process_thumbnails', 1);
659
- } else {
660
- update_option('wp-short-process_thumbnails', 0);
661
- }
662
- $this->prioQ->startBulk();
663
- self::log("BULK: Start: " . $this->prioQ->getStartBulkId() . ", stop: " . $this->prioQ->getStopBulkId() . " PrioQ: "
664
- .json_encode($this->prioQ->get()));
665
- }//end bulk process was clicked
666
-
667
- if(isset($_POST["bulkProcessResume"]))
668
- {
669
- $this->prioQ->resumeBulk();
670
- }//resume was clicked
671
-
672
- //figure out all the files that could be processed
673
- $qry = "SELECT count(*) FilesToBeProcessed FROM " . $wpdb->prefix . "postmeta
674
- WHERE meta_key = '_wp_attached_file' ";
675
- $allFiles = $wpdb->get_results($qry);
676
- //figure out the files that are left to be processed
677
- $qry_left = "SELECT count(*) FilesLeftToBeProcessed FROM " . $wpdb->prefix . "postmeta
678
- WHERE meta_key = '_wp_attached_file' AND post_id <= " . $this->prioQ->getStartBulkId();
679
- $filesLeft = $wpdb->get_results($qry_left);
680
-
681
- if ( $filesLeft[0]->FilesLeftToBeProcessed > 0 && $this->prioQ->bulkRunning() )//bulk processing was started and is still running
682
- {
683
- $msg = $this->bulkProgressMessage($this->prioQ->getDeltaBulkPercent(), $this->prioQ->getTimeRemaining());
684
- $this->view->displayBulkProcessingRunning($this->prioQ->getBulkPercent(), $msg);
685
-
686
- // $imagesLeft = $filesLeft[0]->FilesLeftToBeProcessed;
687
- // $totalImages = $allFiles[0]->FilesToBeProcessed;
688
- // echo "<p>{$imagesLeft} out of {$totalImages} images left to process.</p>";
689
- // echo ' <a class="button button-secondary" href="' . get_admin_url() . 'upload.php">Media Library</a> ';
690
- } else
691
- {
692
- if($this->prioQ->bulkRan() && !$this->prioQ->bulkPaused()) {
693
- $this->prioQ->markBulkComplete();
694
- }
695
-
696
- //image count
697
- $imageCount = $this->countAllProcessableFiles();
698
- $imgProcessedCount = $this->countAllProcessedFiles();
699
- $imageOnlyThumbs = $imageCount['totalFiles'] - $imageCount['mainFiles'];
700
- $thumbsProcessedCount = self::getOpt( 'wp-short-pixel-thumbnail-count', 0);//amount of optimized thumbnails
701
- $under5PercentCount = self::getOpt( 'wp-short-pixel-files-under-5-percent', 0);//amount of under 5% optimized imgs.
702
-
703
- //average compression
704
- $averageCompression = self::getAverageCompression();
705
- // $this->view->displayBulkProcessingForm($imageCount, $imageOnlyThumbs, $this->prioQ->bulkRan(), $averageCompression,
706
- $this->view->displayBulkProcessingForm($imageCount, $imgProcessedCount, $thumbsProcessedCount, $under5PercentCount,
707
- $this->prioQ->bulkRan(), $averageCompression, get_option('wp-short-pixel-fileCount'),
708
- self::formatBytes(get_option('wp-short-pixel-savedSpace')), $this->prioQ->bulkPaused() ? $this->prioQ->getBulkPercent() : false);
709
- }
710
- }
711
- //end bulk processing
712
-
713
- public function bulkProgressMessage($percent, $minutes) {
714
- $timeEst = "";
715
- self::log("bulkProgressMessage(): percent: " . $percent);
716
- if($percent < 1 || $minutes == 0) {
717
- $timeEst = "";
718
- } elseif( $minutes > 2880) {
719
- $timeEst = "~ " . round($minutes / 1440) . " days left";
720
- } elseif ($minutes > 240) {
721
- $timeEst = "~ " . round($minutes / 60) . " hours left";
722
- } elseif ($minutes > 60) {
723
- $timeEst = "~ " . round($minutes / 60) . " hours " . round($minutes%60/10) * 10 . " min. left";
724
- } elseif ($minutes > 20) {
725
- $timeEst = "~ " . round($minutes / 10) * 10 . " minutes left";
726
- } else {
727
- $timeEst = "~ " . $minutes . " minutes left";
728
- }
729
- return $timeEst;
730
- }
731
-
732
- public function emptyBackup(){
733
- if(file_exists(SP_BACKUP_FOLDER)) {
734
-
735
- //extract all images from DB in an array. of course
736
- $attachments = null;
737
- $attachments = get_posts( array(
738
- 'numberposts' => -1,
739
- 'post_type' => 'attachment',
740
- 'post_mime_type' => 'image'
741
- ));
742
-
743
-
744
- //parse all images and set the right flag that the image has no backup
745
- foreach($attachments as $attachment)
746
- {
747
- if(self::isProcessable(get_attached_file($attachment->ID)) == false) continue;
748
-
749
- $meta = wp_get_attachment_metadata($attachment->ID);
750
- $meta['ShortPixel']['NoBackup'] = true;
751
- wp_update_attachment_metadata($attachment->ID, $meta);
752
- }
753
-
754
- //delete the actual files on disk
755
- $this->deleteDir(SP_BACKUP_FOLDER);//call a recursive function to empty files and sub-dirs in backup dir
756
- }
757
- }
758
-
759
- public function renderSettingsMenu() {
760
- if ( !current_user_can( 'manage_options' ) ) {
761
- wp_die('You do not have sufficient permissions to access this page.');
762
- }
763
-
764
- $quotaData = $this->checkQuotaAndAlert();
765
-
766
- echo '<h1>ShortPixel Plugin Settings</h1>';
767
- echo '<p>
768
- <a href="https://shortpixel.com" target="_blank">ShortPixel.com</a> |
769
- <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/installation/" target="_blank">Installation </a> |
770
- <a href="https://shortpixel.com/contact" target="_blank">Support </a>
771
- </p>';
772
- 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="' . get_admin_url() . 'upload.php?page=wp-short-pixel-bulk">Bulk Optimization Tool</a>.</p>';
773
-
774
- $noticeHTML = "<br/><div style=\"background-color: #fff; border-left: 4px solid %s; box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1); padding: 1px 12px;\"><p>%s</p></div>";
775
-
776
- //by default we try to fetch the API Key from wp-config.php (if defined)
777
- if ( !isset($_POST['submit']) && !get_option('wp-short-pixel-verifiedKey') && defined("SHORTPIXEL_API_KEY") && strlen(SHORTPIXEL_API_KEY) == 20 )
778
- {
779
- $_POST['validate'] = "validate";
780
- $_POST['key'] = SHORTPIXEL_API_KEY;
781
- }
782
-
783
- if(isset($_POST['submit']) || isset($_POST['validate'])) {
784
-
785
- //handle API Key - common for submit and validate
786
- $_POST['key'] = trim(str_replace("*","",$_POST['key']));
787
-
788
- if ( strlen($_POST['key']) <> 20 )
789
- {
790
- $KeyLength = strlen($_POST['key']);
791
-
792
- printf($noticeHTML, '#ff0000', "The key you provided has " . $KeyLength . " characters. The API key should have 20 characters, letters and numbers only.<BR> <b>Please check that the API key is the same as the one you received in your confirmation email.</b><BR>
793
- If this problem persists, please contact us at <a href='mailto:support@shortpixel.com?Subject=API Key issues' target='_top'>support@shortpixel.com</a> or <a href='https://shortpixel.com/contact' target='_blank'>here</a>.");
794
- }
795
- else
796
- {
797
- $validityData = $this->getQuotaInformation($_POST['key'], true);
798
-
799
- $this->_apiKey = $_POST['key'];
800
- $this->_apiInterface->setApiKey($this->_apiKey);
801
- update_option('wp-short-pixel-apiKey', $_POST['key']);
802
- if($validityData['APIKeyValid']) {
803
- if(isset($_POST['validate'])) {
804
- //display notification
805
- if(in_array($_SERVER["SERVER_ADDR"], array("127.0.0.1","::1"))) {
806
- printf($noticeHTML, '#FFC800', "API Key is valid but your server seems to have a local address.
807
- Please make sure that your server is accessible from the Internet before using the API or otherwise we won't be able to optimize them.");
808
- } else {
809
-
810
- if ( function_exists("is_multisite") && is_multisite() )
811
- printf($noticeHTML, '#7ad03a', "API Key valid! <br>You seem to be running a multisite, please note that API Key can also be configured in wp-config.php like this:<BR> <b>define('SHORTPIXEL_API_KEY', '".$this->_apiKey."');</b>");
812
- else
813
- printf($noticeHTML, '#7ad03a', 'API Key valid!');
814
- }
815
- }
816
- update_option('wp-short-pixel-verifiedKey', true);
817
- $this->_verifiedKey = true;
818
- //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
819
- if ( !file_exists(SP_BACKUP_FOLDER) && !@mkdir(SP_BACKUP_FOLDER, 0777, true) )
820
- printf($noticeHTML, '#ff0000', "There is something preventing us to create a new folder for backing up your original files.<BR>
821
- Please make sure that folder <b>" .
822
- WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "uploads</b> has the necessary write and read rights." );
823
- } else {
824
- if(isset($_POST['validate'])) {
825
- //display notification
826
- printf($noticeHTML, '#ff0000', $validityData["Message"]);
827
- }
828
- update_option('wp-short-pixel-verifiedKey', false);
829
- $this->_verifiedKey = false;
830
- }
831
- }
832
-
833
-
834
- //if save button - we process the rest of the form elements
835
- if(isset($_POST['submit'])) {
836
- update_option('wp-short-pixel-compression', $_POST['compressionType']);
837
- $this->_compressionType = $_POST['compressionType'];
838
- $this->_apiInterface->setCompressionType($this->_compressionType);
839
- if(isset($_POST['thumbnails'])) { $this->_processThumbnails = 1; } else { $this->_processThumbnails = 0; }
840
- if(isset($_POST['backupImages'])) { $this->_backupImages = 1; } else { $this->_backupImages = 0; }
841
- if(isset($_POST['cmyk2rgb'])) { $this->_CMYKtoRGBconversion = 1; } else { $this->_CMYKtoRGBconversion = 0; }
842
- update_option('wp-short-process_thumbnails', $this->_processThumbnails);
843
- update_option('wp-short-backup_images', $this->_backupImages);
844
- update_option('wp-short-pixel_cmyk2rgb', $this->_CMYKtoRGBconversion);
845
- }
846
- }
847
-
848
-
849
- //empty backup
850
- if(isset($_POST['emptyBackup'])) {
851
- $this->emptyBackup();
852
- }
853
-
854
- $checked = '';
855
- if($this->_processThumbnails) { $checked = 'checked'; }
856
-
857
- $checkedBackupImages = '';
858
- if($this->_backupImages) { $checkedBackupImages = 'checked'; }
859
-
860
- $cmyk2rgb = '';
861
- if($this->_CMYKtoRGBconversion) { $cmyk2rgb = 'checked'; }
862
-
863
-
864
- $formHTML = <<< HTML
865
- <form name='wp_shortpixel_options' action='' method='post' id='wp_shortpixel_options'>
866
- <table class="form-table">
867
- <tbody><tr>
868
- <th scope="row"><label for="key">API Key:</label></th>
869
- <td><input name="key" type="text" id="key" value="{$this->_apiKey}" class="regular-text">
870
- <input type="submit" name="validate" id="validate" class="button button-primary" title="Validate the provided API key" value="Validate">
871
- </td>
872
- </tr>
873
- HTML;
874
-
875
- if(!$this->_verifiedKey) {
876
-
877
- //if invalid key we display the link to the API Key
878
- $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>';
879
- $formHTML .= '</form>';
880
- } else {
881
- //if valid key we display the rest of the options
882
- $formHTML .= <<< HTML
883
- <tr><th scope="row">
884
- <label for="compressionType">Compression type:</label>
885
- </th><td>
886
- HTML;
887
-
888
- if($this->_compressionType == 1) {
889
- $formHTML .= '<input type="radio" name="compressionType" value="1" checked>Lossy</br></br>';
890
- $formHTML .= '<input type="radio" name="compressionType" value="0" >Lossless';
891
- } else {
892
- $formHTML .= '<input type="radio" name="compressionType" value="1">Lossy</br></br>';
893
- $formHTML .= '<input type="radio" name="compressionType" value="0" checked>Lossless';
894
- }
895
-
896
- $formHTML .= <<<HTML
897
- </td>
898
- </tr>
899
- </tbody></table>
900
- <p style="color: #818181;">
901
- <b>Lossy compression: </b>lossy has a better compression rate than lossless compression.</br>The resulting image
902
- is not 100% identical with the original. Works well for photos taken with your camera.</br></br>
903
- <b>Lossless compression: </b> the shrunk image will be identical with the original and smaller in size.</br>Use this
904
- when you do not want to lose any of the original image's details. Works best for technical drawings,
905
- clip art and comics.
906
- </p>
907
- <table class="form-table">
908
- <tbody><tr>
909
- <th scope="row"><label for="thumbnails">Image thumbnails:</label></th>
910
- <td><input name="thumbnails" type="checkbox" id="thumbnails" {$checked}> Apply compression also to image thumbnails.</td>
911
- </tr>
912
- <tr>
913
- <th scope="row"><label for="backupImages">Image backup</label></th>
914
- <td>
915
- <input name="backupImages" type="checkbox" id="backupImages" {$checkedBackupImages}> Save and keep a backup of your original images in a separate folder.
916
- </td>
917
- </tr>
918
- <tr>
919
- <th scope="row"><label for="backupImages">CMYK to RGB conversion</label></th>
920
- <td>
921
- <input name="cmyk2rgb" type="checkbox" id="cmyk2rgb" {$cmyk2rgb}>Adjust your images for computer and mobile screen display.
922
- </td>
923
- </tr>
924
- </tr>
925
- </tbody></table>
926
- <p class="submit">
927
- <input type="submit" name="submit" id="submit" class="button button-primary" title="Save Changes" value="Save Changes">
928
- <a class="button button-primary" title="Process all the images in your Media Library" href="upload.php?page=wp-short-pixel-bulk">Bulk Process</a>
929
- </p>
930
- </form>
931
- <script>
932
- var rad = document.wp_shortpixel_options.compressionType;
933
- var prev = null;
934
- for(var i = 0; i < rad.length; i++) {
935
- rad[i].onclick = function() {
936
-
937
- if(this !== prev) {
938
- prev = this;
939
- }
940
- alert('This type of optimization will apply to new uploaded images. <BR>Images that were already processed will not be re-optimized.');
941
- };
942
- }
943
- </script>
944
- HTML;
945
- }
946
-
947
- echo $formHTML;
948
-
949
- if($this->_verifiedKey) {
950
- $fileCount = number_format(get_option('wp-short-pixel-fileCount'));
951
- $savedSpace = self::formatBytes(get_option('wp-short-pixel-savedSpace'),2);
952
- $averageCompression = self::getAverageCompression();
953
- $savedBandwidth = self::formatBytes(get_option('wp-short-pixel-savedSpace') * 10000,2);
954
- if (is_numeric($quotaData['APICallsQuota'])) {
955
- $quotaData['APICallsQuota'] .= "/month";
956
- }
957
- $backupFolderSize = self::formatBytes(self::folderSize(SP_BACKUP_FOLDER));
958
- $remainingImages = $quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'];
959
- $remainingImages = ( $remainingImages < 0 ) ? 0 : number_format($remainingImages);
960
- $totalCallsMade = number_format($quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric']);
961
-
962
- $statHTML = <<< HTML
963
- <a id="facts"></a>
964
- <h3>Your ShortPixel Stats</h3>
965
- <table class="form-table">
966
- <tbody>
967
- <tr>
968
- <th scope="row"><label for="averagCompression">Average compression of your files:</label></th>
969
- <td>$averageCompression%</td>
970
- </tr>
971
- <tr>
972
- <th scope="row"><label for="savedSpace">Saved disk space by ShortPixel</label></th>
973
- <td>$savedSpace</td>
974
- </tr>
975
- <tr>
976
- <th scope="row"><label for="savedBandwidth">Bandwith* saved with ShortPixel:</label></th>
977
- <td>$savedBandwidth</td>
978
- </tr>
979
- </tbody></table>
980
-
981
- <p style="padding-top: 0px; color: #818181;" >* Saved bandwidth is calculated at 10,000 impressions/image</p>
982
-
983
- <h3>Your ShortPixel Plan</h3>
984
- <table class="form-table">
985
- <tbody>
986
- <tr>
987
- <th scope="row" bgcolor="#ffffff"><label for="apiQuota">Your ShortPixel plan</label></th>
988
- <td bgcolor="#ffffff">{$quotaData['APICallsQuota']}/month ( <a href="https://shortpixel.com/login/{$this->_apiKey}" target="_blank">Need More? See the options available</a> )
989
- </tr>
990
- <tr>
991
- <th scope="row"><label for="usedQUota">One time credits:</label></th>
992
- <td>{$quotaData['APICallsQuotaOneTimeNumeric']}</td>
993
- </tr>
994
- <tr>
995
- <th scope="row"><label for="usedQUota">Number of images processed this month:</label></th>
996
- <td>{$totalCallsMade} (<a href="https://api.shortpixel.com/v2/report.php?key={$this->_apiKey}" target="_blank">see report</a>)</td>
997
- </tr>
998
- <tr>
999
- <th scope="row"><label for="remainingImages">Remaining** images in your plan: </label></th>
1000
- <td>{$remainingImages} images</td>
1001
- </tr>
1002
- </tbody></table>
1003
-
1004
- <p style="padding-top: 0px; color: #818181;" >** Increase your image quota by <a href="https://shortpixel.com/login/{$this->_apiKey}" target="_blank">upgrading</a> your ShortPixel plan.</p>
1005
-
1006
- <table class="form-table">
1007
- <tbody>
1008
- <tr>
1009
- <th scope="row"><label for="totalFiles">Total number of processed files:</label></th>
1010
- <td>{$fileCount}</td>
1011
- </tr>
1012
-
1013
-
1014
-
1015
- HTML;
1016
- if($this->_backupImages) {
1017
- $statHTML .= <<< HTML
1018
- <form action="" method="POST">
1019
- <tr>
1020
- <th scope="row"><label for="sizeBackup">Original images are stored in a backup folder. Your backup folder size is now:</label></th>
1021
- <td>
1022
- {$backupFolderSize}
1023
- <input type="submit" style="margin-left: 15px; vertical-align: middle;" class="button button-secondary" name="emptyBackup" value="Empty backups"/>
1024
- </td>
1025
- </tr>
1026
- </form>
1027
- HTML;
1028
- }
1029
-
1030
- $statHTML .= <<< HTML
1031
- </tbody></table>
1032
- HTML;
1033
-
1034
- echo $statHTML;
1035
-
1036
-
1037
- }
1038
-
1039
- }
1040
-
1041
- public function getAverageCompression(){
1042
- return get_option('wp-short-pixel-total-optimized') > 0
1043
- ? round(( 1 - ( get_option('wp-short-pixel-total-optimized') / get_option('wp-short-pixel-total-original') ) ) * 100, 2)
1044
- : 0;
1045
- }
1046
-
1047
- public function getQuotaInformation($apiKey = null, $appendUserAgent = false) {
1048
-
1049
- if(is_null($apiKey)) { $apiKey = $this->_apiKey; }
1050
-
1051
- $requestURL = 'https://api.shortpixel.com/v2/api-status.php';
1052
- $args = array('timeout'=> SP_MAX_TIMEOUT,
1053
- 'sslverify' => false,
1054
- 'body' => array('key' => $apiKey)
1055
- );
1056
-
1057
- if($appendUserAgent) {
1058
- $args['body']['useragent'] = "Agent" . urlencode($_SERVER['HTTP_USER_AGENT']);
1059
- }
1060
- $response = wp_remote_post($requestURL, $args);
1061
-
1062
- if(is_wp_error( $response )) //some hosting providers won't allow https:// POST connections so we try http:// as well
1063
- $response = wp_remote_post(str_replace('https://', 'http://', $requestURL), $args);
1064
-
1065
- if(is_wp_error( $response ))
1066
- $response = wp_remote_get(str_replace('https://', 'http://', $requestURL), $args);
1067
-
1068
- $defaultData = array(
1069
- "APIKeyValid" => false,
1070
- "Message" => 'API Key could not be validated due to a connectivity error.<BR>Your firewall may be blocking us. Please contact your hosting provider and ask them to allow connections from your site to IP 176.9.106.46.<BR> If you still cannot validate your API Key after this, please <a href="https://shortpixel.com/contact" target="_blank">contact us</a> and we will try to help. ',
1071
- "APICallsMade" => 'Information unavailable. Please check your API key.',
1072
- "APICallsQuota" => 'Information unavailable. Please check your API key.');
1073
-
1074
- if(is_object($response) && get_class($response) == 'WP_Error') {
1075
-
1076
- $urlElements = parse_url($requestURL);
1077
- $portConnect = @fsockopen($urlElements['host'],8,$errno,$errstr,15);
1078
- if(!$portConnect)
1079
- $defaultData['Message'] .= "<BR>Debug info: <i>$errstr</i>";
1080
-
1081
- return $defaultData;
1082
- }
1083
-
1084
- if($response['response']['code'] != 200) {
1085
- return $defaultData;
1086
- }
1087
-
1088
- $data = $response['body'];
1089
- $data = $this->parseJSON($data);
1090
-
1091
- if(empty($data)) { return $defaultData; }
1092
-
1093
- if($data->Status->Code != 2) {
1094
- $defaultData['Message'] = $data->Status->Message;
1095
- return $defaultData;
1096
- }
1097
-
1098
- if ( ( $data->APICallsMade + $data->APICallsMadeOneTime ) < ( $data->APICallsQuota + $data->APICallsQuotaOneTime ) ) //reset quota exceeded flag -> user is allowed to process more images.
1099
- update_option('wp-short-pixel-quota-exceeded',0);
1100
- else
1101
- update_option('wp-short-pixel-quota-exceeded',1);//activate quota limiting
1102
-
1103
- return array(
1104
- "APIKeyValid" => true,
1105
- "APICallsMade" => number_format($data->APICallsMade) . ' images',
1106
- "APICallsQuota" => number_format($data->APICallsQuota) . ' images',
1107
- "APICallsMadeOneTime" => number_format($data->APICallsMadeOneTime) . ' images',
1108
- "APICallsQuotaOneTime" => number_format($data->APICallsQuotaOneTime) . ' images',
1109
- "APICallsMadeNumeric" => $data->APICallsMade,
1110
- "APICallsQuotaNumeric" => $data->APICallsQuota,
1111
- "APICallsMadeOneTimeNumeric" => $data->APICallsMadeOneTime,
1112
- "APICallsQuotaOneTimeNumeric" => $data->APICallsQuotaOneTime
1113
- );
1114
-
1115
-
1116
- }
1117
-
1118
- public function generateCustomColumn( $column_name, $id ) {
1119
- if( 'wp-shortPixel' == $column_name ) {
1120
- $data = wp_get_attachment_metadata($id);
1121
- $file = get_attached_file($id);
1122
- $fileExtension = strtolower(substr($file,strrpos($file,".")+1));
1123
-
1124
- print "<div id='sp-msg-{$id}'>";
1125
-
1126
- if ( empty($data) )
1127
- {
1128
- if ( $fileExtension <> "pdf" )
1129
- {
1130
- if(!$this->_verifiedKey)
1131
- print 'Invalid API Key. <a href="options-general.php?page=wp-shortpixel">Check your Settings</a>';
1132
- else
1133
- print 'Optimization N/A';
1134
- }
1135
- else
1136
- {
1137
- if ( get_option('wp-short-pixel-quota-exceeded') )
1138
- {
1139
- print QUOTA_EXCEEDED;
1140
- return;
1141
- }
1142
- else
1143
- {
1144
- print 'PDF not processed';
1145
- print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1146
- return;
1147
- }
1148
- }
1149
- }
1150
- elseif ( isset( $data['ShortPixelImprovement'] ) )
1151
- {
1152
- if(isset($meta['ShortPixel']['BulkProcessing']))
1153
- {
1154
- if ( get_option('wp-short-pixel-quota-exceeded') )
1155
- {
1156
- print QUOTA_EXCEEDED;
1157
- }
1158
- else
1159
- {
1160
- print 'Waiting for bulk processing';
1161
- print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1162
- }
1163
- }
1164
- elseif( is_numeric($data['ShortPixelImprovement']) && !isset($data['ShortPixel']['NoBackup']) ) {
1165
-
1166
- if ( $data['ShortPixelImprovement'] < 5 )
1167
- {
1168
- print $data['ShortPixelImprovement'] . '%';
1169
- print " optimized<BR> Bonus processing";
1170
-
1171
- }
1172
- else
1173
- {
1174
- print 'Reduced by ';
1175
- print $data['ShortPixelImprovement'] . '%';
1176
- }
1177
- if ( get_option('wp-short-backup_images') ) //display restore backup option only when backup is active
1178
- print " | <a href=\"admin.php?action=shortpixel_restore_backup&amp;attachment_ID={$id}\">Restore backup</a>";
1179
- }
1180
- elseif ( is_numeric($data['ShortPixelImprovement']) )
1181
- {
1182
- if ( $data['ShortPixelImprovement'] < 5 )
1183
- {
1184
- print $data['ShortPixelImprovement'] . '%';
1185
- print " optimized<BR> Bonus processing";
1186
-
1187
- }
1188
- else
1189
- {
1190
- print 'Reduced by ';
1191
- print $data['ShortPixelImprovement'] . '%';
1192
- }
1193
- }
1194
- elseif ( $data['ShortPixelImprovement'] <> "Optimization N/A" )
1195
- {
1196
- if ( trim(strip_tags($data['ShortPixelImprovement'])) == "Quota exceeded" )
1197
- {
1198
- print QUOTA_EXCEEDED;
1199
- if ( !get_option('wp-short-pixel-quota-exceeded') )
1200
- print " | <a href=\"javascript:manualOptimization({$id})\">Try again</a>";
1201
- }
1202
- elseif ( trim(strip_tags($data['ShortPixelImprovement'])) == "Cannot write optimized file" )
1203
- {
1204
- print $data['ShortPixelImprovement'];
1205
- print " - <a href='https://shortpixel.com/faq#cannot-write-optimized-file' target='_blank'>Why?</a>";
1206
- }
1207
- else
1208
- {
1209
- print $data['ShortPixelImprovement'];
1210
- print " | <a href=\"javascript:manualOptimization({$id})\">Try again</a>";
1211
- }
1212
- }
1213
- else
1214
- {
1215
- print "Optimization N/A";
1216
- }
1217
- } elseif(isset($data['ShortPixel']['WaitingProcessing'])) {
1218
- if ( get_option('wp-short-pixel-quota-exceeded') )
1219
- {
1220
- print QUOTA_EXCEEDED;
1221
- }
1222
- else
1223
- {
1224
- print "<img src=\"" . WP_PLUGIN_URL . "/shortpixel-image-optimiser/img/loading.gif\">Image waiting to be processed
1225
- | <a href=\"javascript:manualOptimization({$id})\">Retry</a></div>";
1226
- $this->prioQ->push($id); //should be there but just to make sure
1227
- }
1228
-
1229
- } elseif(isset($data['ShortPixel']['NoFileOnDisk'])) {
1230
- print 'Image does not exist';
1231
-
1232
- } else {
1233
-
1234
- if ( wp_attachment_is_image( $id ) )
1235
- {
1236
- if ( get_option('wp-short-pixel-quota-exceeded') )
1237
- {
1238
- print QUOTA_EXCEEDED;
1239
- }
1240
- else
1241
- {
1242
- print 'Image not processed';
1243
- print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1244
- }
1245
- }
1246
- elseif ( $fileExtension == "pdf" )
1247
- {
1248
- if ( get_option('wp-short-pixel-quota-exceeded') )
1249
- {
1250
- print QUOTA_EXCEEDED;
1251
- }
1252
- else
1253
- {
1254
- print 'PDF not processed';
1255
- print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1256
- }
1257
- }
1258
- }
1259
- print "</div>";
1260
- }
1261
- }
1262
-
1263
- public function columns( $defaults ) {
1264
- $defaults['wp-shortPixel'] = 'ShortPixel Compression';
1265
- return $defaults;
1266
- }
1267
-
1268
- public function generatePluginLinks($links) {
1269
- $in = '<a href="options-general.php?page=wp-shortpixel">Settings</a>';
1270
- array_unshift($links, $in);
1271
- return $links;
1272
- }
1273
-
1274
- public function parseJSON($data) {
1275
- if ( function_exists('json_decode') ) {
1276
- $data = json_decode( $data );
1277
- } else {
1278
- require_once( 'JSON/JSON.php' );
1279
- $json = new Services_JSON( );
1280
- $data = $json->decode( $data );
1281
- }
1282
- return $data;
1283
- }
1284
-
1285
-
1286
- static public function formatBytes($bytes, $precision = 2) {
1287
- $units = array('B', 'KB', 'MB', 'GB', 'TB');
1288
-
1289
- $bytes = max($bytes, 0);
1290
- $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
1291
- $pow = min($pow, count($units) - 1);
1292
-
1293
- $bytes /= pow(1024, $pow);
1294
-
1295
- return round($bytes, $precision) . ' ' . $units[$pow];
1296
- }
1297
-
1298
- static public function isProcessable($ID) {
1299
- $path = get_attached_file($ID);//get the full file PATH
1300
- $pathParts = pathinfo($path);
1301
- if( isset($pathParts['extension']) && in_array(strtolower($pathParts['extension']), array('jpg', 'jpeg', 'gif', 'png', 'pdf'))) {
1302
- return true;
1303
- } else {
1304
- return false;
1305
- }
1306
- }
1307
-
1308
-
1309
- //return an array with URL(s) and PATH(s) for this file
1310
- public function getURLsAndPATHs($ID, $meta = NULL) {
1311
-
1312
- if ( !parse_url(WP_CONTENT_URL, PHP_URL_SCHEME) )
1313
- {//no absolute URLs used -> we implement a hack
1314
- $url = get_site_url() . wp_get_attachment_url($ID);//get the file URL
1315
- }
1316
- else
1317
- $url = wp_get_attachment_url($ID);//get the file URL
1318
-
1319
- $urlList[] = $url;
1320
- $path = get_attached_file($ID);//get the full file PATH
1321
- $filePath[] = $path;
1322
- if ( $meta == NULL ) {
1323
- $meta = wp_get_attachment_metadata($ID);
1324
- }
1325
-
1326
- //it is NOT a PDF file and thumbs are processable
1327
- if ( strtolower(substr($filePath[0],strrpos($filePath[0], ".")+1)) != "pdf"
1328
- && $this->_processThumbnails
1329
- && isset($meta['sizes']) && is_array($meta['sizes']))
1330
- {
1331
- foreach( $meta['sizes'] as $thumbnailInfo )
1332
- {
1333
- $urlList[] = str_replace(ShortPixelAPI::MB_basename($urlList[0]), $thumbnailInfo['file'], $url);
1334
- $filePath[] = str_replace(ShortPixelAPI::MB_basename($filePath[0]), $thumbnailInfo['file'], $path);
1335
- }
1336
- }
1337
- if(!isset($meta['sizes']) || !is_array($meta['sizes'])) {
1338
- self::log("getURLsAndPATHs: no meta sizes for ID $ID : " . json_encode($meta));
1339
- }
1340
- return array("URLs" => $urlList, "PATHs" => $filePath);
1341
- }
1342
-
1343
-
1344
- public static function deleteDir($dirPath) {
1345
- if (substr($dirPath, strlen($dirPath) - 1, 1) !=
1346
- '/') {
1347
- $dirPath .= '/';
1348
- }
1349
- $files = glob($dirPath . '*', GLOB_MARK);
1350
- foreach ($files as $file) {
1351
- if (is_dir($file)) {
1352
- self::deleteDir($file);
1353
- @rmdir($file);//remove empty dir
1354
- } else {
1355
- @unlink($file);//remove file
1356
- }
1357
- }
1358
- }
1359
-
1360
- static public function folderSize($path) {
1361
- $total_size = 0;
1362
- if(file_exists($path)) {
1363
- $files = scandir($path);
1364
- } else {
1365
- return $total_size;
1366
- }
1367
- $cleanPath = rtrim($path, '/'). '/';
1368
- foreach($files as $t) {
1369
- if ($t<>"." && $t<>"..")
1370
- {
1371
- $currentFile = $cleanPath . $t;
1372
- if (is_dir($currentFile)) {
1373
- $size = self::folderSize($currentFile);
1374
- $total_size += $size;
1375
- }
1376
- else {
1377
- $size = filesize($currentFile);
1378
- $total_size += $size;
1379
- }
1380
- }
1381
- }
1382
- return $total_size;
1383
- }
1384
-
1385
- public function getMaxMediaId() {
1386
- global $wpdb;
1387
- $queryMax = "SELECT max(post_id) as QueryID FROM " . $wpdb->prefix . "postmeta";
1388
- $resultQuery = $wpdb->get_results($queryMax);
1389
- return $resultQuery[0]->QueryID;
1390
- }
1391
-
1392
- public function getMinMediaId() {
1393
- global $wpdb;
1394
- $queryMax = "SELECT min(post_id) as QueryID FROM " . $wpdb->prefix . "postmeta";
1395
- $resultQuery = $wpdb->get_results($queryMax);
1396
- return $resultQuery[0]->QueryID;
1397
- }
1398
-
1399
- //count all the processable files in media library (while limiting the results to max 10000)
1400
- public function countAllProcessableFiles($maxId = PHP_INT_MAX, $minId = 0){
1401
- global $wpdb;
1402
-
1403
- $totalFiles = 0;
1404
- $mainFiles = 0;
1405
- $limit = 500;
1406
- $pointer = 0;
1407
-
1408
- //count all the files, main and thumbs
1409
- while ( 1 )
1410
- {
1411
- $filesList= $wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "postmeta
1412
- WHERE ( post_id <= $maxId AND post_id > $minId )
1413
- AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
1414
- LIMIT $pointer,$limit");
1415
- if ( empty($filesList) ) //we parsed all the results
1416
- break;
1417
-
1418
- foreach ( $filesList as $file )
1419
- {
1420
- if ( $file->meta_key == "_wp_attached_file" )
1421
- {//count pdf files only
1422
- $extension = substr($file->meta_value, strrpos($file->meta_value,".") + 1 );
1423
- if ( $extension == "pdf" )
1424
- {
1425
- $totalFiles++;
1426
- $mainFiles++;
1427
- }
1428
- }
1429
- else
1430
- {
1431
- $attachment = unserialize($file->meta_value);
1432
- if ( isset($attachment['sizes']) )
1433
- $totalFiles += count($attachment['sizes']);
1434
-
1435
- if ( isset($attachment['file']) )
1436
- {
1437
- $totalFiles++;
1438
- $mainFiles++;
1439
- }
1440
- }
1441
- }
1442
- unset($filesList);
1443
- $pointer += $limit;
1444
-
1445
- }//end while
1446
-
1447
- return array("totalFiles" => $totalFiles, "mainFiles" => $mainFiles);
1448
- }
1449
-
1450
-
1451
- //count all the processable files in media library (while limiting the results to max 10000)
1452
- public function countAllProcessedFiles($maxId = PHP_INT_MAX, $minId = 0){
1453
- global $wpdb;
1454
-
1455
- $processedMainFiles = $processedTotalFiles = 0;
1456
- $limit = 500;
1457
- $pointer = 0;
1458
-
1459
- //count all the files, main and thumbs
1460
- while ( 1 )
1461
- {
1462
- $filesList= $wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "postmeta
1463
- WHERE ( post_id <= $maxId AND post_id > $minId )
1464
- AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
1465
- LIMIT $pointer,$limit");
1466
- if ( empty($filesList) ) {//we parsed all the results
1467
- break;
1468
- }
1469
- foreach ( $filesList as $file )
1470
- {
1471
- if ( $file->meta_key == "_wp_attached_file" ) {
1472
- continue;
1473
- }
1474
- $attachment = unserialize($file->meta_value);
1475
- if ( isset($attachment['ShortPixelImprovement']) && $attachment['ShortPixelImprovement'] > 0 ) {
1476
- $processedMainFiles++;
1477
- $processedTotalFiles++;
1478
- if ( isset($attachment['sizes']) ) {
1479
- $processedTotalFiles += count($attachment['sizes']);
1480
- }
1481
- }
1482
- }
1483
- unset($filesList);
1484
- $pointer += $limit;
1485
-
1486
- }//end while
1487
-
1488
- return array("totalFiles" => $processedTotalFiles, "mainFiles" => $processedMainFiles);
1489
- }
1490
-
1491
- public function migrateBackupFolder() {
1492
- $oldBackupFolder = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'ShortpixelBackups';
1493
-
1494
- if(!file_exists($oldBackupFolder)) return; //if old backup folder does not exist then there is nothing to do
1495
-
1496
- if(!file_exists(SP_BACKUP_FOLDER)) {
1497
- //we check that the backup folder exists, if not we create it so we can copy into it
1498
- if(!mkdir(SP_BACKUP_FOLDER, 0777, true)) return;
1499
- }
1500
-
1501
- $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
1502
- foreach($scannedDirectory as $file) {
1503
- @rename($oldBackupFolder.DIRECTORY_SEPARATOR.$file, SP_BACKUP_FOLDER.DIRECTORY_SEPARATOR.$file);
1504
- }
1505
- $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
1506
- if(empty($scannedDirectory)) {
1507
- @rmdir($oldBackupFolder);
1508
- }
1509
-
1510
- return;
1511
- }
1512
-
1513
- public function getApiKey() {
1514
- return $this->_apiKey;
1515
- }
1516
-
1517
- public function backupImages() {
1518
- return $this->_backupImages;
1519
- }
1520
-
1521
- public function processThumbnails() {
1522
- return $this->_processThumbnails;
1523
- }
1524
-
1525
- }
1526
-
1527
- $pluginInstance = new WPShortPixel();
1528
- global $pluginInstance;
1529
-
1530
- ?>
1
+ <?php
2
+ /**
3
+ * Plugin Name: ShortPixel Image Optimizer
4
+ * Plugin URI: https://shortpixel.com/
5
+ * Description: ShortPixel optimizes images automatically, while guarding the quality of your images. Check your <a href="options-general.php?page=wp-shortpixel" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
6
+ * Version: 3.0.5
7
+ * Author: ShortPixel
8
+ * Author URI: https://shortpixel.com
9
+ */
10
+
11
+ require_once('shortpixel_api.php');
12
+ require_once('shortpixel_queue.php');
13
+ require_once('shortpixel_view.php');
14
+ require_once( ABSPATH . 'wp-admin/includes/image.php' );
15
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
16
+ if ( !is_plugin_active( 'wpmandrill/wpmandrill.php' ) ) {
17
+ require_once( ABSPATH . 'wp-includes/pluggable.php' );//to avoid conflict with wpmandrill plugin
18
+ }
19
+
20
+ define('SP_RESET_ON_ACTIVATE', false);
21
+
22
+ define('PLUGIN_VERSION', "3.0.5");
23
+ define('SP_MAX_TIMEOUT', 10);
24
+ define('SP_BACKUP', 'ShortpixelBackups');
25
+ define('SP_BACKUP_FOLDER', WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . SP_BACKUP);
26
+ define('MAX_API_RETRIES', 5);
27
+ $MAX_EXECUTION_TIME = ini_get('max_execution_time');
28
+ if ( is_numeric($MAX_EXECUTION_TIME) )
29
+ define('MAX_EXECUTION_TIME', $MAX_EXECUTION_TIME - 5 ); //in seconds
30
+ else
31
+ define('MAX_EXECUTION_TIME', 25 );
32
+ define("SP_MAX_RESULTS_QUERY", 6);
33
+
34
+ class WPShortPixel {
35
+
36
+ const BULK_EMPTY_QUEUE = 0;
37
+
38
+ private $_apiKey = '';
39
+ private $_compressionType = 1;
40
+ private $_processThumbnails = 1;
41
+ private $_CMYKtoRGBconversion = 1;
42
+ private $_backupImages = 1;
43
+ private $_verifiedKey = false;
44
+
45
+ private $_apiInterface = null;
46
+ private $prioQ = null;
47
+ private $view = null;
48
+
49
+ //handling older
50
+ public function WPShortPixel() {
51
+ $this->__construct();
52
+ }
53
+
54
+ public function __construct() {
55
+ session_start();
56
+
57
+ $this->populateOptions();
58
+
59
+ $this->_apiInterface = new ShortPixelAPI($this->_apiKey, $this->_compressionType, $this->_CMYKtoRGBconversion);
60
+ $this->prioQ = new ShortPixelQueue($this);
61
+ $this->view = new ShortPixelView($this);
62
+
63
+ define('QUOTA_EXCEEDED', "Quota Exceeded. <a href='https://shortpixel.com/login/".$this->_apiKey."' target='_blank'>Extend Quota</a>");
64
+
65
+ $this->setDefaultViewModeList();//set default mode as list. only @ first run
66
+
67
+ //add hook for image upload processing
68
+ add_filter( 'wp_generate_attachment_metadata', array( &$this, 'handleImageUpload' ), 10, 2 );
69
+ add_filter( 'manage_media_columns', array( &$this, 'columns' ) );//add media library column header
70
+ add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array(&$this, 'generatePluginLinks'));//for plugin settings page
71
+
72
+ //add_action( 'admin_footer', array(&$this, 'handleImageProcessing'));
73
+ add_action( 'manage_media_custom_column', array( &$this, 'generateCustomColumn' ), 10, 2 );//generate the media library column
74
+
75
+ //add settings page
76
+ add_action( 'admin_menu', array( &$this, 'registerSettingsPage' ) );//display SP in Settings menu
77
+ add_action( 'admin_menu', array( &$this, 'registerAdminPage' ) );
78
+ add_action( 'delete_attachment', array( &$this, 'handleDeleteAttachmentInBackup' ) );
79
+ add_action( 'load-upload.php', array( &$this, 'handleCustomBulk'));
80
+
81
+ //when plugin is activated run this
82
+ register_activation_hook( __FILE__, array( &$this, 'shortPixelActivatePlugin' ) );
83
+ register_deactivation_hook( __FILE__, array( &$this, 'shortPixelDeactivatePlugin' ) );
84
+
85
+ //automatic optimization
86
+ add_action( 'wp_ajax_shortpixel_image_processing', array( &$this, 'handleImageProcessing') );
87
+ //manual optimization
88
+ add_action( 'wp_ajax_shortpixel_manual_optimization', array(&$this, 'handleManualOptimization'));
89
+ //backup restore
90
+ add_action('admin_action_shortpixel_restore_backup', array(&$this, 'handleRestoreBackup'));
91
+
92
+ //This adds the constants used in PHP to be available also in JS
93
+ add_action( 'admin_footer', array( &$this, 'shortPixelJS') );
94
+
95
+
96
+ //example toolbar by fai, to be configured
97
+ add_action( 'admin_bar_menu', array( &$this, 'toolbar_shortpixel_processing'), 999 );
98
+
99
+ $this->migrateBackupFolder();
100
+ }
101
+
102
+ public function populateOptions() {
103
+
104
+ $this->_apiKey = self::getOpt('wp-short-pixel-apiKey', '');
105
+ $this->_verifiedKey = self::getOpt('wp-short-pixel-verifiedKey', $this->_verifiedKey);
106
+ $this->_compressionType = self::getOpt('wp-short-pixel-compression', $this->_compressionType);
107
+ $this->_processThumbnails = self::getOpt('wp-short-process_thumbnails', $this->_processThumbnails);
108
+ $this->_CMYKtoRGBconversion = self::getOpt('wp-short-pixel_cmyk2rgb', $this->_CMYKtoRGBconversion);
109
+ $this->_backupImages = self::getOpt('wp-short-backup_images', $this->_backupImages);
110
+ // the following practically set defaults for options if they're not set
111
+ self::getOpt( 'wp-short-pixel-fileCount', 0);
112
+ self::getOpt( 'wp-short-pixel-thumbnail-count', 0);//amount of optimized thumbnails
113
+ self::getOpt( 'wp-short-pixel-files-under-5-percent', 0);//amount of optimized thumbnails
114
+ self::getOpt( 'wp-short-pixel-savedSpace', 0);
115
+ self::getOpt( 'wp-short-pixel-api-retries', 0);//sometimes we need to retry processing/downloading a file multiple times
116
+ self::getOpt( 'wp-short-pixel-quota-exceeded', 0);
117
+ self::getOpt( 'wp-short-pixel-total-original', 0);//amount of original data
118
+ self::getOpt( 'wp-short-pixel-total-optimized', 0);//amount of optimized
119
+ self::getOpt( 'wp-short-pixel-protocol', 'https');
120
+ }
121
+
122
+ public function shortPixelActivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
123
+ {
124
+ $this->prioQ->resetBulk();
125
+ if(SP_RESET_ON_ACTIVATE === true && WP_DEBUG === true) { //force reset plugin counters, only on specific occasions and on test environments
126
+ update_option( 'wp-short-pixel-fileCount', 0);
127
+ update_option( 'wp-short-pixel-thumbnail-count', 0);
128
+ update_option( 'wp-short-pixel-files-under-5-percent', 0);
129
+ update_option( 'wp-short-pixel-savedSpace', 0);
130
+ update_option( 'wp-short-pixel-api-retries', 0);//sometimes we need to retry processing/downloading a file multiple times
131
+ update_option( 'wp-short-pixel-quota-exceeded', 0);
132
+ update_option( 'wp-short-pixel-total-original', 0);//amount of original data
133
+ update_option( 'wp-short-pixel-total-optimized', 0);//amount of optimized
134
+ update_option( 'wp-short-pixel-bulk-ever-ran', 0);
135
+ delete_option('wp-short-pixel-priorityQueue');
136
+ unset($_SESSION["wp-short-pixel-priorityQueue"]);
137
+ delete_option("wp-short-pixel-bulk-previous-percent");
138
+ }
139
+ }
140
+
141
+ public function shortPixelDeactivatePlugin()//reset some params to avoid trouble for plugins that were activated/deactivated/activated
142
+ {
143
+ $this->prioQ->resetBulk();
144
+ }
145
+
146
+ //set default move as "list". only set once, it won't try to set the default mode again.
147
+ public function setDefaultViewModeList()
148
+ {
149
+ if(get_option('wp-short-pixel-view-mode') === false)
150
+ {
151
+ add_option('wp-short-pixel-view-mode', 1, '', 'yes' );
152
+ if ( function_exists('get_currentuserinfo') )
153
+ {
154
+ global $current_user;
155
+ get_currentuserinfo();
156
+ $currentUserID = $current_user->ID;
157
+ update_user_meta($currentUserID, "wp_media_library_mode", "list");
158
+ }
159
+ }
160
+
161
+ }
162
+
163
+ static function log($message) {
164
+ if (WP_DEBUG === true) {
165
+ if (is_array($message) || is_object($message)) {
166
+ error_log(print_r($message, true));
167
+ } else {
168
+ error_log($message);
169
+ }
170
+ }
171
+ }
172
+
173
+ function shortPixelJS() { ?>
174
+ <script type="text/javascript" >
175
+ jQuery(document).ready(function($){
176
+ ShortPixel.setOptions({
177
+ STATUS_SUCCESS: <?= ShortPixelAPI::STATUS_SUCCESS ?>,
178
+ STATUS_EMPTY_QUEUE: <?= self::BULK_EMPTY_QUEUE ?>,
179
+ STATUS_ERROR: <?= ShortPixelAPI::STATUS_ERROR ?>,
180
+ STATUS_FAIL: <?= ShortPixelAPI::STATUS_FAIL ?>,
181
+ STATUS_SKIP: <?= ShortPixelAPI::STATUS_SKIP ?>,
182
+ STATUS_QUOTA_EXCEEDED: <?= ShortPixelAPI::STATUS_QUOTA_EXCEEDED ?>,
183
+ WP_PLUGIN_URL: '<?= WP_PLUGIN_URL ?>',
184
+ API_KEY: "<?= $this->_apiKey ?>"
185
+ });
186
+ });
187
+ </script> <?php
188
+ wp_enqueue_style('short-pixel.css', plugins_url('/css/short-pixel.css',__FILE__) );
189
+ }
190
+
191
+ function toolbar_shortpixel_processing( $wp_admin_bar ) {
192
+ if ( !is_admin()) {
193
+ return;
194
+ }
195
+ wp_enqueue_script('short-pixel.js', plugins_url('/js/short-pixel.js',__FILE__) );
196
+
197
+ $extraClasses = " shortpixel-hide";
198
+ $tooltip = "ShortPixel optimizing...";
199
+ $icon = "shortpixel.png";
200
+ $link = current_user_can( 'edit_others_posts')? 'upload.php?page=wp-short-pixel-bulk' : 'upload.php';
201
+ $blank = "";
202
+ if($this->prioQ->processing()) {
203
+ $extraClasses = " shortpixel-processing";
204
+ }
205
+ self::log("TOOLBAR: Quota exceeded: " . self::getOpt( 'wp-short-pixel-quota-exceeded', 0));
206
+ if(self::getOpt( 'wp-short-pixel-quota-exceeded', 0)) {
207
+ $extraClasses = " shortpixel-alert shortpixel-quota-exceeded";
208
+ $tooltip = "ShortPixel quota exceeded. Click to top-up";
209
+ $link = "http://shortpixel.com/login/" . $this->_apiKey;
210
+ $blank = '_blank';
211
+ //$icon = "shortpixel-alert.png";
212
+ }
213
+ self::log("TB: Start: " . $this->prioQ->getStartBulkId() . ", stop: " . $this->prioQ->getStopBulkId() . " PrioQ: "
214
+ .json_encode($this->prioQ->get()));
215
+
216
+ $args = array(
217
+ 'id' => 'shortpixel_processing',
218
+ 'title' => '<div title="' . $tooltip . '" ><img src="'
219
+ . WP_PLUGIN_URL . '/shortpixel-image-optimiser/img/' . $icon . '"><span class="shp-alert">!</span></div>',
220
+ 'href' => $link,
221
+ 'meta' => array('target'=> $blank, 'class' => 'shortpixel-toolbar-processing' . $extraClasses)
222
+ );
223
+ $wp_admin_bar->add_node( $args );
224
+ }
225
+
226
+ public static function getOpt($key, $default) {
227
+ if(get_option($key) === false) {
228
+ add_option( $key, $default, '', 'yes' );
229
+ }
230
+ return get_option($key);
231
+ }
232
+
233
+ public function handleCustomBulk() {
234
+ // 1. get the action
235
+ $wp_list_table = _get_list_table('WP_Media_List_Table');
236
+ $action = $wp_list_table->current_action();
237
+
238
+ switch($action) {
239
+ // 2. Perform the action
240
+ case 'short-pixel-bulk':
241
+ // security check
242
+ check_admin_referer('bulk-media');
243
+ if(!is_array($_GET['media'])) {
244
+ break;
245
+ }
246
+ $mediaIds = array_reverse($_GET['media']);
247
+ foreach( $mediaIds as $ID ) {
248
+ $meta = wp_get_attachment_metadata($ID);
249
+ if( (!isset($meta['ShortPixel']) || !isset($meta['ShortPixel']['WaitingProcessing']) || $meta['ShortPixel']['WaitingProcessing'] != true)
250
+ && (!isset($meta['ShortPixelImprovement']) || $meta['ShortPixelImprovement'] != 'Optimization N/A')) {
251
+ $this->prioQ->push($ID);
252
+ $meta['ShortPixel']['WaitingProcessing'] = true;
253
+ wp_update_attachment_metadata($ID, $meta);
254
+ }
255
+ }
256
+ break;
257
+ }
258
+ }
259
+
260
+ public function handleImageUpload($meta, $ID = null)
261
+ {
262
+ if( !$this->_verifiedKey) {// no API Key set/verified -> do nothing here, just return
263
+ return $meta;
264
+ }
265
+ //else
266
+ self::log("IMG: Auto-analyzing file ID #{$ID}");
267
+
268
+ if( self::isProcessable($ID) == false )
269
+ {//not a file that we can process
270
+ $meta['ShortPixelImprovement'] = 'Optimization N/A';
271
+ return $meta;
272
+ }
273
+ else
274
+ {//the kind of file we can process. goody.
275
+ $this->prioQ->push($ID);
276
+ $URLsAndPATHs = $this->getURLsAndPATHs($ID, $meta);
277
+ $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $ID);//send a processing request right after a file was uploaded, do NOT wait for response
278
+ self::log("IMG: sent: " . json_encode($URLsAndPATHs));
279
+ $meta['ShortPixel']['WaitingProcessing'] = true;
280
+ return $meta;
281
+ }
282
+
283
+ }//end handleImageUpload
284
+
285
+ public function getCurrentBulkItemsCount(){
286
+ global $wpdb;
287
+
288
+ $startQueryID = $this->prioQ->getFlagBulkId();
289
+ $endQueryID = $this->prioQ->getStopBulkId();
290
+
291
+ if ( $startQueryID <= $endQueryID ) {
292
+ return 0;
293
+ }
294
+ $queryPostMeta = "SELECT COUNT(DISTINCT post_id) items FROM " . $wpdb->prefix . "postmeta
295
+ WHERE ( post_id <= $startQueryID AND post_id > $endQueryID ) AND (
296
+ meta_key = '_wp_attached_file'
297
+ OR meta_key = '_wp_attachment_metadata' )";
298
+ $res = $wpdb->get_results($queryPostMeta);
299
+ return $res[0]->items;
300
+ }
301
+
302
+ public function getBulkItemsFromDb(){
303
+ global $wpdb;
304
+
305
+ $startQueryID = $this->prioQ->getStartBulkId();
306
+ $endQueryID = $this->prioQ->getStopBulkId();
307
+ $skippedAlreadyProcessed = 0;
308
+
309
+ if ( $startQueryID <= $endQueryID ) {
310
+ return false;
311
+ }
312
+ $idList = array();
313
+ for ($sanityCheck = 0, $crtStartQueryID = $startQueryID;
314
+ $crtStartQueryID > $endQueryID && count($idList) < 3; $sanityCheck++) {
315
+
316
+ self::log("GETDB: current StartID: " . $crtStartQueryID);
317
+
318
+ $queryPostMeta = "SELECT * FROM " . $wpdb->prefix . "postmeta
319
+ WHERE ( post_id <= $crtStartQueryID AND post_id > $endQueryID )
320
+ AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
321
+ ORDER BY post_id DESC
322
+ LIMIT " . SP_MAX_RESULTS_QUERY;
323
+ $resultsPostMeta = $wpdb->get_results($queryPostMeta);
324
+
325
+ if ( empty($resultsPostMeta) ) {
326
+ $crtStartQueryID -= SP_MAX_RESULTS_QUERY;
327
+ continue;
328
+ }
329
+
330
+ foreach ( $resultsPostMeta as $itemMetaData ) {
331
+ $crtStartQueryID = $itemMetaData->post_id;
332
+ if(!in_array($crtStartQueryID, $idList) && self::isProcessable($crtStartQueryID)) {
333
+ $meta = wp_get_attachment_metadata($crtStartQueryID);
334
+ if(!isset($meta["ShortPixelImprovement"]) || !is_numeric($meta["ShortPixelImprovement"])) {
335
+ $idList[] = $crtStartQueryID;
336
+ } elseif($itemMetaData->meta_key == '_wp_attachment_metadata') { //count skipped
337
+ $skippedAlreadyProcessed++;
338
+ }
339
+ }
340
+ }
341
+ if(!count($idList) && $crtStartQueryID <= $startQueryID) {
342
+ //daca n-am adaugat niciuna pana acum, n-are sens sa mai selectez zona asta de id-uri in bulk-ul asta.
343
+ $leapStart = $this->prioQ->getStartBulkId();
344
+ $crtStartQueryID = $startQueryID = $itemMetaData->post_id - 1; //decrement it so we don't select it again
345
+ $res = self::countAllProcessedFiles($leapStart, $crtStartQueryID);
346
+ $skippedAlreadyProcessed += $res["mainFiles"];
347
+ $this->prioQ->setStartBulkId($startQueryID);
348
+ } else {
349
+ $crtStartQueryID--;
350
+ }
351
+ }
352
+ return array("ids" => $idList, "skipped" => $skippedAlreadyProcessed);
353
+ }
354
+
355
+ /**
356
+ * Get last added items from priority
357
+ * @return type
358
+ */
359
+ public function getFromPrioAndCheck() {
360
+ $ids = array();
361
+ $removeIds = array();
362
+
363
+ $idsPrio = $this->prioQ->get();
364
+ for($i = count($idsPrio) - 1, $cnt = 0; $i>=0 && $cnt < 3; $i--) {
365
+ $id = $idsPrio[$i];
366
+ if(wp_get_attachment_url($id)) {
367
+ $ids[] = $id; //valid ID
368
+ } else {
369
+ $removeIds[] = $id;//absent, to remove
370
+ }
371
+ }
372
+ foreach($removeIds as $rId){
373
+ self::log("HIP: Unfound ID $rID Remove from Priority Queue: ".json_encode(get_option($this->prioQ->get())));
374
+ $this->prioQ->remove($rId);
375
+ }
376
+ return $ids;
377
+ }
378
+
379
+ public function handleImageProcessing($ID = null) {
380
+ //die("bau");
381
+ //0: check key
382
+ if( $this->_verifiedKey == false) {
383
+ echo "Missing API Key";
384
+ die();
385
+ }
386
+
387
+ self::log("HIP: 0 Priority Queue: ".json_encode($this->prioQ->get()));
388
+
389
+ //1: get 3 ids to process. Take them with priority from the queue
390
+ $ids = $this->getFromPrioAndCheck();
391
+ if(count($ids) < 3 ) { //take from bulk if bulk processing active
392
+ $bulkStatus = $this->prioQ->bulkRunning();
393
+ if($bulkStatus =='running') {
394
+ $res = $this->getBulkItemsFromDb();
395
+ $bulkItems = $res['ids'];
396
+ if($bulkItems){
397
+ $ids = array_merge ($ids, $bulkItems);
398
+ }
399
+ }
400
+ }
401
+ if ($ids === false || count( $ids ) == 0 ){
402
+ $bulkEverRan = $this->prioQ->stopBulk();
403
+ $avg = self::getAverageCompression();
404
+ $fileCount = get_option('wp-short-pixel-fileCount');
405
+ die(json_encode(array("Status" => self::BULK_EMPTY_QUEUE,
406
+ "Message" => 'Empty queue ' . $this->prioQ->getStartBulkId() . '->' . $this->prioQ->getStopBulkId(),
407
+ "BulkStatus" => ($this->prioQ->bulkRunning()
408
+ ? "1" : ($this->prioQ->bulkPaused() ? "2" : "0")),
409
+ "AverageCompression" => $avg,
410
+ "FileCount" => $fileCount,
411
+ "BulkPercent" => $this->prioQ->getBulkPercent())));
412
+ }
413
+
414
+ self::log("HIP: 1 Prio Queue: ".json_encode($this->prioQ->get()));
415
+
416
+ //2: Send up to 3 files to the server for processing
417
+ for($i = 0; $i < min(3, count($ids)); $i++) {
418
+ $ID = $ids[$i];
419
+ $URLsAndPATHs = $this->sendToProcessing($ID);
420
+ if($i == 0) { //save for later use
421
+ $firstUrlAndPaths = $URLsAndPATHs;
422
+ }
423
+ }
424
+
425
+ self::log("HIP: 2 Prio Queue: ".json_encode($this->prioQ->get()));
426
+
427
+ //3: Retrieve the file for the first element of the list
428
+ $ID = $ids[0];
429
+ $result = $this->_apiInterface->processImage($firstUrlAndPaths['URLs'], $firstUrlAndPaths['PATHs'], $ID);
430
+ $result["ImageID"] = $ID;
431
+
432
+ self::log("HIP: 3 Prio Queue: ".json_encode($this->prioQ->get()));
433
+
434
+ //4: update counters and priority list
435
+ if( $result["Status"] == ShortPixelAPI::STATUS_SUCCESS) {
436
+ self::log("HIP: Image ID $ID optimized successfully: ".json_encode($result));
437
+ $prio = $this->prioQ->remove($ID);
438
+ if(!$prio && $ID <= $this->prioQ->getStartBulkId()) {
439
+ $this->prioQ->setStartBulkId($ID - 1);
440
+ $this->prioQ->logBulkProgress();
441
+
442
+ $deltaBulkPercent = $this->prioQ->getDeltaBulkPercent();
443
+ $msg = $this->bulkProgressMessage($deltaBulkPercent, $this->prioQ->getTimeRemaining());
444
+ $result["BulkPercent"] = $this->prioQ->getBulkPercent();;
445
+ $result["BulkMsg"] = $msg;
446
+
447
+ $thumb = $bkThumb = "";
448
+ $percent = 0;
449
+ $meta = wp_get_attachment_metadata($ID);
450
+ if(isset($meta["ShortPixelImprovement"]) && isset($meta["file"])){
451
+ $percent = $meta["ShortPixelImprovement"];
452
+
453
+ $filePath = explode("/", $meta["file"]);
454
+ $uploadsUrl = content_url() . "/uploads/";
455
+ $urlPath = implode("/", array_slice($filePath, 0, count($filePath) - 1));
456
+ $thumb = (isset($meta["sizes"]["medium"]) ? $meta["sizes"]["medium"]["file"] : (isset($meta["sizes"]["thumbnail"]) ? $meta["sizes"]["thumbnail"]["file"]: ""));
457
+ if(strlen($thumb) && get_option('wp-short-backup_images')) {
458
+ $bkThumb = $uploadsUrl . SP_BACKUP . "/" . $urlPath . "/" . $thumb;
459
+ }
460
+ if(strlen($thumb)) {
461
+ $thumb = $uploadsUrl . $urlPath . "/" . $thumb;
462
+ }
463
+ $result["Thumb"] = $thumb;
464
+ $result["BkThumb"] = $bkThumb;
465
+ }
466
+ }
467
+ }
468
+ elseif ($result["Status"] == ShortPixelAPI::STATUS_SKIP
469
+ || $result["Status"] == ShortPixelAPI::STATUS_FAIL) {
470
+ $prio = $this->prioQ->remove($ID);
471
+ if(!$prio && $ID <= $this->prioQ->getStartBulkId()) {
472
+ $this->prioQ->setStartBulkId($ID - 1);
473
+ }
474
+ }
475
+ die(json_encode($result));
476
+ }
477
+
478
+ private function sendToProcessing($ID) {
479
+ $URLsAndPATHs = $this->getURLsAndPATHs($ID);
480
+ $this->_apiInterface->doRequests($URLsAndPATHs['URLs'], false, $ID);//send a request, do NOT wait for response
481
+ $meta = wp_get_attachment_metadata($ID);
482
+ $meta['ShortPixel']['WaitingProcessing'] = true;
483
+ wp_update_attachment_metadata($ID, $meta);
484
+ return $URLsAndPATHs;
485
+ }
486
+
487
+ public function handleManualOptimization() {
488
+ $imageId = intval($_GET['image_id']);
489
+
490
+ if(self::isProcessable($imageId)) {
491
+ $this->prioQ->push($imageId);
492
+ $this->sendToProcessing($imageId);
493
+ $ret = array("Status" => ShortPixelAPI::STATUS_SUCCESS, "message" => "");
494
+ } else {
495
+ die(var_dump($pathParts));
496
+ }
497
+ die(json_encode($ret));
498
+
499
+ $urlList[] = wp_get_attachment_url($attachmentID);
500
+ $filePath[] = get_attached_file($attachmentID);
501
+ $meta = wp_get_attachment_metadata($attachmentID);
502
+
503
+ $processThumbnails = get_option('wp-short-process_thumbnails');
504
+
505
+ //process all files (including thumbs)
506
+ if($processThumbnails && !empty($meta['sizes'])) {
507
+ //we generate an array with the URLs that need to be handled
508
+ $SubDir = $this->_apiInterface->returnSubDir($meta['file']);
509
+ foreach($meta['sizes'] as $thumbnailInfo)
510
+ {
511
+ $urlList[]= str_replace(ShortPixelAPI::MB_basename($filePath[0]), $thumbnailInfo['file'], $urlList[0]);
512
+ $filePath[] = str_replace(ShortPixelAPI::MB_basename($filePath[0]), $thumbnailInfo['file'], $filePath[0]);
513
+ }
514
+ }
515
+
516
+ $result = $this->_apiInterface->processImage($urlList, $filePath, $attachmentID);//request to process all the images
517
+
518
+ if ( !is_array($result) )//there was an error, we save it in ShortPixelImprovement data
519
+ $this->handleError($attachmentID, $result);
520
+
521
+ // store the referring webpage location
522
+ $sendback = wp_get_referer();
523
+ // sanitize the referring webpage location
524
+ $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
525
+ // send the user back where they came from
526
+ wp_redirect($sendback);
527
+ // we are done,
528
+ }
529
+
530
+ //save error in file's meta data
531
+ public function handleError($ID, $result)
532
+ {
533
+ $meta = wp_get_attachment_metadata($ID);
534
+ $meta['ShortPixelImprovement'] = $result;
535
+ wp_update_attachment_metadata($ID, $meta);
536
+ }
537
+
538
+ public function handleRestoreBackup() {
539
+ $attachmentID = intval($_GET['attachment_ID']);
540
+
541
+ $file = get_attached_file($attachmentID);
542
+ $meta = wp_get_attachment_metadata($attachmentID);
543
+ $pathInfo = pathinfo($file);
544
+
545
+ $fileExtension = strtolower(substr($file,strrpos($file,".")+1));
546
+ $SubDir = $this->_apiInterface->returnSubDir($file);
547
+
548
+ //sometimes the month of original file and backup can differ
549
+ if ( !file_exists(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . ShortPixelAPI::MB_basename($file)) )
550
+ $SubDir = date("Y") . "/" . date("m") . "/";
551
+
552
+ try {
553
+ //main file
554
+ @rename(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . ShortPixelAPI::MB_basename($file), $file);
555
+
556
+ //overwriting thumbnails
557
+ if( !empty($meta['file']) ) {
558
+ foreach($meta["sizes"] as $size => $imageData) {
559
+ $source = SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . $imageData['file'];
560
+ $destination = $pathInfo['dirname'] . DIRECTORY_SEPARATOR . $imageData['file'];
561
+ @rename($source, $destination);
562
+ }
563
+ }
564
+ unset($meta["ShortPixelImprovement"]);
565
+ unset($meta['ShortPixel']['WaitingProcessing']);
566
+ wp_update_attachment_metadata($attachmentID, $meta);
567
+
568
+ } catch(Exception $e) {
569
+ //what to do, what to do?
570
+ }
571
+ // store the referring webpage location
572
+ $sendback = wp_get_referer();
573
+ // sanitize the referring webpage location
574
+ $sendback = preg_replace('|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback);
575
+ // send the user back where they came from
576
+ wp_redirect($sendback);
577
+ // we are done
578
+ }
579
+
580
+
581
+ public function handleDeleteAttachmentInBackup($ID) {
582
+ $file = get_attached_file($ID);
583
+ $meta = wp_get_attachment_metadata($ID);
584
+
585
+ if(self::isProcessable($ID) != false)
586
+ {
587
+ $SubDir = $this->_apiInterface->returnSubDir($file);
588
+ try {
589
+ $SubDir = $this->_apiInterface->returnSubDir($file);
590
+
591
+ @unlink(SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir . ShortPixelAPI::MB_basename($file));
592
+
593
+ if ( !empty($meta['file']) )
594
+ {
595
+ $filesPath = SP_BACKUP_FOLDER . DIRECTORY_SEPARATOR . $SubDir;//base BACKUP path
596
+ //remove thumbs thumbnails
597
+ if(isset($meta["sizes"])) {
598
+ foreach($meta["sizes"] as $size => $imageData) {
599
+ @unlink($filesPath . ShortPixelAPI::MB_basename($imageData['file']));//remove thumbs
600
+ }
601
+ }
602
+ }
603
+
604
+ } catch(Exception $e) {
605
+ //what to do, what to do?
606
+ }
607
+ }
608
+ }
609
+
610
+ public function registerSettingsPage() {
611
+ add_options_page( 'ShortPixel Settings', 'ShortPixel', 'manage_options', 'wp-shortpixel', array($this, 'renderSettingsMenu'));
612
+ }
613
+
614
+ function registerAdminPage( ) {
615
+ add_media_page( 'ShortPixel Bulk Process', 'Bulk ShortPixel', 'edit_others_posts', 'wp-short-pixel-bulk', array( &$this, 'bulkProcess' ) );
616
+ }
617
+
618
+ public function checkQuotaAndAlert() {
619
+ $quotaData = $this->getQuotaInformation();
620
+ if ( !$quotaData['APIKeyValid']) {
621
+ return $quotaData;
622
+ }
623
+ if($quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] > $quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric']) {
624
+ update_option('wp-short-pixel-quota-exceeded','0');
625
+ ?><script>var shortPixelQuotaExceeded = 0;</script><?php
626
+ }
627
+ else {
628
+ $this->view->displayQuotaExceededAlert($quotaData);
629
+ ?><script>var shortPixelQuotaExceeded = 1;</script><?php
630
+ }
631
+ return $quotaData;
632
+ }
633
+
634
+ public function bulkProcess() {
635
+ global $wpdb;
636
+
637
+ if( $this->_verifiedKey == false ) {//invalid API Key
638
+ $this->view->displayApiKeyAlert();
639
+ return;
640
+ }
641
+
642
+ $quotaData = $this->checkQuotaAndAlert();
643
+ if(self::getOpt('wp-short-pixel-quota-exceeded', 0) != 0) return;
644
+
645
+ if(isset($_POST['bulkProcessPause']))
646
+ {//pause an ongoing bulk processing, it might be needed sometimes
647
+ $this->prioQ->pauseBulk();
648
+ }
649
+
650
+ if(isset($_POST["bulkProcess"]))
651
+ {
652
+ //set the thumbnails option
653
+ if ( isset($_POST['thumbnails']) ) {
654
+ update_option('wp-short-process_thumbnails', 1);
655
+ } else {
656
+ update_option('wp-short-process_thumbnails', 0);
657
+ }
658
+ $this->prioQ->startBulk();
659
+ self::log("BULK: Start: " . $this->prioQ->getStartBulkId() . ", stop: " . $this->prioQ->getStopBulkId() . " PrioQ: "
660
+ .json_encode($this->prioQ->get()));
661
+ }//end bulk process was clicked
662
+
663
+ if(isset($_POST["bulkProcessResume"]))
664
+ {
665
+ $this->prioQ->resumeBulk();
666
+ }//resume was clicked
667
+
668
+ //figure out all the files that could be processed
669
+ $qry = "SELECT count(*) FilesToBeProcessed FROM " . $wpdb->prefix . "postmeta
670
+ WHERE meta_key = '_wp_attached_file' ";
671
+ $allFiles = $wpdb->get_results($qry);
672
+ //figure out the files that are left to be processed
673
+ $qry_left = "SELECT count(*) FilesLeftToBeProcessed FROM " . $wpdb->prefix . "postmeta
674
+ WHERE meta_key = '_wp_attached_file' AND post_id <= " . $this->prioQ->getStartBulkId();
675
+ $filesLeft = $wpdb->get_results($qry_left);
676
+
677
+ if ( $filesLeft[0]->FilesLeftToBeProcessed > 0 && $this->prioQ->bulkRunning() )//bulk processing was started and is still running
678
+ {
679
+ $msg = $this->bulkProgressMessage($this->prioQ->getDeltaBulkPercent(), $this->prioQ->getTimeRemaining());
680
+ $this->view->displayBulkProcessingRunning($this->prioQ->getBulkPercent(), $msg);
681
+
682
+ // $imagesLeft = $filesLeft[0]->FilesLeftToBeProcessed;
683
+ // $totalImages = $allFiles[0]->FilesToBeProcessed;
684
+ // echo "<p>{$imagesLeft} out of {$totalImages} images left to process.</p>";
685
+ // echo ' <a class="button button-secondary" href="' . get_admin_url() . 'upload.php">Media Library</a> ';
686
+ } else
687
+ {
688
+ if($this->prioQ->bulkRan() && !$this->prioQ->bulkPaused()) {
689
+ $this->prioQ->markBulkComplete();
690
+ }
691
+
692
+ //image count
693
+ $imageCount = $this->countAllProcessableFiles();
694
+ $imgProcessedCount = $this->countAllProcessedFiles();
695
+ $imageOnlyThumbs = $imageCount['totalFiles'] - $imageCount['mainFiles'];
696
+ $thumbsProcessedCount = self::getOpt( 'wp-short-pixel-thumbnail-count', 0);//amount of optimized thumbnails
697
+ $under5PercentCount = self::getOpt( 'wp-short-pixel-files-under-5-percent', 0);//amount of under 5% optimized imgs.
698
+
699
+ //average compression
700
+ $averageCompression = self::getAverageCompression();
701
+ // $this->view->displayBulkProcessingForm($imageCount, $imageOnlyThumbs, $this->prioQ->bulkRan(), $averageCompression,
702
+ $this->view->displayBulkProcessingForm($imageCount, $imgProcessedCount, $thumbsProcessedCount, $under5PercentCount,
703
+ $this->prioQ->bulkRan(), $averageCompression, get_option('wp-short-pixel-fileCount'),
704
+ self::formatBytes(get_option('wp-short-pixel-savedSpace')), $this->prioQ->bulkPaused() ? $this->prioQ->getBulkPercent() : false);
705
+ }
706
+ }
707
+ //end bulk processing
708
+
709
+ public function bulkProgressMessage($percent, $minutes) {
710
+ $timeEst = "";
711
+ self::log("bulkProgressMessage(): percent: " . $percent);
712
+ if($percent < 1 || $minutes == 0) {
713
+ $timeEst = "";
714
+ } elseif( $minutes > 2880) {
715
+ $timeEst = "~ " . round($minutes / 1440) . " days left";
716
+ } elseif ($minutes > 240) {
717
+ $timeEst = "~ " . round($minutes / 60) . " hours left";
718
+ } elseif ($minutes > 60) {
719
+ $timeEst = "~ " . round($minutes / 60) . " hours " . round($minutes%60/10) * 10 . " min. left";
720
+ } elseif ($minutes > 20) {
721
+ $timeEst = "~ " . round($minutes / 10) * 10 . " minutes left";
722
+ } else {
723
+ $timeEst = "~ " . $minutes . " minutes left";
724
+ }
725
+ return $timeEst;
726
+ }
727
+
728
+ public function emptyBackup(){
729
+ if(file_exists(SP_BACKUP_FOLDER)) {
730
+
731
+ //extract all images from DB in an array. of course
732
+ $attachments = null;
733
+ $attachments = get_posts( array(
734
+ 'numberposts' => -1,
735
+ 'post_type' => 'attachment',
736
+ 'post_mime_type' => 'image'
737
+ ));
738
+
739
+
740
+ //parse all images and set the right flag that the image has no backup
741
+ foreach($attachments as $attachment)
742
+ {
743
+ if(self::isProcessable(get_attached_file($attachment->ID)) == false) continue;
744
+
745
+ $meta = wp_get_attachment_metadata($attachment->ID);
746
+ $meta['ShortPixel']['NoBackup'] = true;
747
+ wp_update_attachment_metadata($attachment->ID, $meta);
748
+ }
749
+
750
+ //delete the actual files on disk
751
+ $this->deleteDir(SP_BACKUP_FOLDER);//call a recursive function to empty files and sub-dirs in backup dir
752
+ }
753
+ }
754
+
755
+ public function renderSettingsMenu() {
756
+ if ( !current_user_can( 'manage_options' ) ) {
757
+ wp_die('You do not have sufficient permissions to access this page.');
758
+ }
759
+
760
+ $quotaData = $this->checkQuotaAndAlert();
761
+
762
+ echo '<h1>ShortPixel Plugin Settings</h1>';
763
+ echo '<p>
764
+ <a href="https://shortpixel.com" target="_blank">ShortPixel.com</a> |
765
+ <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/installation/" target="_blank">Installation </a> |
766
+ <a href="https://shortpixel.com/contact" target="_blank">Support </a>
767
+ </p>';
768
+ 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="' . get_admin_url() . 'upload.php?page=wp-short-pixel-bulk">Bulk Optimization Tool</a>.</p>';
769
+
770
+ $noticeHTML = "<br/><div style=\"background-color: #fff; border-left: 4px solid %s; box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1); padding: 1px 12px;\"><p>%s</p></div>";
771
+
772
+ //by default we try to fetch the API Key from wp-config.php (if defined)
773
+ if ( !isset($_POST['submit']) && !get_option('wp-short-pixel-verifiedKey') && defined("SHORTPIXEL_API_KEY") && strlen(SHORTPIXEL_API_KEY) == 20 )
774
+ {
775
+ $_POST['validate'] = "validate";
776
+ $_POST['key'] = SHORTPIXEL_API_KEY;
777
+ }
778
+
779
+ if(isset($_POST['submit']) || isset($_POST['validate'])) {
780
+
781
+ //handle API Key - common for submit and validate
782
+ $_POST['key'] = trim(str_replace("*","",$_POST['key']));
783
+
784
+ if ( strlen($_POST['key']) <> 20 )
785
+ {
786
+ $KeyLength = strlen($_POST['key']);
787
+
788
+ printf($noticeHTML, '#ff0000', "The key you provided has " . $KeyLength . " characters. The API key should have 20 characters, letters and numbers only.<BR> <b>Please check that the API key is the same as the one you received in your confirmation email.</b><BR>
789
+ If this problem persists, please contact us at <a href='mailto:support@shortpixel.com?Subject=API Key issues' target='_top'>support@shortpixel.com</a> or <a href='https://shortpixel.com/contact' target='_blank'>here</a>.");
790
+ }
791
+ else
792
+ {
793
+ $validityData = $this->getQuotaInformation($_POST['key'], true);
794
+
795
+ $this->_apiKey = $_POST['key'];
796
+ $this->_apiInterface->setApiKey($this->_apiKey);
797
+ update_option('wp-short-pixel-apiKey', $_POST['key']);
798
+ if($validityData['APIKeyValid']) {
799
+ if(isset($_POST['validate'])) {
800
+ //display notification
801
+ if(in_array($_SERVER["SERVER_ADDR"], array("127.0.0.1","::1"))) {
802
+ printf($noticeHTML, '#FFC800', "API Key is valid but your server seems to have a local address.
803
+ Please make sure that your server is accessible from the Internet before using the API or otherwise we won't be able to optimize them.");
804
+ } else {
805
+
806
+ if ( function_exists("is_multisite") && is_multisite() )
807
+ printf($noticeHTML, '#7ad03a', "API Key valid! <br>You seem to be running a multisite, please note that API Key can also be configured in wp-config.php like this:<BR> <b>define('SHORTPIXEL_API_KEY', '".$this->_apiKey."');</b>");
808
+ else
809
+ printf($noticeHTML, '#7ad03a', 'API Key valid!');
810
+ }
811
+ }
812
+ update_option('wp-short-pixel-verifiedKey', true);
813
+ $this->_verifiedKey = true;
814
+ //test that the "uploads" have the right rights and also we can create the backup dir for ShortPixel
815
+ if ( !file_exists(SP_BACKUP_FOLDER) && !@mkdir(SP_BACKUP_FOLDER, 0777, true) )
816
+ printf($noticeHTML, '#ff0000', "There is something preventing us to create a new folder for backing up your original files.<BR>
817
+ Please make sure that folder <b>" .
818
+ WP_CONTENT_DIR . DIRECTORY_SEPARATOR . "uploads</b> has the necessary write and read rights." );
819
+ } else {
820
+ if(isset($_POST['validate'])) {
821
+ //display notification
822
+ printf($noticeHTML, '#ff0000', $validityData["Message"]);
823
+ }
824
+ update_option('wp-short-pixel-verifiedKey', false);
825
+ $this->_verifiedKey = false;
826
+ }
827
+ }
828
+
829
+
830
+ //if save button - we process the rest of the form elements
831
+ if(isset($_POST['submit'])) {
832
+ update_option('wp-short-pixel-compression', $_POST['compressionType']);
833
+ $this->_compressionType = $_POST['compressionType'];
834
+ $this->_apiInterface->setCompressionType($this->_compressionType);
835
+ if(isset($_POST['thumbnails'])) { $this->_processThumbnails = 1; } else { $this->_processThumbnails = 0; }
836
+ if(isset($_POST['backupImages'])) { $this->_backupImages = 1; } else { $this->_backupImages = 0; }
837
+ if(isset($_POST['cmyk2rgb'])) { $this->_CMYKtoRGBconversion = 1; } else { $this->_CMYKtoRGBconversion = 0; }
838
+ update_option('wp-short-process_thumbnails', $this->_processThumbnails);
839
+ update_option('wp-short-backup_images', $this->_backupImages);
840
+ update_option('wp-short-pixel_cmyk2rgb', $this->_CMYKtoRGBconversion);
841
+ }
842
+ }
843
+
844
+
845
+ //empty backup
846
+ if(isset($_POST['emptyBackup'])) {
847
+ $this->emptyBackup();
848
+ }
849
+
850
+ $checked = '';
851
+ if($this->_processThumbnails) { $checked = 'checked'; }
852
+
853
+ $checkedBackupImages = '';
854
+ if($this->_backupImages) { $checkedBackupImages = 'checked'; }
855
+
856
+ $cmyk2rgb = '';
857
+ if($this->_CMYKtoRGBconversion) { $cmyk2rgb = 'checked'; }
858
+
859
+
860
+ $formHTML = <<< HTML
861
+ <form name='wp_shortpixel_options' action='' method='post' id='wp_shortpixel_options'>
862
+ <table class="form-table">
863
+ <tbody><tr>
864
+ <th scope="row"><label for="key">API Key:</label></th>
865
+ <td><input name="key" type="text" id="key" value="{$this->_apiKey}" class="regular-text">
866
+ <input type="submit" name="validate" id="validate" class="button button-primary" title="Validate the provided API key" value="Validate">
867
+ </td>
868
+ </tr>
869
+ HTML;
870
+
871
+ if(!$this->_verifiedKey) {
872
+
873
+ //if invalid key we display the link to the API Key
874
+ $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>';
875
+ $formHTML .= '</form>';
876
+ } else {
877
+ //if valid key we display the rest of the options
878
+ $formHTML .= <<< HTML
879
+ <tr><th scope="row">
880
+ <label for="compressionType">Compression type:</label>
881
+ </th><td>
882
+ HTML;
883
+
884
+ if($this->_compressionType == 1) {
885
+ $formHTML .= '<input type="radio" name="compressionType" value="1" checked>Lossy</br></br>';
886
+ $formHTML .= '<input type="radio" name="compressionType" value="0" >Lossless';
887
+ } else {
888
+ $formHTML .= '<input type="radio" name="compressionType" value="1">Lossy</br></br>';
889
+ $formHTML .= '<input type="radio" name="compressionType" value="0" checked>Lossless';
890
+ }
891
+
892
+ $formHTML .= <<<HTML
893
+ </td>
894
+ </tr>
895
+ </tbody></table>
896
+ <p style="color: #818181;">
897
+ <b>Lossy compression: </b>lossy has a better compression rate than lossless compression.</br>The resulting image
898
+ is not 100% identical with the original. Works well for photos taken with your camera.</br></br>
899
+ <b>Lossless compression: </b> the shrunk image will be identical with the original and smaller in size.</br>Use this
900
+ when you do not want to lose any of the original image's details. Works best for technical drawings,
901
+ clip art and comics.
902
+ </p>
903
+ <table class="form-table">
904
+ <tbody><tr>
905
+ <th scope="row"><label for="thumbnails">Image thumbnails:</label></th>
906
+ <td><input name="thumbnails" type="checkbox" id="thumbnails" {$checked}> Apply compression also to image thumbnails.</td>
907
+ </tr>
908
+ <tr>
909
+ <th scope="row"><label for="backupImages">Image backup</label></th>
910
+ <td>
911
+ <input name="backupImages" type="checkbox" id="backupImages" {$checkedBackupImages}> Save and keep a backup of your original images in a separate folder.
912
+ </td>
913
+ </tr>
914
+ <tr>
915
+ <th scope="row"><label for="backupImages">CMYK to RGB conversion</label></th>
916
+ <td>
917
+ <input name="cmyk2rgb" type="checkbox" id="cmyk2rgb" {$cmyk2rgb}>Adjust your images for computer and mobile screen display.
918
+ </td>
919
+ </tr>
920
+ </tr>
921
+ </tbody></table>
922
+ <p class="submit">
923
+ <input type="submit" name="submit" id="submit" class="button button-primary" title="Save Changes" value="Save Changes">
924
+ <a class="button button-primary" title="Process all the images in your Media Library" href="upload.php?page=wp-short-pixel-bulk">Bulk Process</a>
925
+ </p>
926
+ </form>
927
+ <script>
928
+ var rad = document.wp_shortpixel_options.compressionType;
929
+ var prev = null;
930
+ for(var i = 0; i < rad.length; i++) {
931
+ rad[i].onclick = function() {
932
+
933
+ if(this !== prev) {
934
+ prev = this;
935
+ }
936
+ alert('This type of optimization will apply to new uploaded images. <BR>Images that were already processed will not be re-optimized.');
937
+ };
938
+ }
939
+ </script>
940
+ HTML;
941
+ }
942
+
943
+ echo $formHTML;
944
+
945
+ if($this->_verifiedKey) {
946
+ $fileCount = number_format(get_option('wp-short-pixel-fileCount'));
947
+ $savedSpace = self::formatBytes(get_option('wp-short-pixel-savedSpace'),2);
948
+ $averageCompression = self::getAverageCompression();
949
+ $savedBandwidth = self::formatBytes(get_option('wp-short-pixel-savedSpace') * 10000,2);
950
+ if (is_numeric($quotaData['APICallsQuota'])) {
951
+ $quotaData['APICallsQuota'] .= "/month";
952
+ }
953
+ $backupFolderSize = self::formatBytes(self::folderSize(SP_BACKUP_FOLDER));
954
+ $remainingImages = $quotaData['APICallsQuotaNumeric'] + $quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'];
955
+ $remainingImages = ( $remainingImages < 0 ) ? 0 : number_format($remainingImages);
956
+ $totalCallsMade = number_format($quotaData['APICallsMadeNumeric'] + $quotaData['APICallsMadeOneTimeNumeric']);
957
+
958
+ $statHTML = <<< HTML
959
+ <a id="facts"></a>
960
+ <h3>Your ShortPixel Stats</h3>
961
+ <table class="form-table">
962
+ <tbody>
963
+ <tr>
964
+ <th scope="row"><label for="averagCompression">Average compression of your files:</label></th>
965
+ <td>$averageCompression%</td>
966
+ </tr>
967
+ <tr>
968
+ <th scope="row"><label for="savedSpace">Saved disk space by ShortPixel</label></th>
969
+ <td>$savedSpace</td>
970
+ </tr>
971
+ <tr>
972
+ <th scope="row"><label for="savedBandwidth">Bandwith* saved with ShortPixel:</label></th>
973
+ <td>$savedBandwidth</td>
974
+ </tr>
975
+ </tbody></table>
976
+
977
+ <p style="padding-top: 0px; color: #818181;" >* Saved bandwidth is calculated at 10,000 impressions/image</p>
978
+
979
+ <h3>Your ShortPixel Plan</h3>
980
+ <table class="form-table">
981
+ <tbody>
982
+ <tr>
983
+ <th scope="row" bgcolor="#ffffff"><label for="apiQuota">Your ShortPixel plan</label></th>
984
+ <td bgcolor="#ffffff">{$quotaData['APICallsQuota']}/month ( <a href="https://shortpixel.com/login/{$this->_apiKey}" target="_blank">Need More? See the options available</a> )
985
+ </tr>
986
+ <tr>
987
+ <th scope="row"><label for="usedQUota">One time credits:</label></th>
988
+ <td>{$quotaData['APICallsQuotaOneTimeNumeric']}</td>
989
+ </tr>
990
+ <tr>
991
+ <th scope="row"><label for="usedQUota">Number of images processed this month:</label></th>
992
+ <td>{$totalCallsMade} (<a href="https://api.shortpixel.com/v2/report.php?key={$this->_apiKey}" target="_blank">see report</a>)</td>
993
+ </tr>
994
+ <tr>
995
+ <th scope="row"><label for="remainingImages">Remaining** images in your plan: </label></th>
996
+ <td>{$remainingImages} images</td>
997
+ </tr>
998
+ </tbody></table>
999
+
1000
+ <p style="padding-top: 0px; color: #818181;" >** Increase your image quota by <a href="https://shortpixel.com/login/{$this->_apiKey}" target="_blank">upgrading</a> your ShortPixel plan.</p>
1001
+
1002
+ <table class="form-table">
1003
+ <tbody>
1004
+ <tr>
1005
+ <th scope="row"><label for="totalFiles">Total number of processed files:</label></th>
1006
+ <td>{$fileCount}</td>
1007
+ </tr>
1008
+
1009
+
1010
+
1011
+ HTML;
1012
+ if($this->_backupImages) {
1013
+ $statHTML .= <<< HTML
1014
+ <form action="" method="POST">
1015
+ <tr>
1016
+ <th scope="row"><label for="sizeBackup">Original images are stored in a backup folder. Your backup folder size is now:</label></th>
1017
+ <td>
1018
+ {$backupFolderSize}
1019
+ <input type="submit" style="margin-left: 15px; vertical-align: middle;" class="button button-secondary" name="emptyBackup" value="Empty backups"/>
1020
+ </td>
1021
+ </tr>
1022
+ </form>
1023
+ HTML;
1024
+ }
1025
+
1026
+ $statHTML .= <<< HTML
1027
+ </tbody></table>
1028
+ HTML;
1029
+
1030
+ echo $statHTML;
1031
+
1032
+
1033
+ }
1034
+
1035
+ }
1036
+
1037
+ public function getAverageCompression(){
1038
+ return get_option('wp-short-pixel-total-optimized') > 0
1039
+ ? round(( 1 - ( get_option('wp-short-pixel-total-optimized') / get_option('wp-short-pixel-total-original') ) ) * 100, 2)
1040
+ : 0;
1041
+ }
1042
+
1043
+ public function getQuotaInformation($apiKey = null, $appendUserAgent = false) {
1044
+
1045
+ if(is_null($apiKey)) { $apiKey = $this->_apiKey; }
1046
+
1047
+ $requestURL = 'https://api.shortpixel.com/v2/api-status.php';
1048
+ $args = array('timeout'=> SP_MAX_TIMEOUT,
1049
+ 'sslverify' => false,
1050
+ 'body' => array('key' => $apiKey)
1051
+ );
1052
+
1053
+ if($appendUserAgent) {
1054
+ $args['body']['useragent'] = "Agent" . urlencode($_SERVER['HTTP_USER_AGENT']);
1055
+ }
1056
+ $response = wp_remote_post($requestURL, $args);
1057
+
1058
+ if(is_wp_error( $response )) //some hosting providers won't allow https:// POST connections so we try http:// as well
1059
+ $response = wp_remote_post(str_replace('https://', 'http://', $requestURL), $args);
1060
+
1061
+ if(is_wp_error( $response ))
1062
+ $response = wp_remote_get(str_replace('https://', 'http://', $requestURL), $args);
1063
+
1064
+ $defaultData = array(
1065
+ "APIKeyValid" => false,
1066
+ "Message" => 'API Key could not be validated due to a connectivity error.<BR>Your firewall may be blocking us. Please contact your hosting provider and ask them to allow connections from your site to IP 176.9.106.46.<BR> If you still cannot validate your API Key after this, please <a href="https://shortpixel.com/contact" target="_blank">contact us</a> and we will try to help. ',
1067
+ "APICallsMade" => 'Information unavailable. Please check your API key.',
1068
+ "APICallsQuota" => 'Information unavailable. Please check your API key.');
1069
+
1070
+ if(is_object($response) && get_class($response) == 'WP_Error') {
1071
+
1072
+ $urlElements = parse_url($requestURL);
1073
+ $portConnect = @fsockopen($urlElements['host'],8,$errno,$errstr,15);
1074
+ if(!$portConnect)
1075
+ $defaultData['Message'] .= "<BR>Debug info: <i>$errstr</i>";
1076
+
1077
+ return $defaultData;
1078
+ }
1079
+
1080
+ if($response['response']['code'] != 200) {
1081
+ return $defaultData;
1082
+ }
1083
+
1084
+ $data = $response['body'];
1085
+ $data = $this->parseJSON($data);
1086
+
1087
+ if(empty($data)) { return $defaultData; }
1088
+
1089
+ if($data->Status->Code != 2) {
1090
+ $defaultData['Message'] = $data->Status->Message;
1091
+ return $defaultData;
1092
+ }
1093
+
1094
+ if ( ( $data->APICallsMade + $data->APICallsMadeOneTime ) < ( $data->APICallsQuota + $data->APICallsQuotaOneTime ) ) //reset quota exceeded flag -> user is allowed to process more images.
1095
+ update_option('wp-short-pixel-quota-exceeded',0);
1096
+ else
1097
+ update_option('wp-short-pixel-quota-exceeded',1);//activate quota limiting
1098
+
1099
+ return array(
1100
+ "APIKeyValid" => true,
1101
+ "APICallsMade" => number_format($data->APICallsMade) . ' images',
1102
+ "APICallsQuota" => number_format($data->APICallsQuota) . ' images',
1103
+ "APICallsMadeOneTime" => number_format($data->APICallsMadeOneTime) . ' images',
1104
+ "APICallsQuotaOneTime" => number_format($data->APICallsQuotaOneTime) . ' images',
1105
+ "APICallsMadeNumeric" => $data->APICallsMade,
1106
+ "APICallsQuotaNumeric" => $data->APICallsQuota,
1107
+ "APICallsMadeOneTimeNumeric" => $data->APICallsMadeOneTime,
1108
+ "APICallsQuotaOneTimeNumeric" => $data->APICallsQuotaOneTime
1109
+ );
1110
+
1111
+
1112
+ }
1113
+
1114
+ public function generateCustomColumn( $column_name, $id ) {
1115
+ if( 'wp-shortPixel' == $column_name ) {
1116
+ $data = wp_get_attachment_metadata($id);
1117
+ $file = get_attached_file($id);
1118
+ $fileExtension = strtolower(substr($file,strrpos($file,".")+1));
1119
+
1120
+ print "<div id='sp-msg-{$id}'>";
1121
+
1122
+ if ( empty($data) )
1123
+ {
1124
+ if ( $fileExtension <> "pdf" )
1125
+ {
1126
+ if(!$this->_verifiedKey)
1127
+ print 'Invalid API Key. <a href="options-general.php?page=wp-shortpixel">Check your Settings</a>';
1128
+ else
1129
+ print 'Optimization N/A';
1130
+ }
1131
+ else
1132
+ {
1133
+ if ( get_option('wp-short-pixel-quota-exceeded') )
1134
+ {
1135
+ print QUOTA_EXCEEDED;
1136
+ return;
1137
+ }
1138
+ else
1139
+ {
1140
+ print 'PDF not processed';
1141
+ print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1142
+ return;
1143
+ }
1144
+ }
1145
+ }
1146
+ elseif ( isset( $data['ShortPixelImprovement'] ) )
1147
+ {
1148
+ if(isset($meta['ShortPixel']['BulkProcessing']))
1149
+ {
1150
+ if ( get_option('wp-short-pixel-quota-exceeded') )
1151
+ {
1152
+ print QUOTA_EXCEEDED;
1153
+ }
1154
+ else
1155
+ {
1156
+ print 'Waiting for bulk processing';
1157
+ print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1158
+ }
1159
+ }
1160
+ elseif( is_numeric($data['ShortPixelImprovement']) && !isset($data['ShortPixel']['NoBackup']) ) {
1161
+
1162
+ if ( $data['ShortPixelImprovement'] < 5 )
1163
+ {
1164
+ print $data['ShortPixelImprovement'] . '%';
1165
+ print " optimized<BR> Bonus processing";
1166
+
1167
+ }
1168
+ else
1169
+ {
1170
+ print 'Reduced by ';
1171
+ print $data['ShortPixelImprovement'] . '%';
1172
+ }
1173
+ if ( get_option('wp-short-backup_images') ) //display restore backup option only when backup is active
1174
+ print " | <a href=\"admin.php?action=shortpixel_restore_backup&amp;attachment_ID={$id}\">Restore backup</a>";
1175
+ }
1176
+ elseif ( is_numeric($data['ShortPixelImprovement']) )
1177
+ {
1178
+ if ( $data['ShortPixelImprovement'] < 5 )
1179
+ {
1180
+ print $data['ShortPixelImprovement'] . '%';
1181
+ print " optimized<BR> Bonus processing";
1182
+
1183
+ }
1184
+ else
1185
+ {
1186
+ print 'Reduced by ';
1187
+ print $data['ShortPixelImprovement'] . '%';
1188
+ }
1189
+ }
1190
+ elseif ( $data['ShortPixelImprovement'] <> "Optimization N/A" )
1191
+ {
1192
+ if ( trim(strip_tags($data['ShortPixelImprovement'])) == "Quota exceeded" )
1193
+ {
1194
+ print QUOTA_EXCEEDED;
1195
+ if ( !get_option('wp-short-pixel-quota-exceeded') )
1196
+ print " | <a href=\"javascript:manualOptimization({$id})\">Try again</a>";
1197
+ }
1198
+ elseif ( trim(strip_tags($data['ShortPixelImprovement'])) == "Cannot write optimized file" )
1199
+ {
1200
+ print $data['ShortPixelImprovement'];
1201
+ print " - <a href='https://shortpixel.com/faq#cannot-write-optimized-file' target='_blank'>Why?</a>";
1202
+ }
1203
+ else
1204
+ {
1205
+ print $data['ShortPixelImprovement'];
1206
+ print " | <a href=\"javascript:manualOptimization({$id})\">Try again</a>";
1207
+ }
1208
+ }
1209
+ else
1210
+ {
1211
+ print "Optimization N/A";
1212
+ }
1213
+ } elseif(isset($data['ShortPixel']['WaitingProcessing'])) {
1214
+ if ( get_option('wp-short-pixel-quota-exceeded') )
1215
+ {
1216
+ print QUOTA_EXCEEDED;
1217
+ }
1218
+ else
1219
+ {
1220
+ print "<img src=\"" . WP_PLUGIN_URL . "/shortpixel-image-optimiser/img/loading.gif\">Image waiting to be processed
1221
+ | <a href=\"javascript:manualOptimization({$id})\">Retry</a></div>";
1222
+ $this->prioQ->push($id); //should be there but just to make sure
1223
+ }
1224
+
1225
+ } elseif(isset($data['ShortPixel']['NoFileOnDisk'])) {
1226
+ print 'Image does not exist';
1227
+
1228
+ } else {
1229
+
1230
+ if ( wp_attachment_is_image( $id ) )
1231
+ {
1232
+ if ( get_option('wp-short-pixel-quota-exceeded') )
1233
+ {
1234
+ print QUOTA_EXCEEDED;
1235
+ }
1236
+ else
1237
+ {
1238
+ print 'Image not processed';
1239
+ print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1240
+ }
1241
+ }
1242
+ elseif ( $fileExtension == "pdf" )
1243
+ {
1244
+ if ( get_option('wp-short-pixel-quota-exceeded') )
1245
+ {
1246
+ print QUOTA_EXCEEDED;
1247
+ }
1248
+ else
1249
+ {
1250
+ print 'PDF not processed';
1251
+ print " | <a href=\"javascript:manualOptimization({$id})\">Optimize now</a>";
1252
+ }
1253
+ }
1254
+ }
1255
+ print "</div>";
1256
+ }
1257
+ }
1258
+
1259
+ public function columns( $defaults ) {
1260
+ $defaults['wp-shortPixel'] = 'ShortPixel Compression';
1261
+ return $defaults;
1262
+ }
1263
+
1264
+ public function generatePluginLinks($links) {
1265
+ $in = '<a href="options-general.php?page=wp-shortpixel">Settings</a>';
1266
+ array_unshift($links, $in);
1267
+ return $links;
1268
+ }
1269
+
1270
+ public function parseJSON($data) {
1271
+ if ( function_exists('json_decode') ) {
1272
+ $data = json_decode( $data );
1273
+ } else {
1274
+ require_once( 'JSON/JSON.php' );
1275
+ $json = new Services_JSON( );
1276
+ $data = $json->decode( $data );
1277
+ }
1278
+ return $data;
1279
+ }
1280
+
1281
+
1282
+ static public function formatBytes($bytes, $precision = 2) {
1283
+ $units = array('B', 'KB', 'MB', 'GB', 'TB');
1284
+
1285
+ $bytes = max($bytes, 0);
1286
+ $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
1287
+ $pow = min($pow, count($units) - 1);
1288
+
1289
+ $bytes /= pow(1024, $pow);
1290
+
1291
+ return round($bytes, $precision) . ' ' . $units[$pow];
1292
+ }
1293
+
1294
+ static public function isProcessable($ID) {
1295
+ $path = get_attached_file($ID);//get the full file PATH
1296
+ $pathParts = pathinfo($path);
1297
+ if( isset($pathParts['extension']) && in_array(strtolower($pathParts['extension']), array('jpg', 'jpeg', 'gif', 'png', 'pdf'))) {
1298
+ return true;
1299
+ } else {
1300
+ return false;
1301
+ }
1302
+ }
1303
+
1304
+
1305
+ //return an array with URL(s) and PATH(s) for this file
1306
+ public function getURLsAndPATHs($ID, $meta = NULL) {
1307
+
1308
+ if ( !parse_url(WP_CONTENT_URL, PHP_URL_SCHEME) )
1309
+ {//no absolute URLs used -> we implement a hack
1310
+ $url = get_site_url() . wp_get_attachment_url($ID);//get the file URL
1311
+ }
1312
+ else
1313
+ $url = wp_get_attachment_url($ID);//get the file URL
1314
+
1315
+ $urlList[] = $url;
1316
+ $path = get_attached_file($ID);//get the full file PATH
1317
+ $filePath[] = $path;
1318
+ if ( $meta == NULL ) {
1319
+ $meta = wp_get_attachment_metadata($ID);
1320
+ }
1321
+
1322
+ //it is NOT a PDF file and thumbs are processable
1323
+ if ( strtolower(substr($filePath[0],strrpos($filePath[0], ".")+1)) != "pdf"
1324
+ && $this->_processThumbnails
1325
+ && isset($meta['sizes']) && is_array($meta['sizes']))
1326
+ {
1327
+ foreach( $meta['sizes'] as $thumbnailInfo )
1328
+ {
1329
+ $urlList[] = str_replace(ShortPixelAPI::MB_basename($urlList[0]), $thumbnailInfo['file'], $url);
1330
+ $filePath[] = str_replace(ShortPixelAPI::MB_basename($filePath[0]), $thumbnailInfo['file'], $path);
1331
+ }
1332
+ }
1333
+ if(!isset($meta['sizes']) || !is_array($meta['sizes'])) {
1334
+ self::log("getURLsAndPATHs: no meta sizes for ID $ID : " . json_encode($meta));
1335
+ }
1336
+ return array("URLs" => $urlList, "PATHs" => $filePath);
1337
+ }
1338
+
1339
+
1340
+ public static function deleteDir($dirPath) {
1341
+ if (substr($dirPath, strlen($dirPath) - 1, 1) !=
1342
+ '/') {
1343
+ $dirPath .= '/';
1344
+ }
1345
+ $files = glob($dirPath . '*', GLOB_MARK);
1346
+ foreach ($files as $file) {
1347
+ if (is_dir($file)) {
1348
+ self::deleteDir($file);
1349
+ @rmdir($file);//remove empty dir
1350
+ } else {
1351
+ @unlink($file);//remove file
1352
+ }
1353
+ }
1354
+ }
1355
+
1356
+ static public function folderSize($path) {
1357
+ $total_size = 0;
1358
+ if(file_exists($path)) {
1359
+ $files = scandir($path);
1360
+ } else {
1361
+ return $total_size;
1362
+ }
1363
+ $cleanPath = rtrim($path, '/'). '/';
1364
+ foreach($files as $t) {
1365
+ if ($t<>"." && $t<>"..")
1366
+ {
1367
+ $currentFile = $cleanPath . $t;
1368
+ if (is_dir($currentFile)) {
1369
+ $size = self::folderSize($currentFile);
1370
+ $total_size += $size;
1371
+ }
1372
+ else {
1373
+ $size = filesize($currentFile);
1374
+ $total_size += $size;
1375
+ }
1376
+ }
1377
+ }
1378
+ return $total_size;
1379
+ }
1380
+
1381
+ public function getMaxMediaId() {
1382
+ global $wpdb;
1383
+ $queryMax = "SELECT max(post_id) as QueryID FROM " . $wpdb->prefix . "postmeta";
1384
+ $resultQuery = $wpdb->get_results($queryMax);
1385
+ return $resultQuery[0]->QueryID;
1386
+ }
1387
+
1388
+ public function getMinMediaId() {
1389
+ global $wpdb;
1390
+ $queryMax = "SELECT min(post_id) as QueryID FROM " . $wpdb->prefix . "postmeta";
1391
+ $resultQuery = $wpdb->get_results($queryMax);
1392
+ return $resultQuery[0]->QueryID;
1393
+ }
1394
+
1395
+ //count all the processable files in media library (while limiting the results to max 10000)
1396
+ public function countAllProcessableFiles($maxId = PHP_INT_MAX, $minId = 0){
1397
+ global $wpdb;
1398
+
1399
+ $totalFiles = 0;
1400
+ $mainFiles = 0;
1401
+ $limit = 500;
1402
+ $pointer = 0;
1403
+
1404
+ //count all the files, main and thumbs
1405
+ while ( 1 )
1406
+ {
1407
+ $filesList= $wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "postmeta
1408
+ WHERE ( post_id <= $maxId AND post_id > $minId )
1409
+ AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
1410
+ LIMIT $pointer,$limit");
1411
+ if ( empty($filesList) ) //we parsed all the results
1412
+ break;
1413
+
1414
+ foreach ( $filesList as $file )
1415
+ {
1416
+ if ( $file->meta_key == "_wp_attached_file" )
1417
+ {//count pdf files only
1418
+ $extension = substr($file->meta_value, strrpos($file->meta_value,".") + 1 );
1419
+ if ( $extension == "pdf" )
1420
+ {
1421
+ $totalFiles++;
1422
+ $mainFiles++;
1423
+ }
1424
+ }
1425
+ else
1426
+ {
1427
+ $attachment = unserialize($file->meta_value);
1428
+ if ( isset($attachment['sizes']) )
1429
+ $totalFiles += count($attachment['sizes']);
1430
+
1431
+ if ( isset($attachment['file']) )
1432
+ {
1433
+ $totalFiles++;
1434
+ $mainFiles++;
1435
+ }
1436
+ }
1437
+ }
1438
+ unset($filesList);
1439
+ $pointer += $limit;
1440
+
1441
+ }//end while
1442
+
1443
+ return array("totalFiles" => $totalFiles, "mainFiles" => $mainFiles);
1444
+ }
1445
+
1446
+
1447
+ //count all the processable files in media library (while limiting the results to max 10000)
1448
+ public function countAllProcessedFiles($maxId = PHP_INT_MAX, $minId = 0){
1449
+ global $wpdb;
1450
+
1451
+ $processedMainFiles = $processedTotalFiles = 0;
1452
+ $limit = 500;
1453
+ $pointer = 0;
1454
+
1455
+ //count all the files, main and thumbs
1456
+ while ( 1 )
1457
+ {
1458
+ $filesList= $wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "postmeta
1459
+ WHERE ( post_id <= $maxId AND post_id > $minId )
1460
+ AND ( meta_key = '_wp_attached_file' OR meta_key = '_wp_attachment_metadata' )
1461
+ LIMIT $pointer,$limit");
1462
+ if ( empty($filesList) ) {//we parsed all the results
1463
+ break;
1464
+ }
1465
+ foreach ( $filesList as $file )
1466
+ {
1467
+ if ( $file->meta_key == "_wp_attached_file" ) {
1468
+ continue;
1469
+ }
1470
+ $attachment = unserialize($file->meta_value);
1471
+ if ( isset($attachment['ShortPixelImprovement']) && $attachment['ShortPixelImprovement'] > 0 ) {
1472
+ $processedMainFiles++;
1473
+ $processedTotalFiles++;
1474
+ if ( isset($attachment['sizes']) ) {
1475
+ $processedTotalFiles += count($attachment['sizes']);
1476
+ }
1477
+ }
1478
+ }
1479
+ unset($filesList);
1480
+ $pointer += $limit;
1481
+
1482
+ }//end while
1483
+
1484
+ return array("totalFiles" => $processedTotalFiles, "mainFiles" => $processedMainFiles);
1485
+ }
1486
+
1487
+ public function migrateBackupFolder() {
1488
+ $oldBackupFolder = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'ShortpixelBackups';
1489
+
1490
+ if(!file_exists($oldBackupFolder)) return; //if old backup folder does not exist then there is nothing to do
1491
+
1492
+ if(!file_exists(SP_BACKUP_FOLDER)) {
1493
+ //we check that the backup folder exists, if not we create it so we can copy into it
1494
+ if(!mkdir(SP_BACKUP_FOLDER, 0777, true)) return;
1495
+ }
1496
+
1497
+ $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
1498
+ foreach($scannedDirectory as $file) {
1499
+ @rename($oldBackupFolder.DIRECTORY_SEPARATOR.$file, SP_BACKUP_FOLDER.DIRECTORY_SEPARATOR.$file);
1500
+ }
1501
+ $scannedDirectory = array_diff(scandir($oldBackupFolder), array('..', '.'));
1502
+ if(empty($scannedDirectory)) {
1503
+ @rmdir($oldBackupFolder);
1504
+ }
1505
+
1506
+ return;
1507
+ }
1508
+
1509
+ public function getApiKey() {
1510
+ return $this->_apiKey;
1511
+ }
1512
+
1513
+ public function backupImages() {
1514
+ return $this->_backupImages;
1515
+ }
1516
+
1517
+ public function processThumbnails() {
1518
+ return $this->_processThumbnails;
1519
+ }
1520
+
1521
+ }
1522
+
1523
+ $pluginInstance = new WPShortPixel();
1524
+ global $pluginInstance;
1525
+
1526
+ ?>