ShortPixel Image Optimizer - Version 3.0.3

Version Description

  • Progress bar improvements
Download this release

Release Info

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

Code changes from version 3.0.2 to 3.0.3

{trunk/JSON → JSON}/JSON.php RENAMED
File without changes
{trunk/JSON → JSON}/LICENSE RENAMED
File without changes
{trunk/css → css}/short-pixel.css RENAMED
File without changes
{trunk/img → img}/loading-dark-big.gif RENAMED
File without changes
{trunk/img → img}/loading-dark.gif RENAMED
File without changes
{trunk/img → img}/loading.gif RENAMED
File without changes
{trunk/img → img}/shortpixel-alert.png RENAMED
File without changes
{trunk/img → img}/shortpixel.png RENAMED
File without changes
{trunk/img → img}/slider.png RENAMED
File without changes
{trunk/js → js}/short-pixel.js RENAMED
File without changes
trunk/readme.txt → readme.txt RENAMED
@@ -5,7 +5,7 @@ Tags: picture, optimization, image editor, pngout, upload speed, shortpixel, co
5
 
6
  Requires at least: 3.0.1 or higher
7
  Tested up to: 4.2
8
- Stable tag: 3.0.2
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -119,7 +119,7 @@ The ShortPixel team is here to help. <a href="https://shortpixel.com/contact">Co
119
 
120
  == Changelog ==
121
 
122
- = 3.0.2 =
123
 
124
  * Progress bar improvements
125
 
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
 
120
  == Changelog ==
121
 
122
+ = 3.0.3 =
123
 
124
  * Progress bar improvements
125
 
trunk/shortpixel_api.php → shortpixel_api.php RENAMED
File without changes
trunk/shortpixel_queue.php → shortpixel_queue.php RENAMED
File without changes
trunk/shortpixel_view.php → shortpixel_view.php RENAMED
File without changes
trunk/wp-shortpixel.php → wp-shortpixel.php RENAMED
@@ -1,1530 +1,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.2
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.2");
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.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
+ ?>