Imagify Image Optimizer - Version 1.7.1

Version Description

  • 2018/04/10 =
  • New: compatibility with Regenerate Thumbnails (v3) plugin.
  • Improvement: better performance of the bulk optimization on sites with huge media library. This is done by not updating the statistics display periodically, but only when the job is done.
  • Improvement: SiteGround cache testing is not blocked anymore.
  • Improvement: proxies are now handled.
  • Improvement: test for ImageMagick or GD availability.
  • Dev stuff: improved the way we use the filesystem. This should solve few edge cases.
Download this release

Release Info

Developer wp_media
Plugin Icon 128x128 Imagify Image Optimizer
Version 1.7.1
Comparing to
See all releases

Code changes from version 1.7 to 1.7.1

Files changed (60) hide show
  1. assets/js/bulk.js +211 -86
  2. assets/js/bulk.min.js +1 -1
  3. imagify.php +3 -4
  4. inc/3rd-party/3rd-party.php +1 -0
  5. inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf-attachment.php +55 -75
  6. inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf.php +22 -10
  7. inc/3rd-party/enable-media-replace/inc/classes/class-imagify-enable-media-replace.php +15 -5
  8. inc/3rd-party/hosting/siteground.php +40 -7
  9. inc/3rd-party/nextgen-gallery/inc/admin/bulk.php +48 -8
  10. inc/3rd-party/nextgen-gallery/inc/admin/enqueue.php +0 -1
  11. inc/3rd-party/nextgen-gallery/inc/admin/heartbeat.php +0 -37
  12. inc/3rd-party/nextgen-gallery/inc/classes/class-imagify-ngg-attachment.php +50 -35
  13. inc/3rd-party/nextgen-gallery/inc/common/attachments.php +2 -2
  14. inc/3rd-party/regenerate-thumbnails/inc/classes/class-imagify-regenerate-thumbnails.php +358 -0
  15. inc/3rd-party/regenerate-thumbnails/regenerate-thumbnails.php +8 -0
  16. inc/admin/custom-folders.php +2 -4
  17. inc/admin/heartbeat.php +32 -66
  18. inc/admin/media.php +1 -1
  19. inc/admin/meta-boxes.php +2 -2
  20. inc/admin/upgrader.php +1 -1
  21. inc/classes/class-imagify-abstract-attachment.php +223 -27
  22. inc/classes/class-imagify-abstract-db.php +5 -5
  23. inc/classes/class-imagify-admin-ajax-post.php +101 -32
  24. inc/classes/class-imagify-assets.php +1 -1
  25. inc/classes/class-imagify-attachment.php +166 -64
  26. inc/classes/class-imagify-cron-rating.php +0 -5
  27. inc/classes/class-imagify-cron-sync-files.php +2 -4
  28. inc/classes/class-imagify-custom-folders.php +23 -28
  29. inc/classes/class-imagify-file-attachment.php +30 -20
  30. inc/classes/class-imagify-files-iterator.php +13 -2
  31. inc/classes/class-imagify-files-list-table.php +20 -10
  32. inc/classes/class-imagify-files-recursive-iterator.php +26 -2
  33. inc/classes/class-imagify-files-scan.php +29 -16
  34. inc/classes/class-imagify-filesystem.php +719 -0
  35. inc/classes/class-imagify-notices.php +4 -4
  36. inc/classes/class-imagify-requirements.php +342 -0
  37. inc/classes/class-imagify-user.php +5 -1
  38. inc/classes/class-imagify-views.php +72 -80
  39. inc/classes/class-imagify.php +30 -5
  40. inc/common/attachments.php +2 -2
  41. inc/functions/admin-stats.php +123 -3
  42. inc/functions/admin-ui.php +4 -7
  43. inc/functions/admin.php +2 -2
  44. inc/functions/api.php +1 -58
  45. inc/functions/attachments.php +6 -44
  46. inc/functions/common.php +23 -8
  47. inc/functions/compat.php +15 -1
  48. inc/functions/deprecated.php +192 -2
  49. inc/functions/files.php +0 -233
  50. inc/functions/i18n.php +22 -17
  51. inc/functions/options.php +0 -35
  52. inc/functions/process.php +32 -40
  53. readme.txt +10 -2
  54. views/notice-backup-folder-not-writable.php +2 -3
  55. views/page-bulk.php +5 -5
  56. views/page-settings.php +7 -7
  57. views/part-bulk-optimization-overquota-alert.php +1 -1
  58. views/part-settings-account.php +5 -5
  59. views/part-settings-custom-folders.php +8 -7
  60. views/part-settings-footer.php +1 -1
assets/js/bulk.js CHANGED
@@ -130,11 +130,6 @@
130
  // Overview chart.
131
  this.drawOverviewChart();
132
 
133
- if ( ! imagifyBulk.keyIsValid ) {
134
- $( '#imagify-bulk-action' ).on( 'click.imagify', this.maybeLaunchAllProcesses );
135
- return;
136
- }
137
-
138
  this.hasMultipleRows = $( '.imagify-bulk-table [name="group[]"]' ).length > 1;
139
 
140
  // Optimization level selector.
@@ -155,20 +150,27 @@
155
  $( '#imagify-bulk-action' ).on( 'click.imagify', this.maybeLaunchAllProcesses );
156
  $( '.imagify-share-networks a' ).on( 'click.imagify', this.share );
157
 
158
- if ( imagifyBulk.curlMissing ) {
159
- return;
160
- }
161
-
162
  // Optimization events.
163
  $( w )
164
  .on( 'processQueue.imagify', this.processQueue )
165
  .on( 'optimizeFiles.imagify', this.optimizeFiles )
166
  .on( 'queueEmpty.imagify', this.queueEmpty );
167
 
168
- // Heartbeat.
 
 
 
 
 
 
 
 
 
 
 
169
  $( d )
170
- .on( 'heartbeat-send', this.addHeartbeat )
171
- .on( 'heartbeat-tick', this.processHeartbeat );
172
  },
173
 
174
  /**
@@ -373,6 +375,28 @@
373
  return ajaxurl + w.imagify.concat + '_wpnonce=' + imagifyBulk.ajaxNonce + '&optimization_level=' + item.level + '&action=' + action + '&folder_type=' + item.groupId;
374
  },
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  /*
377
  * Get the message displayed to the user when (s)he leaves the page.
378
  *
@@ -420,6 +444,77 @@
420
  $( w ).trigger( 'queueEmpty.imagify' );
421
  },
422
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  /*
424
  * Display an error message in a modal.
425
  *
@@ -574,6 +669,61 @@
574
  this.globalOptimizedSize = 0;
575
  },
576
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
  // Event callbacks =========================================================================
578
 
579
  /*
@@ -667,16 +817,7 @@
667
  maybeLaunchAllProcesses: function () {
668
  var $infosModal;
669
 
670
- if ( ! imagifyBulk.keyIsValid ) {
671
- w.imagify.bulk.displayError( {
672
- title: imagifyBulk.labels.invalidAPIKeyTitle,
673
- type: 'info'
674
- } );
675
- return;
676
- }
677
-
678
- if ( imagifyBulk.curlMissing ) {
679
- w.imagify.bulk.displayError( '', imagifyBulk.labels.curlMissing );
680
  return;
681
  }
682
 
@@ -684,19 +825,7 @@
684
  return;
685
  }
686
 
687
- if ( $( this ).attr( 'disabled' ) ) {
688
- return;
689
- }
690
-
691
- if ( imagifyBulk.isOverQuota ) {
692
- // Swal information when over quota.
693
- w.imagify.bulk.displayError( {
694
- title: imagifyBulk.labels.overQuotaTitle,
695
- html: $( '#tmpl-imagify-overquota-alert' ).html(),
696
- type: 'info',
697
- customClass: 'imagify-swal-has-subtitle imagify-swal-error-header',
698
- showConfirmButton: false
699
- } );
700
  return;
701
  }
702
 
@@ -1082,6 +1211,20 @@
1082
  // Reset the queue.
1083
  w.imagify.bulk.queue = [];
1084
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
  // Maybe display error.
1086
  if ( ! $.isEmptyObject( w.imagify.bulk.status ) ) {
1087
  $.each( w.imagify.bulk.status, function( groupId, typeStatus ) {
@@ -1159,15 +1302,10 @@
1159
  * @param {object} data Object containing all Heartbeat IDs.
1160
  */
1161
  addHeartbeat: function ( e, data ) {
1162
- data.imagify_heartbeat = imagifyBulk.heartbeatId;
1163
-
1164
- if ( ! w.imagify.bulk.folderTypes.length ) {
1165
- $( '.imagify-row-folder-type' ).each( function() {
1166
- w.imagify.bulk.folderTypes.push( $( this ).data( 'group-id' ) );
1167
- } );
1168
- }
1169
 
1170
- data.imagify_types = w.imagify.bulk.folderTypes;
1171
  },
1172
 
1173
  /**
@@ -1178,55 +1316,42 @@
1178
  * @param {object} data Object containing all Heartbeat IDs.
1179
  */
1180
  processHeartbeat: function ( e, data ) {
1181
- var donutData;
1182
-
1183
- if ( ! data.imagify_bulk_data ) {
1184
- return;
1185
  }
 
1186
 
1187
- data = data.imagify_bulk_data;
1188
-
1189
- if ( w.imagify.bulk.charts.overview.donut.data ) {
1190
- donutData = w.imagify.bulk.charts.overview.donut.data.datasets[0].data;
 
 
 
 
 
 
1191
 
1192
- if ( data.unoptimized_attachments === donutData[0] && data.optimized_attachments === donutData[1] && data.errors_attachments === donutData[2] ) {
1193
- return;
1194
- }
 
 
 
 
 
 
 
1195
  }
1196
 
1197
- /**
1198
- * User account.
1199
- */
1200
- data.unconsumed_quota = data.unconsumed_quota.toFixed( 1 ); // A mystery where a float rounded on php side is not rounded here anymore. JavaScript is fun, it always surprises you in a manner you didn't expect.
1201
- $( '.imagify-unconsumed-percent' ).html( data.unconsumed_quota + '%' );
1202
- $( '.imagify-unconsumed-bar' ).css( 'width', data.unconsumed_quota + '%' );
1203
 
1204
- /**
1205
- * Global chart.
1206
- */
1207
- $( '#imagify-overview-chart-percent' ).html( data.optimized_attachments_percent + '<span>%</span>' );
1208
- $( '.imagify-total-percent' ).html( data.optimized_attachments_percent + '%' );
1209
-
1210
- w.imagify.bulk.drawOverviewChart( [
1211
- data.unoptimized_attachments,
1212
- data.optimized_attachments,
1213
- data.errors_attachments
1214
- ] );
1215
-
1216
- /**
1217
- * Stats block.
1218
- */
1219
- // The total optimized images.
1220
- $( '#imagify-total-optimized-attachments' ).html( data.already_optimized_attachments );
1221
-
1222
- // The original bar.
1223
- $( '#imagify-original-bar' ).find( '.imagify-barnb' ).html( data.original_human );
1224
-
1225
- // The optimized bar.
1226
- $( '#imagify-optimized-bar' ).css( 'width', ( 100 - data.optimized_percent ) + '%' ).find( '.imagify-barnb' ).html( data.optimized_human );
1227
-
1228
- // The Percent data.
1229
- $( '#imagify-total-optimized-attachments-pct' ).html( data.optimized_percent + '%' );
1230
  },
1231
 
1232
  /**
130
  // Overview chart.
131
  this.drawOverviewChart();
132
 
 
 
 
 
 
133
  this.hasMultipleRows = $( '.imagify-bulk-table [name="group[]"]' ).length > 1;
134
 
135
  // Optimization level selector.
150
  $( '#imagify-bulk-action' ).on( 'click.imagify', this.maybeLaunchAllProcesses );
151
  $( '.imagify-share-networks a' ).on( 'click.imagify', this.share );
152
 
 
 
 
 
153
  // Optimization events.
154
  $( w )
155
  .on( 'processQueue.imagify', this.processQueue )
156
  .on( 'optimizeFiles.imagify', this.optimizeFiles )
157
  .on( 'queueEmpty.imagify', this.queueEmpty );
158
 
159
+ if ( imagifyBulk.ajaxActions.getStats && $( '.imagify-bulk-table [data-group-id="library"][data-context="wp"]' ).length ) {
160
+ // If large library.
161
+ imagifyBulk.heartbeatId = false;
162
+ }
163
+
164
+ if ( imagifyBulk.heartbeatId ) {
165
+ $( d )
166
+ .on( 'heartbeat-send', this.addHeartbeat )
167
+ .on( 'heartbeat-tick', this.processHeartbeat );
168
+ }
169
+
170
+ // Heartbeat for requirements.
171
  $( d )
172
+ .on( 'heartbeat-send', this.addRequirementsHeartbeat )
173
+ .on( 'heartbeat-tick', this.processRequirementsHeartbeat );
174
  },
175
 
176
  /**
375
  return ajaxurl + w.imagify.concat + '_wpnonce=' + imagifyBulk.ajaxNonce + '&optimization_level=' + item.level + '&action=' + action + '&folder_type=' + item.groupId;
376
  },
377
 
378
+ /**
379
+ * Get folder types used in the page.
380
+ *
381
+ * @return {array}
382
+ */
383
+ getFolderTypes: function () {
384
+ if ( ! w.imagify.bulk.folderTypes.length ) {
385
+ $( '.imagify-row-folder-type' ).each( function() {
386
+ var $this = $( this ),
387
+ groupID = $this.data( 'group-id' );
388
+
389
+ if ( 'library' === groupID ) {
390
+ groupID += '|' + $this.data( 'context' );
391
+ }
392
+
393
+ w.imagify.bulk.folderTypes.push( groupID );
394
+ } );
395
+ }
396
+
397
+ return w.imagify.bulk.folderTypes;
398
+ },
399
+
400
  /*
401
  * Get the message displayed to the user when (s)he leaves the page.
402
  *
444
  $( w ).trigger( 'queueEmpty.imagify' );
445
  },
446
 
447
+ /*
448
+ * Tell if we have a blocking error. Can also display an error message in a swal.
449
+ *
450
+ * @param {bool} displayErrorMessage False to not display any error message.
451
+ * @return {bool}
452
+ */
453
+ hasBlockingError: function ( displayErrorMessage ) {
454
+ displayErrorMessage = undefined !== displayErrorMessage && displayErrorMessage;
455
+
456
+ if ( imagifyBulk.curlMissing ) {
457
+ if ( displayErrorMessage ) {
458
+ w.imagify.bulk.displayError( {
459
+ html: imagifyBulk.labels.curlMissing
460
+ } );
461
+ }
462
+ return true;
463
+ }
464
+
465
+ if ( imagifyBulk.editorMissing ) {
466
+ if ( displayErrorMessage ) {
467
+ w.imagify.bulk.displayError( {
468
+ html: imagifyBulk.labels.editorMissing
469
+ } );
470
+ }
471
+ return true;
472
+ }
473
+
474
+ if ( imagifyBulk.extHttpBlocked ) {
475
+ if ( displayErrorMessage ) {
476
+ w.imagify.bulk.displayError( {
477
+ html: imagifyBulk.labels.extHttpBlocked
478
+ } );
479
+ }
480
+ return true;
481
+ }
482
+
483
+ if ( imagifyBulk.apiDown ) {
484
+ if ( displayErrorMessage ) {
485
+ w.imagify.bulk.displayError( {
486
+ html: imagifyBulk.labels.apiDown
487
+ } );
488
+ }
489
+ return true;
490
+ }
491
+
492
+ if ( ! imagifyBulk.keyIsValid ) {
493
+ if ( displayErrorMessage ) {
494
+ w.imagify.bulk.displayError( {
495
+ title: imagifyBulk.labels.invalidAPIKeyTitle,
496
+ type: 'info'
497
+ } );
498
+ }
499
+ return true;
500
+ }
501
+
502
+ if ( imagifyBulk.isOverQuota ) {
503
+ if ( displayErrorMessage ) {
504
+ w.imagify.bulk.displayError( {
505
+ title: imagifyBulk.labels.overQuotaTitle,
506
+ html: $( '#tmpl-imagify-overquota-alert' ).html(),
507
+ type: 'info',
508
+ customClass: 'imagify-swal-has-subtitle imagify-swal-error-header',
509
+ showConfirmButton: false
510
+ } );
511
+ }
512
+ return true;
513
+ }
514
+
515
+ return false;
516
+ },
517
+
518
  /*
519
  * Display an error message in a modal.
520
  *
669
  this.globalOptimizedSize = 0;
670
  },
671
 
672
+ /**
673
+ * Print optimization stats.
674
+ *
675
+ * @param {object} data Object containing all Heartbeat IDs.
676
+ */
677
+ updateStats: function ( data ) {
678
+ var donutData;
679
+
680
+ if ( ! data || ! $.isPlainObject( data ) ) {
681
+ return;
682
+ }
683
+
684
+ if ( w.imagify.bulk.charts.overview.donut.data ) {
685
+ donutData = w.imagify.bulk.charts.overview.donut.data.datasets[0].data;
686
+
687
+ if ( data.unoptimized_attachments === donutData[0] && data.optimized_attachments === donutData[1] && data.errors_attachments === donutData[2] ) {
688
+ return;
689
+ }
690
+ }
691
+
692
+ /**
693
+ * User account.
694
+ */
695
+ data.unconsumed_quota = data.unconsumed_quota.toFixed( 1 ); // A mystery where a float rounded on php side is not rounded here anymore. JavaScript is fun, it always surprises you in a manner you didn't expect.
696
+ $( '.imagify-unconsumed-percent' ).html( data.unconsumed_quota + '%' );
697
+ $( '.imagify-unconsumed-bar' ).css( 'width', data.unconsumed_quota + '%' );
698
+
699
+ /**
700
+ * Global chart.
701
+ */
702
+ $( '#imagify-overview-chart-percent' ).html( data.optimized_attachments_percent + '<span>%</span>' );
703
+ $( '.imagify-total-percent' ).html( data.optimized_attachments_percent + '%' );
704
+
705
+ w.imagify.bulk.drawOverviewChart( [
706
+ data.unoptimized_attachments,
707
+ data.optimized_attachments,
708
+ data.errors_attachments
709
+ ] );
710
+
711
+ /**
712
+ * Stats block.
713
+ */
714
+ // The total optimized images.
715
+ $( '#imagify-total-optimized-attachments' ).html( data.already_optimized_attachments );
716
+
717
+ // The original bar.
718
+ $( '#imagify-original-bar' ).find( '.imagify-barnb' ).html( data.original_human );
719
+
720
+ // The optimized bar.
721
+ $( '#imagify-optimized-bar' ).css( 'width', ( 100 - data.optimized_percent ) + '%' ).find( '.imagify-barnb' ).html( data.optimized_human );
722
+
723
+ // The Percent data.
724
+ $( '#imagify-total-optimized-attachments-pct' ).html( data.optimized_percent + '%' );
725
+ },
726
+
727
  // Event callbacks =========================================================================
728
 
729
  /*
817
  maybeLaunchAllProcesses: function () {
818
  var $infosModal;
819
 
820
+ if ( $( this ).attr( 'disabled' ) ) {
 
 
 
 
 
 
 
 
 
821
  return;
822
  }
823
 
825
  return;
826
  }
827
 
828
+ if ( w.imagify.bulk.hasBlockingError( true ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
829
  return;
830
  }
831
 
1211
  // Reset the queue.
1212
  w.imagify.bulk.queue = [];
1213
 
1214
+ // Fetch and display generic stats if heartbeat is disabled.
1215
+ if ( ! imagifyBulk.heartbeatId ) {
1216
+ $.get( ajaxurl, {
1217
+ _wpnonce: imagifyBulk.ajaxNonce,
1218
+ action: imagifyBulk.ajaxActions.getStats,
1219
+ types: w.imagify.bulk.getFolderTypes()
1220
+ } )
1221
+ .done( function( response ) {
1222
+ if ( response.success ) {
1223
+ w.imagify.bulk.updateStats( response.data );
1224
+ }
1225
+ } );
1226
+ }
1227
+
1228
  // Maybe display error.
1229
  if ( ! $.isEmptyObject( w.imagify.bulk.status ) ) {
1230
  $.each( w.imagify.bulk.status, function( groupId, typeStatus ) {
1302
  * @param {object} data Object containing all Heartbeat IDs.
1303
  */
1304
  addHeartbeat: function ( e, data ) {
1305
+ data.imagify_ids = data.imagify_ids || {};
1306
+ data.imagify_ids[ imagifyBulk.heartbeatId ] = 1;
 
 
 
 
 
1307
 
1308
+ data.imagify_types = w.imagify.bulk.getFolderTypes();
1309
  },
1310
 
1311
  /**
1316
  * @param {object} data Object containing all Heartbeat IDs.
1317
  */
1318
  processHeartbeat: function ( e, data ) {
1319
+ if ( data.imagify_bulk_data ) {
1320
+ w.imagify.bulk.updateStats( data.imagify_bulk_data );
 
 
1321
  }
1322
+ },
1323
 
1324
+ /**
1325
+ * Add our Heartbeat ID for requirements on "heartbeat-send" event.
1326
+ *
1327
+ * @param {object} e Event object.
1328
+ * @param {object} data Object containing all Heartbeat IDs.
1329
+ */
1330
+ addRequirementsHeartbeat: function ( e, data ) {
1331
+ data.imagify_ids = data.imagify_ids || {};
1332
+ data.imagify_ids[ imagifyBulk.reqsHeartbeatId ] = 1;
1333
+ },
1334
 
1335
+ /**
1336
+ * Listen for the custom event "heartbeat-tick" on $(document).
1337
+ * It allows to update requirements status periodically.
1338
+ *
1339
+ * @param {object} e Event object.
1340
+ * @param {object} data Object containing all Heartbeat IDs.
1341
+ */
1342
+ processRequirementsHeartbeat: function ( e, data ) {
1343
+ if ( ! data.imagify_bulk_requirements ) {
1344
+ return;
1345
  }
1346
 
1347
+ data = data.imagify_bulk_requirements;
 
 
 
 
 
1348
 
1349
+ imagifyBulk.curlMissing = data.curl_missing;
1350
+ imagifyBulk.editorMissing = data.editor_missing;
1351
+ imagifyBulk.extHttpBlocked = data.external_http_blocked;
1352
+ imagifyBulk.apiDown = data.api_down;
1353
+ imagifyBulk.keyIsValid = data.key_is_valid;
1354
+ imagifyBulk.isOverQuota = data.is_over_quota;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1355
  },
1356
 
1357
  /**
assets/js/bulk.min.js CHANGED
@@ -1 +1 @@
1
- !function(a,b){var c=a.propHooks.checked;a.propHooks.checked={set:function(b,d,e){var f;return f=void 0===c?b[e]=d:c(b,d,e),a(b).trigger("change.imagify"),f}},a.fn.imagifyHide=function(b,c){return b&&b>0?this.hide(b,function(){a(this).addClass("hidden").css("display",""),void 0!==c&&c()}):(this.addClass("hidden"),void 0!==c&&c()),this.attr("aria-hidden","true")},a.fn.imagifyShow=function(b,c){return void 0!==c&&c(),b&&b>0?this.show(b,function(){a(this).removeClass("hidden").css("display","")}):this.removeClass("hidden"),this.attr("aria-hidden","false")}}(jQuery),function(a,b,c,d){c.imagify.bulk={charts:{overview:{canvas:!1,donut:!1,data:{labels:[imagifyBulk.labels.overviewChartLabels.unoptimized,imagifyBulk.labels.overviewChartLabels.optimized,imagifyBulk.labels.overviewChartLabels.error],datasets:[{data:[],backgroundColor:["#10121A","#46B1CE","#C51162"],borderWidth:0}]}},files:{donuts:{}},share:{canvas:!1,donut:!1}},queue:[],status:{},displayedWaitMessage:!1,hasMultipleRows:!0,processIsStopped:!1,globalGain:0,globalOriginalSize:0,globalOptimizedSize:0,folderTypes:[],init:function(){if(this.drawOverviewChart(),!imagifyBulk.keyIsValid)return void a("#imagify-bulk-action").on("click.imagify",this.maybeLaunchAllProcesses);this.hasMultipleRows=a('.imagify-bulk-table [name="group[]"]').length>1,a(".imagify-level-selector-button").on("click.imagify",this.openLevelSelectorFromButton),a(".imagify-level-selector-list input").on("change.imagify init.imagify",this.syncLevelSelectorFromRadio).filter(":checked").trigger("init.imagify"),a(b).on("keypress.imagify click.imagify",this.closeLevelSelectors),a('.imagify-bulk-table [name="group[]"]').on("change.imagify init.imagify",this.toggleOptimizationButton).trigger("init.imagify"),a(".imagify-show-table-details").on("click.imagify open.imagify close.imagify",this.toggleOptimizationDetails),a("#imagify-bulk-action").on("click.imagify",this.maybeLaunchAllProcesses),a(".imagify-share-networks a").on("click.imagify",this.share),imagifyBulk.curlMissing||(a(c).on("processQueue.imagify",this.processQueue).on("optimizeFiles.imagify",this.optimizeFiles).on("queueEmpty.imagify",this.queueEmpty),a(b).on("heartbeat-send",this.addHeartbeat).on("heartbeat-tick",this.processHeartbeat))},drawOverviewChart:function(d){var e,f;if(this.charts.overview.canvas||(this.charts.overview.canvas=b.getElementById("imagify-overview-chart"),this.charts.overview.canvas)){if(d=d&&a.isArray(d)?d:[],this.charts.overview.donut)return void(d.length&&(0===d.reduce(function(a,b){return a+b},0)&&(d[0]=1),this.charts.overview.donut.data.datasets[0].data=d,this.charts.overview.donut.update()));this.charts.overview.data.datasets[0].data=[parseInt(this.charts.overview.canvas.getAttribute("data-unoptimized"),10),parseInt(this.charts.overview.canvas.getAttribute("data-optimized"),10),parseInt(this.charts.overview.canvas.getAttribute("data-errors"),10)],e=a.extend({},this.charts.overview.data),d.length&&(e.datasets[0].data=d),0===e.datasets[0].data.reduce(function(a,b){return a+b},0)&&(e.datasets[0].data[0]=1),this.charts.overview.donut=new c.imagify.Chart(this.charts.overview.canvas,{type:"doughnut",data:e,options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{displayColors:!1,callbacks:{label:function(a,b){return b.datasets[a.datasetIndex].data[a.index]}}},responsive:!1,cutoutPercentage:85}}),f='<ul class="imagify-doughnut-legend">',a.each(e.labels,function(a,b){f+='<li><span style="background-color:'+e.datasets[0].backgroundColor[a]+'"></span>'+b+"</li>"}),f+="</ul>",b.getElementById("imagify-overview-chart-legend").innerHTML=f}},drawFileChart:function(b){var d=this.charts.files.donuts;b.each(function(){var b=parseInt(a(this).closest(".imagify-chart").next(".imagipercent").text().replace("%",""),10);if(void 0!==d[this.id])return d[this.id].data.datasets[0].data[0]=b,d[this.id].data.datasets[0].data[1]=100-b,void d[this.id].update();d[this.id]=new c.imagify.Chart(this,{type:"doughnut",data:{datasets:[{data:[b,100-b],backgroundColor:["#00B3D3","#D8D8D8"],borderColor:"#fff",borderWidth:1}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1}})}),this.charts.files.donuts=d},drawShareChart:function(){var d;if(this.charts.share.canvas||(this.charts.share.canvas=b.getElementById("imagify-ac-chart"),this.charts.share.canvas)){if(d=parseInt(a(this.charts.share.canvas).closest(".imagify-ac-chart").attr("data-percent"),10),this.charts.share.donut)return this.charts.share.donut.data.datasets[0].data[0]=d,this.charts.share.donut.data.datasets[0].data[1]=100-d,void this.charts.share.donut.update();this.charts.share.donut=new c.imagify.Chart(this.charts.share.canvas,{type:"doughnut",data:{datasets:[{data:[d,100-d],backgroundColor:["#40B1D0","#FFFFFF"],borderWidth:0}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1,cutoutPercentage:70}})}},getAjaxUrl:function(a,b){var d=b.groupId.replace(/[\s_-](\S)/g,function(a,b){return b.toUpperCase()});return a=a.replace("%GROUP_ID%",d),a=imagifyBulk.ajaxActions[a],ajaxurl+c.imagify.concat+"_wpnonce="+imagifyBulk.ajaxNonce+"&optimization_level="+b.level+"&action="+a+"&folder_type="+b.groupId},getConfirmMessage:function(){return imagifyBulk.labels.processing},closeLevelSelector:function(a,b){if(a&&a.length)return void 0!==b&&b>0?void c.setTimeout(function(){c.imagify.bulk.closeLevelSelector(a)},b):void a.attr("aria-hidden","true")},stopProcess:function(b,d){this.processIsStopped=!0,c.imagify.bulk.status[d.groupId]={isError:!0,id:b},a(c).trigger("queueEmpty.imagify")},displayError:function(b,c,d){var e={title:"",html:"",type:"error",customClass:"",width:620,padding:0,showCloseButton:!0,showConfirmButton:!0};a.isPlainObject(b)?d=a.extend({},e,b):(d=d||{},d=a.extend({},e,{title:b||"",html:c||""},d)),d.title=d.title||imagifyBulk.labels.error,d.customClass+=" imagify-sweet-alert",swal(d).catch(swal.noop)},displayErrorInRow:function(b,c){var d,e;return b=a(b()),d=b.find(".imagify-cell-status ~ td"),e=d.length,c=c||"",d.remove(),b.find(".imagify-cell-status").after('<td colspan="'+e+'">'+c+"</td>"),b},displayFolderRow:function(a,b){var d,e,f,g;return"resting"===a?(b.next(".imagify-row-waiting, .imagify-row-working").remove(),void b.imagifyShow()):(d=b.next(".imagify-row-waiting, .imagify-row-working"),"waiting"===a?(f="#d2d3d6",g=imagifyBulk.labels.waitingOtimizationsText):(f="#40b1d0",g=imagifyBulk.labels.imagesOptimizedText.replace("%s","<span>0</span>")),d.length?(d.hasClass("imagify-row-"+a)||(d.attr("class","imagify-row-"+a),d.find(".imagify-cell-checkbox svg").attr("fill",f),d.children(".imagify-cell-images-optimized").html(g)),b.imagifyHide(),void d.imagifyShow()):(d=b.clone().attr({class:"imagify-row-"+a,"aria-hidden":"false"}),e=c.imagify.template("imagify-spinner"),d.children(".imagify-cell-checkbox").html(e()).find("svg").attr("fill",f),d.children(".imagify-cell-title").html('<span class="imagify-cell-label">'+d.children(".imagify-cell-title").text()+"</span>"),d.children(".imagify-cell-images-optimized").html(g),d.children(".imagify-cell-errors, .imagify-cell-optimized, .imagify-cell-original, .imagify-cell-level").text(""),void b.imagifyHide().after(d)))},displayShareBox:function(){var b,d,e,f,g=imagifyBulk.labels.textToShare;if(!this.globalGain||this.queue.length)return this.globalGain=0,this.globalOriginalSize=0,void(this.globalOptimizedSize=0);b=(100-this.globalOptimizedSize/this.globalOriginalSize*100).toFixed(2),d=c.imagify.humanSize(this.globalGain,1),e=c.imagify.humanSize(this.globalOriginalSize,1),g=g.replace("%1$s",d),g=g.replace("%2$s",e),g=encodeURIComponent(g),f=a(".imagify-row-complete"),f.find(".imagify-ac-rt-total-gain").html(d),f.find(".imagify-ac-rt-total-original").html(e),f.find(".imagify-ac-chart").attr("data-percent",b),f.find(".imagify-sn-twitter").attr("href",imagifyBulk.labels.twitterShareURL+"&amp;text="+g),this.drawShareChart(),f.addClass("done").imagifyShow(),a("html, body").animate({scrollTop:f.offset().top},200),this.globalGain=0,this.globalOriginalSize=0,this.globalOptimizedSize=0},openLevelSelectorFromButton:function(b){var c=a("#"+a(this).attr("aria-controls"));b.stopPropagation(),a(".imagify-level-selector-list").not(c).attr("aria-hidden","true"),c.attr("aria-hidden","false").find(":checked").trigger("focus.imagify")},syncLevelSelectorFromRadio:function(){var b=a(this).closest(".imagify-level-choice");b.addClass("imagify-current-level").attr("aria-current","true").siblings(".imagify-level-choice").removeClass("imagify-current-level").attr("aria-current","false"),b.closest(".imagify-level-selector").find(".imagify-current-level-info").html(b.find("label").html())},closeLevelSelectors:function(b){"keypress"===b.type&&27!==b.keyCode&&13!==b.keyCode||c.imagify.bulk.closeLevelSelector(a('.imagify-level-selector-list[aria-hidden="false"]'))},toggleOptimizationButton:function(){if(!c.imagify.bulk.hasMultipleRows&&!this.checked)return void a(this).prop("checked",!0);a('.imagify-bulk-table [name="group[]"]:checked').length?a("#imagify-bulk-action").removeAttr("disabled"):a("#imagify-bulk-action").attr("disabled","disabled")},toggleOptimizationDetails:function(b){var c,d=a(this),e=d.closest(".imagify-bulk-table").find(".imagify-bulk-table-details");c="open"===b.type||"close"!==b.type&&e.hasClass("hidden"),c?(d.html(d.data("label-hide")+'<span class="dashicons dashicons-no-alt"></span>'),e.imagifyShow()):(d.html(d.data("label-show")+'<span class="dashicons dashicons-menu"></span>'),e.imagifyHide())},maybeLaunchAllProcesses:function(){var b;if(!imagifyBulk.keyIsValid)return void c.imagify.bulk.displayError({title:imagifyBulk.labels.invalidAPIKeyTitle,type:"info"});if(imagifyBulk.curlMissing)return void c.imagify.bulk.displayError("",imagifyBulk.labels.curlMissing);if(a('.imagify-bulk-table [name="group[]"]:checked').length&&!a(this).attr("disabled")){if(imagifyBulk.isOverQuota)return void c.imagify.bulk.displayError({title:imagifyBulk.labels.overQuotaTitle,html:a("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1});b=a("#tmpl-imagify-bulk-infos"),b.length?swal({title:imagifyBulk.labels.bulkInfoTitle,html:b.html(),type:"",customClass:"imagify-sweet-alert imagify-swal-has-subtitle imagify-before-bulk-infos",showCancelButton:!0,padding:0,width:554,confirmButtonText:imagifyBulk.labels.confirmBulk,cancelButtonText:imagifySwal.labels.cancelButtonText,reverseButtons:!0}).then(function(){var d=a('.imagify-bulk-table [name="group[]"]:checked').first().closest(".imagify-row-folder-type"),e=d.data("group-id"),f=d.data("context");a.get(ajaxurl+c.imagify.concat+"_wpnonce="+imagifyBulk.ajaxNonce+"&action="+imagifyBulk.ajaxActions.bulkInfoSeen+"&folder_type="+e+"&context="+f),b.remove(),c.imagify.bulk.launchAllProcesses()}).catch(swal.noop):c.imagify.bulk.launchAllProcesses()}},launchAllProcesses:function(){var b=a(c),d=a("#imagify-bulk-action"),e=!0;d.attr("disabled","disabled").find(".dashicons").addClass("rotate"),b.on("beforeunload",this.getConfirmMessage),a(".imagify-row-complete").imagifyHide(200,function(){a(this).removeClass("done")}),a(".imagify-show-table-details").trigger("close.imagify"),this.queue=[],this.status={},this.displayedWaitMessage=!1,this.processIsStopped=!1,this.globalGain=0,this.globalOriginalSize=0,this.globalOptimizedSize=0,a('.imagify-bulk-table [name="group[]"]:checked').each(function(){var b=a(this),d=b.closest(".imagify-row-folder-type"),f=d.data("group-id"),g=d.data("context"),h=d.find('.imagify-cell-level [name="level['+f+']"]:checked').val();if(c.imagify.bulk.queue.push({groupId:f,context:g,level:void 0===h?-1:parseInt(h,10)}),c.imagify.bulk.status[f]={isError:!1,id:"waiting"},e)return e=!1,!0;c.imagify.bulk.displayFolderRow("waiting",d)}),b.trigger("processQueue.imagify")},processQueue:function(){var b,d;if(!c.imagify.bulk.processIsStopped){if(!c.imagify.bulk.queue.length)return void a(c).trigger("queueEmpty.imagify");c.imagify.bulk.displayedWaitMessage||(swal({title:imagifyBulk.labels.waitTitle,html:imagifyBulk.labels.waitText,showConfirmButton:!1,padding:0,imageUrl:imagifyBulk.waitImageUrl,customClass:"imagify-sweet-alert"}).catch(swal.noop),c.imagify.bulk.displayedWaitMessage=!0),d=c.imagify.bulk.queue.shift(),b=a("#cb-select-"+d.groupId).closest(".imagify-row-folder-type"),c.imagify.bulk.status[d.groupId].id="fetching",c.imagify.bulk.displayFolderRow("working",b),a.get(c.imagify.bulk.getAjaxUrl("%GROUP_ID%Fetch",d)).done(function(e){if(!c.imagify.bulk.processIsStopped)if(swal.close(),e.success&&(a.isArray(e.data)||a.isPlainObject(e.data))){if(!a.isEmptyObject(e.data))return void a(c).trigger("optimizeFiles.imagify",[d,e.data]);if(c.imagify.bulk.status[d.groupId].id="no-images",!c.imagify.bulk.processIsStopped){if(c.imagify.bulk.hasMultipleRows&&a("#cb-select-"+d.groupId).prop("checked",!1),!c.imagify.bulk.queue.length)return void a(c).trigger("queueEmpty.imagify");c.imagify.bulk.displayFolderRow("resting",b),a(c).trigger("processQueue.imagify")}}else c.imagify.bulk.stopProcess(e.data.message,d)}).fail(function(){c.imagify.bulk.stopProcess("get-unoptimized-images",d)})}},optimizeFiles:function(b,d,e){var f,g,h=a("#cb-select-"+d.groupId).closest(".imagify-row-folder-type"),i=h.next(".imagify-row-working"),j=i.find(".imagify-cell-images-optimized span"),k=parseInt(j.text(),10),l=i.find(".imagify-cell-errors span"),m=parseInt(l.text(),10),n=h.closest(".imagify-bulk-table"),o=n.find(".imagify-row-progress"),p=o.find(".bar"),q={groupId:d.groupId,id:0,thumbnail:"",filename:"",status:"",icon:"",label:"",thumbnails:"",original_size_human:"",new_size_human:"",chartSuffix:"",percent_human:"",overall_saving_human:""};c.imagify.bulk.processIsStopped||(c.imagify.bulk.status[d.groupId].id="optimizing",n.find(".imagify-bulk-table-details thead").html(a("#tmpl-imagify-file-header-"+d.groupId).html()),g=n.find(".imagify-bulk-table-details tbody").text(""),p.css("width","0%").find(".percent").text("0%"),o.slideDown().attr("aria-hidden","false"),f=new ImagifyGulp({buffer_size:imagifyBulk.bufferSizes[d.context]||1,lib:c.imagify.bulk.getAjaxUrl("%GROUP_ID%Optimize",d),images:e,context:d.context}),f.before(function(b){var e=c.imagify.template("imagify-file-row-"+d.groupId);g.prepend(e(a.extend({},q,{status:"compressing",icon:"admin-generic rotate",label:imagifyBulk.labels.optimizing,chartSuffix:b.image_id},b)))}),f.each(function(b){var e=c.imagify.template("imagify-file-row-"+d.groupId),g=a("#"+d.groupId+"-"+b.image);return p.css("width",b.progress+"%").find(".percent").html(b.progress+"%"),b.success?(g.replaceWith(e(a.extend({},q,{status:"complete",icon:"yes",label:imagifyBulk.labels.complete,chartSuffix:b.image},b))),c.imagify.bulk.drawFileChart(a("#"+d.groupId+"-"+b.image).find(".imagify-cell-percentage canvas")),k+=1,void j.text(k)):"already-optimized"===b.error_code?(g.replaceWith(c.imagify.bulk.displayErrorInRow(e(a.extend({},q,{status:"complete",icon:"yes",label:imagifyBulk.labels.alreadyOptimized,chartSuffix:b.image},b)),b.error)),k+=1,void j.text(k)):(g.replaceWith(c.imagify.bulk.displayErrorInRow(e(a.extend({},q,{status:"error",icon:"dismiss",label:imagifyBulk.labels.error,chartSuffix:b.image},b)),b.error||b)),l.length?(m+=1,l.text(m)):(m=1,l=i.find(".imagify-cell-errors").html(imagifyBulk.labels.imagesErrorText.replace("%s","<span>1</span>")).find("span")),void("over-quota"===b.error_code&&(f.stopProcess(),c.imagify.bulk.stopProcess(b.error_code,d))))}),f.done(function(b){c.imagify.bulk.hasMultipleRows&&a("#cb-select-"+d.groupId).prop("checked",!1),b.global_original_size&&(c.imagify.bulk.globalGain+=parseInt(b.global_gain,10),c.imagify.bulk.globalOriginalSize+=parseInt(b.global_original_size,10),c.imagify.bulk.globalOptimizedSize+=parseInt(b.global_optimized_size,10)),c.imagify.bulk.processIsStopped||(c.imagify.bulk.status[d.groupId].isError||(c.imagify.bulk.status[d.groupId].id="done"),h.addClass("updating"),a.get(c.imagify.bulk.getAjaxUrl("getFolderData",d)).done(function(b){b.success&&a.each(b.data,function(a,b){h.children(".imagify-cell-"+a).html(b)}),c.imagify.bulk.displayFolderRow("resting",h)}).always(function(){h.removeClass("updating"),c.imagify.bulk.queue.length?a(c).trigger("processQueue.imagify"):a(c).trigger("queueEmpty.imagify")}))}),f.run())},queueEmpty:function(){var b=a(".imagify-bulk-table"),d={},e=!1,f=!0;c.imagify.bulk.displayShareBox(),c.imagify.bulk.queue=[],a.isEmptyObject(c.imagify.bulk.status)||(a.each(c.imagify.bulk.status,function(a,b){return b.isError?(e=b.id,f=!1,!1):"no-images"!==b.id?(f=!1,!1):void 0}),e?("invalid-api-key"===e?d={title:imagifyBulk.labels.invalidAPIKeyTitle,type:"info"}:"over-quota"===e?d={title:imagifyBulk.labels.overQuotaTitle,html:a("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1}:"get-unoptimized-images"!==e&&"consumed-all-data"!==e||(d={title:imagifyBulk.labels.getUnoptimizedImagesErrorTitle,html:imagifyBulk.labels.getUnoptimizedImagesErrorText,type:"info"}),c.imagify.bulk.displayError(d)):f&&c.imagify.bulk.displayError({title:imagifyBulk.labels.noAttachmentToOptimizeTitle,html:imagifyBulk.labels.noAttachmentToOptimizeText,type:"info"})),c.imagify.bulk.status={},a(c).off("beforeunload",c.imagify.bulk.getConfirmMessage),c.imagify.bulk.displayFolderRow("resting",b.find(".imagify-row-folder-type").not(".updating")),b.find(".imagify-row-progress").slideUp().attr("aria-hidden","true").find(".bar").removeAttr("style").find(".percent").text("0%"),a('.imagify-bulk-table [name="group[]"]:checked').length?a("#imagify-bulk-action").removeAttr("disabled").find(".dashicons").removeClass("rotate"):a("#imagify-bulk-action").find(".dashicons").removeClass("rotate")},addHeartbeat:function(b,d){d.imagify_heartbeat=imagifyBulk.heartbeatId,c.imagify.bulk.folderTypes.length||a(".imagify-row-folder-type").each(function(){c.imagify.bulk.folderTypes.push(a(this).data("group-id"))}),d.imagify_types=c.imagify.bulk.folderTypes},processHeartbeat:function(b,d){var e;d.imagify_bulk_data&&(d=d.imagify_bulk_data,c.imagify.bulk.charts.overview.donut.data&&(e=c.imagify.bulk.charts.overview.donut.data.datasets[0].data,d.unoptimized_attachments===e[0]&&d.optimized_attachments===e[1]&&d.errors_attachments===e[2])||(d.unconsumed_quota=d.unconsumed_quota.toFixed(1),a(".imagify-unconsumed-percent").html(d.unconsumed_quota+"%"),a(".imagify-unconsumed-bar").css("width",d.unconsumed_quota+"%"),a("#imagify-overview-chart-percent").html(d.optimized_attachments_percent+"<span>%</span>"),a(".imagify-total-percent").html(d.optimized_attachments_percent+"%"),c.imagify.bulk.drawOverviewChart([d.unoptimized_attachments,d.optimized_attachments,d.errors_attachments]),a("#imagify-total-optimized-attachments").html(d.already_optimized_attachments),a("#imagify-original-bar").find(".imagify-barnb").html(d.original_human),a("#imagify-optimized-bar").css("width",100-d.optimized_percent+"%").find(".imagify-barnb").html(d.optimized_human),a("#imagify-total-optimized-attachments-pct").html(d.optimized_percent+"%")))},share:function(a){var d,e;a.preventDefault(),c.innerWidth?(d=(c.innerWidth-700)/2,e=(c.innerHeight-290)/2):(d=(b.body.clientWidth-700)/2,e=(b.body.clientHeight-290)/2),c.open(this.href,"","status=no, scrollbars=no, menubar=no, top="+e+", left="+d+", width=700, height=290")}},c.imagify.bulk.init()}(jQuery,document,window);
1
+ !function(a,b){var c=a.propHooks.checked;a.propHooks.checked={set:function(b,d,e){var f;return f=void 0===c?b[e]=d:c(b,d,e),a(b).trigger("change.imagify"),f}},a.fn.imagifyHide=function(b,c){return b&&b>0?this.hide(b,function(){a(this).addClass("hidden").css("display",""),void 0!==c&&c()}):(this.addClass("hidden"),void 0!==c&&c()),this.attr("aria-hidden","true")},a.fn.imagifyShow=function(b,c){return void 0!==c&&c(),b&&b>0?this.show(b,function(){a(this).removeClass("hidden").css("display","")}):this.removeClass("hidden"),this.attr("aria-hidden","false")}}(jQuery),function(a,b,c,d){c.imagify.bulk={charts:{overview:{canvas:!1,donut:!1,data:{labels:[imagifyBulk.labels.overviewChartLabels.unoptimized,imagifyBulk.labels.overviewChartLabels.optimized,imagifyBulk.labels.overviewChartLabels.error],datasets:[{data:[],backgroundColor:["#10121A","#46B1CE","#C51162"],borderWidth:0}]}},files:{donuts:{}},share:{canvas:!1,donut:!1}},queue:[],status:{},displayedWaitMessage:!1,hasMultipleRows:!0,processIsStopped:!1,globalGain:0,globalOriginalSize:0,globalOptimizedSize:0,folderTypes:[],init:function(){this.drawOverviewChart(),this.hasMultipleRows=a('.imagify-bulk-table [name="group[]"]').length>1,a(".imagify-level-selector-button").on("click.imagify",this.openLevelSelectorFromButton),a(".imagify-level-selector-list input").on("change.imagify init.imagify",this.syncLevelSelectorFromRadio).filter(":checked").trigger("init.imagify"),a(b).on("keypress.imagify click.imagify",this.closeLevelSelectors),a('.imagify-bulk-table [name="group[]"]').on("change.imagify init.imagify",this.toggleOptimizationButton).trigger("init.imagify"),a(".imagify-show-table-details").on("click.imagify open.imagify close.imagify",this.toggleOptimizationDetails),a("#imagify-bulk-action").on("click.imagify",this.maybeLaunchAllProcesses),a(".imagify-share-networks a").on("click.imagify",this.share),a(c).on("processQueue.imagify",this.processQueue).on("optimizeFiles.imagify",this.optimizeFiles).on("queueEmpty.imagify",this.queueEmpty),imagifyBulk.ajaxActions.getStats&&a('.imagify-bulk-table [data-group-id="library"][data-context="wp"]').length&&(imagifyBulk.heartbeatId=!1),imagifyBulk.heartbeatId&&a(b).on("heartbeat-send",this.addHeartbeat).on("heartbeat-tick",this.processHeartbeat),a(b).on("heartbeat-send",this.addRequirementsHeartbeat).on("heartbeat-tick",this.processRequirementsHeartbeat)},drawOverviewChart:function(d){var e,f;if(this.charts.overview.canvas||(this.charts.overview.canvas=b.getElementById("imagify-overview-chart"),this.charts.overview.canvas)){if(d=d&&a.isArray(d)?d:[],this.charts.overview.donut)return void(d.length&&(0===d.reduce(function(a,b){return a+b},0)&&(d[0]=1),this.charts.overview.donut.data.datasets[0].data=d,this.charts.overview.donut.update()));this.charts.overview.data.datasets[0].data=[parseInt(this.charts.overview.canvas.getAttribute("data-unoptimized"),10),parseInt(this.charts.overview.canvas.getAttribute("data-optimized"),10),parseInt(this.charts.overview.canvas.getAttribute("data-errors"),10)],e=a.extend({},this.charts.overview.data),d.length&&(e.datasets[0].data=d),0===e.datasets[0].data.reduce(function(a,b){return a+b},0)&&(e.datasets[0].data[0]=1),this.charts.overview.donut=new c.imagify.Chart(this.charts.overview.canvas,{type:"doughnut",data:e,options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{displayColors:!1,callbacks:{label:function(a,b){return b.datasets[a.datasetIndex].data[a.index]}}},responsive:!1,cutoutPercentage:85}}),f='<ul class="imagify-doughnut-legend">',a.each(e.labels,function(a,b){f+='<li><span style="background-color:'+e.datasets[0].backgroundColor[a]+'"></span>'+b+"</li>"}),f+="</ul>",b.getElementById("imagify-overview-chart-legend").innerHTML=f}},drawFileChart:function(b){var d=this.charts.files.donuts;b.each(function(){var b=parseInt(a(this).closest(".imagify-chart").next(".imagipercent").text().replace("%",""),10);if(void 0!==d[this.id])return d[this.id].data.datasets[0].data[0]=b,d[this.id].data.datasets[0].data[1]=100-b,void d[this.id].update();d[this.id]=new c.imagify.Chart(this,{type:"doughnut",data:{datasets:[{data:[b,100-b],backgroundColor:["#00B3D3","#D8D8D8"],borderColor:"#fff",borderWidth:1}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1}})}),this.charts.files.donuts=d},drawShareChart:function(){var d;if(this.charts.share.canvas||(this.charts.share.canvas=b.getElementById("imagify-ac-chart"),this.charts.share.canvas)){if(d=parseInt(a(this.charts.share.canvas).closest(".imagify-ac-chart").attr("data-percent"),10),this.charts.share.donut)return this.charts.share.donut.data.datasets[0].data[0]=d,this.charts.share.donut.data.datasets[0].data[1]=100-d,void this.charts.share.donut.update();this.charts.share.donut=new c.imagify.Chart(this.charts.share.canvas,{type:"doughnut",data:{datasets:[{data:[d,100-d],backgroundColor:["#40B1D0","#FFFFFF"],borderWidth:0}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1,cutoutPercentage:70}})}},getAjaxUrl:function(a,b){var d=b.groupId.replace(/[\s_-](\S)/g,function(a,b){return b.toUpperCase()});return a=a.replace("%GROUP_ID%",d),a=imagifyBulk.ajaxActions[a],ajaxurl+c.imagify.concat+"_wpnonce="+imagifyBulk.ajaxNonce+"&optimization_level="+b.level+"&action="+a+"&folder_type="+b.groupId},getFolderTypes:function(){return c.imagify.bulk.folderTypes.length||a(".imagify-row-folder-type").each(function(){var b=a(this),d=b.data("group-id");"library"===d&&(d+="|"+b.data("context")),c.imagify.bulk.folderTypes.push(d)}),c.imagify.bulk.folderTypes},getConfirmMessage:function(){return imagifyBulk.labels.processing},closeLevelSelector:function(a,b){if(a&&a.length)return void 0!==b&&b>0?void c.setTimeout(function(){c.imagify.bulk.closeLevelSelector(a)},b):void a.attr("aria-hidden","true")},stopProcess:function(b,d){this.processIsStopped=!0,c.imagify.bulk.status[d.groupId]={isError:!0,id:b},a(c).trigger("queueEmpty.imagify")},hasBlockingError:function(b){return b=void 0!==b&&b,imagifyBulk.curlMissing?(b&&c.imagify.bulk.displayError({html:imagifyBulk.labels.curlMissing}),!0):imagifyBulk.editorMissing?(b&&c.imagify.bulk.displayError({html:imagifyBulk.labels.editorMissing}),!0):imagifyBulk.extHttpBlocked?(b&&c.imagify.bulk.displayError({html:imagifyBulk.labels.extHttpBlocked}),!0):imagifyBulk.apiDown?(b&&c.imagify.bulk.displayError({html:imagifyBulk.labels.apiDown}),!0):imagifyBulk.keyIsValid?!!imagifyBulk.isOverQuota&&(b&&c.imagify.bulk.displayError({title:imagifyBulk.labels.overQuotaTitle,html:a("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1}),!0):(b&&c.imagify.bulk.displayError({title:imagifyBulk.labels.invalidAPIKeyTitle,type:"info"}),!0)},displayError:function(b,c,d){var e={title:"",html:"",type:"error",customClass:"",width:620,padding:0,showCloseButton:!0,showConfirmButton:!0};a.isPlainObject(b)?d=a.extend({},e,b):(d=d||{},d=a.extend({},e,{title:b||"",html:c||""},d)),d.title=d.title||imagifyBulk.labels.error,d.customClass+=" imagify-sweet-alert",swal(d).catch(swal.noop)},displayErrorInRow:function(b,c){var d,e;return b=a(b()),d=b.find(".imagify-cell-status ~ td"),e=d.length,c=c||"",d.remove(),b.find(".imagify-cell-status").after('<td colspan="'+e+'">'+c+"</td>"),b},displayFolderRow:function(a,b){var d,e,f,g;return"resting"===a?(b.next(".imagify-row-waiting, .imagify-row-working").remove(),void b.imagifyShow()):(d=b.next(".imagify-row-waiting, .imagify-row-working"),"waiting"===a?(f="#d2d3d6",g=imagifyBulk.labels.waitingOtimizationsText):(f="#40b1d0",g=imagifyBulk.labels.imagesOptimizedText.replace("%s","<span>0</span>")),d.length?(d.hasClass("imagify-row-"+a)||(d.attr("class","imagify-row-"+a),d.find(".imagify-cell-checkbox svg").attr("fill",f),d.children(".imagify-cell-images-optimized").html(g)),b.imagifyHide(),void d.imagifyShow()):(d=b.clone().attr({class:"imagify-row-"+a,"aria-hidden":"false"}),e=c.imagify.template("imagify-spinner"),d.children(".imagify-cell-checkbox").html(e()).find("svg").attr("fill",f),d.children(".imagify-cell-title").html('<span class="imagify-cell-label">'+d.children(".imagify-cell-title").text()+"</span>"),d.children(".imagify-cell-images-optimized").html(g),d.children(".imagify-cell-errors, .imagify-cell-optimized, .imagify-cell-original, .imagify-cell-level").text(""),void b.imagifyHide().after(d)))},displayShareBox:function(){var b,d,e,f,g=imagifyBulk.labels.textToShare;if(!this.globalGain||this.queue.length)return this.globalGain=0,this.globalOriginalSize=0,void(this.globalOptimizedSize=0);b=(100-this.globalOptimizedSize/this.globalOriginalSize*100).toFixed(2),d=c.imagify.humanSize(this.globalGain,1),e=c.imagify.humanSize(this.globalOriginalSize,1),g=g.replace("%1$s",d),g=g.replace("%2$s",e),g=encodeURIComponent(g),f=a(".imagify-row-complete"),f.find(".imagify-ac-rt-total-gain").html(d),f.find(".imagify-ac-rt-total-original").html(e),f.find(".imagify-ac-chart").attr("data-percent",b),f.find(".imagify-sn-twitter").attr("href",imagifyBulk.labels.twitterShareURL+"&amp;text="+g),this.drawShareChart(),f.addClass("done").imagifyShow(),a("html, body").animate({scrollTop:f.offset().top},200),this.globalGain=0,this.globalOriginalSize=0,this.globalOptimizedSize=0},updateStats:function(b){var d;b&&a.isPlainObject(b)&&(c.imagify.bulk.charts.overview.donut.data&&(d=c.imagify.bulk.charts.overview.donut.data.datasets[0].data,b.unoptimized_attachments===d[0]&&b.optimized_attachments===d[1]&&b.errors_attachments===d[2])||(b.unconsumed_quota=b.unconsumed_quota.toFixed(1),a(".imagify-unconsumed-percent").html(b.unconsumed_quota+"%"),a(".imagify-unconsumed-bar").css("width",b.unconsumed_quota+"%"),a("#imagify-overview-chart-percent").html(b.optimized_attachments_percent+"<span>%</span>"),a(".imagify-total-percent").html(b.optimized_attachments_percent+"%"),c.imagify.bulk.drawOverviewChart([b.unoptimized_attachments,b.optimized_attachments,b.errors_attachments]),a("#imagify-total-optimized-attachments").html(b.already_optimized_attachments),a("#imagify-original-bar").find(".imagify-barnb").html(b.original_human),a("#imagify-optimized-bar").css("width",100-b.optimized_percent+"%").find(".imagify-barnb").html(b.optimized_human),a("#imagify-total-optimized-attachments-pct").html(b.optimized_percent+"%")))},openLevelSelectorFromButton:function(b){var c=a("#"+a(this).attr("aria-controls"));b.stopPropagation(),a(".imagify-level-selector-list").not(c).attr("aria-hidden","true"),c.attr("aria-hidden","false").find(":checked").trigger("focus.imagify")},syncLevelSelectorFromRadio:function(){var b=a(this).closest(".imagify-level-choice");b.addClass("imagify-current-level").attr("aria-current","true").siblings(".imagify-level-choice").removeClass("imagify-current-level").attr("aria-current","false"),b.closest(".imagify-level-selector").find(".imagify-current-level-info").html(b.find("label").html())},closeLevelSelectors:function(b){"keypress"===b.type&&27!==b.keyCode&&13!==b.keyCode||c.imagify.bulk.closeLevelSelector(a('.imagify-level-selector-list[aria-hidden="false"]'))},toggleOptimizationButton:function(){if(!c.imagify.bulk.hasMultipleRows&&!this.checked)return void a(this).prop("checked",!0);a('.imagify-bulk-table [name="group[]"]:checked').length?a("#imagify-bulk-action").removeAttr("disabled"):a("#imagify-bulk-action").attr("disabled","disabled")},toggleOptimizationDetails:function(b){var c,d=a(this),e=d.closest(".imagify-bulk-table").find(".imagify-bulk-table-details");c="open"===b.type||"close"!==b.type&&e.hasClass("hidden"),c?(d.html(d.data("label-hide")+'<span class="dashicons dashicons-no-alt"></span>'),e.imagifyShow()):(d.html(d.data("label-show")+'<span class="dashicons dashicons-menu"></span>'),e.imagifyHide())},maybeLaunchAllProcesses:function(){var b;a(this).attr("disabled")||a('.imagify-bulk-table [name="group[]"]:checked').length&&(c.imagify.bulk.hasBlockingError(!0)||(b=a("#tmpl-imagify-bulk-infos"),b.length?swal({title:imagifyBulk.labels.bulkInfoTitle,html:b.html(),type:"",customClass:"imagify-sweet-alert imagify-swal-has-subtitle imagify-before-bulk-infos",showCancelButton:!0,padding:0,width:554,confirmButtonText:imagifyBulk.labels.confirmBulk,cancelButtonText:imagifySwal.labels.cancelButtonText,reverseButtons:!0}).then(function(){var d=a('.imagify-bulk-table [name="group[]"]:checked').first().closest(".imagify-row-folder-type"),e=d.data("group-id"),f=d.data("context");a.get(ajaxurl+c.imagify.concat+"_wpnonce="+imagifyBulk.ajaxNonce+"&action="+imagifyBulk.ajaxActions.bulkInfoSeen+"&folder_type="+e+"&context="+f),b.remove(),c.imagify.bulk.launchAllProcesses()}).catch(swal.noop):c.imagify.bulk.launchAllProcesses()))},launchAllProcesses:function(){var b=a(c),d=a("#imagify-bulk-action"),e=!0;d.attr("disabled","disabled").find(".dashicons").addClass("rotate"),b.on("beforeunload",this.getConfirmMessage),a(".imagify-row-complete").imagifyHide(200,function(){a(this).removeClass("done")}),a(".imagify-show-table-details").trigger("close.imagify"),this.queue=[],this.status={},this.displayedWaitMessage=!1,this.processIsStopped=!1,this.globalGain=0,this.globalOriginalSize=0,this.globalOptimizedSize=0,a('.imagify-bulk-table [name="group[]"]:checked').each(function(){var b=a(this),d=b.closest(".imagify-row-folder-type"),f=d.data("group-id"),g=d.data("context"),h=d.find('.imagify-cell-level [name="level['+f+']"]:checked').val();if(c.imagify.bulk.queue.push({groupId:f,context:g,level:void 0===h?-1:parseInt(h,10)}),c.imagify.bulk.status[f]={isError:!1,id:"waiting"},e)return e=!1,!0;c.imagify.bulk.displayFolderRow("waiting",d)}),b.trigger("processQueue.imagify")},processQueue:function(){var b,d;if(!c.imagify.bulk.processIsStopped){if(!c.imagify.bulk.queue.length)return void a(c).trigger("queueEmpty.imagify");c.imagify.bulk.displayedWaitMessage||(swal({title:imagifyBulk.labels.waitTitle,html:imagifyBulk.labels.waitText,showConfirmButton:!1,padding:0,imageUrl:imagifyBulk.waitImageUrl,customClass:"imagify-sweet-alert"}).catch(swal.noop),c.imagify.bulk.displayedWaitMessage=!0),d=c.imagify.bulk.queue.shift(),b=a("#cb-select-"+d.groupId).closest(".imagify-row-folder-type"),c.imagify.bulk.status[d.groupId].id="fetching",c.imagify.bulk.displayFolderRow("working",b),a.get(c.imagify.bulk.getAjaxUrl("%GROUP_ID%Fetch",d)).done(function(e){if(!c.imagify.bulk.processIsStopped)if(swal.close(),e.success&&(a.isArray(e.data)||a.isPlainObject(e.data))){if(!a.isEmptyObject(e.data))return void a(c).trigger("optimizeFiles.imagify",[d,e.data]);if(c.imagify.bulk.status[d.groupId].id="no-images",!c.imagify.bulk.processIsStopped){if(c.imagify.bulk.hasMultipleRows&&a("#cb-select-"+d.groupId).prop("checked",!1),!c.imagify.bulk.queue.length)return void a(c).trigger("queueEmpty.imagify");c.imagify.bulk.displayFolderRow("resting",b),a(c).trigger("processQueue.imagify")}}else c.imagify.bulk.stopProcess(e.data.message,d)}).fail(function(){c.imagify.bulk.stopProcess("get-unoptimized-images",d)})}},optimizeFiles:function(b,d,e){var f,g,h=a("#cb-select-"+d.groupId).closest(".imagify-row-folder-type"),i=h.next(".imagify-row-working"),j=i.find(".imagify-cell-images-optimized span"),k=parseInt(j.text(),10),l=i.find(".imagify-cell-errors span"),m=parseInt(l.text(),10),n=h.closest(".imagify-bulk-table"),o=n.find(".imagify-row-progress"),p=o.find(".bar"),q={groupId:d.groupId,id:0,thumbnail:"",filename:"",status:"",icon:"",label:"",thumbnails:"",original_size_human:"",new_size_human:"",chartSuffix:"",percent_human:"",overall_saving_human:""};c.imagify.bulk.processIsStopped||(c.imagify.bulk.status[d.groupId].id="optimizing",n.find(".imagify-bulk-table-details thead").html(a("#tmpl-imagify-file-header-"+d.groupId).html()),g=n.find(".imagify-bulk-table-details tbody").text(""),p.css("width","0%").find(".percent").text("0%"),o.slideDown().attr("aria-hidden","false"),f=new ImagifyGulp({buffer_size:imagifyBulk.bufferSizes[d.context]||1,lib:c.imagify.bulk.getAjaxUrl("%GROUP_ID%Optimize",d),images:e,context:d.context}),f.before(function(b){var e=c.imagify.template("imagify-file-row-"+d.groupId);g.prepend(e(a.extend({},q,{status:"compressing",icon:"admin-generic rotate",label:imagifyBulk.labels.optimizing,chartSuffix:b.image_id},b)))}),f.each(function(b){var e=c.imagify.template("imagify-file-row-"+d.groupId),g=a("#"+d.groupId+"-"+b.image);return p.css("width",b.progress+"%").find(".percent").html(b.progress+"%"),b.success?(g.replaceWith(e(a.extend({},q,{status:"complete",icon:"yes",label:imagifyBulk.labels.complete,chartSuffix:b.image},b))),c.imagify.bulk.drawFileChart(a("#"+d.groupId+"-"+b.image).find(".imagify-cell-percentage canvas")),k+=1,void j.text(k)):"already-optimized"===b.error_code?(g.replaceWith(c.imagify.bulk.displayErrorInRow(e(a.extend({},q,{status:"complete",icon:"yes",label:imagifyBulk.labels.alreadyOptimized,chartSuffix:b.image},b)),b.error)),k+=1,void j.text(k)):(g.replaceWith(c.imagify.bulk.displayErrorInRow(e(a.extend({},q,{status:"error",icon:"dismiss",label:imagifyBulk.labels.error,chartSuffix:b.image},b)),b.error||b)),l.length?(m+=1,l.text(m)):(m=1,l=i.find(".imagify-cell-errors").html(imagifyBulk.labels.imagesErrorText.replace("%s","<span>1</span>")).find("span")),void("over-quota"===b.error_code&&(f.stopProcess(),c.imagify.bulk.stopProcess(b.error_code,d))))}),f.done(function(b){c.imagify.bulk.hasMultipleRows&&a("#cb-select-"+d.groupId).prop("checked",!1),b.global_original_size&&(c.imagify.bulk.globalGain+=parseInt(b.global_gain,10),c.imagify.bulk.globalOriginalSize+=parseInt(b.global_original_size,10),c.imagify.bulk.globalOptimizedSize+=parseInt(b.global_optimized_size,10)),c.imagify.bulk.processIsStopped||(c.imagify.bulk.status[d.groupId].isError||(c.imagify.bulk.status[d.groupId].id="done"),h.addClass("updating"),a.get(c.imagify.bulk.getAjaxUrl("getFolderData",d)).done(function(b){b.success&&a.each(b.data,function(a,b){h.children(".imagify-cell-"+a).html(b)}),c.imagify.bulk.displayFolderRow("resting",h)}).always(function(){h.removeClass("updating"),c.imagify.bulk.queue.length?a(c).trigger("processQueue.imagify"):a(c).trigger("queueEmpty.imagify")}))}),f.run())},queueEmpty:function(){var b=a(".imagify-bulk-table"),d={},e=!1,f=!0;c.imagify.bulk.displayShareBox(),c.imagify.bulk.queue=[],imagifyBulk.heartbeatId||a.get(ajaxurl,{_wpnonce:imagifyBulk.ajaxNonce,action:imagifyBulk.ajaxActions.getStats,types:c.imagify.bulk.getFolderTypes()}).done(function(a){a.success&&c.imagify.bulk.updateStats(a.data)}),a.isEmptyObject(c.imagify.bulk.status)||(a.each(c.imagify.bulk.status,function(a,b){return b.isError?(e=b.id,f=!1,!1):"no-images"!==b.id?(f=!1,!1):void 0}),e?("invalid-api-key"===e?d={title:imagifyBulk.labels.invalidAPIKeyTitle,type:"info"}:"over-quota"===e?d={title:imagifyBulk.labels.overQuotaTitle,html:a("#tmpl-imagify-overquota-alert").html(),type:"info",customClass:"imagify-swal-has-subtitle imagify-swal-error-header",showConfirmButton:!1}:"get-unoptimized-images"!==e&&"consumed-all-data"!==e||(d={title:imagifyBulk.labels.getUnoptimizedImagesErrorTitle,html:imagifyBulk.labels.getUnoptimizedImagesErrorText,type:"info"}),c.imagify.bulk.displayError(d)):f&&c.imagify.bulk.displayError({title:imagifyBulk.labels.noAttachmentToOptimizeTitle,html:imagifyBulk.labels.noAttachmentToOptimizeText,type:"info"})),c.imagify.bulk.status={},a(c).off("beforeunload",c.imagify.bulk.getConfirmMessage),c.imagify.bulk.displayFolderRow("resting",b.find(".imagify-row-folder-type").not(".updating")),b.find(".imagify-row-progress").slideUp().attr("aria-hidden","true").find(".bar").removeAttr("style").find(".percent").text("0%"),a('.imagify-bulk-table [name="group[]"]:checked').length?a("#imagify-bulk-action").removeAttr("disabled").find(".dashicons").removeClass("rotate"):a("#imagify-bulk-action").find(".dashicons").removeClass("rotate")},addHeartbeat:function(a,b){b.imagify_ids=b.imagify_ids||{},b.imagify_ids[imagifyBulk.heartbeatId]=1,b.imagify_types=c.imagify.bulk.getFolderTypes()},processHeartbeat:function(a,b){b.imagify_bulk_data&&c.imagify.bulk.updateStats(b.imagify_bulk_data)},addRequirementsHeartbeat:function(a,b){b.imagify_ids=b.imagify_ids||{},b.imagify_ids[imagifyBulk.reqsHeartbeatId]=1},processRequirementsHeartbeat:function(a,b){b.imagify_bulk_requirements&&(b=b.imagify_bulk_requirements,imagifyBulk.curlMissing=b.curl_missing,imagifyBulk.editorMissing=b.editor_missing,imagifyBulk.extHttpBlocked=b.external_http_blocked,imagifyBulk.apiDown=b.api_down,imagifyBulk.keyIsValid=b.key_is_valid,imagifyBulk.isOverQuota=b.is_over_quota)},share:function(a){var d,e;a.preventDefault(),c.innerWidth?(d=(c.innerWidth-700)/2,e=(c.innerHeight-290)/2):(d=(b.body.clientWidth-700)/2,e=(b.body.clientHeight-290)/2),c.open(this.href,"","status=no, scrollbars=no, menubar=no, top="+e+", left="+d+", width=700, height=290")}},c.imagify.bulk.init()}(jQuery,document,window);
imagify.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Imagify
4
  * Plugin URI: https://wordpress.org/plugins/imagify/
5
  * Description: Dramaticaly reduce image file sizes without losing quality, make your website load faster, boost your SEO and save money on your bandwidth using Imagify, the new most advanced image optimization tool.
6
- * Version: 1.7
7
  * Author: WP Media
8
  * Author URI: https://wp-media.me/
9
  * Licence: GPLv2
@@ -11,13 +11,13 @@
11
  * Text Domain: imagify
12
  * Domain Path: languages
13
  *
14
- * Copyright 2017 WP Media
15
  */
16
 
17
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
18
 
19
  // Imagify defines.
20
- define( 'IMAGIFY_VERSION' , '1.7' );
21
  define( 'IMAGIFY_SLUG' , 'imagify' );
22
  define( 'IMAGIFY_FILE' , __FILE__ );
23
  define( 'IMAGIFY_PATH' , realpath( plugin_dir_path( IMAGIFY_FILE ) ) . '/' );
@@ -61,7 +61,6 @@ function _imagify_init() {
61
 
62
  require( IMAGIFY_FUNCTIONS_PATH . 'options.php' );
63
  require( IMAGIFY_FUNCTIONS_PATH . 'formatting.php' );
64
- require( IMAGIFY_FUNCTIONS_PATH . 'files.php' );
65
  require( IMAGIFY_FUNCTIONS_PATH . 'admin.php' );
66
  require( IMAGIFY_FUNCTIONS_PATH . 'api.php' );
67
  require( IMAGIFY_FUNCTIONS_PATH . 'attachments.php' );
3
  * Plugin Name: Imagify
4
  * Plugin URI: https://wordpress.org/plugins/imagify/
5
  * Description: Dramaticaly reduce image file sizes without losing quality, make your website load faster, boost your SEO and save money on your bandwidth using Imagify, the new most advanced image optimization tool.
6
+ * Version: 1.7.1
7
  * Author: WP Media
8
  * Author URI: https://wp-media.me/
9
  * Licence: GPLv2
11
  * Text Domain: imagify
12
  * Domain Path: languages
13
  *
14
+ * Copyright 2018 WP Media
15
  */
16
 
17
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
18
 
19
  // Imagify defines.
20
+ define( 'IMAGIFY_VERSION' , '1.7.1' );
21
  define( 'IMAGIFY_SLUG' , 'imagify' );
22
  define( 'IMAGIFY_FILE' , __FILE__ );
23
  define( 'IMAGIFY_PATH' , realpath( plugin_dir_path( IMAGIFY_FILE ) ) . '/' );
61
 
62
  require( IMAGIFY_FUNCTIONS_PATH . 'options.php' );
63
  require( IMAGIFY_FUNCTIONS_PATH . 'formatting.php' );
 
64
  require( IMAGIFY_FUNCTIONS_PATH . 'admin.php' );
65
  require( IMAGIFY_FUNCTIONS_PATH . 'api.php' );
66
  require( IMAGIFY_FUNCTIONS_PATH . 'attachments.php' );
inc/3rd-party/3rd-party.php CHANGED
@@ -8,6 +8,7 @@ require( IMAGIFY_3RD_PARTY_PATH . 'amazon-s3-and-cloudfront/amazon-s3-and-cloudf
8
  require( IMAGIFY_3RD_PARTY_PATH . 'enable-media-replace/enable-media-replace.php' );
9
  require( IMAGIFY_3RD_PARTY_PATH . 'formidable-pro/formidable-pro.php' );
10
  require( IMAGIFY_3RD_PARTY_PATH . 'nextgen-gallery/nextgen-gallery.php' );
 
11
  require( IMAGIFY_3RD_PARTY_PATH . 'screets-lc.php' );
12
  require( IMAGIFY_3RD_PARTY_PATH . 'wp-real-media-library.php' );
13
  require( IMAGIFY_3RD_PARTY_PATH . 'wp-retina-2x.php' );
8
  require( IMAGIFY_3RD_PARTY_PATH . 'enable-media-replace/enable-media-replace.php' );
9
  require( IMAGIFY_3RD_PARTY_PATH . 'formidable-pro/formidable-pro.php' );
10
  require( IMAGIFY_3RD_PARTY_PATH . 'nextgen-gallery/nextgen-gallery.php' );
11
+ require( IMAGIFY_3RD_PARTY_PATH . 'regenerate-thumbnails/regenerate-thumbnails.php' );
12
  require( IMAGIFY_3RD_PARTY_PATH . 'screets-lc.php' );
13
  require( IMAGIFY_3RD_PARTY_PATH . 'wp-real-media-library.php' );
14
  require( IMAGIFY_3RD_PARTY_PATH . 'wp-retina-2x.php' );
inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf-attachment.php CHANGED
@@ -14,7 +14,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.1';
18
 
19
  /**
20
  * Tell if AS3CF settings will be used for this attachment.
@@ -30,27 +30,6 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
30
  */
31
  protected $delete_files;
32
 
33
- /**
34
- * The name of the transient that tells if optimization is processing.
35
- *
36
- * @var string.
37
- */
38
- protected $optimization_state_transient;
39
-
40
- /**
41
- * The constructor.
42
- *
43
- * @since 1.6.6
44
- * @author Grégory Viguier
45
- *
46
- * @param int $id The attachment ID.
47
- */
48
- public function __construct( $id = 0 ) {
49
- parent::__construct( $id );
50
-
51
- $this->optimization_state_transient = 'imagify-async-in-progress-' . $this->id;
52
- }
53
-
54
 
55
  /** ----------------------------------------------------------------------------------------- */
56
  /** ATTACHMENT PATHS AND URLS =============================================================== */
@@ -86,7 +65,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
86
 
87
  if ( $size_file ) {
88
  // It's not the full size.
89
- $file_path = dirname( $file_path ) . '/' . $size_file;
90
  }
91
 
92
  return $file_path;
@@ -122,7 +101,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
122
 
123
  if ( $size_file ) {
124
  // It's not the full size.
125
- $file_url = trailingslashit( dirname( $file_url ) ) . $size_file;
126
  }
127
 
128
  return $file_url;
@@ -175,9 +154,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
175
  return false;
176
  }
177
 
178
- $filesystem = imagify_get_filesystem();
179
-
180
- if ( ! $filesystem->exists( $attachment_path ) && ! $this->get_file_from_s3( $attachment_path ) ) {
181
  // The file doesn't exist and couldn't be retrieved from S3.
182
  return false;
183
  }
@@ -185,9 +162,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
185
  /**
186
  * Start the process.
187
  */
188
-
189
- // Set a "optimization status" transient.
190
- set_transient( $this->optimization_state_transient, true, 10 * MINUTE_IN_SECONDS );
191
 
192
  /** This hook is documented in /inc/classes/class-imagify-attachment.php. */
193
  do_action( 'before_imagify_optimize_attachment', $this->id );
@@ -202,11 +177,11 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
202
  $resized = $this->maybe_resize( $attachment_path );
203
 
204
  if ( $resized ) {
205
- $size = @getimagesize( $attachment_path );
206
 
207
- if ( isset( $size[0], $size[1] ) ) {
208
- $metadata['width'] = $size[0];
209
- $metadata['height'] = $size[1];
210
  $metadata_changed = true;
211
  }
212
  }
@@ -224,7 +199,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
224
  if ( $this->delete_files ) {
225
  $to_delete[] = $attachment_path;
226
  // This is used by AS3CF.
227
- $bytes = $filesystem->size( $attachment_path );
228
 
229
  if ( false !== $bytes ) {
230
  $metadata_changed = true;
@@ -257,7 +232,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
257
  $to_delete[] = $thumbnail_path;
258
 
259
  // Even if this size must not be optimized ($disallowed_sizes), we must fetch the file from S3 to get its size.
260
- if ( ! $filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
261
  // Doesn't exist and couldn't be retrieved from S3.
262
  $data['sizes'][ $size_key ] = array(
263
  'success' => false,
@@ -267,7 +242,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
267
  }
268
 
269
  // This is used by AS3CF.
270
- $bytes = $filesystem->size( $thumbnail_path );
271
 
272
  if ( false !== $bytes ) {
273
  $filesize_total += $bytes;
@@ -283,7 +258,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
283
  continue;
284
  }
285
 
286
- if ( ! $this->delete_files && ! $filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
287
  // Doesn't exist and couldn't be retrieved from S3.
288
  $data['sizes'][ $size_key ] = array(
289
  'success' => false,
@@ -356,8 +331,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
356
  $result = parent::optimize_missing_thumbnails( $optimization_level );
357
  $result_sizes = array();
358
 
359
- // Set the "optimization status" transient back.
360
- set_transient( $this->optimization_state_transient, true, 10 * MINUTE_IN_SECONDS );
361
 
362
  if ( is_array( $result ) ) {
363
  // All good.
@@ -370,7 +344,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
370
 
371
  if ( ! $result_sizes ) {
372
  // No thumbnails created.
373
- delete_transient( $this->optimization_state_transient );
374
  return $result;
375
  }
376
 
@@ -382,7 +356,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
382
 
383
  if ( ! $this->can_send_to_s3() ) {
384
  // The other thumbnails are not on S3, so we don't need to send the new ones.
385
- delete_transient( $this->optimization_state_transient );
386
  return $result;
387
  }
388
 
@@ -390,7 +364,6 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
390
  * The main file.
391
  */
392
  $attachment_path = $this->get_original_path();
393
- $filesystem = imagify_get_filesystem();
394
  $to_delete = array();
395
  $to_skip = array();
396
  $filesize_total = 0;
@@ -406,11 +379,11 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
406
  $result->add( 'no_attachment_path', __( 'Files could not be sent to Amazon S3.', 'imagify' ) );
407
  }
408
 
409
- delete_transient( $this->optimization_state_transient );
410
  return $result;
411
  }
412
 
413
- if ( ! $filesystem->exists( $attachment_path ) && ! $this->get_file_from_s3( $attachment_path ) ) {
414
  // The file doesn't exist and couldn't be retrieved from S3.
415
  if ( ! is_wp_error( $result ) ) {
416
  $result = new WP_Error( 'main_file_not_on_s3', __( 'The main image could not be retrieved from Amazon S3.', 'imagify' ), array(
@@ -420,7 +393,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
420
  $result->add( 'main_file_not_on_s3', __( 'The main image could not be retrieved from Amazon S3.', 'imagify' ) );
421
  }
422
 
423
- delete_transient( $this->optimization_state_transient );
424
  return $result;
425
  }
426
 
@@ -435,7 +408,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
435
  $to_delete = array_merge( $to_delete, $to_skip );
436
 
437
  // This is used by AS3CF.
438
- $bytes = $filesystem->size( $attachment_path );
439
 
440
  if ( false !== $bytes ) {
441
  $metadata_changed = true;
@@ -460,7 +433,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
460
  continue;
461
  }
462
 
463
- if ( ! $filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
464
  // The file doesn't exist and couldn't be retrieved from S3.
465
  if ( ! is_wp_error( $result ) ) {
466
  $result = new WP_Error( 'thumbnail_not_on_s3', __( 'This size could not be retrieved from Amazon S3.', 'imagify' ), array(
@@ -473,7 +446,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
473
  ) );
474
  }
475
 
476
- delete_transient( $this->optimization_state_transient );
477
  return $result;
478
  }
479
 
@@ -481,7 +454,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
481
  $to_delete[] = $thumbnail_path;
482
 
483
  // This is used by AS3CF.
484
- $bytes = $filesystem->size( $thumbnail_path );
485
 
486
  if ( false !== $bytes ) {
487
  $filesize_total += $bytes;
@@ -506,6 +479,19 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
506
  return $result;
507
  }
508
 
 
 
 
 
 
 
 
 
 
 
 
 
 
509
  /**
510
  * Process an attachment restoration from the backup file.
511
  *
@@ -530,17 +516,16 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
530
 
531
  $backup_path = $this->get_backup_path();
532
  $attachment_path = $this->get_original_path();
533
- $filesystem = imagify_get_filesystem();
534
 
535
  if ( ! $attachment_path ) {
536
  return false;
537
  }
538
 
539
  // Create the original image from the backup.
540
- $filesystem->copy( $backup_path, $attachment_path, true );
541
- imagify_chmod_file( $attachment_path );
542
 
543
- if ( ! $filesystem->exists( $attachment_path ) ) {
544
  return false;
545
  }
546
 
@@ -571,7 +556,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
571
 
572
  if ( $this->delete_files ) {
573
  // This is used by AS3CF.
574
- $bytes = $filesystem->size( $attachment_path );
575
 
576
  if ( false !== $bytes ) {
577
  $filesize_total += $bytes;
@@ -591,7 +576,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
591
 
592
  if ( $this->delete_files ) {
593
  // This is used by AS3CF.
594
- $bytes = $filesystem->size( $thumbnail_path );
595
 
596
  if ( false !== $bytes ) {
597
  $filesize_total += $bytes;
@@ -666,7 +651,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
666
  /**
667
  * Delete the "optimization status" transient.
668
  */
669
- delete_transient( $this->optimization_state_transient );
670
  }
671
 
672
  /**
@@ -681,9 +666,9 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
681
  protected function maybe_resize( $attachment_path ) {
682
  $do_resize = get_imagify_option( 'resize_larger' );
683
  $resize_width = get_imagify_option( 'resize_larger_w' );
684
- $attachment_size = @getimagesize( $attachment_path );
685
 
686
- if ( ! $do_resize || ! isset( $attachment_size[0] ) || $resize_width >= $attachment_size[0] ) {
687
  return false;
688
  }
689
 
@@ -699,17 +684,15 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
699
  return false;
700
  }
701
 
702
- $filesystem = imagify_get_filesystem();
703
-
704
- $filesystem->move( $resized_attachment_path, $attachment_path, true );
705
- imagify_chmod_file( $attachment_path );
706
 
707
  // If resized temp file still exists, delete it.
708
- if ( $filesystem->exists( $resized_attachment_path ) ) {
709
- $filesystem->delete( $resized_attachment_path );
710
  }
711
 
712
- return $filesystem->exists( $attachment_path );
713
  }
714
 
715
  /**
@@ -813,15 +796,14 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
813
  return false;
814
  }
815
 
816
- $filesystem = imagify_get_filesystem();
817
- $directory = dirname( $s3_object['key'] );
818
- $directory = '.' === $directory || '' === $directory ? '' : $directory . '/';
819
- $s3_object['key'] = $directory . wp_basename( $file_path );
820
 
821
  // Retrieve file from S3.
822
  $as3cf->plugin_compat->copy_s3_file_to_server( $s3_object, $file_path );
823
 
824
- return $filesystem->exists( $file_path ) ? $file_path : false;
825
  }
826
 
827
  /**
@@ -923,13 +905,11 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
923
  return false;
924
  }
925
 
926
- $filesystem = imagify_get_filesystem();
927
-
928
- if ( ! $filesystem->exists( $file_path ) ) {
929
  return true;
930
  }
931
 
932
- return $filesystem->delete( $file_path, false, 'f' );
933
  }
934
 
935
  /**
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.1.2';
18
 
19
  /**
20
  * Tell if AS3CF settings will be used for this attachment.
30
  */
31
  protected $delete_files;
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  /** ----------------------------------------------------------------------------------------- */
35
  /** ATTACHMENT PATHS AND URLS =============================================================== */
65
 
66
  if ( $size_file ) {
67
  // It's not the full size.
68
+ $file_path = $this->filesystem->dir_path( $file_path ) . $size_file;
69
  }
70
 
71
  return $file_path;
101
 
102
  if ( $size_file ) {
103
  // It's not the full size.
104
+ $file_url = $this->filesystem->dir_path( $file_url ) . $size_file;
105
  }
106
 
107
  return $file_url;
154
  return false;
155
  }
156
 
157
+ if ( ! $this->filesystem->exists( $attachment_path ) && ! $this->get_file_from_s3( $attachment_path ) ) {
 
 
158
  // The file doesn't exist and couldn't be retrieved from S3.
159
  return false;
160
  }
162
  /**
163
  * Start the process.
164
  */
165
+ $this->set_running_status();
 
 
166
 
167
  /** This hook is documented in /inc/classes/class-imagify-attachment.php. */
168
  do_action( 'before_imagify_optimize_attachment', $this->id );
177
  $resized = $this->maybe_resize( $attachment_path );
178
 
179
  if ( $resized ) {
180
+ $size = $this->filesystem->get_image_size( $attachment_path );
181
 
182
+ if ( $size ) {
183
+ $metadata['width'] = $size['width'];
184
+ $metadata['height'] = $size['height'];
185
  $metadata_changed = true;
186
  }
187
  }
199
  if ( $this->delete_files ) {
200
  $to_delete[] = $attachment_path;
201
  // This is used by AS3CF.
202
+ $bytes = $this->filesystem->size( $attachment_path );
203
 
204
  if ( false !== $bytes ) {
205
  $metadata_changed = true;
232
  $to_delete[] = $thumbnail_path;
233
 
234
  // Even if this size must not be optimized ($disallowed_sizes), we must fetch the file from S3 to get its size.
235
+ if ( ! $this->filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
236
  // Doesn't exist and couldn't be retrieved from S3.
237
  $data['sizes'][ $size_key ] = array(
238
  'success' => false,
242
  }
243
 
244
  // This is used by AS3CF.
245
+ $bytes = $this->filesystem->size( $thumbnail_path );
246
 
247
  if ( false !== $bytes ) {
248
  $filesize_total += $bytes;
258
  continue;
259
  }
260
 
261
+ if ( ! $this->delete_files && ! $this->filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
262
  // Doesn't exist and couldn't be retrieved from S3.
263
  $data['sizes'][ $size_key ] = array(
264
  'success' => false,
331
  $result = parent::optimize_missing_thumbnails( $optimization_level );
332
  $result_sizes = array();
333
 
334
+ $this->set_running_status();
 
335
 
336
  if ( is_array( $result ) ) {
337
  // All good.
344
 
345
  if ( ! $result_sizes ) {
346
  // No thumbnails created.
347
+ $this->delete_running_status();
348
  return $result;
349
  }
350
 
356
 
357
  if ( ! $this->can_send_to_s3() ) {
358
  // The other thumbnails are not on S3, so we don't need to send the new ones.
359
+ $this->delete_running_status();
360
  return $result;
361
  }
362
 
364
  * The main file.
365
  */
366
  $attachment_path = $this->get_original_path();
 
367
  $to_delete = array();
368
  $to_skip = array();
369
  $filesize_total = 0;
379
  $result->add( 'no_attachment_path', __( 'Files could not be sent to Amazon S3.', 'imagify' ) );
380
  }
381
 
382
+ $this->delete_running_status();
383
  return $result;
384
  }
385
 
386
+ if ( ! $this->filesystem->exists( $attachment_path ) && ! $this->get_file_from_s3( $attachment_path ) ) {
387
  // The file doesn't exist and couldn't be retrieved from S3.
388
  if ( ! is_wp_error( $result ) ) {
389
  $result = new WP_Error( 'main_file_not_on_s3', __( 'The main image could not be retrieved from Amazon S3.', 'imagify' ), array(
393
  $result->add( 'main_file_not_on_s3', __( 'The main image could not be retrieved from Amazon S3.', 'imagify' ) );
394
  }
395
 
396
+ $this->delete_running_status();
397
  return $result;
398
  }
399
 
408
  $to_delete = array_merge( $to_delete, $to_skip );
409
 
410
  // This is used by AS3CF.
411
+ $bytes = $this->filesystem->size( $attachment_path );
412
 
413
  if ( false !== $bytes ) {
414
  $metadata_changed = true;
433
  continue;
434
  }
435
 
436
+ if ( ! $this->filesystem->exists( $thumbnail_path ) && ! $this->get_file_from_s3( $thumbnail_path ) ) {
437
  // The file doesn't exist and couldn't be retrieved from S3.
438
  if ( ! is_wp_error( $result ) ) {
439
  $result = new WP_Error( 'thumbnail_not_on_s3', __( 'This size could not be retrieved from Amazon S3.', 'imagify' ), array(
446
  ) );
447
  }
448
 
449
+ $this->delete_running_status();
450
  return $result;
451
  }
452
 
454
  $to_delete[] = $thumbnail_path;
455
 
456
  // This is used by AS3CF.
457
+ $bytes = $this->filesystem->size( $thumbnail_path );
458
 
459
  if ( false !== $bytes ) {
460
  $filesize_total += $bytes;
479
  return $result;
480
  }
481
 
482
+ /**
483
+ * Re-optimize the given thumbnail sizes to the same level.
484
+ * Not supported yet in this context.
485
+ *
486
+ * @since 1.7.1
487
+ * @access public
488
+ * @author Grégory Viguier
489
+ *
490
+ * @param array $sizes The sizes to optimize.
491
+ * @return array|void A WP_Error object on failure.
492
+ */
493
+ public function reoptimize_thumbnails( $sizes ) {}
494
+
495
  /**
496
  * Process an attachment restoration from the backup file.
497
  *
516
 
517
  $backup_path = $this->get_backup_path();
518
  $attachment_path = $this->get_original_path();
 
519
 
520
  if ( ! $attachment_path ) {
521
  return false;
522
  }
523
 
524
  // Create the original image from the backup.
525
+ $this->filesystem->copy( $backup_path, $attachment_path, true );
526
+ $this->filesystem->chmod_file( $attachment_path );
527
 
528
+ if ( ! $this->filesystem->exists( $attachment_path ) ) {
529
  return false;
530
  }
531
 
556
 
557
  if ( $this->delete_files ) {
558
  // This is used by AS3CF.
559
+ $bytes = $this->filesystem->size( $attachment_path );
560
 
561
  if ( false !== $bytes ) {
562
  $filesize_total += $bytes;
576
 
577
  if ( $this->delete_files ) {
578
  // This is used by AS3CF.
579
+ $bytes = $this->filesystem->size( $thumbnail_path );
580
 
581
  if ( false !== $bytes ) {
582
  $filesize_total += $bytes;
651
  /**
652
  * Delete the "optimization status" transient.
653
  */
654
+ $this->delete_running_status();
655
  }
656
 
657
  /**
666
  protected function maybe_resize( $attachment_path ) {
667
  $do_resize = get_imagify_option( 'resize_larger' );
668
  $resize_width = get_imagify_option( 'resize_larger_w' );
669
+ $attachment_size = $this->filesystem->get_image_size( $attachment_path );
670
 
671
+ if ( ! $do_resize || ! $attachment_size || $resize_width >= $attachment_size['width'] ) {
672
  return false;
673
  }
674
 
684
  return false;
685
  }
686
 
687
+ $this->filesystem->move( $resized_attachment_path, $attachment_path, true );
688
+ $this->filesystem->chmod_file( $attachment_path );
 
 
689
 
690
  // If resized temp file still exists, delete it.
691
+ if ( $this->filesystem->exists( $resized_attachment_path ) ) {
692
+ $this->filesystem->delete( $resized_attachment_path );
693
  }
694
 
695
+ return $this->filesystem->exists( $attachment_path );
696
  }
697
 
698
  /**
796
  return false;
797
  }
798
 
799
+ $directory = $this->filesystem->dir_path( $s3_object['key'] );
800
+ $directory = $this->filesystem->is_root( $directory ) ? '' : $directory;
801
+ $s3_object['key'] = $directory . $this->filesystem->file_name( $file_path );
 
802
 
803
  // Retrieve file from S3.
804
  $as3cf->plugin_compat->copy_s3_file_to_server( $s3_object, $file_path );
805
 
806
+ return $this->filesystem->exists( $file_path ) ? $file_path : false;
807
  }
808
 
809
  /**
905
  return false;
906
  }
907
 
908
+ if ( ! $this->filesystem->exists( $file_path ) ) {
 
 
909
  return true;
910
  }
911
 
912
+ return $this->filesystem->delete( $file_path, false, 'f' );
913
  }
914
 
915
  /**
inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf.php CHANGED
@@ -14,7 +14,7 @@ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.0.1';
18
 
19
  /**
20
  * Context used with get_imagify_attachment().
@@ -31,6 +31,16 @@ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
31
  */
32
  protected $uploads = array();
33
 
 
 
 
 
 
 
 
 
 
 
34
  /**
35
  * The single instance of the class.
36
  *
@@ -62,7 +72,9 @@ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
62
  * @since 1.6.6
63
  * @author Grégory Viguier
64
  */
65
- protected function __construct() {}
 
 
66
 
67
  /**
68
  * Launch the hooks.
@@ -184,7 +196,7 @@ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
184
  /** This filter is documented in inc/functions/process.php. */
185
  $file_path = apply_filters( 'imagify_file_path', $file_path, $id, 'as3cf_maybe_copy_files_from_s3' );
186
 
187
- if ( ! $file_path || file_exists( $file_path ) ) {
188
  // The file exists, no need to retrieve it from S3.
189
  unset( $ids[ $id ] );
190
  } else {
@@ -231,14 +243,14 @@ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
231
  $attachment_optimization_level = isset( $results['optimization_levels'][ $id ] ) ? $results['optimization_levels'][ $id ] : false;
232
 
233
  // Don't try to re-optimize if there is no backup file.
234
- if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! file_exists( $attachment_backup_path ) ) {
235
  unset( $s3_data[ $id ], $ids[ $id ] );
236
  continue;
237
  }
238
 
239
- $directory = dirname( $s3_object['key'] );
240
- $directory = '.' === $directory || '' === $directory ? '' : $directory . '/';
241
- $s3_object['key'] = $directory . wp_basename( $file_path );
242
 
243
  // Retrieve file from S3.
244
  $as3cf->plugin_compat->copy_s3_file_to_server( $s3_object, $file_path );
@@ -259,14 +271,14 @@ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
259
  * @param array $image_ids An array of all attachment IDs.
260
  * @return bool|array False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails).
261
  */
262
- function add_stats_for_s3_files( $size_and_count, $image_id, $files, $image_ids ) {
263
  static $data;
264
 
265
  if ( is_array( $size_and_count ) ) {
266
  return $size_and_count;
267
  }
268
 
269
- if ( file_exists( $files['full'] ) ) {
270
  // If the full size is on the server, that probably means all files are on the server too.
271
  return $size_and_count;
272
  }
@@ -341,7 +353,7 @@ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
341
  }
342
 
343
  if ( ! isset( $auto_optimize ) ) {
344
- $auto_optimize = imagify_valid_key() && get_imagify_option( 'auto_optimize' );
345
  }
346
 
347
  if ( $is_new_upload ) {
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.0.2';
18
 
19
  /**
20
  * Context used with get_imagify_attachment().
31
  */
32
  protected $uploads = array();
33
 
34
+ /**
35
+ * Filesystem object.
36
+ *
37
+ * @var object Imagify_Filesystem
38
+ * @since 1.7.1
39
+ * @access protected
40
+ * @author Grégory Viguier
41
+ */
42
+ protected $filesystem;
43
+
44
  /**
45
  * The single instance of the class.
46
  *
72
  * @since 1.6.6
73
  * @author Grégory Viguier
74
  */
75
+ protected function __construct() {
76
+ $this->filesystem = Imagify_Filesystem::get_instance();
77
+ }
78
 
79
  /**
80
  * Launch the hooks.
196
  /** This filter is documented in inc/functions/process.php. */
197
  $file_path = apply_filters( 'imagify_file_path', $file_path, $id, 'as3cf_maybe_copy_files_from_s3' );
198
 
199
+ if ( ! $file_path || $this->filesystem->exists( $file_path ) ) {
200
  // The file exists, no need to retrieve it from S3.
201
  unset( $ids[ $id ] );
202
  } else {
243
  $attachment_optimization_level = isset( $results['optimization_levels'][ $id ] ) ? $results['optimization_levels'][ $id ] : false;
244
 
245
  // Don't try to re-optimize if there is no backup file.
246
+ if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! $this->filesystem->exists( $attachment_backup_path ) ) {
247
  unset( $s3_data[ $id ], $ids[ $id ] );
248
  continue;
249
  }
250
 
251
+ $directory = $this->filesystem->dir_path( $s3_object['key'] );
252
+ $directory = $this->filesystem->is_root( $directory ) ? '' : $directory;
253
+ $s3_object['key'] = $directory . $this->filesystem->file_name( $file_path );
254
 
255
  // Retrieve file from S3.
256
  $as3cf->plugin_compat->copy_s3_file_to_server( $s3_object, $file_path );
271
  * @param array $image_ids An array of all attachment IDs.
272
  * @return bool|array False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails).
273
  */
274
+ public function add_stats_for_s3_files( $size_and_count, $image_id, $files, $image_ids ) {
275
  static $data;
276
 
277
  if ( is_array( $size_and_count ) ) {
278
  return $size_and_count;
279
  }
280
 
281
+ if ( $this->filesystem->exists( $files['full'] ) ) {
282
  // If the full size is on the server, that probably means all files are on the server too.
283
  return $size_and_count;
284
  }
353
  }
354
 
355
  if ( ! isset( $auto_optimize ) ) {
356
+ $auto_optimize = Imagify_Requirements::is_api_key_valid() && get_imagify_option( 'auto_optimize' );
357
  }
358
 
359
  if ( $is_new_upload ) {
inc/3rd-party/enable-media-replace/inc/classes/class-imagify-enable-media-replace.php CHANGED
@@ -37,6 +37,16 @@ class Imagify_Enable_Media_Replace {
37
  */
38
  protected $old_backup_path;
39
 
 
 
 
 
 
 
 
 
 
 
40
  /**
41
  * The single instance of the class.
42
  *
@@ -68,7 +78,9 @@ class Imagify_Enable_Media_Replace {
68
  * @since 1.6.9
69
  * @author Grégory Viguier
70
  */
71
- protected function __construct() {}
 
 
72
 
73
  /**
74
  * Launch the hooks before the files and data are replaced.
@@ -156,9 +168,7 @@ class Imagify_Enable_Media_Replace {
156
  /**
157
  * Delete the old backup file.
158
  */
159
- $filesystem = imagify_get_filesystem();
160
-
161
- if ( ! $this->old_backup_path || ! $filesystem->exists( $this->old_backup_path ) ) {
162
  // The user didn't choose to rename the files, or there is no old backup.
163
  $this->old_backup_path = null;
164
  return $return_url;
@@ -173,7 +183,7 @@ class Imagify_Enable_Media_Replace {
173
  }
174
 
175
  // Finally, delete the old backup file.
176
- $filesystem->delete( $this->old_backup_path );
177
 
178
  $this->old_backup_path = null;
179
  return $return_url;
37
  */
38
  protected $old_backup_path;
39
 
40
+ /**
41
+ * Filesystem object.
42
+ *
43
+ * @var object Imagify_Filesystem
44
+ * @since 1.7.1
45
+ * @access protected
46
+ * @author Grégory Viguier
47
+ */
48
+ protected $filesystem;
49
+
50
  /**
51
  * The single instance of the class.
52
  *
78
  * @since 1.6.9
79
  * @author Grégory Viguier
80
  */
81
+ protected function __construct() {
82
+ $this->filesystem = Imagify_Filesystem::get_instance();
83
+ }
84
 
85
  /**
86
  * Launch the hooks before the files and data are replaced.
168
  /**
169
  * Delete the old backup file.
170
  */
171
+ if ( ! $this->old_backup_path || ! $this->filesystem->exists( $this->old_backup_path ) ) {
 
 
172
  // The user didn't choose to rename the files, or there is no old backup.
173
  $this->old_backup_path = null;
174
  return $return_url;
183
  }
184
 
185
  // Finally, delete the old backup file.
186
+ $this->filesystem->delete( $this->old_backup_path );
187
 
188
  $this->old_backup_path = null;
189
  return $return_url;
inc/3rd-party/hosting/siteground.php CHANGED
@@ -3,9 +3,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
  add_filter( 'http_request_args', 'imagify_siteground_change_user_agent', 10, 2 );
5
  /**
6
- * Filter the arguments used in a HTTP request to change the User Agent for requests "to self".
7
- * SiteGround blocks (error 403) HTTP requests with a User-Agent containing the site's domain.
8
- * The problem is that, for a given site at https://example.com, WordPress uses the UA `WordPress/4.8.2; https://example.com`.
9
  *
10
  * @since 1.6.13
11
  * @author Grégory Viguier
@@ -16,20 +14,55 @@ add_filter( 'http_request_args', 'imagify_siteground_change_user_agent', 10, 2 )
16
  */
17
  function imagify_siteground_change_user_agent( $r, $url ) {
18
  static $user_agent;
19
- $site_url = site_url( '/' );
20
 
21
- if ( false === strpos( $url, $site_url ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  return $r;
23
  }
24
 
25
- $site_url = wp_parse_url( $site_url );
 
 
 
 
 
 
26
 
27
- if ( false === strpos( $r['user-agent'], $site_url['host'] ) ) {
28
  return $r;
29
  }
30
 
 
31
  if ( ! isset( $user_agent ) ) {
32
  $user_agent = wp_generate_password( 12, false );
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
 
35
  $r['user-agent'] = $user_agent;
3
 
4
  add_filter( 'http_request_args', 'imagify_siteground_change_user_agent', 10, 2 );
5
  /**
6
+ * Filter the arguments used in a HTTP request to change the User Agent for requests "to self", to prevent firewalls to be triggered.
 
 
7
  *
8
  * @since 1.6.13
9
  * @author Grégory Viguier
14
  */
15
  function imagify_siteground_change_user_agent( $r, $url ) {
16
  static $user_agent;
17
+ static $site_url;
18
 
19
+ $url = wp_parse_url( $url );
20
+
21
+ if ( empty( $url['path'] ) ) {
22
+ return $r;
23
+ }
24
+
25
+ $paths = array(
26
+ '/wp-admin/admin-ajax.php',
27
+ '/wp-admin/admin-post.php',
28
+ '/wp-cron.php',
29
+ );
30
+
31
+ if ( ! isset( $site_url ) ) {
32
+ $site_url = wp_parse_url( site_url( '/' ) );
33
+ }
34
+
35
+ // Limit to requests to self.
36
+ if ( false === strpos( $url['path'], $site_url['path'] ) && false === strpos( $site_url['path'], $url['path'] ) ) {
37
  return $r;
38
  }
39
 
40
+ // Limit to requests to admin-ajax.php, admin-post.php, and wp-cron.php.
41
+ foreach ( $paths as $i => $path ) {
42
+ if ( false !== strpos( $url['path'], $path ) ) {
43
+ $paths = false;
44
+ break;
45
+ }
46
+ }
47
 
48
+ if ( $paths ) {
49
  return $r;
50
  }
51
 
52
+ // Randomize the User-Agent.
53
  if ( ! isset( $user_agent ) ) {
54
  $user_agent = wp_generate_password( 12, false );
55
+ /**
56
+ * Filter the User-Agent used for requests "to self".
57
+ *
58
+ * @since 1.7.1
59
+ * @author Grégory Viguier
60
+ *
61
+ * @param string $user_agent The User-Agent.
62
+ * @param array $r An array of HTTP request arguments.
63
+ * @param array $url The request URL, parsed.
64
+ */
65
+ $user_agent = apply_filters( 'imagify_user_agent_for_internal_requests', $user_agent, $r, $url );
66
  }
67
 
68
  $r['user-agent'] = $user_agent;
inc/3rd-party/nextgen-gallery/inc/admin/bulk.php CHANGED
@@ -1,18 +1,37 @@
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
- add_filter( 'imagify_bulk_page_data', 'imagify_ngg_bulk_page_data' );
5
  /**
6
- * Filter the data to use on the bulk optimization page.
7
  *
8
- * @since 1.7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  * @author Grégory Viguier
10
  *
11
- * @param array $data The data to use.
 
12
  * @return array
13
  */
14
- function imagify_ngg_bulk_page_data( $data ) {
15
- if ( empty( $_GET['page'] ) || imagify_get_ngg_bulk_screen_slug() !== $_GET['page'] ) { // WPCS: CSRF ok.
16
  return $data;
17
  }
18
 
@@ -33,8 +52,29 @@ function imagify_ngg_bulk_page_data( $data ) {
33
  $data['errors_attachments'] += imagify_ngg_count_error_attachments();
34
  // Stats block.
35
  $data['already_optimized_attachments'] += $total_saving_data['count'];
36
- $data['original_size'] += $total_saving_data['original_size'];
37
- $data['optimized_size'] += $total_saving_data['optimized_size'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  // Limits.
39
  $data['unoptimized_attachment_limit'] += imagify_get_unoptimized_attachment_limit();
40
  // Group.
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
+ add_filter( 'imagify_bulk_page_types', 'imagify_ngg_bulk_page_types' );
5
  /**
6
+ * Filter the types to display in the bulk optimization page.
7
  *
8
+ * @since 1.7.1
9
+ * @author Grégory Viguier
10
+ *
11
+ * @param array $types The folder types displayed on the page. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
12
+ * @return array
13
+ */
14
+ function imagify_ngg_bulk_page_types( $types ) {
15
+ if ( ! empty( $_GET['page'] ) && imagify_get_ngg_bulk_screen_slug() === $_GET['page'] ) { // WPCS: CSRF ok.
16
+ $types['library|NGG'] = 1;
17
+ }
18
+
19
+ return $types;
20
+ }
21
+
22
+ add_filter( 'imagify_bulk_stats', 'imagify_ngg_bulk_stats', 10, 2 );
23
+ /**
24
+ * Filter the generic stats used in the bulk optimization page.
25
+ *
26
+ * @since 1.7.1
27
  * @author Grégory Viguier
28
  *
29
+ * @param array $data The data.
30
+ * @param array $types The folder types. They are passed as array keys.
31
  * @return array
32
  */
33
+ function imagify_ngg_bulk_stats( $data, $types ) {
34
+ if ( ! isset( $types['library|NGG'] ) ) {
35
  return $data;
36
  }
37
 
52
  $data['errors_attachments'] += imagify_ngg_count_error_attachments();
53
  // Stats block.
54
  $data['already_optimized_attachments'] += $total_saving_data['count'];
55
+ $data['original_human'] += $total_saving_data['original_size'];
56
+ $data['optimized_human'] += $total_saving_data['optimized_size'];
57
+
58
+ return $data;
59
+ }
60
+
61
+ add_filter( 'imagify_bulk_page_data', 'imagify_ngg_bulk_page_data', 10, 2 );
62
+ /**
63
+ * Filter the data to use on the bulk optimization page.
64
+ *
65
+ * @since 1.7
66
+ * @since 1.7.1 Added the $types parameter.
67
+ * @author Grégory Viguier
68
+ *
69
+ * @param array $data The data to use.
70
+ * @param array $types The folder types displayed on the page. They are passed as array keys.
71
+ * @return array
72
+ */
73
+ function imagify_ngg_bulk_page_data( $data, $types ) {
74
+ if ( ! isset( $types['library|NGG'] ) ) {
75
+ return $data;
76
+ }
77
+
78
  // Limits.
79
  $data['unoptimized_attachment_limit'] += imagify_get_unoptimized_attachment_limit();
80
  // Group.
inc/3rd-party/nextgen-gallery/inc/admin/enqueue.php CHANGED
@@ -33,7 +33,6 @@ function _imagify_ngg_admin_print_styles() {
33
  $assets->remove_deferred_localization( 'bulk', 'imagifyBulk' );
34
 
35
  $l10n = $assets->get_localization_data( 'bulk', array(
36
- 'heartbeatId' => 'update_ngg_bulk_data',
37
  'bufferSizes' => array(
38
  'NGG' => get_imagify_bulk_buffer_size( 3 ),
39
  ),
33
  $assets->remove_deferred_localization( 'bulk', 'imagifyBulk' );
34
 
35
  $l10n = $assets->get_localization_data( 'bulk', array(
 
36
  'bufferSizes' => array(
37
  'NGG' => get_imagify_bulk_buffer_size( 3 ),
38
  ),
inc/3rd-party/nextgen-gallery/inc/admin/heartbeat.php CHANGED
@@ -3,43 +3,6 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
  global $pagenow;
5
 
6
- add_filter( 'heartbeat_received', '_imagify_ngg_heartbeat_received', 10, 2 );
7
- /**
8
- * Prepare the data that goes back with the Heartbeat API.
9
- *
10
- * @since 1.5
11
- *
12
- * @param array $response The Heartbeat response.
13
- * @param array $data The $_POST data sent.
14
- * @return array
15
- */
16
- function _imagify_ngg_heartbeat_received( $response, $data ) {
17
- if ( ! isset( $data['imagify_heartbeat'] ) || 'update_ngg_bulk_data' !== $data['imagify_heartbeat'] ) {
18
- return $response;
19
- }
20
-
21
- add_filter( 'imagify_count_saving_data', 'imagify_ngg_count_saving_data', 8 );
22
- $saving_data = imagify_count_saving_data();
23
- $user = new Imagify_User();
24
-
25
- $response['imagify_bulk_data'] = array(
26
- // User account.
27
- 'unconsumed_quota' => is_wp_error( $user ) ? 0 : $user->get_percent_unconsumed_quota(),
28
- // Global chart.
29
- 'optimized_attachments_percent' => imagify_ngg_percent_optimized_attachments(),
30
- 'unoptimized_attachments' => imagify_ngg_count_unoptimized_attachments(),
31
- 'optimized_attachments' => imagify_ngg_count_optimized_attachments(),
32
- 'errors_attachments' => imagify_ngg_count_error_attachments(),
33
- // Stats block.
34
- 'already_optimized_attachments' => number_format_i18n( $saving_data['count'] ),
35
- 'original_human' => imagify_size_format( $saving_data['original_size'], 1 ),
36
- 'optimized_human' => imagify_size_format( $saving_data['optimized_size'], 1 ),
37
- 'optimized_percent' => $saving_data['percent'],
38
- );
39
-
40
- return $response;
41
- }
42
-
43
  /**
44
  * Update the Heartbeat API settings.
45
  *
3
 
4
  global $pagenow;
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  /**
7
  * Update the Heartbeat API settings.
8
  *
inc/3rd-party/nextgen-gallery/inc/classes/class-imagify-ngg-attachment.php CHANGED
@@ -14,7 +14,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.3';
18
 
19
  /**
20
  * The attachment SQL DB class.
@@ -65,10 +65,13 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
65
 
66
  $this->get_row();
67
 
 
 
 
68
  // Load nggAdmin class.
69
  $ngg_admin_functions_path = WP_PLUGIN_DIR . '/' . NGGFOLDER . '/products/photocrati_nextgen/modules/ngglegacy/admin/functions.php';
70
 
71
- if ( ! class_exists( 'nggAdmin' ) && file_exists( $ngg_admin_functions_path ) ) {
72
  require_once( $ngg_admin_functions_path );
73
  }
74
  }
@@ -137,7 +140,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
137
  return false;
138
  }
139
 
140
- return site_url( '/' ) . imagify_make_file_path_relative( $this->get_raw_backup_path() );
141
  }
142
 
143
  /**
@@ -226,7 +229,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
226
  return $this->is_mime_type_supported;
227
  }
228
 
229
- $mime_type = imagify_get_mime_type_from_file( $this->get_original_path() );
230
 
231
  if ( ! $mime_type ) {
232
  $this->is_mime_type_supported = false;
@@ -267,13 +270,13 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
267
  * @return void
268
  */
269
  public function update_metadata_size() {
270
- $size = @getimagesize( $this->get_original_path() );
271
 
272
- if ( isset( $size[0], $size[1] ) ) {
273
- $this->image->meta_data['width'] = $size[0];
274
- $this->image->meta_data['height'] = $size[1];
275
- $this->image->meta_data['full']['width'] = $size[0];
276
- $this->image->meta_data['full']['height'] = $size[1];
277
 
278
  nggdb::update_image_meta( $this->id, $this->image->meta_data );
279
  }
@@ -353,8 +356,8 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
353
  $optimized_size = (int) $response->new_size;
354
  } else {
355
  $file_path = $this->get_original_path();
356
- $file_path = $file_path && imagify_get_filesystem()->exists( $file_path ) ? $file_path : false;
357
- $optimized_size = $file_path ? imagify_get_filesystem()->size( $file_path ) : 0;
358
  }
359
 
360
  if ( $original_size && $optimized_size ) {
@@ -420,7 +423,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
420
  */
421
  do_action( 'before_imagify_ngg_optimize_attachment', $this->id );
422
 
423
- set_transient( 'imagify-ngg-async-in-progress-' . $this->id, true, 10 * MINUTE_IN_SECONDS );
424
 
425
  // Optimize the original size.
426
  $response = do_imagify( $attachment_path, array(
@@ -442,7 +445,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
442
 
443
  if ( ! $data ) {
444
  // Already optimized.
445
- delete_transient( 'imagify-ngg-async-in-progress-' . $this->id );
446
  return;
447
  }
448
 
@@ -471,7 +474,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
471
  $image = $storage->_image_mapper->find( $this->id );
472
 
473
  if ( $image ) {
474
- $dimensions = getimagesize( $attachment_path );
475
  $md5 = md5_file( $attachment_path );
476
 
477
  if ( ( $dimensions || $md5 ) && ( empty( $image->meta_data['full'] ) || ! is_array( $image->meta_data['full'] ) ) ) {
@@ -483,10 +486,10 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
483
  }
484
 
485
  if ( $dimensions ) {
486
- $image->meta_data['width'] = $dimensions[0];
487
- $image->meta_data['height'] = $dimensions[1];
488
- $image->meta_data['full']['width'] = $dimensions[0];
489
- $image->meta_data['full']['height'] = $dimensions[1];
490
  }
491
 
492
  if ( $md5 ) {
@@ -497,7 +500,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
497
  $storage->_image_mapper->save( $image );
498
  }
499
 
500
- delete_transient( 'imagify-ngg-async-in-progress-' . $this->id );
501
 
502
  return $data;
503
  }
@@ -588,6 +591,19 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
588
  return $data;
589
  }
590
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
  /**
592
  * Process an attachment restoration from the backup file.
593
  *
@@ -620,7 +636,6 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
620
  /**
621
  * Make some more tests before restoring the backup.
622
  */
623
- $filesystem = imagify_get_filesystem();
624
  $full_abspath = $storage->get_image_abspath( $image );
625
  $backup_abspath = $storage->get_image_abspath( $image, 'backup' );
626
 
@@ -628,7 +643,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
628
  return new WP_Error( 'same_path', __( 'Image path and backup path are identical.', 'imagify' ) );
629
  }
630
 
631
- if ( ! $filesystem->is_writable( $full_abspath ) || ! $filesystem->is_writable( dirname( $full_abspath ) ) ) {
632
  return new WP_Error( 'destination_not_writable', __( 'The image to replace is not writable.', 'imagify' ) );
633
  }
634
 
@@ -641,7 +656,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
641
  */
642
  do_action( 'before_imagify_ngg_restore_attachment', $this->id );
643
 
644
- if ( ! $filesystem->copy( $backup_abspath, $full_abspath, true, FS_CHMOD_FILE ) ) {
645
  return new WP_Error( 'copy_failed', __( 'Restoration failed.', 'imagify' ) );
646
  }
647
 
@@ -654,10 +669,10 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
654
  * Fill in the NGG meta data.
655
  */
656
  // 1- Meta data for the backup file.
657
- $dimensions = getimagesize( $backup_abspath );
658
  $backup_data = array(
659
  'backup' => array(
660
- 'filename' => basename( $full_abspath ), // Yes, $full_abspath.
661
  'width' => 0,
662
  'height' => 0,
663
  'generated' => microtime(),
@@ -665,8 +680,8 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
665
  );
666
 
667
  if ( $dimensions ) {
668
- $backup_data['backup']['width'] = $dimensions[0];
669
- $backup_data['backup']['height'] = $dimensions[1];
670
  }
671
 
672
  // 2- Meta data for the full sized image.
@@ -681,13 +696,13 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
681
  ),
682
  );
683
 
684
- $dimensions = getimagesize( $full_abspath );
685
 
686
  if ( $dimensions ) {
687
- $full_data['width'] = $dimensions[0];
688
- $full_data['height'] = $dimensions[1];
689
- $full_data['full']['width'] = $dimensions[0];
690
- $full_data['full']['height'] = $dimensions[1];
691
  }
692
 
693
  $md5 = md5_file( $full_abspath );
@@ -761,11 +776,11 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
761
  'generated' => microtime(),
762
  );
763
 
764
- $dimensions = getimagesize( $thumbnail->fileName );
765
 
766
  if ( $dimensions ) {
767
- $size_meta['width'] = $dimensions[0];
768
- $size_meta['height'] = $dimensions[1];
769
  }
770
 
771
  if ( isset( $params['crop_frame'] ) ) {
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.3.2';
18
 
19
  /**
20
  * The attachment SQL DB class.
65
 
66
  $this->get_row();
67
 
68
+ $this->filesystem = Imagify_Filesystem::get_instance();
69
+ $this->optimization_state_transient = 'imagify-ngg-async-in-progress-' . $this->id;
70
+
71
  // Load nggAdmin class.
72
  $ngg_admin_functions_path = WP_PLUGIN_DIR . '/' . NGGFOLDER . '/products/photocrati_nextgen/modules/ngglegacy/admin/functions.php';
73
 
74
+ if ( ! class_exists( 'nggAdmin' ) && $this->filesystem->exists( $ngg_admin_functions_path ) ) {
75
  require_once( $ngg_admin_functions_path );
76
  }
77
  }
140
  return false;
141
  }
142
 
143
+ return site_url( '/' ) . $this->filesystem->make_path_relative( $this->get_raw_backup_path() );
144
  }
145
 
146
  /**
229
  return $this->is_mime_type_supported;
230
  }
231
 
232
+ $mime_type = $this->filesystem->get_mime_type( $this->get_original_path() );
233
 
234
  if ( ! $mime_type ) {
235
  $this->is_mime_type_supported = false;
270
  * @return void
271
  */
272
  public function update_metadata_size() {
273
+ $size = $this->filesystem->get_image_size( $this->get_original_path() );
274
 
275
+ if ( $size ) {
276
+ $this->image->meta_data['width'] = $size['width'];
277
+ $this->image->meta_data['height'] = $size['height'];
278
+ $this->image->meta_data['full']['width'] = $size['width'];
279
+ $this->image->meta_data['full']['height'] = $size['height'];
280
 
281
  nggdb::update_image_meta( $this->id, $this->image->meta_data );
282
  }
356
  $optimized_size = (int) $response->new_size;
357
  } else {
358
  $file_path = $this->get_original_path();
359
+ $file_path = $file_path && $this->filesystem->exists( $file_path ) ? $file_path : false;
360
+ $optimized_size = $file_path ? $this->filesystem->size( $file_path ) : 0;
361
  }
362
 
363
  if ( $original_size && $optimized_size ) {
423
  */
424
  do_action( 'before_imagify_ngg_optimize_attachment', $this->id );
425
 
426
+ $this->set_running_status();
427
 
428
  // Optimize the original size.
429
  $response = do_imagify( $attachment_path, array(
445
 
446
  if ( ! $data ) {
447
  // Already optimized.
448
+ $this->delete_running_status();
449
  return;
450
  }
451
 
474
  $image = $storage->_image_mapper->find( $this->id );
475
 
476
  if ( $image ) {
477
+ $dimensions = $this->filesystem->get_image_size( $attachment_path );
478
  $md5 = md5_file( $attachment_path );
479
 
480
  if ( ( $dimensions || $md5 ) && ( empty( $image->meta_data['full'] ) || ! is_array( $image->meta_data['full'] ) ) ) {
486
  }
487
 
488
  if ( $dimensions ) {
489
+ $image->meta_data['width'] = $dimensions['width'];
490
+ $image->meta_data['height'] = $dimensions['height'];
491
+ $image->meta_data['full']['width'] = $dimensions['width'];
492
+ $image->meta_data['full']['height'] = $dimensions['height'];
493
  }
494
 
495
  if ( $md5 ) {
500
  $storage->_image_mapper->save( $image );
501
  }
502
 
503
+ $this->delete_running_status();
504
 
505
  return $data;
506
  }
591
  return $data;
592
  }
593
 
594
+ /**
595
+ * Re-optimize the given thumbnail sizes to the same level.
596
+ * This is not used in this context.
597
+ *
598
+ * @since 1.7.1
599
+ * @access public
600
+ * @author Grégory Viguier
601
+ *
602
+ * @param array $sizes The sizes to optimize.
603
+ * @return array|void A WP_Error object on failure.
604
+ */
605
+ public function reoptimize_thumbnails( $sizes ) {}
606
+
607
  /**
608
  * Process an attachment restoration from the backup file.
609
  *
636
  /**
637
  * Make some more tests before restoring the backup.
638
  */
 
639
  $full_abspath = $storage->get_image_abspath( $image );
640
  $backup_abspath = $storage->get_image_abspath( $image, 'backup' );
641
 
643
  return new WP_Error( 'same_path', __( 'Image path and backup path are identical.', 'imagify' ) );
644
  }
645
 
646
+ if ( ! $this->filesystem->is_writable( $full_abspath ) || ! $this->filesystem->is_writable( $this->filesystem->dir_path( $full_abspath ) ) ) {
647
  return new WP_Error( 'destination_not_writable', __( 'The image to replace is not writable.', 'imagify' ) );
648
  }
649
 
656
  */
657
  do_action( 'before_imagify_ngg_restore_attachment', $this->id );
658
 
659
+ if ( ! $this->filesystem->copy( $backup_abspath, $full_abspath, true, FS_CHMOD_FILE ) ) {
660
  return new WP_Error( 'copy_failed', __( 'Restoration failed.', 'imagify' ) );
661
  }
662
 
669
  * Fill in the NGG meta data.
670
  */
671
  // 1- Meta data for the backup file.
672
+ $dimensions = $this->filesystem->get_image_size( $backup_abspath );
673
  $backup_data = array(
674
  'backup' => array(
675
+ 'filename' => $this->filesystem->file_name( $full_abspath ), // Yes, $full_abspath.
676
  'width' => 0,
677
  'height' => 0,
678
  'generated' => microtime(),
680
  );
681
 
682
  if ( $dimensions ) {
683
+ $backup_data['backup']['width'] = $dimensions['width'];
684
+ $backup_data['backup']['height'] = $dimensions['height'];
685
  }
686
 
687
  // 2- Meta data for the full sized image.
696
  ),
697
  );
698
 
699
+ $dimensions = $this->filesystem->get_image_size( $full_abspath );
700
 
701
  if ( $dimensions ) {
702
+ $full_data['width'] = $dimensions['width'];
703
+ $full_data['height'] = $dimensions['height'];
704
+ $full_data['full']['width'] = $dimensions['width'];
705
+ $full_data['full']['height'] = $dimensions['height'];
706
  }
707
 
708
  $md5 = md5_file( $full_abspath );
776
  'generated' => microtime(),
777
  );
778
 
779
+ $dimensions = $this->filesystem->get_image_size( $thumbnail->fileName );
780
 
781
  if ( $dimensions ) {
782
+ $size_meta['width'] = $dimensions['width'];
783
+ $size_meta['height'] = $dimensions['height'];
784
  }
785
 
786
  if ( isset( $params['crop_frame'] ) ) {
inc/3rd-party/nextgen-gallery/inc/common/attachments.php CHANGED
@@ -13,7 +13,7 @@ add_action( 'ngg_after_new_images_added', '_imagify_ngg_optimize_attachment', IM
13
  */
14
  function _imagify_ngg_optimize_attachment( $gallery_id, $image_ids ) {
15
 
16
- if ( ! imagify_valid_key() || ! get_imagify_option( 'auto_optimize' ) ) {
17
  return;
18
  }
19
 
@@ -99,7 +99,7 @@ function _imagify_ngg_media_library_imported_image_data( $image, $attachment ) {
99
  $ngg_backup_path = $imagify_image->get_raw_backup_path();
100
 
101
  imagify_get_filesystem()->copy( $attachment_backup_path, $ngg_backup_path, true );
102
- imagify_chmod_file( $ngg_backup_path );
103
  }
104
 
105
  // Optimize thumbnails.
13
  */
14
  function _imagify_ngg_optimize_attachment( $gallery_id, $image_ids ) {
15
 
16
+ if ( ! Imagify_Requirements::is_api_key_valid() || ! get_imagify_option( 'auto_optimize' ) ) {
17
  return;
18
  }
19
 
99
  $ngg_backup_path = $imagify_image->get_raw_backup_path();
100
 
101
  imagify_get_filesystem()->copy( $attachment_backup_path, $ngg_backup_path, true );
102
+ imagify_get_filesystem()->chmod_file( $ngg_backup_path );
103
  }
104
 
105
  // Optimize thumbnails.
inc/3rd-party/regenerate-thumbnails/inc/classes/class-imagify-regenerate-thumbnails.php ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that handles compatibility with Regenerate Thumbnails plugin.
6
+ *
7
+ * @since 1.7.1
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Regenerate_Thumbnails {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7.1
17
+ * @author Grégory Viguier
18
+ */
19
+ const VERSION = '1.0';
20
+
21
+ /**
22
+ * Action used for the ajax callback.
23
+ *
24
+ * @var string
25
+ * @since 1.7.1
26
+ * @author Grégory Viguier
27
+ */
28
+ const ACTION = 'imagify_regenerate_thumbnails';
29
+
30
+ /**
31
+ * List of the attachments to regenerate.
32
+ *
33
+ * @var array An array of Imagify attachments. The array keys are the attachment IDs.
34
+ * @since 1.7.1
35
+ * @access protected
36
+ * @author Grégory Viguier
37
+ */
38
+ protected $attachments = array();
39
+
40
+ /**
41
+ * The single instance of the class.
42
+ *
43
+ * @var object
44
+ */
45
+ protected static $_instance;
46
+
47
+
48
+ /** ----------------------------------------------------------------------------------------- */
49
+ /** INSTANCE ================================================================================ */
50
+ /** ----------------------------------------------------------------------------------------- */
51
+
52
+ /**
53
+ * Get the main Instance.
54
+ *
55
+ * @since 1.6.11
56
+ * @access public
57
+ * @author Grégory Viguier
58
+ *
59
+ * @return object Main instance.
60
+ */
61
+ public static function get_instance() {
62
+ if ( ! isset( self::$_instance ) ) {
63
+ self::$_instance = new self();
64
+ }
65
+
66
+ return self::$_instance;
67
+ }
68
+
69
+ /**
70
+ * The constructor.
71
+ *
72
+ * @since 1.7.1
73
+ * @access protected
74
+ * @author Grégory Viguier
75
+ */
76
+ protected function __construct() {}
77
+
78
+
79
+ /** ----------------------------------------------------------------------------------------- */
80
+ /** INIT ==================================================================================== */
81
+ /** ----------------------------------------------------------------------------------------- */
82
+
83
+ /**
84
+ * Launch the hooks.
85
+ *
86
+ * @since 1.7.1
87
+ * @access public
88
+ * @author Grégory Viguier
89
+ */
90
+ public function init() {
91
+ add_filter( 'rest_dispatch_request', array( $this, 'maybe_init_attachment' ), 4, 4 );
92
+ add_filter( 'wp_generate_attachment_metadata', array( $this, 'launch_async_optimization' ), IMAGIFY_INT_MAX, 2 );
93
+ add_action( 'wp_ajax_' . self::ACTION, array( $this, 'regenerate_thumbnails_callback' ) );
94
+ }
95
+
96
+
97
+ /** ----------------------------------------------------------------------------------------- */
98
+ /** HOOKS =================================================================================== */
99
+ /** ----------------------------------------------------------------------------------------- */
100
+
101
+ /**
102
+ * Filter the REST dispatch request result, to hook before Regenerate Thumbnails starts its magic.
103
+ *
104
+ * @since 1.7.1
105
+ * @access public
106
+ * @author Grégory Viguier
107
+ *
108
+ * @param bool $dispatch_result Dispatch result, will be used if not empty.
109
+ * @param WP_REST_Request $request Request used to generate the response.
110
+ * @param string $route Route matched for the request.
111
+ * @param array $handler Route handler used for the request.
112
+ * @return bool
113
+ */
114
+ public function maybe_init_attachment( $dispatch_result, $request, $route = null, $handler = null ) {
115
+ if ( strpos( $route, self::get_route_prefix() ) === false ) {
116
+ return $dispatch_result;
117
+ }
118
+
119
+ $attachment_id = $request->get_param( 'id' );
120
+ $attachment = $this->set_attachment( $attachment_id );
121
+
122
+ if ( $attachment ) {
123
+ // The attachment can be regenerated: backup the optimized full-sized file.
124
+ $this->backup_optimized_file( $attachment_id );
125
+ }
126
+
127
+ return $dispatch_result;
128
+ }
129
+
130
+ /**
131
+ * Auto-optimize after an attachment is regenerated.
132
+ *
133
+ * @since 1.7.1
134
+ * @access public
135
+ * @author Grégory Viguier
136
+ *
137
+ * @param array $metadata An array of attachment meta data.
138
+ * @param int $attachment_id Current attachment ID.
139
+ * @return array
140
+ */
141
+ public function launch_async_optimization( $metadata, $attachment_id ) {
142
+ if ( ! $this->get_attachment( $attachment_id ) ) {
143
+ return $metadata;
144
+ }
145
+
146
+ $sizes = is_array( $metadata['sizes'] ) ? $metadata['sizes'] : array();
147
+
148
+ if ( ! $sizes ) {
149
+ // Put the optimized full-sized file back.
150
+ $this->put_optimized_file_back( $this->get_attachment( $attachment_id ) );
151
+
152
+ return $metadata;
153
+ }
154
+
155
+ $action = Imagify_Regenerate_Thumbnails::ACTION;
156
+ $context = $this->get_attachment( $attachment_id )->get_context();
157
+ $_ajax_nonce = wp_create_nonce( Imagify_Regenerate_Thumbnails::get_nonce_name( $attachment_id, $context ) );
158
+
159
+ imagify_do_async_job( compact( 'action', '_ajax_nonce', 'sizes', 'attachment_id', 'context' ) );
160
+
161
+ return $metadata;
162
+ }
163
+
164
+ /**
165
+ * Optimize the newly regenerated thumbnails.
166
+ *
167
+ * @since 1.7.1
168
+ * @access public
169
+ * @author Grégory Viguier
170
+ */
171
+ public function regenerate_thumbnails_callback() {
172
+ if ( empty( $_POST['_ajax_nonce'] ) || empty( $_POST['attachment_id'] ) || empty( $_POST['context'] ) ) { // WPCS: CSRF ok.
173
+ imagify_die( __( 'Invalid request', 'imagify' ) );
174
+ }
175
+
176
+ if ( empty( $_POST['sizes'] ) || ! is_array( $_POST['sizes'] ) ) { // WPCS: CSRF ok.
177
+ imagify_die( __( 'No thumbnail sizes selected', 'imagify' ) );
178
+ }
179
+
180
+ $attachment_id = absint( $_POST['attachment_id'] );
181
+ $context = imagify_sanitize_context( $_POST['context'] );
182
+
183
+ imagify_check_nonce( self::get_nonce_name( $attachment_id, $context ) );
184
+ imagify_check_user_capacity( 'manual-optimize', $attachment_id );
185
+
186
+ $attachment = get_imagify_attachment( $context, $attachment_id, self::ACTION );
187
+
188
+ // Optimize.
189
+ $attachment->reoptimize_thumbnails( $_POST['sizes'] );
190
+
191
+ // Put the optimized original file back.
192
+ $this->put_optimized_file_back( $attachment );
193
+
194
+ wp_send_json_success();
195
+ }
196
+
197
+
198
+ /** ----------------------------------------------------------------------------------------- */
199
+ /** INTERNAL TOOLS ========================================================================== */
200
+ /** ----------------------------------------------------------------------------------------- */
201
+
202
+ /**
203
+ * Set the Imagify attachment.
204
+ *
205
+ * @since 1.7.1
206
+ * @access protected
207
+ * @author Grégory Viguier
208
+ *
209
+ * @param int $attachment_id Attachment ID.
210
+ * @return object|false An Imagify attachment object. False on failure.
211
+ */
212
+ protected function set_attachment( $attachment_id ) {
213
+ if ( ! $attachment_id || ! Imagify_Requirements::is_api_key_valid() ) {
214
+ return false;
215
+ }
216
+
217
+ $attachment = get_imagify_attachment( 'wp', $attachment_id, 'regenerate_thumbnails' );
218
+
219
+ if ( ! $attachment->is_valid() || ! $attachment->is_extension_supported() || ! $attachment->is_optimized() ) {
220
+ return false;
221
+ }
222
+
223
+ // This attachment can be optimized.
224
+ $this->attachments[ $attachment_id ] = $attachment;
225
+ return $this->attachments[ $attachment_id ];
226
+ }
227
+
228
+ /**
229
+ * Unset the Imagify attachment.
230
+ *
231
+ * @since 1.7.1
232
+ * @access protected
233
+ * @author Grégory Viguier
234
+ *
235
+ * @param int $attachment_id Attachment ID.
236
+ */
237
+ protected function unset_attachment( $attachment_id ) {
238
+ unset( $this->attachments[ $attachment_id ] );
239
+ }
240
+
241
+ /**
242
+ * Get the Imagify attachment.
243
+ *
244
+ * @since 1.7.1
245
+ * @access protected
246
+ * @author Grégory Viguier
247
+ *
248
+ * @param int $attachment_id Attachment ID.
249
+ * @return object|false An Imagify attachment object. False on failure.
250
+ */
251
+ protected function get_attachment( $attachment_id ) {
252
+ return ! empty( $this->attachments[ $attachment_id ] ) ? $this->attachments[ $attachment_id ] : false;
253
+ }
254
+
255
+ /**
256
+ * Backup the optimized full-sized file and replace it by the original backup file.
257
+ *
258
+ * @since 1.7.1
259
+ * @access protected
260
+ * @author Grégory Viguier
261
+ *
262
+ * @param int $attachment_id Attachment ID.
263
+ */
264
+ protected function backup_optimized_file( $attachment_id ) {
265
+ $attachment = $this->get_attachment( $attachment_id );
266
+ $backup_path = $attachment->get_backup_path();
267
+
268
+ if ( ! $backup_path ) {
269
+ return;
270
+ }
271
+
272
+ /**
273
+ * Replace the optimized full-sized file by the backup, so any optimization will not use an optimized file, but the original one.
274
+ * The optimized full-sized file is kept and renamed, and will be put back in place at the end of the optimization process.
275
+ */
276
+ $filesystem = Imagify_Filesystem::get_instance();
277
+ $file_path = $attachment->get_original_path();
278
+ $tmp_file_path = self::get_temporary_file_path( $file_path );
279
+
280
+ if ( $filesystem->exists( $file_path ) ) {
281
+ $filesystem->move( $file_path, $tmp_file_path, true );
282
+ }
283
+
284
+ $filesystem->copy( $backup_path, $file_path );
285
+ }
286
+
287
+ /**
288
+ * Backup the optimized full-sized file and replace it by the original backup file.
289
+ *
290
+ * @since 1.7.1
291
+ * @access protected
292
+ * @author Grégory Viguier
293
+ *
294
+ * @param object $attachment An Imagify attachment.
295
+ */
296
+ protected function put_optimized_file_back( $attachment ) {
297
+ $filesystem = Imagify_Filesystem::get_instance();
298
+ $file_path = $attachment->get_original_path();
299
+ $tmp_file_path = self::get_temporary_file_path( $file_path );
300
+
301
+ if ( $filesystem->exists( $tmp_file_path ) ) {
302
+ $filesystem->move( $tmp_file_path, $file_path, true );
303
+ }
304
+ }
305
+
306
+
307
+ /** ----------------------------------------------------------------------------------------- */
308
+ /** PUBLIC TOOLS ============================================================================ */
309
+ /** ----------------------------------------------------------------------------------------- */
310
+
311
+ /**
312
+ * Get the beginning of the route used to regenerate thumbnails.
313
+ *
314
+ * @since 1.7.1
315
+ * @access public
316
+ * @author Grégory Viguier
317
+ *
318
+ * @return string
319
+ */
320
+ public static function get_route_prefix() {
321
+ static $route;
322
+
323
+ if ( ! isset( $route ) ) {
324
+ $route = '/' . trim( RegenerateThumbnails()->rest_api->namespace, '/' ) . '/regenerate/';
325
+ }
326
+
327
+ return $route;
328
+ }
329
+
330
+ /**
331
+ * Get the path to the temporary file.
332
+ *
333
+ * @since 1.7.1
334
+ * @access public
335
+ * @author Grégory Viguier
336
+ *
337
+ * @param string $file_path The optimized full-sized file path.
338
+ * @return string
339
+ */
340
+ public static function get_temporary_file_path( $file_path ) {
341
+ return $file_path . '_backup';
342
+ }
343
+
344
+ /**
345
+ * Get the name of the nonce used for the ajax callback.
346
+ *
347
+ * @since 1.7.1
348
+ * @access public
349
+ * @author Grégory Viguier
350
+ *
351
+ * @param int $attachment_id The attachment ID.
352
+ * @param string $context The context.
353
+ * @return string
354
+ */
355
+ public static function get_nonce_name( $attachment_id, $context ) {
356
+ return self::ACTION . '-' . $attachment_id . '-' . $context;
357
+ }
358
+ }
inc/3rd-party/regenerate-thumbnails/regenerate-thumbnails.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ if ( ! class_exists( 'RegenerateThumbnails_Regenerator' ) || ! function_exists( 'RegenerateThumbnails' ) ) {
5
+ return;
6
+ }
7
+
8
+ Imagify_Regenerate_Thumbnails::get_instance()->init();
inc/admin/custom-folders.php CHANGED
@@ -26,13 +26,11 @@ function imagify_sync_theme_plugin_files_on_update( $response, $hook_extra, $res
26
  return;
27
  }
28
 
29
- if ( ! imagify_valid_key() ) {
30
  return;
31
  }
32
 
33
- $user = new Imagify_User();
34
-
35
- if ( $user->is_over_quota() ) {
36
  return;
37
  }
38
 
26
  return;
27
  }
28
 
29
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
30
  return;
31
  }
32
 
33
+ if ( Imagify_Requirements::is_over_quota() ) {
 
 
34
  return;
35
  }
36
 
inc/admin/heartbeat.php CHANGED
@@ -14,81 +14,47 @@ add_filter( 'heartbeat_received', '_imagify_heartbeat_received', 10, 2 );
14
  * @return array
15
  */
16
  function _imagify_heartbeat_received( $response, $data ) {
17
- if ( empty( $data['imagify_heartbeat'] ) || 'update_bulk_data' !== $data['imagify_heartbeat'] ) {
18
  return $response;
19
  }
20
 
21
- $user = new Imagify_User();
22
- $types = ! empty( $data['imagify_types'] ) && is_array( $data['imagify_types'] ) ? array_flip( $data['imagify_types'] ) : array();
23
- $new_data = array(
24
- // User account.
25
- 'unconsumed_quota' => is_wp_error( $user ) ? 0 : $user->get_percent_unconsumed_quota(),
26
- // Global chart.
27
- 'total_attachments' => 0,
28
- 'unoptimized_attachments' => 0,
29
- 'optimized_attachments' => 0,
30
- 'errors_attachments' => 0,
31
- // Stats block.
32
- 'already_optimized_attachments' => 0,
33
- 'original_human' => 0,
34
- 'optimized_human' => 0,
35
- );
36
-
37
- if ( isset( $types['library'] ) ) {
38
- /**
39
- * Library.
40
- */
41
- $saving_data = imagify_count_saving_data();
42
-
43
- // Global chart.
44
- $new_data['total_attachments'] += imagify_count_attachments();
45
- $new_data['unoptimized_attachments'] += imagify_count_unoptimized_attachments();
46
- $new_data['optimized_attachments'] += imagify_count_optimized_attachments();
47
- $new_data['errors_attachments'] += imagify_count_error_attachments();
48
- // Stats block.
49
- $new_data['already_optimized_attachments'] += $saving_data['count'];
50
- $new_data['original_human'] += $saving_data['original_size'];
51
- $new_data['optimized_human'] += $saving_data['optimized_size'];
52
  }
53
 
54
- if ( isset( $types['custom-folders'] ) ) {
55
- /**
56
- * Custom folders.
57
- */
58
- // Global chart.
59
- $new_data['total_attachments'] += Imagify_Files_Stats::count_all_files();
60
- $new_data['unoptimized_attachments'] += Imagify_Files_Stats::count_no_status_files();
61
- $new_data['optimized_attachments'] += Imagify_Files_Stats::count_optimized_files();
62
- $new_data['errors_attachments'] += Imagify_Files_Stats::count_error_files();
63
- // Stats block.
64
- $new_data['already_optimized_attachments'] += Imagify_Files_Stats::count_success_files();
65
- $new_data['original_human'] += Imagify_Files_Stats::get_original_size();
66
- $new_data['optimized_human'] += Imagify_Files_Stats::get_optimized_size();
67
- }
68
 
69
- /**
70
- * Percentages.
71
- */
72
- if ( $new_data['total_attachments'] && $new_data['optimized_attachments'] ) {
73
- $new_data['optimized_attachments_percent'] = round( 100 * $new_data['optimized_attachments'] / $new_data['total_attachments'] );
74
- } else {
75
- $new_data['optimized_attachments_percent'] = 0;
76
- }
77
 
78
- if ( $new_data['original_human'] && $new_data['optimized_human'] ) {
79
- $new_data['optimized_percent'] = ceil( 100 - ( 100 * $new_data['optimized_human'] / $new_data['original_human'] ) );
80
- } else {
81
- $new_data['optimized_percent'] = 0;
82
- }
83
 
84
- /**
85
- * Formating.
86
- */
87
- $new_data['already_optimized_attachments'] = number_format_i18n( $new_data['already_optimized_attachments'] );
88
- $new_data['original_human'] = imagify_size_format( $new_data['original_human'], 1 );
89
- $new_data['optimized_human'] = imagify_size_format( $new_data['optimized_human'], 1 );
 
 
 
 
 
 
 
 
 
90
 
91
- $response['imagify_bulk_data'] = $new_data;
 
 
 
 
 
 
 
92
 
93
  return $response;
94
  }
14
  * @return array
15
  */
16
  function _imagify_heartbeat_received( $response, $data ) {
17
+ if ( empty( $data['imagify_ids']['update_bulk_data'] ) ) {
18
  return $response;
19
  }
20
 
21
+ if ( empty( $data['imagify_types'] ) || ! is_array( $data['imagify_types'] ) ) {
22
+ return $response;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
 
25
+ $folder_types = array_flip( array_filter( $data['imagify_types'] ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ $response['imagify_bulk_data'] = imagify_get_bulk_stats( $folder_types, array(
28
+ 'fullset' => true,
29
+ ) );
 
 
 
 
 
30
 
31
+ return $response;
32
+ }
 
 
 
33
 
34
+ add_filter( 'heartbeat_received', 'imagify_heartbeat_requirements_received', 10, 2 );
35
+ /**
36
+ * Prepare the data that goes back with the Heartbeat API.
37
+ *
38
+ * @since 1.7.1
39
+ * @author Grégory Viguier
40
+ *
41
+ * @param array $response The Heartbeat response.
42
+ * @param array $data The $_POST data sent.
43
+ * @return array
44
+ */
45
+ function imagify_heartbeat_requirements_received( $response, $data ) {
46
+ if ( empty( $data['imagify_ids']['update_bulk_requirements'] ) ) {
47
+ return $response;
48
+ }
49
 
50
+ $response['imagify_bulk_requirements'] = array(
51
+ 'curl_missing' => ! Imagify_Requirements::supports_curl(),
52
+ 'editor_missing' => ! Imagify_Requirements::supports_image_editor(),
53
+ 'external_http_blocked' => Imagify_Requirements::is_imagify_blocked(),
54
+ 'api_down' => Imagify_Requirements::is_imagify_blocked() || ! Imagify_Requirements::is_api_up(),
55
+ 'key_is_valid' => ! Imagify_Requirements::is_imagify_blocked() && Imagify_Requirements::is_api_up() && Imagify_Requirements::is_api_key_valid(),
56
+ 'is_over_quota' => ! Imagify_Requirements::is_imagify_blocked() && Imagify_Requirements::is_api_up() && Imagify_Requirements::is_api_key_valid() && Imagify_Requirements::is_over_quota(),
57
+ );
58
 
59
  return $response;
60
  }
inc/admin/media.php CHANGED
@@ -60,7 +60,7 @@ function _imagify_add_actions_to_media_list_row( $actions, $post ) {
60
  }
61
 
62
  // If Imagify license not valid, or image is not optimized, do nothing.
63
- if ( ! imagify_valid_key() || ! $attachment->is_optimized() ) {
64
  return $actions;
65
  }
66
 
60
  }
61
 
62
  // If Imagify license not valid, or image is not optimized, do nothing.
63
+ if ( ! Imagify_Requirements::is_api_key_valid() || ! $attachment->is_optimized() ) {
64
  return $actions;
65
  }
66
 
inc/admin/meta-boxes.php CHANGED
@@ -24,7 +24,7 @@ function _imagify_attachment_submitbox_misc_actions() {
24
  return;
25
  }
26
 
27
- if ( ! imagify_valid_key() && ! $attachment->is_optimized() ) {
28
 
29
  echo '<div class="misc-pub-section misc-pub-imagify"><h4>' . __( 'Imagify', 'imagify' ) . '</h4></div>';
30
  echo '<div class="misc-pub-section misc-pub-imagify">';
@@ -38,7 +38,7 @@ function _imagify_attachment_submitbox_misc_actions() {
38
  echo '<div class="misc-pub-section misc-pub-imagify"><h4>' . __( 'Imagify', 'imagify' ) . '</h4></div>';
39
  echo get_imagify_attachment_optimization_text( $attachment );
40
 
41
- } elseif ( false !== get_transient( 'imagify-async-in-progress-' . $post->ID ) ) {
42
 
43
  echo '<div class="misc-pub-section misc-pub-imagify">';
44
  echo '<div class="button">';
24
  return;
25
  }
26
 
27
+ if ( ! Imagify_Requirements::is_api_key_valid() && ! $attachment->is_optimized() ) {
28
 
29
  echo '<div class="misc-pub-section misc-pub-imagify"><h4>' . __( 'Imagify', 'imagify' ) . '</h4></div>';
30
  echo '<div class="misc-pub-section misc-pub-imagify">';
38
  echo '<div class="misc-pub-section misc-pub-imagify"><h4>' . __( 'Imagify', 'imagify' ) . '</h4></div>';
39
  echo get_imagify_attachment_optimization_text( $attachment );
40
 
41
+ } elseif ( $attachment->is_running() ) {
42
 
43
  echo '<div class="misc-pub-section misc-pub-imagify">';
44
  echo '<div class="button">';
inc/admin/upgrader.php CHANGED
@@ -259,7 +259,7 @@ function _imagify_new_upgrade( $network_version, $site_version ) {
259
  // 1.7
260
  if ( version_compare( $site_version, '1.7' ) < 0 ) {
261
  // Migrate data.
262
- _do_imagify_update_library_size_calculations();
263
 
264
  if ( ! imagify_is_active_for_network() ) {
265
  // Make sure the settings are autoloaded.
259
  // 1.7
260
  if ( version_compare( $site_version, '1.7' ) < 0 ) {
261
  // Migrate data.
262
+ Imagify_Cron_Library_Size::get_instance()->do_event();
263
 
264
  if ( ! imagify_is_active_for_network() ) {
265
  // Make sure the settings are autoloaded.
inc/classes/class-imagify-abstract-attachment.php CHANGED
@@ -13,7 +13,7 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
13
  *
14
  * @var string
15
  */
16
- const VERSION = '1.3';
17
 
18
  /**
19
  * The attachment ID.
@@ -24,6 +24,16 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
24
  */
25
  public $id = 0;
26
 
 
 
 
 
 
 
 
 
 
 
27
  /**
28
  * The attachment SQL DB class.
29
  *
@@ -53,6 +63,46 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
53
  */
54
  protected $is_extension_supported;
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  /**
57
  * The constructor.
58
  *
@@ -75,7 +125,31 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
75
  $this->id = $post->ID;
76
  }
77
 
78
- $this->id = (int) $this->id;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
 
81
  /**
@@ -150,7 +224,7 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
150
 
151
  $backup_path = $this->get_raw_backup_path();
152
 
153
- if ( $backup_path && imagify_get_filesystem()->exists( $backup_path ) ) {
154
  return $backup_path;
155
  }
156
 
@@ -288,15 +362,14 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
288
  * @since 1.0
289
  * @access public
290
  *
291
- * @return string
292
  */
293
  public function get_extension() {
294
  if ( ! $this->is_valid() ) {
295
  return '';
296
  }
297
 
298
- $fullsize_path = $this->get_original_path();
299
- return pathinfo( $fullsize_path, PATHINFO_EXTENSION );
300
  }
301
 
302
  /**
@@ -396,10 +469,10 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
396
 
397
  if ( ! $filepath ) {
398
  $filepath = $this->get_original_path();
399
- $filepath = $filepath && imagify_get_filesystem()->exists( $filepath ) ? $filepath : false;
400
  }
401
 
402
- $size = $filepath ? imagify_get_filesystem()->size( $filepath ) : 0;
403
  }
404
 
405
  if ( $human_format ) {
@@ -429,8 +502,8 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
429
 
430
  if ( ! $size ) {
431
  $filepath = $this->get_original_path();
432
- $filepath = $filepath && imagify_get_filesystem()->exists( $filepath ) ? $filepath : false;
433
- $size = $filepath ? imagify_get_filesystem()->size( $filepath ) : 0;
434
  }
435
 
436
  if ( $human_format ) {
@@ -565,8 +638,8 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
565
  $filepath = $this->get_original_path();
566
  $size = 0;
567
 
568
- if ( $filepath && imagify_get_filesystem()->exists( $filepath ) ) {
569
- $size = imagify_get_filesystem()->size( $filepath );
570
  }
571
 
572
  return $size > IMAGIFY_MAX_BYTES;
@@ -596,6 +669,58 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
596
  return 'error' === $this->get_status();
597
  }
598
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  /**
600
  * Update the metadata size of the attachment
601
  *
@@ -618,7 +743,7 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
618
  $backup_path = $this->get_backup_path();
619
 
620
  if ( $backup_path ) {
621
- imagify_get_filesystem()->delete( $backup_path );
622
  }
623
  }
624
 
@@ -677,8 +802,8 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
677
  return array();
678
  }
679
 
680
- $orig_f = pathinfo( $orig_f );
681
- $orig_f = $orig_f['filename'] . '-{%suffix%}.' . $orig_f['extension'];
682
 
683
  // Test if the missing sizes are needed.
684
  $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
@@ -755,6 +880,19 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
755
  */
756
  abstract public function optimize_missing_thumbnails( $optimization_level = null );
757
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  /**
759
  * Process an attachment restoration from the backup file.
760
  *
@@ -769,6 +907,7 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
769
  * Resize an image if bigger than the maximum width defined in the settings.
770
  *
771
  * @since 1.5.7
 
772
  * @access public
773
  * @author Remy Perona
774
  *
@@ -785,19 +924,20 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
785
  // Prevent removal of the exif/meta data when resizing (only works with Imagick).
786
  add_filter( 'image_strip_meta', '__return_false' );
787
 
788
- $new_sizes = wp_constrain_dimensions( $attachment_sizes[0], $attachment_sizes[1], $max_width );
789
- $editor = wp_get_image_editor( $attachment_path );
 
790
 
791
  if ( is_wp_error( $editor ) ) {
792
  return $editor;
793
  }
794
 
795
- $image_type = pathinfo( $attachment_path, PATHINFO_EXTENSION );
796
 
797
  // Try to correct for auto-rotation if the info is available.
798
- if ( function_exists( 'exif_read_data' ) && ( 'jpg' === $image_type || 'jpe' === $image_type || 'jpeg' === $image_type ) ) {
799
- $exif = @exif_read_data( $attachment_path );
800
- $orientation = is_array( $exif ) && array_key_exists( 'Orientation', $exif ) ? $exif['Orientation'] : 0;
801
 
802
  switch ( $orientation ) {
803
  case 3:
@@ -831,6 +971,52 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
831
  }
832
 
833
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
  /** ----------------------------------------------------------------------------------------- */
835
  /** DB ROW ================================================================================== */
836
  /** ----------------------------------------------------------------------------------------- */
@@ -853,8 +1039,7 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
853
  return $this->invalidate_row();
854
  }
855
 
856
- $classname = $this->db_class_name;
857
- $this->row = $classname::get_instance()->get( $this->id );
858
 
859
  if ( ! $this->row ) {
860
  return $this->invalidate_row();
@@ -877,8 +1062,7 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
877
  return;
878
  }
879
 
880
- $classname = $this->db_class_name;
881
- $classname::get_instance()->update( $this->id, $data );
882
 
883
  $this->reset_row_cache();
884
  }
@@ -895,12 +1079,24 @@ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_D
895
  return;
896
  }
897
 
898
- $classname = $this->db_class_name;
899
- $classname::get_instance()->delete( $this->id );
900
 
901
  $this->invalidate_row();
902
  }
903
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
  /**
905
  * Invalidate the row.
906
  *
13
  *
14
  * @var string
15
  */
16
+ const VERSION = '1.3.2';
17
 
18
  /**
19
  * The attachment ID.
24
  */
25
  public $id = 0;
26
 
27
+ /**
28
+ * Context.
29
+ *
30
+ * @var string
31
+ * @since 1.7.1
32
+ * @access protected
33
+ * @author Grégory Viguier
34
+ */
35
+ protected $context;
36
+
37
  /**
38
  * The attachment SQL DB class.
39
  *
63
  */
64
  protected $is_extension_supported;
65
 
66
+ /**
67
+ * Filesystem object.
68
+ *
69
+ * @var object Imagify_Filesystem
70
+ * @since 1.7.1
71
+ * @access protected
72
+ * @author Grégory Viguier
73
+ */
74
+ protected $filesystem;
75
+
76
+ /**
77
+ * The editor instances used to resize files.
78
+ *
79
+ * @var array An array of image editor objects (WP_Image_Editor_Imagick, WP_Image_Editor_GD).
80
+ * @since 1.7.1
81
+ * @access protected
82
+ * @author Grégory Viguier
83
+ */
84
+ protected $editors = array();
85
+
86
+ /**
87
+ * The name of the transient that tells if optimization is processing.
88
+ *
89
+ * @var string
90
+ * @since 1.7.1
91
+ * @access protected
92
+ * @author Grégory Viguier
93
+ */
94
+ protected $optimization_state_transient;
95
+
96
+ /**
97
+ * Tell if the optimization status is network-wide.
98
+ *
99
+ * @var bool
100
+ * @since 1.7.1
101
+ * @access protected
102
+ * @author Grégory Viguier
103
+ */
104
+ protected $optimization_state_network_wide = false;
105
+
106
  /**
107
  * The constructor.
108
  *
125
  $this->id = $post->ID;
126
  }
127
 
128
+ $this->id = (int) $this->id;
129
+ $this->filesystem = Imagify_Filesystem::get_instance();
130
+ $this->optimization_state_transient = 'wp' !== $this->get_context() ? strtolower( $this->get_context() ) . '-' : '';
131
+ $this->optimization_state_transient = 'imagify-' . $this->optimization_state_transient . 'async-in-progress-' . $this->id;
132
+ }
133
+
134
+ /**
135
+ * Get the attachment context.
136
+ *
137
+ * @since 1.7.1
138
+ * @access public
139
+ * @author Grégory Viguier
140
+ *
141
+ * @return string
142
+ */
143
+ public function get_context() {
144
+ if ( $this->context ) {
145
+ return $this->context;
146
+ }
147
+
148
+ $this->context = str_replace( array( 'Imagify_', 'Attachment' ), '', get_class( $this ) );
149
+ $this->context = trim( $this->context, '_' );
150
+ $this->context = $this->context ? $this->context : 'wp';
151
+
152
+ return $this->context;
153
  }
154
 
155
  /**
224
 
225
  $backup_path = $this->get_raw_backup_path();
226
 
227
+ if ( $backup_path && $this->filesystem->exists( $backup_path ) ) {
228
  return $backup_path;
229
  }
230
 
362
  * @since 1.0
363
  * @access public
364
  *
365
+ * @return string|null
366
  */
367
  public function get_extension() {
368
  if ( ! $this->is_valid() ) {
369
  return '';
370
  }
371
 
372
+ return $this->filesystem->path_info( $this->get_original_path(), 'extension' );
 
373
  }
374
 
375
  /**
469
 
470
  if ( ! $filepath ) {
471
  $filepath = $this->get_original_path();
472
+ $filepath = $filepath && $this->filesystem->exists( $filepath ) ? $filepath : false;
473
  }
474
 
475
+ $size = $filepath ? $this->filesystem->size( $filepath ) : 0;
476
  }
477
 
478
  if ( $human_format ) {
502
 
503
  if ( ! $size ) {
504
  $filepath = $this->get_original_path();
505
+ $filepath = $filepath && $this->filesystem->exists( $filepath ) ? $filepath : false;
506
+ $size = $filepath ? $this->filesystem->size( $filepath ) : 0;
507
  }
508
 
509
  if ( $human_format ) {
638
  $filepath = $this->get_original_path();
639
  $size = 0;
640
 
641
+ if ( $filepath && $this->filesystem->exists( $filepath ) ) {
642
+ $size = $this->filesystem->size( $filepath );
643
  }
644
 
645
  return $size > IMAGIFY_MAX_BYTES;
669
  return 'error' === $this->get_status();
670
  }
671
 
672
+ /**
673
+ * Get an image editor instance (WP_Image_Editor_Imagick, WP_Image_Editor_GD).
674
+ *
675
+ * @since 1.7.1
676
+ * @access protected
677
+ * @author Grégory Viguier
678
+ *
679
+ * @param string $path A file path.
680
+ * @return object An image editor instance (WP_Image_Editor_Imagick, WP_Image_Editor_GD). A WP_Error object on error.
681
+ */
682
+ protected function get_editor( $path ) {
683
+ if ( isset( $this->editors[ $path ] ) ) {
684
+ return $this->editors[ $path ];
685
+ }
686
+
687
+ $this->editors[ $path ] = wp_get_image_editor( $path, array(
688
+ 'methods' => self::get_editor_methods(),
689
+ ) );
690
+
691
+ return $this->editors[ $path ];
692
+ }
693
+
694
+ /**
695
+ * Get the image editor methods we will use.
696
+ *
697
+ * @since 1.7.1
698
+ * @access public
699
+ * @author Grégory Viguier
700
+ *
701
+ * @return array
702
+ */
703
+ public static function get_editor_methods() {
704
+ static $methods;
705
+
706
+ if ( isset( $methods ) ) {
707
+ return $methods;
708
+ }
709
+
710
+ $methods = array(
711
+ 'resize',
712
+ 'multi_resize',
713
+ 'generate_filename',
714
+ 'save',
715
+ );
716
+
717
+ if ( Imagify_Filesystem::get_instance()->can_get_exif() ) {
718
+ $methods[] = 'rotate';
719
+ }
720
+
721
+ return $methods;
722
+ }
723
+
724
  /**
725
  * Update the metadata size of the attachment
726
  *
743
  $backup_path = $this->get_backup_path();
744
 
745
  if ( $backup_path ) {
746
+ $this->filesystem->delete( $backup_path );
747
  }
748
  }
749
 
802
  return array();
803
  }
804
 
805
+ $orig_f = $this->filesystem->path_info( $orig_f );
806
+ $orig_f = $orig_f['file_base'] . '-{%suffix%}.' . $orig_f['extension'];
807
 
808
  // Test if the missing sizes are needed.
809
  $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
880
  */
881
  abstract public function optimize_missing_thumbnails( $optimization_level = null );
882
 
883
+ /**
884
+ * Re-optimize the given thumbnail sizes to the same level.
885
+ * Before doing this, the given sizes must be restored.
886
+ *
887
+ * @since 1.7.1
888
+ * @access public
889
+ * @author Grégory Viguier
890
+ *
891
+ * @param array $sizes The sizes to optimize.
892
+ * @return array|void A WP_Error object on failure.
893
+ */
894
+ abstract public function reoptimize_thumbnails( $sizes );
895
+
896
  /**
897
  * Process an attachment restoration from the backup file.
898
  *
907
  * Resize an image if bigger than the maximum width defined in the settings.
908
  *
909
  * @since 1.5.7
910
+ * @since 1.7.1 Keys for width and height in $attachment_sizes are now 'width' and 'height' instead of 0 and 1.
911
  * @access public
912
  * @author Remy Perona
913
  *
924
  // Prevent removal of the exif/meta data when resizing (only works with Imagick).
925
  add_filter( 'image_strip_meta', '__return_false' );
926
 
927
+ $new_sizes = wp_constrain_dimensions( $attachment_sizes['width'], $attachment_sizes['height'], $max_width );
928
+
929
+ $editor = $this->get_editor( $attachment_path );
930
 
931
  if ( is_wp_error( $editor ) ) {
932
  return $editor;
933
  }
934
 
935
+ $image_type = strtolower( (string) $this->filesystem->path_info( $attachment_path, 'extension' ) );
936
 
937
  // Try to correct for auto-rotation if the info is available.
938
+ if ( $this->filesystem->can_get_exif() && ( 'jpg' === $image_type || 'jpe' === $image_type || 'jpeg' === $image_type ) ) {
939
+ $exif = $this->filesystem->get_image_exif( $attachment_path );
940
+ $orientation = isset( $exif['Orientation'] ) ? (int) $exif['Orientation'] : 1;
941
 
942
  switch ( $orientation ) {
943
  case 3:
971
  }
972
 
973
 
974
+ /** ----------------------------------------------------------------------------------------- */
975
+ /** WORKING STATUS ========================================================================== */
976
+ /** ----------------------------------------------------------------------------------------- */
977
+
978
+ /**
979
+ * Tell if the file is currently being optimized (or restored, etc).
980
+ *
981
+ * @since 1.7.1
982
+ * @author Grégory Viguier
983
+ * @access public
984
+ *
985
+ * @return bool
986
+ */
987
+ public function is_running() {
988
+ $callback = $this->optimization_state_network_wide ? 'get_site_transient' : 'get_transient';
989
+
990
+ return false !== call_user_func( $callback, $this->optimization_state_transient );
991
+ }
992
+
993
+ /**
994
+ * Set the running status to "running" for 10 minutes.
995
+ *
996
+ * @since 1.7.1
997
+ * @author Grégory Viguier
998
+ * @access public
999
+ */
1000
+ public function set_running_status() {
1001
+ $callback = $this->optimization_state_network_wide ? 'set_site_transient' : 'set_transient';
1002
+
1003
+ call_user_func( $callback, $this->optimization_state_transient, true, 10 * MINUTE_IN_SECONDS );
1004
+ }
1005
+
1006
+ /**
1007
+ * Unset the running status.
1008
+ *
1009
+ * @since 1.7.1
1010
+ * @author Grégory Viguier
1011
+ * @access public
1012
+ */
1013
+ public function delete_running_status() {
1014
+ $callback = $this->optimization_state_network_wide ? 'delete_site_transient' : 'delete_transient';
1015
+
1016
+ call_user_func( $callback, $this->optimization_state_transient );
1017
+ }
1018
+
1019
+
1020
  /** ----------------------------------------------------------------------------------------- */
1021
  /** DB ROW ================================================================================== */
1022
  /** ----------------------------------------------------------------------------------------- */
1039
  return $this->invalidate_row();
1040
  }
1041
 
1042
+ $this->row = $this->get_row_db_instance()->get( $this->id );
 
1043
 
1044
  if ( ! $this->row ) {
1045
  return $this->invalidate_row();
1062
  return;
1063
  }
1064
 
1065
+ $this->get_row_db_instance()->update( $this->id, $data );
 
1066
 
1067
  $this->reset_row_cache();
1068
  }
1079
  return;
1080
  }
1081
 
1082
+ $this->get_row_db_instance()->delete( $this->id );
 
1083
 
1084
  $this->invalidate_row();
1085
  }
1086
 
1087
+ /**
1088
+ * Shorthand to get the DB table instance.
1089
+ *
1090
+ * @since 1.7.1
1091
+ * @author Grégory Viguier
1092
+ * @access public
1093
+ *
1094
+ * @return object The DB table instance.
1095
+ */
1096
+ public function get_row_db_instance() {
1097
+ return call_user_func( array( $this->db_class_name, 'get_instance' ) );
1098
+ }
1099
+
1100
  /**
1101
  * Invalidate the row.
1102
  *
inc/classes/class-imagify-abstract-db.php CHANGED
@@ -14,7 +14,7 @@ abstract class Imagify_Abstract_DB extends Imagify_Abstract_DB_Deprecated {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.2';
18
 
19
  /**
20
  * Suffix used in the name of the options that store the table versions.
@@ -561,9 +561,9 @@ abstract class Imagify_Abstract_DB extends Imagify_Abstract_DB_Deprecated {
561
  $wpdb->{$this->table} = $this->table_name;
562
 
563
  if ( $this->table_is_global ) {
564
- $wpdb->global_tables[] = $this->table_name;
565
  } else {
566
- $wpdb->tables[] = $this->table_name;
567
  }
568
  }
569
 
@@ -581,9 +581,9 @@ abstract class Imagify_Abstract_DB extends Imagify_Abstract_DB_Deprecated {
581
  unset( $wpdb->{$this->table} );
582
 
583
  if ( $this->table_is_global ) {
584
- $wpdb->global_tables = array_diff( $wpdb->global_tables, array( $this->table_name ) );
585
  } else {
586
- $wpdb->tables = array_diff( $wpdb->tables, array( $this->table_name ) );
587
  }
588
  }
589
 
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.2.1';
18
 
19
  /**
20
  * Suffix used in the name of the options that store the table versions.
561
  $wpdb->{$this->table} = $this->table_name;
562
 
563
  if ( $this->table_is_global ) {
564
+ $wpdb->global_tables[] = $this->table;
565
  } else {
566
+ $wpdb->tables[] = $this->table;
567
  }
568
  }
569
 
581
  unset( $wpdb->{$this->table} );
582
 
583
  if ( $this->table_is_global ) {
584
+ $wpdb->global_tables = array_diff( $wpdb->global_tables, array( $this->table ) );
585
  } else {
586
+ $wpdb->tables = array_diff( $wpdb->tables, array( $this->table ) );
587
  }
588
  }
589
 
inc/classes/class-imagify-admin-ajax-post.php CHANGED
@@ -12,14 +12,19 @@ class Imagify_Admin_Ajax_Post {
12
  /**
13
  * Class version.
14
  *
15
- * @var string
 
 
16
  */
17
- const VERSION = '1.0.1';
18
 
19
  /**
20
  * Actions to be triggered on admin ajax and admin post.
21
  *
22
- * @var array
 
 
 
23
  */
24
  protected $ajax_post_actions = array(
25
  'imagify_manual_upload',
@@ -35,7 +40,10 @@ class Imagify_Admin_Ajax_Post {
35
  /**
36
  * Actions to be triggered only on admin ajax.
37
  *
38
- * @var array
 
 
 
39
  */
40
  protected $ajax_only_actions = array(
41
  'imagify_bulk_upload',
@@ -58,18 +66,32 @@ class Imagify_Admin_Ajax_Post {
58
  'imagify_get_files_tree',
59
  'imagify_get_folder_type_data',
60
  'imagify_bulk_info_seen',
 
61
  );
62
 
63
  /**
64
  * Actions to be triggered only on admin post.
65
  *
66
- * @var array
 
 
 
67
  */
68
  protected $post_only_actions = array(
69
  'imagify_scan_custom_folders',
70
  'imagify_dismiss_ad',
71
  );
72
 
 
 
 
 
 
 
 
 
 
 
73
  /**
74
  * The single instance of the class.
75
  *
@@ -80,9 +102,13 @@ class Imagify_Admin_Ajax_Post {
80
  /**
81
  * The constructor.
82
  *
83
- * @return void
 
 
84
  */
85
- protected function __construct() {}
 
 
86
 
87
 
88
  /** ----------------------------------------------------------------------------------------- */
@@ -93,6 +119,7 @@ class Imagify_Admin_Ajax_Post {
93
  * Get the main Instance.
94
  *
95
  * @since 1.6.11
 
96
  * @author Grégory Viguier
97
  *
98
  * @return object Main instance.
@@ -109,6 +136,7 @@ class Imagify_Admin_Ajax_Post {
109
  * Launch the hooks.
110
  *
111
  * @since 1.6.11
 
112
  * @author Grégory Viguier
113
  */
114
  public function init() {
@@ -138,6 +166,7 @@ class Imagify_Admin_Ajax_Post {
138
  * Optimize all thumbnails of a specific image with the manual method.
139
  *
140
  * @since 1.6.11
 
141
  * @author Jonathan Buttigieg
142
  */
143
  public function imagify_manual_upload_callback() {
@@ -167,6 +196,7 @@ class Imagify_Admin_Ajax_Post {
167
  * Optimize all thumbnails of a specific image with a different optimization level.
168
  *
169
  * @since 1.6.11
 
170
  * @author Jonathan Buttigieg
171
  */
172
  public function imagify_manual_override_upload_callback() {
@@ -199,6 +229,7 @@ class Imagify_Admin_Ajax_Post {
199
  * Optimize one or some thumbnails that are not optimized yet.
200
  *
201
  * @since 1.6.11
 
202
  * @author Grégory Viguier
203
  */
204
  public function imagify_optimize_missing_sizes_callback() {
@@ -228,6 +259,7 @@ class Imagify_Admin_Ajax_Post {
228
  * Process a restoration to the original attachment.
229
  *
230
  * @since 1.6.11
 
231
  * @author Jonathan Buttigieg
232
  */
233
  public function imagify_restore_upload_callback() {
@@ -258,6 +290,7 @@ class Imagify_Admin_Ajax_Post {
258
  * Optimize all thumbnails of a specific image with the bulk method.
259
  *
260
  * @since 1.6.11
 
261
  * @author Jonathan Buttigieg
262
  */
263
  public function imagify_bulk_upload_callback() {
@@ -558,6 +591,7 @@ class Imagify_Admin_Ajax_Post {
558
  * Optimize image on picture uploading with async request.
559
  *
560
  * @since 1.6.11
 
561
  * @author Julio Potier
562
  * @see _imagify_optimize_attachment()
563
  */
@@ -583,6 +617,7 @@ class Imagify_Admin_Ajax_Post {
583
  * Optimize image on picture editing (resize, crop...) with async request.
584
  *
585
  * @since 1.6.11
 
586
  * @author Julio Potier
587
  */
588
  public function imagify_async_optimize_save_image_editor_file_callback() {
@@ -635,6 +670,7 @@ class Imagify_Admin_Ajax_Post {
635
  * Get all unoptimized attachment ids.
636
  *
637
  * @since 1.6.11
 
638
  * @author Jonathan Buttigieg
639
  */
640
  public function imagify_get_unoptimized_attachment_ids_callback() {
@@ -753,8 +789,7 @@ class Imagify_Admin_Ajax_Post {
753
  */
754
  do_action( 'imagify_bulk_optimize_before_file_existence_tests', $ids, $results, $optimization_level );
755
 
756
- $data = array();
757
- $filesystem = imagify_get_filesystem();
758
 
759
  foreach ( $ids as $i => $id ) {
760
  if ( empty( $results['filenames'][ $id ] ) ) {
@@ -767,7 +802,7 @@ class Imagify_Admin_Ajax_Post {
767
  /** This filter is documented in inc/functions/process.php. */
768
  $file_path = apply_filters( 'imagify_file_path', $file_path );
769
 
770
- if ( ! $file_path || ! $filesystem->exists( $file_path ) ) {
771
  continue;
772
  }
773
 
@@ -776,7 +811,7 @@ class Imagify_Admin_Ajax_Post {
776
  $attachment_optimization_level = isset( $results['optimization_levels'][ $id ] ) ? $results['optimization_levels'][ $id ] : false;
777
 
778
  // Don't try to re-optimize if there is no backup file.
779
- if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! $filesystem->exists( $attachment_backup_path ) ) {
780
  continue;
781
  }
782
 
@@ -794,6 +829,7 @@ class Imagify_Admin_Ajax_Post {
794
  * Get all unoptimized file ids.
795
  *
796
  * @since 1.7
 
797
  * @author Grégory Viguier
798
  */
799
  public function imagify_get_unoptimized_file_ids_callback() {
@@ -905,11 +941,42 @@ class Imagify_Admin_Ajax_Post {
905
  wp_send_json_success();
906
  }
907
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
908
  /**
909
  * Check if the backup directory is writable.
910
  * This is used to display an error message in the plugin's settings page.
911
  *
912
  * @since 1.6.11
 
913
  * @author Grégory Viguier
914
  */
915
  public function imagify_check_backup_dir_is_writable_callback() {
@@ -917,7 +984,7 @@ class Imagify_Admin_Ajax_Post {
917
  imagify_check_user_capacity();
918
 
919
  wp_send_json_success( array(
920
- 'is_writable' => (int) imagify_backup_dir_is_writable(),
921
  ) );
922
  }
923
 
@@ -926,6 +993,7 @@ class Imagify_Admin_Ajax_Post {
926
  * When XML-RPC is used, a current user is set, but no cookies are set, so they cannot be sent with the request. Instead we stored the user ID in a transient.
927
  *
928
  * @since 1.6.11
 
929
  * @author Grégory Viguier
930
  * @see imagify_do_async_job()
931
  */
@@ -976,6 +1044,7 @@ class Imagify_Admin_Ajax_Post {
976
  * Create a new Imagify account.
977
  *
978
  * @since 1.6.11
 
979
  * @author Jonathan Buttigieg
980
  */
981
  public function imagify_signup_callback() {
@@ -1009,6 +1078,7 @@ class Imagify_Admin_Ajax_Post {
1009
  * Check the API key validity.
1010
  *
1011
  * @since 1.6.11
 
1012
  * @author Jonathan Buttigieg
1013
  */
1014
  public function imagify_check_api_key_validity_callback() {
@@ -1034,6 +1104,7 @@ class Imagify_Admin_Ajax_Post {
1034
  * Get admin bar profile output.
1035
  *
1036
  * @since 1.6.11
 
1037
  * @author Jonathan Buttigieg
1038
  */
1039
  public function imagify_get_admin_bar_profile_callback() {
@@ -1119,6 +1190,7 @@ class Imagify_Admin_Ajax_Post {
1119
  * Get pricings from API for Onetime and Plans at the same time.
1120
  *
1121
  * @since 1.6.11
 
1122
  * @author Geoffrey Crofte
1123
  */
1124
  public function imagify_get_prices_callback() {
@@ -1145,6 +1217,7 @@ class Imagify_Admin_Ajax_Post {
1145
  * Check Coupon code on modal popin.
1146
  *
1147
  * @since 1.6.11
 
1148
  * @author Geoffrey Crofte
1149
  */
1150
  public function imagify_check_coupon_callback() {
@@ -1184,6 +1257,7 @@ class Imagify_Admin_Ajax_Post {
1184
  * Get estimated sizes from the WordPress library.
1185
  *
1186
  * @since 1.6.11
 
1187
  * @author Geoffrey Crofte
1188
  */
1189
  public function imagify_get_images_counts_callback() {
@@ -1208,6 +1282,7 @@ class Imagify_Admin_Ajax_Post {
1208
  * Estimate sizes and update the options values for them.
1209
  *
1210
  * @since 1.6.11
 
1211
  * @author Remy Perona
1212
  */
1213
  public function imagify_update_estimate_sizes_callback() {
@@ -1229,6 +1304,7 @@ class Imagify_Admin_Ajax_Post {
1229
  * Get the Imagify User data.
1230
  *
1231
  * @since 1.7
 
1232
  * @author Grégory Viguier
1233
  */
1234
  public function imagify_get_user_data_callback() {
@@ -1259,11 +1335,10 @@ class Imagify_Admin_Ajax_Post {
1259
  * Get files and folders that are direct children of a given folder.
1260
  *
1261
  * @since 1.7
 
1262
  * @author Grégory Viguier
1263
  */
1264
  public function imagify_get_files_tree_callback() {
1265
- static $abspath;
1266
-
1267
  imagify_check_nonce( 'get-files-tree' );
1268
  imagify_check_user_capacity( 'optimize-file' );
1269
 
@@ -1272,13 +1347,13 @@ class Imagify_Admin_Ajax_Post {
1272
  }
1273
 
1274
  $folder = trailingslashit( sanitize_text_field( $_POST['folder'] ) );
1275
- $folder = realpath( ABSPATH . ltrim( $folder, '/' ) );
1276
 
1277
- if ( ! $folder || ! imagify_get_filesystem()->exists( $folder ) ) {
1278
  imagify_die( __( 'This folder doesn\'t exist.', 'imagify' ) );
1279
  }
1280
 
1281
- if ( ! imagify_get_filesystem()->is_dir( $folder ) ) {
1282
  imagify_die( __( 'This file is not a folder.', 'imagify' ) );
1283
  }
1284
 
@@ -1286,17 +1361,13 @@ class Imagify_Admin_Ajax_Post {
1286
  imagify_die( __( 'This folder is not allowed.', 'imagify' ) );
1287
  }
1288
 
1289
- if ( ! isset( $abspath ) ) {
1290
- $abspath = wp_normalize_path( ABSPATH );
1291
- }
1292
-
1293
  // Finally we made all our validations.
1294
  $selected = ! empty( $_POST['selected'] ) && is_array( $_POST['selected'] ) ? array_flip( $_POST['selected'] ) : array();
1295
- $folder = wp_normalize_path( trailingslashit( $folder ) );
1296
  $views = Imagify_Views::get_instance();
1297
  $output = '';
1298
 
1299
- if ( $folder === $abspath ) {
1300
  $output .= $views->get_template( 'part-settings-files-tree-row', array(
1301
  'relative_path' => '/',
1302
  // Value #///# Label.
@@ -1318,17 +1389,17 @@ class Imagify_Admin_Ajax_Post {
1318
  continue;
1319
  }
1320
 
1321
- $folder_path = $file->getPathname();
1322
- $relative_path = esc_attr( imagify_make_file_path_relative( trailingslashit( $folder_path ) ) );
1323
- $placeholder = Imagify_Files_Scan::add_placeholder( trailingslashit( $folder_path ) );
1324
 
1325
  $output .= $views->get_template( 'part-settings-files-tree-row', array(
1326
- 'relative_path' => $relative_path,
1327
  // Value #///# Label.
1328
  'checkbox_value' => esc_attr( $placeholder ) . '#///#' . esc_attr( $relative_path ),
1329
  'checkbox_id' => sanitize_html_class( $placeholder ),
1330
  'checkbox_selected' => isset( $selected[ $placeholder ] ),
1331
- 'label' => str_replace( $folder, '', $folder_path ),
1332
  ) );
1333
  }
1334
 
@@ -1415,13 +1486,11 @@ class Imagify_Admin_Ajax_Post {
1415
  * @author Grégory Viguier
1416
  */
1417
  public function check_can_optimize() {
1418
- if ( ! imagify_valid_key() ) {
1419
  wp_send_json_error( array( 'message' => 'invalid-api-key' ) );
1420
  }
1421
 
1422
- $user = new Imagify_User();
1423
-
1424
- if ( $user->is_over_quota() ) {
1425
  wp_send_json_error( array( 'message' => 'over-quota' ) );
1426
  }
1427
  }
12
  /**
13
  * Class version.
14
  *
15
+ * @var string
16
+ * @since 1.6.11
17
+ * @author Grégory Viguier
18
  */
19
+ const VERSION = '1.0.3';
20
 
21
  /**
22
  * Actions to be triggered on admin ajax and admin post.
23
  *
24
+ * @var array
25
+ * @since 1.6.11
26
+ * @access protected
27
+ * @author Grégory Viguier
28
  */
29
  protected $ajax_post_actions = array(
30
  'imagify_manual_upload',
40
  /**
41
  * Actions to be triggered only on admin ajax.
42
  *
43
+ * @var array
44
+ * @since 1.6.11
45
+ * @access protected
46
+ * @author Grégory Viguier
47
  */
48
  protected $ajax_only_actions = array(
49
  'imagify_bulk_upload',
66
  'imagify_get_files_tree',
67
  'imagify_get_folder_type_data',
68
  'imagify_bulk_info_seen',
69
+ 'imagify_bulk_get_stats',
70
  );
71
 
72
  /**
73
  * Actions to be triggered only on admin post.
74
  *
75
+ * @var array
76
+ * @since 1.6.11
77
+ * @access protected
78
+ * @author Grégory Viguier
79
  */
80
  protected $post_only_actions = array(
81
  'imagify_scan_custom_folders',
82
  'imagify_dismiss_ad',
83
  );
84
 
85
+ /**
86
+ * Filesystem object.
87
+ *
88
+ * @var object Imagify_Filesystem
89
+ * @since 1.7.1
90
+ * @access protected
91
+ * @author Grégory Viguier
92
+ */
93
+ protected $filesystem;
94
+
95
  /**
96
  * The single instance of the class.
97
  *
102
  /**
103
  * The constructor.
104
  *
105
+ * @since 1.6.11
106
+ * @access protected
107
+ * @author Grégory Viguier
108
  */
109
+ protected function __construct() {
110
+ $this->filesystem = Imagify_Filesystem::get_instance();
111
+ }
112
 
113
 
114
  /** ----------------------------------------------------------------------------------------- */
119
  * Get the main Instance.
120
  *
121
  * @since 1.6.11
122
+ * @access public
123
  * @author Grégory Viguier
124
  *
125
  * @return object Main instance.
136
  * Launch the hooks.
137
  *
138
  * @since 1.6.11
139
+ * @access public
140
  * @author Grégory Viguier
141
  */
142
  public function init() {
166
  * Optimize all thumbnails of a specific image with the manual method.
167
  *
168
  * @since 1.6.11
169
+ * @access public
170
  * @author Jonathan Buttigieg
171
  */
172
  public function imagify_manual_upload_callback() {
196
  * Optimize all thumbnails of a specific image with a different optimization level.
197
  *
198
  * @since 1.6.11
199
+ * @access public
200
  * @author Jonathan Buttigieg
201
  */
202
  public function imagify_manual_override_upload_callback() {
229
  * Optimize one or some thumbnails that are not optimized yet.
230
  *
231
  * @since 1.6.11
232
+ * @access public
233
  * @author Grégory Viguier
234
  */
235
  public function imagify_optimize_missing_sizes_callback() {
259
  * Process a restoration to the original attachment.
260
  *
261
  * @since 1.6.11
262
+ * @access public
263
  * @author Jonathan Buttigieg
264
  */
265
  public function imagify_restore_upload_callback() {
290
  * Optimize all thumbnails of a specific image with the bulk method.
291
  *
292
  * @since 1.6.11
293
+ * @access public
294
  * @author Jonathan Buttigieg
295
  */
296
  public function imagify_bulk_upload_callback() {
591
  * Optimize image on picture uploading with async request.
592
  *
593
  * @since 1.6.11
594
+ * @access public
595
  * @author Julio Potier
596
  * @see _imagify_optimize_attachment()
597
  */
617
  * Optimize image on picture editing (resize, crop...) with async request.
618
  *
619
  * @since 1.6.11
620
+ * @access public
621
  * @author Julio Potier
622
  */
623
  public function imagify_async_optimize_save_image_editor_file_callback() {
670
  * Get all unoptimized attachment ids.
671
  *
672
  * @since 1.6.11
673
+ * @access public
674
  * @author Jonathan Buttigieg
675
  */
676
  public function imagify_get_unoptimized_attachment_ids_callback() {
789
  */
790
  do_action( 'imagify_bulk_optimize_before_file_existence_tests', $ids, $results, $optimization_level );
791
 
792
+ $data = array();
 
793
 
794
  foreach ( $ids as $i => $id ) {
795
  if ( empty( $results['filenames'][ $id ] ) ) {
802
  /** This filter is documented in inc/functions/process.php. */
803
  $file_path = apply_filters( 'imagify_file_path', $file_path );
804
 
805
+ if ( ! $file_path || ! $this->filesystem->exists( $file_path ) ) {
806
  continue;
807
  }
808
 
811
  $attachment_optimization_level = isset( $results['optimization_levels'][ $id ] ) ? $results['optimization_levels'][ $id ] : false;
812
 
813
  // Don't try to re-optimize if there is no backup file.
814
+ if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! $this->filesystem->exists( $attachment_backup_path ) ) {
815
  continue;
816
  }
817
 
829
  * Get all unoptimized file ids.
830
  *
831
  * @since 1.7
832
+ * @access public
833
  * @author Grégory Viguier
834
  */
835
  public function imagify_get_unoptimized_file_ids_callback() {
941
  wp_send_json_success();
942
  }
943
 
944
+ /**
945
+ * Get generic stats to display in the bulk page.
946
+ *
947
+ * @since 1.7.1
948
+ * @access public
949
+ * @author Grégory Viguier
950
+ */
951
+ public function imagify_bulk_get_stats_callback() {
952
+ imagify_check_nonce( 'imagify-bulk-upload' );
953
+
954
+ $folder_types = filter_input( INPUT_GET, 'types', FILTER_SANITIZE_STRING, FILTER_REQUIRE_ARRAY );
955
+ $folder_types = is_array( $folder_types ) ? array_flip( array_filter( $folder_types ) ) : array();
956
+
957
+ if ( ! $folder_types ) {
958
+ imagify_die( __( 'Invalid request', 'imagify' ) );
959
+ }
960
+
961
+ foreach ( $folder_types as $folder_type ) {
962
+ if ( 'library' === $folder_type ) {
963
+ imagify_check_user_capacity( 'bulk-optimize' );
964
+ } elseif ( 'custom-folders' === $folder_type ) {
965
+ imagify_check_user_capacity( 'optimize-file' );
966
+ } else {
967
+ imagify_check_user_capacity( 'bulk-optimize', $folder_type );
968
+ }
969
+ }
970
+
971
+ wp_send_json_success( imagify_get_bulk_stats( $folder_types ) );
972
+ }
973
+
974
  /**
975
  * Check if the backup directory is writable.
976
  * This is used to display an error message in the plugin's settings page.
977
  *
978
  * @since 1.6.11
979
+ * @access public
980
  * @author Grégory Viguier
981
  */
982
  public function imagify_check_backup_dir_is_writable_callback() {
984
  imagify_check_user_capacity();
985
 
986
  wp_send_json_success( array(
987
+ 'is_writable' => (int) Imagify_Requirements::attachments_backup_dir_is_writable(),
988
  ) );
989
  }
990
 
993
  * When XML-RPC is used, a current user is set, but no cookies are set, so they cannot be sent with the request. Instead we stored the user ID in a transient.
994
  *
995
  * @since 1.6.11
996
+ * @access public
997
  * @author Grégory Viguier
998
  * @see imagify_do_async_job()
999
  */
1044
  * Create a new Imagify account.
1045
  *
1046
  * @since 1.6.11
1047
+ * @access public
1048
  * @author Jonathan Buttigieg
1049
  */
1050
  public function imagify_signup_callback() {
1078
  * Check the API key validity.
1079
  *
1080
  * @since 1.6.11
1081
+ * @access public
1082
  * @author Jonathan Buttigieg
1083
  */
1084
  public function imagify_check_api_key_validity_callback() {
1104
  * Get admin bar profile output.
1105
  *
1106
  * @since 1.6.11
1107
+ * @access public
1108
  * @author Jonathan Buttigieg
1109
  */
1110
  public function imagify_get_admin_bar_profile_callback() {
1190
  * Get pricings from API for Onetime and Plans at the same time.
1191
  *
1192
  * @since 1.6.11
1193
+ * @access public
1194
  * @author Geoffrey Crofte
1195
  */
1196
  public function imagify_get_prices_callback() {
1217
  * Check Coupon code on modal popin.
1218
  *
1219
  * @since 1.6.11
1220
+ * @access public
1221
  * @author Geoffrey Crofte
1222
  */
1223
  public function imagify_check_coupon_callback() {
1257
  * Get estimated sizes from the WordPress library.
1258
  *
1259
  * @since 1.6.11
1260
+ * @access public
1261
  * @author Geoffrey Crofte
1262
  */
1263
  public function imagify_get_images_counts_callback() {
1282
  * Estimate sizes and update the options values for them.
1283
  *
1284
  * @since 1.6.11
1285
+ * @access public
1286
  * @author Remy Perona
1287
  */
1288
  public function imagify_update_estimate_sizes_callback() {
1304
  * Get the Imagify User data.
1305
  *
1306
  * @since 1.7
1307
+ * @access public
1308
  * @author Grégory Viguier
1309
  */
1310
  public function imagify_get_user_data_callback() {
1335
  * Get files and folders that are direct children of a given folder.
1336
  *
1337
  * @since 1.7
1338
+ * @access public
1339
  * @author Grégory Viguier
1340
  */
1341
  public function imagify_get_files_tree_callback() {
 
 
1342
  imagify_check_nonce( 'get-files-tree' );
1343
  imagify_check_user_capacity( 'optimize-file' );
1344
 
1347
  }
1348
 
1349
  $folder = trailingslashit( sanitize_text_field( $_POST['folder'] ) );
1350
+ $folder = realpath( $this->filesystem->get_abspath() . ltrim( $folder, '/' ) );
1351
 
1352
+ if ( ! $folder ) {
1353
  imagify_die( __( 'This folder doesn\'t exist.', 'imagify' ) );
1354
  }
1355
 
1356
+ if ( ! $this->filesystem->is_dir( $folder ) ) {
1357
  imagify_die( __( 'This file is not a folder.', 'imagify' ) );
1358
  }
1359
 
1361
  imagify_die( __( 'This folder is not allowed.', 'imagify' ) );
1362
  }
1363
 
 
 
 
 
1364
  // Finally we made all our validations.
1365
  $selected = ! empty( $_POST['selected'] ) && is_array( $_POST['selected'] ) ? array_flip( $_POST['selected'] ) : array();
1366
+ $folder = $this->filesystem->normalize_dir_path( $folder );
1367
  $views = Imagify_Views::get_instance();
1368
  $output = '';
1369
 
1370
+ if ( $this->filesystem->is_abspath( $folder ) ) {
1371
  $output .= $views->get_template( 'part-settings-files-tree-row', array(
1372
  'relative_path' => '/',
1373
  // Value #///# Label.
1389
  continue;
1390
  }
1391
 
1392
+ $folder_path = trailingslashit( $file->getPathname() );
1393
+ $relative_path = $this->filesystem->make_path_relative( $folder_path );
1394
+ $placeholder = Imagify_Files_Scan::add_placeholder( $folder_path );
1395
 
1396
  $output .= $views->get_template( 'part-settings-files-tree-row', array(
1397
+ 'relative_path' => esc_attr( $relative_path ),
1398
  // Value #///# Label.
1399
  'checkbox_value' => esc_attr( $placeholder ) . '#///#' . esc_attr( $relative_path ),
1400
  'checkbox_id' => sanitize_html_class( $placeholder ),
1401
  'checkbox_selected' => isset( $selected[ $placeholder ] ),
1402
+ 'label' => str_replace( $folder, '', untrailingslashit( $folder_path ) ),
1403
  ) );
1404
  }
1405
 
1486
  * @author Grégory Viguier
1487
  */
1488
  public function check_can_optimize() {
1489
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
1490
  wp_send_json_error( array( 'message' => 'invalid-api-key' ) );
1491
  }
1492
 
1493
+ if ( Imagify_Requirements::is_over_quota() ) {
 
 
1494
  wp_send_json_error( array( 'message' => 'over-quota' ) );
1495
  }
1496
  }
inc/classes/class-imagify-assets.php CHANGED
@@ -346,7 +346,7 @@ class Imagify_Assets {
346
  * @author Grégory Viguier
347
  */
348
  public function print_support_script() {
349
- if ( ! imagify_valid_key() ) {
350
  return;
351
  }
352
 
346
  * @author Grégory Viguier
347
  */
348
  public function print_support_script() {
349
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
350
  return;
351
  }
352
 
inc/classes/class-imagify-attachment.php CHANGED
@@ -13,17 +13,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
13
  *
14
  * @var string
15
  */
16
- const VERSION = '1.1.1';
17
-
18
- /**
19
- * The editor instance used to resize files.
20
- *
21
- * @since 1.6.10
22
- *
23
- * @var object
24
- * @access protected
25
- */
26
- protected $editor;
27
 
28
  /**
29
  * Get the attachment backup file path, even if the file doesn't exist.
@@ -133,15 +123,15 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
133
  return false;
134
  }
135
 
136
- $size = @getimagesize( $this->get_original_path() );
137
 
138
- if ( ! isset( $size[0], $size[1] ) ) {
139
  return false;
140
  }
141
 
142
  $metadata = wp_get_attachment_metadata( $this->id );
143
- $metadata['width'] = $size[0];
144
- $metadata['height'] = $size[1];
145
 
146
  wp_update_attachment_metadata( $this->id, $metadata );
147
  return true;
@@ -230,48 +220,45 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
230
  $metadata = wp_get_attachment_metadata( $this->id );
231
  $metadata_sizes = ! empty( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ? $metadata['sizes'] : array();
232
 
233
- $original_dirname = trailingslashit( dirname( $this->get_original_path() ) );
234
  $thumbnail_path = $original_dirname . $thumbnail_data['file'];
235
- $filesystem = imagify_get_filesystem();
236
 
237
- if ( ! empty( $metadata_sizes[ $thumbnail_size ] ) && $filesystem->exists( $thumbnail_path ) ) {
238
- imagify_chmod_file( $thumbnail_path );
239
  return true;
240
  }
241
 
242
  // Get the editor.
243
- if ( ! isset( $this->editor ) ) {
244
- $this->editor = wp_get_image_editor( $this->get_backup_path() );
245
- }
246
 
247
- if ( is_wp_error( $this->editor ) ) {
248
- return $this->editor;
249
  }
250
 
251
  // Create the file.
252
- $result = $this->editor->multi_resize( array( $thumbnail_size => $thumbnail_data ) );
253
 
254
  if ( ! $result ) {
255
  return new WP_Error( 'image_resize_error' );
256
  }
257
 
258
  // The file name can change from what we expected (1px wider, etc).
259
- $backup_dirname = trailingslashit( dirname( $this->get_backup_path() ) );
260
  $backup_thumb_path = $backup_dirname . $result[ $thumbnail_size ]['file'];
261
  $thumbnail_path = $original_dirname . $result[ $thumbnail_size ]['file'];
262
 
263
  // Since we used the backup image as source, the new image is still in the backup folder, we need to move it.
264
- $filesystem->move( $backup_thumb_path, $thumbnail_path, true );
265
 
266
- if ( $filesystem->exists( $backup_thumb_path ) ) {
267
- $filesystem->delete( $backup_thumb_path );
268
  }
269
 
270
- if ( ! $filesystem->exists( $thumbnail_path ) ) {
271
  return new WP_Error( 'image_resize_error' );
272
  }
273
 
274
- imagify_chmod_file( $thumbnail_path );
275
 
276
  return reset( $result );
277
  }
@@ -367,39 +354,40 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
367
  */
368
  do_action( 'before_imagify_optimize_attachment', $this->id );
369
 
370
- set_transient( 'imagify-async-in-progress-' . $this->id, true, 10 * MINUTE_IN_SECONDS );
371
 
372
  // Get the resize values for the original size.
373
- $resized = false;
374
- $do_resize = get_imagify_option( 'resize_larger' );
375
- $resize_width = get_imagify_option( 'resize_larger_w' );
376
- $attachment_size = @getimagesize( $attachment_path );
377
 
378
- if ( $do_resize && isset( $attachment_size[0] ) && $resize_width < $attachment_size[0] ) {
379
- $resized_attachment_path = $this->resize( $attachment_path, $attachment_size, $resize_width );
 
380
 
381
- if ( ! is_wp_error( $resized_attachment_path ) ) {
382
- // TODO (@Greg): Send an error message if the backup fails.
383
- imagify_backup_file( $attachment_path );
384
 
385
- $filesystem = imagify_get_filesystem();
 
 
386
 
387
- $filesystem->move( $resized_attachment_path, $attachment_path, true );
388
- imagify_chmod_file( $attachment_path );
389
 
390
- // If resized temp file still exists, delete it.
391
- if ( $filesystem->exists( $resized_attachment_path ) ) {
392
- $filesystem->delete( $resized_attachment_path );
393
- }
394
 
395
- $resized = true;
 
396
  }
397
  }
398
 
399
  // Optimize the original size.
400
  $response = do_imagify( $attachment_path, array(
401
  'optimization_level' => $optimization_level,
402
- 'context' => 'wp',
403
  'resized' => $resized,
404
  'original_size' => $attachment_original_size,
405
  ) );
@@ -416,7 +404,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
416
  }
417
 
418
  if ( ! $data ) {
419
- delete_transient( 'imagify-async-in-progress-' . $this->id );
420
  return;
421
  }
422
 
@@ -424,8 +412,8 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
424
  if ( $sizes ) {
425
  $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
426
  $is_active_for_network = imagify_is_active_for_network();
427
- $attachment_path_dirname = trailingslashit( dirname( $attachment_path ) );
428
- $attachment_url_dirname = trailingslashit( dirname( $attachment_url ) );
429
 
430
  foreach ( $sizes as $size_key => $size_data ) {
431
  // Check if this size has to be optimized.
@@ -444,7 +432,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
444
  $response = do_imagify( $thumbnail_path, array(
445
  'backup' => false,
446
  'optimization_level' => $optimization_level,
447
- 'context' => 'wp',
448
  ) );
449
 
450
  $data = $this->fill_data( $data, $response, $size_key );
@@ -484,7 +472,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
484
  */
485
  do_action( 'after_imagify_optimize_attachment', $this->id, $optimized_data );
486
 
487
- delete_transient( 'imagify-async-in-progress-' . $this->id );
488
 
489
  return $optimized_data;
490
  }
@@ -530,7 +518,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
530
  */
531
  do_action( 'before_imagify_optimize_missing_thumbnails', $this->id, $missing_sizes );
532
 
533
- set_transient( 'imagify-async-in-progress-' . $this->id, true, 10 * MINUTE_IN_SECONDS );
534
 
535
  $errors = new WP_Error();
536
 
@@ -547,14 +535,14 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
547
  }
548
 
549
  if ( ! $result_sizes ) {
550
- delete_transient( 'imagify-async-in-progress-' . $this->id );
551
  return $errors;
552
  }
553
 
554
  // Optimize.
555
  $imagify_data = $this->get_data();
556
- $original_dirname = trailingslashit( dirname( $this->get_original_path() ) );
557
- $orig_url_dirname = trailingslashit( dirname( $this->get_original_url() ) );
558
 
559
  foreach ( $result_sizes as $size_name => $thumbnail_data ) {
560
  $thumbnail_path = $original_dirname . $thumbnail_data['file'];
@@ -564,7 +552,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
564
  $response = do_imagify( $thumbnail_path, array(
565
  'backup' => false,
566
  'optimization_level' => $optimization_level,
567
- 'context' => 'wp',
568
  ) );
569
 
570
  $imagify_data = $this->fill_data( $imagify_data, $response, $size_name );
@@ -591,7 +579,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
591
  */
592
  do_action( 'after_imagify_optimize_missing_thumbnails', $this->id, $result_sizes, $errors );
593
 
594
- delete_transient( 'imagify-async-in-progress-' . $this->id );
595
 
596
  // Return the result.
597
  if ( $errors->get_error_codes() ) {
@@ -601,6 +589,121 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
601
  return $result_sizes;
602
  }
603
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
604
  /**
605
  * Process an attachment restoration from the backup file.
606
  *
@@ -622,7 +725,6 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
622
 
623
  $backup_path = $this->get_backup_path();
624
  $attachment_path = $this->get_original_path();
625
- $filesystem = imagify_get_filesystem();
626
 
627
  /**
628
  * Fires before restoring an attachment.
@@ -634,8 +736,8 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
634
  do_action( 'before_imagify_restore_attachment', $this->id );
635
 
636
  // Create the original image from the backup.
637
- $filesystem->copy( $backup_path, $attachment_path, true );
638
- imagify_chmod_file( $attachment_path );
639
 
640
  if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
641
  require_once( ABSPATH . 'wp-admin/includes/image.php' );
13
  *
14
  * @var string
15
  */
16
+ const VERSION = '1.1.3';
 
 
 
 
 
 
 
 
 
 
17
 
18
  /**
19
  * Get the attachment backup file path, even if the file doesn't exist.
123
  return false;
124
  }
125
 
126
+ $size = $this->filesystem->get_image_size( $this->get_original_path() );
127
 
128
+ if ( ! $size ) {
129
  return false;
130
  }
131
 
132
  $metadata = wp_get_attachment_metadata( $this->id );
133
+ $metadata['width'] = $size['width'];
134
+ $metadata['height'] = $size['height'];
135
 
136
  wp_update_attachment_metadata( $this->id, $metadata );
137
  return true;
220
  $metadata = wp_get_attachment_metadata( $this->id );
221
  $metadata_sizes = ! empty( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ? $metadata['sizes'] : array();
222
 
223
+ $original_dirname = $this->filesystem->dir_path( $this->get_original_path() );
224
  $thumbnail_path = $original_dirname . $thumbnail_data['file'];
 
225
 
226
+ if ( ! empty( $metadata_sizes[ $thumbnail_size ] ) && $this->filesystem->exists( $thumbnail_path ) ) {
227
+ $this->filesystem->chmod_file( $thumbnail_path );
228
  return true;
229
  }
230
 
231
  // Get the editor.
232
+ $editor = $this->get_editor( $this->get_backup_path() );
 
 
233
 
234
+ if ( is_wp_error( $editor ) ) {
235
+ return $editor;
236
  }
237
 
238
  // Create the file.
239
+ $result = $editor->multi_resize( array( $thumbnail_size => $thumbnail_data ) );
240
 
241
  if ( ! $result ) {
242
  return new WP_Error( 'image_resize_error' );
243
  }
244
 
245
  // The file name can change from what we expected (1px wider, etc).
246
+ $backup_dirname = $this->filesystem->dir_path( $this->get_backup_path() );
247
  $backup_thumb_path = $backup_dirname . $result[ $thumbnail_size ]['file'];
248
  $thumbnail_path = $original_dirname . $result[ $thumbnail_size ]['file'];
249
 
250
  // Since we used the backup image as source, the new image is still in the backup folder, we need to move it.
251
+ $this->filesystem->move( $backup_thumb_path, $thumbnail_path, true );
252
 
253
+ if ( $this->filesystem->exists( $backup_thumb_path ) ) {
254
+ $this->filesystem->delete( $backup_thumb_path );
255
  }
256
 
257
+ if ( ! $this->filesystem->exists( $thumbnail_path ) ) {
258
  return new WP_Error( 'image_resize_error' );
259
  }
260
 
261
+ $this->filesystem->chmod_file( $thumbnail_path );
262
 
263
  return reset( $result );
264
  }
354
  */
355
  do_action( 'before_imagify_optimize_attachment', $this->id );
356
 
357
+ $this->set_running_status();
358
 
359
  // Get the resize values for the original size.
360
+ $resized = false;
361
+ $do_resize = get_imagify_option( 'resize_larger' );
 
 
362
 
363
+ if ( $do_resize ) {
364
+ $resize_width = get_imagify_option( 'resize_larger_w' );
365
+ $attachment_size = $this->filesystem->get_image_size( $attachment_path );
366
 
367
+ if ( $attachment_size && $resize_width < $attachment_size['width'] ) {
368
+ $resized_attachment_path = $this->resize( $attachment_path, $attachment_size, $resize_width );
 
369
 
370
+ if ( ! is_wp_error( $resized_attachment_path ) ) {
371
+ // TODO (@Greg): Send an error message if the backup fails.
372
+ imagify_backup_file( $attachment_path );
373
 
374
+ $this->filesystem->move( $resized_attachment_path, $attachment_path, true );
375
+ $this->filesystem->chmod_file( $attachment_path );
376
 
377
+ // If resized temp file still exists, delete it.
378
+ if ( $this->filesystem->exists( $resized_attachment_path ) ) {
379
+ $this->filesystem->delete( $resized_attachment_path );
380
+ }
381
 
382
+ $resized = true;
383
+ }
384
  }
385
  }
386
 
387
  // Optimize the original size.
388
  $response = do_imagify( $attachment_path, array(
389
  'optimization_level' => $optimization_level,
390
+ 'context' => $this->get_context(),
391
  'resized' => $resized,
392
  'original_size' => $attachment_original_size,
393
  ) );
404
  }
405
 
406
  if ( ! $data ) {
407
+ $this->delete_running_status();
408
  return;
409
  }
410
 
412
  if ( $sizes ) {
413
  $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
414
  $is_active_for_network = imagify_is_active_for_network();
415
+ $attachment_path_dirname = $this->filesystem->dir_path( $attachment_path );
416
+ $attachment_url_dirname = $this->filesystem->dir_path( $attachment_url );
417
 
418
  foreach ( $sizes as $size_key => $size_data ) {
419
  // Check if this size has to be optimized.
432
  $response = do_imagify( $thumbnail_path, array(
433
  'backup' => false,
434
  'optimization_level' => $optimization_level,
435
+ 'context' => $this->get_context(),
436
  ) );
437
 
438
  $data = $this->fill_data( $data, $response, $size_key );
472
  */
473
  do_action( 'after_imagify_optimize_attachment', $this->id, $optimized_data );
474
 
475
+ $this->delete_running_status();
476
 
477
  return $optimized_data;
478
  }
518
  */
519
  do_action( 'before_imagify_optimize_missing_thumbnails', $this->id, $missing_sizes );
520
 
521
+ $this->set_running_status();
522
 
523
  $errors = new WP_Error();
524
 
535
  }
536
 
537
  if ( ! $result_sizes ) {
538
+ $this->delete_running_status();
539
  return $errors;
540
  }
541
 
542
  // Optimize.
543
  $imagify_data = $this->get_data();
544
+ $original_dirname = $this->filesystem->dir_path( $this->get_original_path() );
545
+ $orig_url_dirname = $this->filesystem->dir_path( $this->get_original_url() );
546
 
547
  foreach ( $result_sizes as $size_name => $thumbnail_data ) {
548
  $thumbnail_path = $original_dirname . $thumbnail_data['file'];
552
  $response = do_imagify( $thumbnail_path, array(
553
  'backup' => false,
554
  'optimization_level' => $optimization_level,
555
+ 'context' => $this->get_context(),
556
  ) );
557
 
558
  $imagify_data = $this->fill_data( $imagify_data, $response, $size_name );
579
  */
580
  do_action( 'after_imagify_optimize_missing_thumbnails', $this->id, $result_sizes, $errors );
581
 
582
+ $this->delete_running_status();
583
 
584
  // Return the result.
585
  if ( $errors->get_error_codes() ) {
589
  return $result_sizes;
590
  }
591
 
592
+ /**
593
+ * Re-optimize the given thumbnail sizes to the same level.
594
+ * Before doing this, the given sizes must be restored.
595
+ *
596
+ * @since 1.7.1
597
+ * @access public
598
+ * @author Grégory Viguier
599
+ *
600
+ * @param array $sizes The sizes to optimize.
601
+ * @return array|void A WP_Error object on failure.
602
+ */
603
+ public function reoptimize_thumbnails( $sizes ) {
604
+ // Check if the attachment extension is allowed.
605
+ if ( ! $this->is_extension_supported() ) {
606
+ return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
607
+ }
608
+
609
+ if ( ! $sizes || ! is_array( $sizes ) ) {
610
+ return;
611
+ }
612
+
613
+ /**
614
+ * Fires before re-optimizing some thumbnails of an attachment.
615
+ *
616
+ * @since 1.7.1
617
+ * @author Grégory Viguier
618
+ *
619
+ * @param int $id The attachment ID.
620
+ * @param array $sizes The sizes to optimize.
621
+ */
622
+ do_action( 'before_imagify_reoptimize_attachment_thumbnails', $this->id, $sizes );
623
+
624
+ $this->set_running_status();
625
+
626
+ $data = $this->get_data();
627
+
628
+ $data['sizes'] = ! empty( $data['sizes'] ) && is_array( $data['sizes'] ) ? $data['sizes'] : array();
629
+
630
+ foreach ( $sizes as $size_key => $size_data ) {
631
+ // In case it's a disallowed size, fill in the new data. If it's not, it will be overwritten by $this->fill_data() later.
632
+ $data['sizes'][ $size_key ] = array(
633
+ 'success' => false,
634
+ 'error' => __( 'This size isn\'t authorized to be optimized. Update your Imagify settings if you want to optimize it.', 'imagify' ),
635
+ );
636
+ }
637
+
638
+ // Update global attachment stats.
639
+ $data['stats'] = array(
640
+ 'original_size' => 0,
641
+ 'optimized_size' => 0,
642
+ 'percent' => 0,
643
+ );
644
+
645
+ foreach ( $data['sizes'] as $size_data ) {
646
+ if ( ! empty( $size_data['original_size'] ) ) {
647
+ $data['stats']['original_size'] += $size_data['original_size'];
648
+ }
649
+ if ( ! empty( $size_data['optimized_size'] ) ) {
650
+ $data['stats']['optimized_size'] += $size_data['optimized_size'];
651
+ }
652
+ }
653
+
654
+ // Remove disallowed sizes.
655
+ if ( ! imagify_is_active_for_network() ) {
656
+ $sizes = array_diff_key( $sizes, get_imagify_option( 'disallowed-sizes' ) );
657
+ }
658
+
659
+ if ( ! $sizes ) {
660
+ $data['stats']['percent'] = $data['stats']['original_size'] ? round( ( ( $data['stats']['original_size'] - $data['stats']['optimized_size'] ) / $data['stats']['original_size'] ) * 100, 2 ) : 0;
661
+ update_post_meta( $this->id, '_imagify_data', $data );
662
+ $this->delete_running_status();
663
+ return;
664
+ }
665
+
666
+ $optimization_level = $this->get_optimization_level();
667
+ $thumbnail_path = $this->get_original_path();
668
+ $thumbnail_url = $this->get_original_url();
669
+ $attachment_path_dirname = $this->filesystem->dir_path( $thumbnail_path );
670
+ $attachment_url_dirname = $this->filesystem->dir_path( $thumbnail_url );
671
+
672
+ foreach ( $sizes as $size_key => $size_data ) {
673
+ $thumbnail_path = $attachment_path_dirname . $size_data['file'];
674
+ $thumbnail_url = $attachment_url_dirname . $size_data['file'];
675
+
676
+ // Optimize the thumbnail size.
677
+ $response = do_imagify( $thumbnail_path, array(
678
+ 'backup' => false,
679
+ 'optimization_level' => $optimization_level,
680
+ 'context' => $this->get_context(),
681
+ ) );
682
+
683
+ $data = $this->fill_data( $data, $response, $size_key );
684
+
685
+ /** This filter is documented in /inc/classes/class-imagify-attachment.php. */
686
+ $data = apply_filters( 'imagify_fill_thumbnail_data', $data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level );
687
+ } // End foreach().
688
+
689
+ $data['stats']['percent'] = round( ( ( $data['stats']['original_size'] - $data['stats']['optimized_size'] ) / $data['stats']['original_size'] ) * 100, 2 );
690
+
691
+ update_post_meta( $this->id, '_imagify_data', $data );
692
+
693
+ /**
694
+ * Fires after re-optimizing some thumbnails of an attachment.
695
+ *
696
+ * @since 1.7.1
697
+ * @author Grégory Viguier
698
+ *
699
+ * @param int $id The attachment ID.
700
+ * @param array $sizes The sizes to optimize.
701
+ */
702
+ do_action( 'after_imagify_reoptimize_attachment_thumbnails', $this->id, $sizes );
703
+
704
+ $this->delete_running_status();
705
+ }
706
+
707
  /**
708
  * Process an attachment restoration from the backup file.
709
  *
725
 
726
  $backup_path = $this->get_backup_path();
727
  $attachment_path = $this->get_original_path();
 
728
 
729
  /**
730
  * Fires before restoring an attachment.
736
  do_action( 'before_imagify_restore_attachment', $this->id );
737
 
738
  // Create the original image from the backup.
739
+ $this->filesystem->copy( $backup_path, $attachment_path, true );
740
+ $this->filesystem->chmod_file( $attachment_path );
741
 
742
  if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
743
  require_once( ABSPATH . 'wp-admin/includes/image.php' );
inc/classes/class-imagify-cron-rating.php CHANGED
@@ -101,11 +101,6 @@ class Imagify_Cron_Rating extends Imagify_Abstract_Cron {
101
  return;
102
  }
103
 
104
- // Check if the Imagify servers & the API are accessible.
105
- if ( ! is_imagify_servers_up() ) {
106
- return;
107
- }
108
-
109
  $user = get_imagify_user();
110
 
111
  if ( ! is_wp_error( $user ) && (int) $user->image_count > 100 ) {
101
  return;
102
  }
103
 
 
 
 
 
 
104
  $user = get_imagify_user();
105
 
106
  if ( ! is_wp_error( $user ) && (int) $user->image_count > 100 ) {
inc/classes/class-imagify-cron-sync-files.php CHANGED
@@ -92,13 +92,11 @@ class Imagify_Cron_Sync_Files extends Imagify_Abstract_Cron {
92
  return;
93
  }
94
 
95
- if ( ! imagify_valid_key() ) {
96
  return;
97
  }
98
 
99
- $user = new Imagify_User();
100
-
101
- if ( $user->is_over_quota() ) {
102
  return;
103
  }
104
 
92
  return;
93
  }
94
 
95
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
96
  return;
97
  }
98
 
99
+ if ( Imagify_Requirements::is_over_quota() ) {
 
 
100
  return;
101
  }
102
 
inc/classes/class-imagify-custom-folders.php CHANGED
@@ -14,7 +14,7 @@ class Imagify_Custom_Folders {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.0';
18
 
19
 
20
  /** ----------------------------------------------------------------------------------------- */
@@ -37,7 +37,8 @@ class Imagify_Custom_Folders {
37
  return $backup_dir;
38
  }
39
 
40
- $backup_dir = imagify_get_abspath() . 'imagify-backup/';
 
41
 
42
  /**
43
  * Filter the backup directory path (custom folders).
@@ -48,7 +49,7 @@ class Imagify_Custom_Folders {
48
  * @param string $backup_dir The backup directory path.
49
  */
50
  $backup_dir = apply_filters( 'imagify_files_backup_directory', $backup_dir );
51
- $backup_dir = trailingslashit( wp_normalize_path( $backup_dir ) );
52
 
53
  return $backup_dir;
54
  }
@@ -63,14 +64,7 @@ class Imagify_Custom_Folders {
63
  * @return bool
64
  */
65
  public static function backup_dir_is_writable() {
66
- if ( ! get_imagify_backup_dir_path() ) {
67
- return false;
68
- }
69
-
70
- $filesystem = imagify_get_filesystem();
71
- $has_backup_dir = wp_mkdir_p( self::get_backup_dir_path() );
72
-
73
- return $has_backup_dir && $filesystem->is_writable( self::get_backup_dir_path() );
74
  }
75
 
76
  /**
@@ -85,7 +79,7 @@ class Imagify_Custom_Folders {
85
  */
86
  public static function get_file_backup_path( $file_path ) {
87
  $file_path = wp_normalize_path( (string) $file_path );
88
- $abspath = imagify_get_abspath();
89
  $backup_dir = self::get_backup_dir_path();
90
 
91
  if ( ! $file_path ) {
@@ -134,17 +128,17 @@ class Imagify_Custom_Folders {
134
  }
135
 
136
  if ( empty( $args['file_date'] ) || '0000-00-00 00:00:00' === $args['file_date'] ) {
137
- $args['file_date'] = imagify_get_file_date( $args['file_path'] );
138
  }
139
 
140
  if ( empty( $args['mime_type'] ) ) {
141
- $args['mime_type'] = imagify_get_mime_type_from_file( $args['file_path'] );
142
  }
143
 
144
  if ( ( empty( $args['width'] ) || empty( $args['height'] ) ) && strpos( $args['mime_type'], 'image/' ) === 0 ) {
145
- $file_size = @getimagesize( $args['file_path'] );
146
- $args['width'] = $file_size && isset( $file_size[0] ) ? $file_size[0] : 0;
147
- $args['height'] = $file_size && isset( $file_size[1] ) ? $file_size[1] : 0;
148
  }
149
 
150
  if ( empty( $args['hash'] ) ) {
@@ -329,18 +323,18 @@ class Imagify_Custom_Folders {
329
  if ( $new_data['modified'] ) {
330
  // Delete all optimization data and update file data.
331
  $modified = true;
332
- $mime_type = ! empty( $old_data['mime_type'] ) ? $old_data['mime_type'] : imagify_get_mime_type_from_file( $file_path );
333
 
334
  if ( strpos( $mime_type, 'image/' ) === 0 ) {
335
- $size = @getimagesize( $file_path );
336
  } else {
337
  $size = false;
338
  }
339
 
340
  $new_data = array_merge( $new_data, array(
341
- 'file_date' => imagify_get_file_date( $file_path ),
342
- 'width' => $size && isset( $size[0] ) ? $size[0] : 0,
343
- 'height' => $size && isset( $size[1] ) ? $size[1] : 0,
344
  'original_size' => $filesystem->size( $file_path ),
345
  'optimized_size' => null,
346
  'percent' => null,
@@ -356,19 +350,19 @@ class Imagify_Custom_Folders {
356
  } else {
357
  // Update file data to make sure nothing is missing.
358
  $path = $backup_path ? $backup_path : $file_path;
359
- $mime_type = ! empty( $old_data['mime_type'] ) ? $old_data['mime_type'] : imagify_get_mime_type_from_file( $path );
360
- $file_date = ! empty( $old_data['file_date'] ) && '0000-00-00 00:00:00' !== $old_data['file_date'] ? $old_data['file_date'] : imagify_get_file_date( $path );
361
 
362
  if ( strpos( $mime_type, 'image/' ) === 0 ) {
363
- $size = @getimagesize( $path );
364
  } else {
365
  $size = false;
366
  }
367
 
368
  $new_data = array_merge( $new_data, array(
369
  'file_date' => $file_date,
370
- 'width' => $size && isset( $size[0] ) ? $size[0] : 0,
371
- 'height' => $size && isset( $size[1] ) ? $size[1] : 0,
372
  'original_size' => $filesystem->size( $path ),
373
  ) );
374
  }
@@ -1042,6 +1036,7 @@ class Imagify_Custom_Folders {
1042
  return;
1043
  }
1044
 
 
1045
  $file_ids_by_folder = array();
1046
  $active_folders = self::sort_folders( $active_folders, true );
1047
 
@@ -1050,7 +1045,7 @@ class Imagify_Custom_Folders {
1050
  $inactive_file['full_path'] = Imagify_Files_Scan::remove_placeholder( $inactive_file['path'] );
1051
 
1052
  if ( $has_abspath ) {
1053
- $inactive_file['dirname'] = trailingslashit( dirname( $inactive_file['full_path'] ) );
1054
  }
1055
 
1056
  foreach ( $active_folders as $active_folder ) {
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.0.1';
18
 
19
 
20
  /** ----------------------------------------------------------------------------------------- */
37
  return $backup_dir;
38
  }
39
 
40
+ $filesystem = imagify_get_filesystem();
41
+ $backup_dir = $filesystem->get_abspath() . 'imagify-backup/';
42
 
43
  /**
44
  * Filter the backup directory path (custom folders).
49
  * @param string $backup_dir The backup directory path.
50
  */
51
  $backup_dir = apply_filters( 'imagify_files_backup_directory', $backup_dir );
52
+ $backup_dir = $filesystem->normalize_dir_path( $backup_dir );
53
 
54
  return $backup_dir;
55
  }
64
  * @return bool
65
  */
66
  public static function backup_dir_is_writable() {
67
+ return imagify_get_filesystem()->make_dir( self::get_backup_dir_path() );
 
 
 
 
 
 
 
68
  }
69
 
70
  /**
79
  */
80
  public static function get_file_backup_path( $file_path ) {
81
  $file_path = wp_normalize_path( (string) $file_path );
82
+ $abspath = imagify_get_filesystem()->get_abspath();
83
  $backup_dir = self::get_backup_dir_path();
84
 
85
  if ( ! $file_path ) {
128
  }
129
 
130
  if ( empty( $args['file_date'] ) || '0000-00-00 00:00:00' === $args['file_date'] ) {
131
+ $args['file_date'] = $filesystem->get_date( $args['file_path'] );
132
  }
133
 
134
  if ( empty( $args['mime_type'] ) ) {
135
+ $args['mime_type'] = $filesystem->get_mime_type( $args['file_path'] );
136
  }
137
 
138
  if ( ( empty( $args['width'] ) || empty( $args['height'] ) ) && strpos( $args['mime_type'], 'image/' ) === 0 ) {
139
+ $file_size = $filesystem->get_image_size( $args['file_path'] );
140
+ $args['width'] = $file_size ? $file_size['width'] : 0;
141
+ $args['height'] = $file_size ? $file_size['height'] : 0;
142
  }
143
 
144
  if ( empty( $args['hash'] ) ) {
323
  if ( $new_data['modified'] ) {
324
  // Delete all optimization data and update file data.
325
  $modified = true;
326
+ $mime_type = ! empty( $old_data['mime_type'] ) ? $old_data['mime_type'] : $filesystem->get_mime_type( $file_path );
327
 
328
  if ( strpos( $mime_type, 'image/' ) === 0 ) {
329
+ $size = $filesystem->get_image_size( $file_path );
330
  } else {
331
  $size = false;
332
  }
333
 
334
  $new_data = array_merge( $new_data, array(
335
+ 'file_date' => $filesystem->get_date( $file_path ),
336
+ 'width' => $size ? $size['width'] : 0,
337
+ 'height' => $size ? $size['height'] : 0,
338
  'original_size' => $filesystem->size( $file_path ),
339
  'optimized_size' => null,
340
  'percent' => null,
350
  } else {
351
  // Update file data to make sure nothing is missing.
352
  $path = $backup_path ? $backup_path : $file_path;
353
+ $mime_type = ! empty( $old_data['mime_type'] ) ? $old_data['mime_type'] : $filesystem->get_mime_type( $path );
354
+ $file_date = ! empty( $old_data['file_date'] ) && '0000-00-00 00:00:00' !== $old_data['file_date'] ? $old_data['file_date'] : $filesystem->get_date( $path );
355
 
356
  if ( strpos( $mime_type, 'image/' ) === 0 ) {
357
+ $size = $filesystem->get_image_size( $path );
358
  } else {
359
  $size = false;
360
  }
361
 
362
  $new_data = array_merge( $new_data, array(
363
  'file_date' => $file_date,
364
+ 'width' => $size ? $size['width'] : 0,
365
+ 'height' => $size ? $size['height'] : 0,
366
  'original_size' => $filesystem->size( $path ),
367
  ) );
368
  }
1036
  return;
1037
  }
1038
 
1039
+ $filesystem = imagify_get_filesystem();
1040
  $file_ids_by_folder = array();
1041
  $active_folders = self::sort_folders( $active_folders, true );
1042
 
1045
  $inactive_file['full_path'] = Imagify_Files_Scan::remove_placeholder( $inactive_file['path'] );
1046
 
1047
  if ( $has_abspath ) {
1048
+ $inactive_file['dirname'] = $filesystem->dir_path( $inactive_file['full_path'] );
1049
  }
1050
 
1051
  foreach ( $active_folders as $active_folder ) {
inc/classes/class-imagify-file-attachment.php CHANGED
@@ -16,7 +16,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
16
  * @since 1.7
17
  * @author Grégory Viguier
18
  */
19
- const VERSION = '1.0';
20
 
21
  /**
22
  * The attachment SQL DB class.
@@ -27,6 +27,16 @@ class Imagify_File_Attachment extends Imagify_Attachment {
27
  */
28
  protected $db_class_name = 'Imagify_Files_DB';
29
 
 
 
 
 
 
 
 
 
 
 
30
  /**
31
  * The constructor.
32
  *
@@ -41,13 +51,15 @@ class Imagify_File_Attachment extends Imagify_Attachment {
41
  $this->id = (int) $id;
42
  $this->get_row();
43
  } elseif ( is_array( $id ) || is_object( $id ) ) {
44
- $classname = $this->db_class_name;
45
- $prim_key = $classname::get_instance()->get_primary_key();
46
  $this->row = (array) $id;
47
  $this->id = $this->row[ $prim_key ];
48
  } else {
49
  $this->invalidate_row();
50
  }
 
 
 
51
  }
52
 
53
  /**
@@ -119,7 +131,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
119
  return false;
120
  }
121
 
122
- return site_url( '/' ) . imagify_make_file_path_relative( $this->get_raw_backup_path() );
123
  }
124
 
125
  /**
@@ -136,8 +148,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
136
  return array();
137
  }
138
 
139
- $classname = $this->db_class_name;
140
- $data = array_merge( $classname::get_instance()->get_column_defaults(), $this->get_row() );
141
 
142
  unset( $data['file_id'] );
143
  return $data;
@@ -299,10 +310,10 @@ class Imagify_File_Attachment extends Imagify_Attachment {
299
 
300
  if ( ! $file_path ) {
301
  $file_path = $this->get_original_path();
302
- $file_path = $file_path && imagify_get_filesystem()->exists( $file_path ) ? $file_path : false;
303
  }
304
 
305
- $size = $file_path ? imagify_get_filesystem()->size( $file_path ) : 0;
306
  }
307
 
308
  if ( $human_format ) {
@@ -334,8 +345,8 @@ class Imagify_File_Attachment extends Imagify_Attachment {
334
  $size = $row['optimized_size'];
335
  } else {
336
  $file_path = $this->get_original_path();
337
- $file_path = $file_path && imagify_get_filesystem()->exists( $file_path ) ? $file_path : false;
338
- $size = $file_path ? imagify_get_filesystem()->size( $file_path ) : 0;
339
  }
340
 
341
  if ( $human_format ) {
@@ -505,8 +516,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
505
  static $column_defaults;
506
 
507
  if ( ! isset( $column_defaults ) ) {
508
- $classname = $this->db_class_name;
509
- $column_defaults = $classname::get_instance()->get_column_defaults();
510
 
511
  // All DB columns that have `null` as default value, are Imagify data.
512
  foreach ( $column_defaults as $column_name => $value ) {
@@ -525,7 +535,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
525
  // Also set the new file hash.
526
  $file_path = $this->get_original_path();
527
 
528
- if ( $file_path && imagify_get_filesystem()->exists( $file_path ) ) {
529
  $imagify_columns['hash'] = md5_file( $file_path );
530
  }
531
 
@@ -576,9 +586,9 @@ class Imagify_File_Attachment extends Imagify_Attachment {
576
  $data['optimized_size'] = (int) $response->new_size;
577
  } else {
578
  $file_path = $this->get_original_path();
579
- $file_path = $file_path && imagify_get_filesystem()->exists( $file_path ) ? $file_path : false;
580
 
581
- $data['optimized_size'] = $file_path ? imagify_get_filesystem()->size( $file_path ) : 0;
582
  }
583
 
584
  if ( $original_size && $data['optimized_size'] ) {
@@ -622,7 +632,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
622
  */
623
  do_action( 'before_imagify_optimize_file', $this->id );
624
 
625
- set_site_transient( 'imagify-file-async-in-progress-' . $this->id, true, 10 * MINUTE_IN_SECONDS );
626
 
627
  // Optimize the image.
628
  $response = do_imagify( $this->get_original_path(), array(
@@ -641,7 +651,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
641
  $this->update_row( $data );
642
 
643
  if ( is_wp_error( $response ) ) {
644
- delete_site_transient( 'imagify-file-async-in-progress-' . $this->id );
645
 
646
  if ( 'error' === $data['status'] ) {
647
  return $response;
@@ -662,7 +672,7 @@ class Imagify_File_Attachment extends Imagify_Attachment {
662
  */
663
  do_action( 'after_imagify_optimize_file', $this->id, $this->get_data() );
664
 
665
- delete_site_transient( 'imagify-file-async-in-progress-' . $this->id );
666
 
667
  return true;
668
  }
@@ -706,8 +716,8 @@ class Imagify_File_Attachment extends Imagify_Attachment {
706
  do_action( 'before_imagify_restore_file', $this->id );
707
 
708
  // Create the original image from the backup.
709
- imagify_get_filesystem()->copy( $backup_path, $file_path, true );
710
- imagify_chmod_file( $file_path );
711
 
712
  // Remove old optimization data.
713
  $this->delete_imagify_data();
16
  * @since 1.7
17
  * @author Grégory Viguier
18
  */
19
+ const VERSION = '1.0.1';
20
 
21
  /**
22
  * The attachment SQL DB class.
27
  */
28
  protected $db_class_name = 'Imagify_Files_DB';
29
 
30
+ /**
31
+ * Tell if the optimization status is network-wide.
32
+ *
33
+ * @var bool
34
+ * @since 1.7.1
35
+ * @access protected
36
+ * @author Grégory Viguier
37
+ */
38
+ protected $optimization_state_network_wide = true;
39
+
40
  /**
41
  * The constructor.
42
  *
51
  $this->id = (int) $id;
52
  $this->get_row();
53
  } elseif ( is_array( $id ) || is_object( $id ) ) {
54
+ $prim_key = $this->get_row_db_instance()->get_primary_key();
 
55
  $this->row = (array) $id;
56
  $this->id = $this->row[ $prim_key ];
57
  } else {
58
  $this->invalidate_row();
59
  }
60
+
61
+ $this->filesystem = Imagify_Filesystem::get_instance();
62
+ $this->optimization_state_transient = 'imagify-file-async-in-progress-' . $this->id;
63
  }
64
 
65
  /**
131
  return false;
132
  }
133
 
134
+ return site_url( '/' ) . $this->filesystem->make_path_relative( $this->get_raw_backup_path() );
135
  }
136
 
137
  /**
148
  return array();
149
  }
150
 
151
+ $data = array_merge( $this->get_row_db_instance()->get_column_defaults(), $this->get_row() );
 
152
 
153
  unset( $data['file_id'] );
154
  return $data;
310
 
311
  if ( ! $file_path ) {
312
  $file_path = $this->get_original_path();
313
+ $file_path = $file_path && $this->filesystem->exists( $file_path ) ? $file_path : false;
314
  }
315
 
316
+ $size = $file_path ? $this->filesystem->size( $file_path ) : 0;
317
  }
318
 
319
  if ( $human_format ) {
345
  $size = $row['optimized_size'];
346
  } else {
347
  $file_path = $this->get_original_path();
348
+ $file_path = $file_path && $this->filesystem->exists( $file_path ) ? $file_path : false;
349
+ $size = $file_path ? $this->filesystem->size( $file_path ) : 0;
350
  }
351
 
352
  if ( $human_format ) {
516
  static $column_defaults;
517
 
518
  if ( ! isset( $column_defaults ) ) {
519
+ $column_defaults = $this->get_row_db_instance()->get_column_defaults();
 
520
 
521
  // All DB columns that have `null` as default value, are Imagify data.
522
  foreach ( $column_defaults as $column_name => $value ) {
535
  // Also set the new file hash.
536
  $file_path = $this->get_original_path();
537
 
538
+ if ( $file_path && $this->filesystem->exists( $file_path ) ) {
539
  $imagify_columns['hash'] = md5_file( $file_path );
540
  }
541
 
586
  $data['optimized_size'] = (int) $response->new_size;
587
  } else {
588
  $file_path = $this->get_original_path();
589
+ $file_path = $file_path && $this->filesystem->exists( $file_path ) ? $file_path : false;
590
 
591
+ $data['optimized_size'] = $file_path ? $this->filesystem->size( $file_path ) : 0;
592
  }
593
 
594
  if ( $original_size && $data['optimized_size'] ) {
632
  */
633
  do_action( 'before_imagify_optimize_file', $this->id );
634
 
635
+ $this->set_running_status();
636
 
637
  // Optimize the image.
638
  $response = do_imagify( $this->get_original_path(), array(
651
  $this->update_row( $data );
652
 
653
  if ( is_wp_error( $response ) ) {
654
+ $this->delete_running_status();
655
 
656
  if ( 'error' === $data['status'] ) {
657
  return $response;
672
  */
673
  do_action( 'after_imagify_optimize_file', $this->id, $this->get_data() );
674
 
675
+ $this->delete_running_status();
676
 
677
  return true;
678
  }
716
  do_action( 'before_imagify_restore_file', $this->id );
717
 
718
  // Create the original image from the backup.
719
+ $this->filesystem->copy( $backup_path, $file_path, true );
720
+ $this->filesystem->chmod_file( $file_path );
721
 
722
  // Remove old optimization data.
723
  $this->delete_imagify_data();
inc/classes/class-imagify-files-iterator.php CHANGED
@@ -17,7 +17,7 @@ class Imagify_Files_Iterator extends FilterIterator {
17
  * @since 1.7
18
  * @author Grégory Viguier
19
  */
20
- const VERSION = '1.0';
21
 
22
  /**
23
  * Tell if the iterator will return both folders and images, or only images.
@@ -27,6 +27,16 @@ class Imagify_Files_Iterator extends FilterIterator {
27
  */
28
  protected $include_folders;
29
 
 
 
 
 
 
 
 
 
 
 
30
  /**
31
  * Check whether the current element of the iterator is acceptable.
32
  *
@@ -40,6 +50,7 @@ class Imagify_Files_Iterator extends FilterIterator {
40
  public function __construct( $iterator, $include_folders = true ) {
41
  parent::__construct( $iterator );
42
  $this->include_folders = (bool) $include_folders;
 
43
  }
44
 
45
  /**
@@ -85,7 +96,7 @@ class Imagify_Files_Iterator extends FilterIterator {
85
  if ( $has_extension_method ) {
86
  $file_extension = strtolower( $this->current()->getExtension() );
87
  } else {
88
- $file_extension = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
89
  }
90
 
91
  return preg_match( '@^' . $extensions . '$@', $file_extension );
17
  * @since 1.7
18
  * @author Grégory Viguier
19
  */
20
+ const VERSION = '1.0.1';
21
 
22
  /**
23
  * Tell if the iterator will return both folders and images, or only images.
27
  */
28
  protected $include_folders;
29
 
30
+ /**
31
+ * Filesystem object.
32
+ *
33
+ * @var object Imagify_Filesystem
34
+ * @since 1.7.1
35
+ * @access protected
36
+ * @author Grégory Viguier
37
+ */
38
+ protected $filesystem;
39
+
40
  /**
41
  * Check whether the current element of the iterator is acceptable.
42
  *
50
  public function __construct( $iterator, $include_folders = true ) {
51
  parent::__construct( $iterator );
52
  $this->include_folders = (bool) $include_folders;
53
+ $this->filesystem = Imagify_Filesystem::get_instance();
54
  }
55
 
56
  /**
96
  if ( $has_extension_method ) {
97
  $file_extension = strtolower( $this->current()->getExtension() );
98
  } else {
99
+ $file_extension = strtolower( $this->filesystem->path_info( $file_path, 'extension' ) );
100
  }
101
 
102
  return preg_match( '@^' . $extensions . '$@', $file_extension );
inc/classes/class-imagify-files-list-table.php CHANGED
@@ -17,7 +17,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
17
  * @since 1.7
18
  * @author Grégory Viguier
19
  */
20
- const VERSION = '1.0';
21
 
22
  /**
23
  * Class version.
@@ -38,6 +38,16 @@ class Imagify_Files_List_Table extends WP_List_Table {
38
  */
39
  protected $folders = array();
40
 
 
 
 
 
 
 
 
 
 
 
41
  /**
42
  * Constructor.
43
  *
@@ -56,6 +66,8 @@ class Imagify_Files_List_Table extends WP_List_Table {
56
  $this->modes = array(
57
  'list' => __( 'List View', 'imagify' ),
58
  );
 
 
59
  }
60
 
61
  /**
@@ -301,7 +313,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
301
  $root_id = $folder->folder_id;
302
  $folder_filters[ $folder->folder_id ] = '/';
303
  } else {
304
- $folder_filters[ $folder->folder_id ] = '/' . trim( imagify_make_file_path_relative( Imagify_Files_Scan::remove_placeholder( $folder->path ) ), '/' );
305
  }
306
  }
307
 
@@ -494,7 +506,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
494
  $item = $this->maybe_set_item_folder( $item );
495
  $url = $item->get_original_url();
496
  $base = ! empty( $item->folder_path ) ? Imagify_Files_Scan::remove_placeholder( $item->folder_path ) : '';
497
- $title = imagify_make_file_path_relative( $item->get_original_path(), $base );
498
  $dimensions = $item->get_dimensions();
499
  $orientation = $dimensions['width'] > $dimensions['height'] ? ' landscape' : ' portrait';
500
  $orientation = $dimensions['width'] && $dimensions['height'] ? $orientation : '';
@@ -550,7 +562,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
550
  // It's the site's root.
551
  printf( $format, __( 'Site\'s root', 'imagify' ) );
552
  } else {
553
- printf( $format, '<code>/' . trim( imagify_make_file_path_relative( Imagify_Files_Scan::remove_placeholder( $item->folder_path ) ), '/' ) . '</code>' );
554
  }
555
 
556
  if ( ! $item->is_folder_active ) {
@@ -619,7 +631,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
619
  $messages[] = '<strong class="imagify-status-not-optimized">' . esc_html_x( 'Not optimized', 'image', 'imagify' ) . '</strong>';
620
  } elseif ( $error_text ) {
621
  // Error or already optimized.
622
- $messages[] = '<span class="imagify-status-' . $status . '">' . esc_html( $error_text ) . '</span>';
623
  }
624
 
625
  if ( ! $row['modified'] && ! $messages ) {
@@ -662,7 +674,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
662
  public function column_actions( $item ) {
663
  static $done = false;
664
 
665
- if ( ! imagify_valid_key() ) {
666
  // Stop the process if the API key isn't valid.
667
  if ( ! $done ) {
668
  // No need to display this on every row.
@@ -673,9 +685,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
673
  return;
674
  }
675
 
676
- $transient_name = 'imagify-file-async-in-progress-' . $item->get_id();
677
-
678
- if ( false !== get_site_transient( $transient_name ) ) {
679
  echo '<div class="button"><span class="imagify-spinner"></span>' . __( 'Optimizing...', 'imagify' ) . '</div>';
680
  return;
681
  }
@@ -865,7 +875,7 @@ class Imagify_Files_List_Table extends WP_List_Table {
865
 
866
  $file_path = $item->get_original_path();
867
 
868
- if ( ! $file_path || ! imagify_get_filesystem()->exists( $file_path ) ) {
869
  return;
870
  }
871
 
17
  * @since 1.7
18
  * @author Grégory Viguier
19
  */
20
+ const VERSION = '1.0.1';
21
 
22
  /**
23
  * Class version.
38
  */
39
  protected $folders = array();
40
 
41
+ /**
42
+ * Filesystem object.
43
+ *
44
+ * @var object Imagify_Filesystem
45
+ * @since 1.7.1
46
+ * @access protected
47
+ * @author Grégory Viguier
48
+ */
49
+ protected $filesystem;
50
+
51
  /**
52
  * Constructor.
53
  *
66
  $this->modes = array(
67
  'list' => __( 'List View', 'imagify' ),
68
  );
69
+
70
+ $this->filesystem = Imagify_Filesystem::get_instance();
71
  }
72
 
73
  /**
313
  $root_id = $folder->folder_id;
314
  $folder_filters[ $folder->folder_id ] = '/';
315
  } else {
316
+ $folder_filters[ $folder->folder_id ] = '/' . trim( $this->filesystem->make_path_relative( Imagify_Files_Scan::remove_placeholder( $folder->path ) ), '/' );
317
  }
318
  }
319
 
506
  $item = $this->maybe_set_item_folder( $item );
507
  $url = $item->get_original_url();
508
  $base = ! empty( $item->folder_path ) ? Imagify_Files_Scan::remove_placeholder( $item->folder_path ) : '';
509
+ $title = $this->filesystem->make_path_relative( $item->get_original_path(), $base );
510
  $dimensions = $item->get_dimensions();
511
  $orientation = $dimensions['width'] > $dimensions['height'] ? ' landscape' : ' portrait';
512
  $orientation = $dimensions['width'] && $dimensions['height'] ? $orientation : '';
562
  // It's the site's root.
563
  printf( $format, __( 'Site\'s root', 'imagify' ) );
564
  } else {
565
+ printf( $format, '<code>/' . trim( $this->filesystem->make_path_relative( Imagify_Files_Scan::remove_placeholder( $item->folder_path ) ), '/' ) . '</code>' );
566
  }
567
 
568
  if ( ! $item->is_folder_active ) {
631
  $messages[] = '<strong class="imagify-status-not-optimized">' . esc_html_x( 'Not optimized', 'image', 'imagify' ) . '</strong>';
632
  } elseif ( $error_text ) {
633
  // Error or already optimized.
634
+ $messages[] = '<span class="imagify-status-' . $status . '">' . esc_html( imagify_translate_api_message( $error_text ) ) . '</span>';
635
  }
636
 
637
  if ( ! $row['modified'] && ! $messages ) {
674
  public function column_actions( $item ) {
675
  static $done = false;
676
 
677
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
678
  // Stop the process if the API key isn't valid.
679
  if ( ! $done ) {
680
  // No need to display this on every row.
685
  return;
686
  }
687
 
688
+ if ( $item->is_running() ) {
 
 
689
  echo '<div class="button"><span class="imagify-spinner"></span>' . __( 'Optimizing...', 'imagify' ) . '</div>';
690
  return;
691
  }
875
 
876
  $file_path = $item->get_original_path();
877
 
878
+ if ( ! $file_path || ! $this->filesystem->exists( $file_path ) ) {
879
  return;
880
  }
881
 
inc/classes/class-imagify-files-recursive-iterator.php CHANGED
@@ -17,7 +17,31 @@ class Imagify_Files_Recursive_Iterator extends RecursiveFilterIterator {
17
  * @since 1.7
18
  * @author Grégory Viguier
19
  */
20
- const VERSION = '1.0';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  /**
23
  * Check whether the current element of the iterator is acceptable.
@@ -62,7 +86,7 @@ class Imagify_Files_Recursive_Iterator extends RecursiveFilterIterator {
62
  if ( $has_extension_method ) {
63
  $file_extension = strtolower( $this->current()->getExtension() );
64
  } else {
65
- $file_extension = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
66
  }
67
 
68
  return preg_match( '@^' . $extensions . '$@', $file_extension );
17
  * @since 1.7
18
  * @author Grégory Viguier
19
  */
20
+ const VERSION = '1.0.1';
21
+
22
+ /**
23
+ * Filesystem object.
24
+ *
25
+ * @var object Imagify_Filesystem
26
+ * @since 1.7.1
27
+ * @access protected
28
+ * @author Grégory Viguier
29
+ */
30
+ protected $filesystem;
31
+
32
+ /**
33
+ * Check whether the current element of the iterator is acceptable.
34
+ *
35
+ * @since 1.7.1
36
+ * @access public
37
+ * @author Grégory Viguier
38
+ *
39
+ * @param object $iterator The iterator that is being filtered.
40
+ */
41
+ public function __construct( $iterator ) {
42
+ parent::__construct( $iterator );
43
+ $this->filesystem = Imagify_Filesystem::get_instance();
44
+ }
45
 
46
  /**
47
  * Check whether the current element of the iterator is acceptable.
86
  if ( $has_extension_method ) {
87
  $file_extension = strtolower( $this->current()->getExtension() );
88
  } else {
89
+ $file_extension = strtolower( $this->filesystem->path_info( $file_path, 'extension' ) );
90
  }
91
 
92
  return preg_match( '@^' . $extensions . '$@', $file_extension );
inc/classes/class-imagify-files-scan.php CHANGED
@@ -16,7 +16,7 @@ class Imagify_Files_Scan {
16
  * @since 1.7
17
  * @author Grégory Viguier
18
  */
19
- const VERSION = '1.0';
20
 
21
  /**
22
  * Get files (optimizable by Imagify) recursively from a specific folder.
@@ -29,10 +29,10 @@ class Imagify_Files_Scan {
29
  * @return array|object An array of absolute paths. A WP_Error object on error.
30
  */
31
  public static function get_files_from_folder( $folder ) {
32
- static $abspath;
33
 
34
  // Formate and validate the folder path.
35
- if ( ! is_string( $folder ) || '' === $folder || '/' === $folder || '\\' === $folder ) {
36
  return new WP_Error( 'invalid_folder', __( 'Invalid folder.', 'imagify' ) );
37
  }
38
 
@@ -42,7 +42,7 @@ class Imagify_Files_Scan {
42
  return new WP_Error( 'folder_not_exists', __( 'This folder does not exist.', 'imagify' ) );
43
  }
44
 
45
- if ( ! imagify_get_filesystem()->is_dir( $folder ) ) {
46
  return new WP_Error( 'not_a_folder', __( 'This file is not a folder.', 'imagify' ) );
47
  }
48
 
@@ -50,12 +50,8 @@ class Imagify_Files_Scan {
50
  return new WP_Error( 'folder_forbidden', __( 'This folder is not allowed.', 'imagify' ) );
51
  }
52
 
53
- if ( ! isset( $abspath ) ) {
54
- $abspath = realpath( ABSPATH );
55
- }
56
-
57
  // Finally we made all our validations.
58
- if ( $folder === $abspath ) {
59
  // For the site's root, we don't look in sub-folders.
60
  $dir = new DirectoryIterator( $folder );
61
  $dir = new Imagify_Files_Iterator( $dir, false );
@@ -80,6 +76,20 @@ class Imagify_Files_Scan {
80
  return $images;
81
  }
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  /**
84
  * Tell if a path is forbidden.
85
  *
@@ -93,11 +103,13 @@ class Imagify_Files_Scan {
93
  public static function is_path_forbidden( $file_path ) {
94
  static $folders;
95
 
96
- if ( self::is_filename_forbidden( basename( $file_path ) ) ) {
 
 
97
  return true;
98
  }
99
 
100
- if ( imagify_file_is_symlinked( $file_path ) ) {
101
  // Files outside the site's folder are forbidden.
102
  return true;
103
  }
@@ -147,12 +159,13 @@ class Imagify_Files_Scan {
147
  return $folders;
148
  }
149
 
150
- $folders = array(
 
151
  // Server.
152
- imagify_get_abspath() . 'cgi-bin', // `cgi-bin`
153
  // WordPress.
154
- imagify_get_abspath() . 'wp-admin', // `wp-admin`
155
- imagify_get_abspath() . WPINC, // `wp-includes`
156
  get_imagify_upload_basedir( true ), // Media library.
157
  WP_CONTENT_DIR . '/languages', // Translations.
158
  WP_CONTENT_DIR . '/mu-plugins', // MU plugins.
@@ -498,7 +511,7 @@ class Imagify_Files_Scan {
498
  if ( 'content' === $ngg_root ) {
499
  $ngg_root = WP_CONTENT_DIR . '/';
500
  } else {
501
- $ngg_root = imagify_get_abspath();
502
  }
503
 
504
  if ( is_multisite() ) {
16
  * @since 1.7
17
  * @author Grégory Viguier
18
  */
19
+ const VERSION = '1.0.1';
20
 
21
  /**
22
  * Get files (optimizable by Imagify) recursively from a specific folder.
29
  * @return array|object An array of absolute paths. A WP_Error object on error.
30
  */
31
  public static function get_files_from_folder( $folder ) {
32
+ $filesystem = imagify_get_filesystem();
33
 
34
  // Formate and validate the folder path.
35
+ if ( ! is_string( $folder ) ) {
36
  return new WP_Error( 'invalid_folder', __( 'Invalid folder.', 'imagify' ) );
37
  }
38
 
42
  return new WP_Error( 'folder_not_exists', __( 'This folder does not exist.', 'imagify' ) );
43
  }
44
 
45
+ if ( ! $filesystem->is_dir( $folder ) ) {
46
  return new WP_Error( 'not_a_folder', __( 'This file is not a folder.', 'imagify' ) );
47
  }
48
 
50
  return new WP_Error( 'folder_forbidden', __( 'This folder is not allowed.', 'imagify' ) );
51
  }
52
 
 
 
 
 
53
  // Finally we made all our validations.
54
+ if ( $filesystem->is_abspath( $folder ) ) {
55
  // For the site's root, we don't look in sub-folders.
56
  $dir = new DirectoryIterator( $folder );
57
  $dir = new Imagify_Files_Iterator( $dir, false );
76
  return $images;
77
  }
78
 
79
+ /**
80
+ * Tell if a path is autorized.
81
+ *
82
+ * @since 1.7.1
83
+ * @access public
84
+ * @author Grégory Viguier
85
+ *
86
+ * @param string $file_path A file or folder absolute path.
87
+ * @return bool
88
+ */
89
+ public static function is_path_autorized( $file_path ) {
90
+ return ! self::is_path_forbidden( $file_path );
91
+ }
92
+
93
  /**
94
  * Tell if a path is forbidden.
95
  *
103
  public static function is_path_forbidden( $file_path ) {
104
  static $folders;
105
 
106
+ $filesystem = imagify_get_filesystem();
107
+
108
+ if ( self::is_filename_forbidden( $filesystem->file_name( $file_path ) ) ) {
109
  return true;
110
  }
111
 
112
+ if ( $filesystem->is_symlinked( $file_path ) ) {
113
  // Files outside the site's folder are forbidden.
114
  return true;
115
  }
159
  return $folders;
160
  }
161
 
162
+ $filesystem = imagify_get_filesystem();
163
+ $folders = array(
164
  // Server.
165
+ $filesystem->get_abspath() . 'cgi-bin', // `cgi-bin`
166
  // WordPress.
167
+ $filesystem->get_abspath() . 'wp-admin', // `wp-admin`
168
+ $filesystem->get_abspath() . WPINC, // `wp-includes`
169
  get_imagify_upload_basedir( true ), // Media library.
170
  WP_CONTENT_DIR . '/languages', // Translations.
171
  WP_CONTENT_DIR . '/mu-plugins', // MU plugins.
511
  if ( 'content' === $ngg_root ) {
512
  $ngg_root = WP_CONTENT_DIR . '/';
513
  } else {
514
+ $ngg_root = imagify_get_filesystem()->get_abspath();
515
  }
516
 
517
  if ( is_multisite() ) {
inc/classes/class-imagify-filesystem.php ADDED
@@ -0,0 +1,719 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
5
+ require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
6
+
7
+ /**
8
+ * Class that enhance the WP filesystem class.
9
+ *
10
+ * @since 1.7.1
11
+ * @author Grégory Viguier
12
+ */
13
+ class Imagify_Filesystem extends WP_Filesystem_Direct {
14
+
15
+ /**
16
+ * Class version.
17
+ *
18
+ * @var string
19
+ */
20
+ const VERSION = '1.0';
21
+
22
+ /**
23
+ * The single instance of the class.
24
+ *
25
+ * @var object
26
+ * @access protected
27
+ */
28
+ protected static $_instance;
29
+
30
+
31
+ /** ----------------------------------------------------------------------------------------- */
32
+ /** INSTANCIATION =========================================================================== */
33
+ /** ----------------------------------------------------------------------------------------- */
34
+
35
+ /**
36
+ * Constructor.
37
+ *
38
+ * @since 1.7.1
39
+ * @access public
40
+ * @author Grégory Viguier
41
+ */
42
+ public function __construct() {
43
+ // Define the permission constants if not already done.
44
+ if ( ! defined( 'FS_CHMOD_DIR' ) ) {
45
+ define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
46
+ }
47
+ if ( ! defined( 'FS_CHMOD_FILE' ) ) {
48
+ define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
49
+ }
50
+
51
+ parent::__construct( '' );
52
+ }
53
+
54
+ /**
55
+ * Get the main Instance.
56
+ *
57
+ * @since 1.7.1
58
+ * @access public
59
+ * @author Grégory Viguier
60
+ *
61
+ * @return object Main instance.
62
+ */
63
+ public static function get_instance() {
64
+ if ( ! isset( self::$_instance ) ) {
65
+ self::$_instance = new self();
66
+ }
67
+
68
+ return self::$_instance;
69
+ }
70
+
71
+
72
+ /** ----------------------------------------------------------------------------------------- */
73
+ /** CUSTOM TOOLS ============================================================================ */
74
+ /** ----------------------------------------------------------------------------------------- */
75
+
76
+ /**
77
+ * Get the file name.
78
+ * Replacement for basename().
79
+ *
80
+ * @since 1.7.1
81
+ * @access public
82
+ * @author Grégory Viguier
83
+ *
84
+ * @param string $file_path Path to the file.
85
+ * @return string|bool The base name of the given path. False on failure.
86
+ */
87
+ public function file_name( $file_path ) {
88
+ if ( ! $file_path ) {
89
+ return false;
90
+ }
91
+
92
+ return wp_basename( $file_path );
93
+ }
94
+
95
+ /**
96
+ * Get the parent directory's path.
97
+ * Replacement for dirname().
98
+ *
99
+ * @since 1.7.1
100
+ * @access public
101
+ * @author Grégory Viguier
102
+ *
103
+ * @param string $file_path Path to the file.
104
+ * @return string|bool The directory path with a trailing slash. False on failure.
105
+ */
106
+ public function dir_path( $file_path ) {
107
+ if ( ! $file_path ) {
108
+ return false;
109
+ }
110
+
111
+ $file_path = dirname( $file_path );
112
+
113
+ return $this->is_root( $file_path ) ? $this->get_root() : trailingslashit( $file_path );
114
+ }
115
+
116
+ /**
117
+ * Get information about a file path.
118
+ * Replacement for pathinfo().
119
+ *
120
+ * @since 1.7.1
121
+ * @access public
122
+ * @author Grégory Viguier
123
+ *
124
+ * @param string $file_path Path to the file.
125
+ * @param string $option If present, specifies a specific element to be returned; one of 'dir_path', 'file_name', 'extension' or 'file_base'.
126
+ * If option is not specified, returns all available elements.
127
+ * @return array|string If the option parameter is not passed, an associative array containing the following elements is returned: 'dir_path' (with trailing slash), 'file_name' (with extension), 'extension' (if any), and 'file_base' (without extension).
128
+ */
129
+ public function path_info( $file_path, $option = null ) {
130
+ if ( ! $file_path ) {
131
+ if ( isset( $option ) ) {
132
+ return '';
133
+ }
134
+
135
+ return array(
136
+ 'dir_path' => '',
137
+ 'file_name' => '',
138
+ 'extension' => null,
139
+ 'file_base' => '',
140
+ );
141
+ }
142
+
143
+ if ( isset( $option ) ) {
144
+ $options = array(
145
+ 'dir_path' => PATHINFO_DIRNAME,
146
+ 'file_name' => PATHINFO_BASENAME,
147
+ 'extension' => PATHINFO_EXTENSION,
148
+ 'file_base' => PATHINFO_FILENAME,
149
+ );
150
+
151
+ if ( ! isset( $options[ $option ] ) ) {
152
+ return '';
153
+ }
154
+
155
+ $output = pathinfo( $file_path, $options[ $option ] );
156
+
157
+ if ( 'dir_path' !== $option ) {
158
+ return $output;
159
+ }
160
+
161
+ return $this->is_root( $output ) ? $this->get_root() : trailingslashit( $output );
162
+ }
163
+
164
+ $output = pathinfo( $file_path );
165
+
166
+ $output['dirname'] = $this->is_root( $output['dirname'] ) ? $this->get_root() : trailingslashit( $output['dirname'] );
167
+ $output['extension'] = isset( $output['extension'] ) ? $output['extension'] : null;
168
+
169
+ // '/www/htdocs/inc/lib.inc.php'
170
+ return array(
171
+ 'dir_path' => $output['dirname'], // '/www/htdocs/inc/'
172
+ 'file_name' => $output['basename'], // 'lib.inc.php'
173
+ 'extension' => $output['extension'], // 'php'
174
+ 'file_base' => $output['filename'], // 'lib.inc'
175
+ );
176
+ }
177
+
178
+ /**
179
+ * Determine if a file or directory is writable.
180
+ * This function is used to work around certain ACL issues in PHP primarily affecting Windows Servers.
181
+ * Replacement for is_writable().
182
+ *
183
+ * @param string $file_path Path to the file.
184
+ * @return bool
185
+ */
186
+ public function is_writable( $file_path ) {
187
+ if ( ! $file_path ) {
188
+ return false;
189
+ }
190
+
191
+ return wp_is_writable( $file_path );
192
+ }
193
+
194
+ /**
195
+ * Recursive directory creation based on full path. Will attempt to set permissions on folders.
196
+ * Replacement for recursive mkdir().
197
+ *
198
+ * @since 1.7.1
199
+ * @access public
200
+ * @author Grégory Viguier
201
+ *
202
+ * @param string $path Full path to attempt to create.
203
+ * @return bool Whether the path was created. True if path already exists.
204
+ */
205
+ public function make_dir( $path ) {
206
+ /*
207
+ * Safe mode fails with a trailing slash under certain PHP versions.
208
+ */
209
+ $path = untrailingslashit( wp_normalize_path( $path ) );
210
+
211
+ if ( $this->is_root( $path ) ) {
212
+ return $this->is_dir( $this->get_root() ) && $this->is_writable( $this->get_root() );
213
+ }
214
+
215
+ if ( $this->exists( $path ) ) {
216
+ return $this->is_dir( $path ) && $this->is_writable( $path );
217
+ }
218
+
219
+ $abspath = $this->get_abspath();
220
+
221
+ if ( strpos( $path, $abspath ) !== 0 ) {
222
+ return false;
223
+ }
224
+
225
+ $bits = str_replace( $abspath, '', $path );
226
+ $bits = explode( '/', $bits );
227
+ $path = untrailingslashit( $abspath );
228
+
229
+ foreach ( $bits as $bit ) {
230
+ $path .= '/' . $bit;
231
+
232
+ if ( ! $this->exists( $path ) ) {
233
+ $this->mkdir( $path );
234
+ } elseif ( ! $this->is_dir( $path ) ) {
235
+ return false;
236
+ }
237
+
238
+ if ( ! $this->is_writable( $path ) ) {
239
+ $this->chmod_dir( $path );
240
+
241
+ if ( ! $this->is_writable( $path ) ) {
242
+ return false;
243
+ }
244
+ }
245
+ }
246
+
247
+ return true;
248
+ }
249
+
250
+ /**
251
+ * Set a file permissions using FS_CHMOD_FILE.
252
+ *
253
+ * @since 1.7.1
254
+ * @access public
255
+ * @author Grégory Viguier
256
+ *
257
+ * @param string $file_path Path to the file.
258
+ * @return bool True on success, false on failure.
259
+ */
260
+ public function chmod_file( $file_path ) {
261
+ if ( ! $file_path ) {
262
+ return false;
263
+ }
264
+
265
+ return $this->chmod( $file_path, FS_CHMOD_FILE );
266
+ }
267
+
268
+ /**
269
+ * Set a directory permissions using FS_CHMOD_DIR.
270
+ *
271
+ * @since 1.7.1
272
+ * @access public
273
+ * @author Grégory Viguier
274
+ *
275
+ * @param string $file_path Path to the directory.
276
+ * @return bool True on success, false on failure.
277
+ */
278
+ public function chmod_dir( $file_path ) {
279
+ if ( ! $file_path ) {
280
+ return false;
281
+ }
282
+
283
+ return $this->chmod( $file_path, FS_CHMOD_DIR );
284
+ }
285
+
286
+ /**
287
+ * Get a file mime type.
288
+ *
289
+ * @since 1.7.1
290
+ * @access public
291
+ * @author Grégory Viguier
292
+ *
293
+ * @param string $file_path A file path (prefered) or a filename.
294
+ * @return string|bool A mime type. False on failure: the test is limited to mime types supported by Imagify.
295
+ */
296
+ public function get_mime_type( $file_path ) {
297
+ if ( ! $file_path ) {
298
+ return false;
299
+ }
300
+
301
+ $file_type = wp_check_filetype( $file_path, imagify_get_mime_types() );
302
+
303
+ return $file_type['type'];
304
+ }
305
+
306
+ /**
307
+ * Get a file modification date, formated as "mysql". Fallback to current date.
308
+ *
309
+ * @since 1.7.1
310
+ * @access public
311
+ * @author Grégory Viguier
312
+ *
313
+ * @param string $file_path Path to the file.
314
+ * @return string The date.
315
+ */
316
+ public function get_date( $file_path ) {
317
+ static $offset;
318
+
319
+ if ( ! $file_path ) {
320
+ return current_time( 'mysql' );
321
+ }
322
+
323
+ $date = $this->mtime( $file_path );
324
+
325
+ if ( ! $date ) {
326
+ return current_time( 'mysql' );
327
+ }
328
+
329
+ if ( ! isset( $offset ) ) {
330
+ $offset = get_option( 'gmt_offset' ) * HOUR_IN_SECONDS;
331
+ }
332
+
333
+ return gmdate( 'Y-m-d H:i:s', $date + $offset );
334
+ }
335
+
336
+ /**
337
+ * Tell if a file is symlinked.
338
+ *
339
+ * @since 1.7.1
340
+ * @access public
341
+ * @author Grégory Viguier
342
+ *
343
+ * @param string $file_path An absolute path.
344
+ * @return bool
345
+ */
346
+ public function is_symlinked( $file_path ) {
347
+ static $abspath;
348
+ static $plugin_paths = array();
349
+ global $wp_plugin_paths;
350
+
351
+ if ( ! $file_path ) {
352
+ return false;
353
+ }
354
+
355
+ $real_path = realpath( $file_path );
356
+
357
+ if ( ! $real_path ) {
358
+ return false;
359
+ }
360
+
361
+ if ( ! isset( $abspath ) ) {
362
+ $abspath = $this->normalize_path_for_comparison( $this->get_abspath() );
363
+ }
364
+
365
+ $lower_file_path = $this->normalize_path_for_comparison( $real_path );
366
+
367
+ if ( strpos( $lower_file_path, $abspath ) !== 0 ) {
368
+ return true;
369
+ }
370
+
371
+ if ( $wp_plugin_paths && is_array( $wp_plugin_paths ) ) {
372
+ if ( ! $plugin_paths ) {
373
+ foreach ( $wp_plugin_paths as $dir => $real_dir ) {
374
+ $dir = $this->normalize_path_for_comparison( $dir );
375
+ $plugin_paths[ $dir ] = $this->normalize_path_for_comparison( $real_dir );
376
+ }
377
+ }
378
+
379
+ $lower_file_path = $this->normalize_path_for_comparison( $file_path );
380
+
381
+ foreach ( $plugin_paths as $dir => $real_dir ) {
382
+ if ( strpos( $lower_file_path, $dir ) === 0 ) {
383
+ return true;
384
+ }
385
+ }
386
+ }
387
+
388
+ return false;
389
+ }
390
+
391
+
392
+ /** ----------------------------------------------------------------------------------------- */
393
+ /** WORK WITH IMAGES ======================================================================== */
394
+ /** ----------------------------------------------------------------------------------------- */
395
+
396
+ /**
397
+ * Get an image data.
398
+ * Replacement for getimagesize().
399
+ *
400
+ * @since 1.7.1
401
+ * @access public
402
+ * @author Grégory Viguier
403
+ *
404
+ * @param string $file_path Path to the file.
405
+ * @return array|bool The image data. An empty array on failure.
406
+ */
407
+ public function get_image_size( $file_path ) {
408
+ if ( ! $file_path ) {
409
+ return array();
410
+ }
411
+
412
+ $size = @getimagesize( $file_path );
413
+
414
+ if ( ! $size || ! isset( $size[0], $size[1] ) ) {
415
+ return array();
416
+ }
417
+
418
+ return array(
419
+ 0 => (int) $size[0],
420
+ 1 => (int) $size[1],
421
+ 'width' => (int) $size[0],
422
+ 'height' => (int) $size[1],
423
+ 'type' => (int) $size[2],
424
+ 'attr' => $size[3],
425
+ 'channels' => isset( $size['channels'] ) ? (int) $size['channels'] : null,
426
+ 'bits' => isset( $size['bits'] ) ? (int) $size['bits'] : null,
427
+ 'mime' => $size['mime'],
428
+ );
429
+ }
430
+
431
+ /**
432
+ * Tell if exif_read_data() is available.
433
+ *
434
+ * @since 1.7.1
435
+ * @access public
436
+ * @author Grégory Viguier
437
+ *
438
+ * @return bool
439
+ */
440
+ public function can_get_exif() {
441
+ static $callable;
442
+
443
+ if ( ! isset( $callable ) ) {
444
+ $callable = is_callable( 'exif_read_data' );
445
+ }
446
+
447
+ return $callable;
448
+ }
449
+
450
+ /**
451
+ * Get the EXIF headers from an image file.
452
+ * Replacement for exif_read_data().
453
+ *
454
+ * @since 1.7.1
455
+ * @access public
456
+ * @author Grégory Viguier
457
+ * @see https://secure.php.net/manual/en/function.exif-read-data.php
458
+ *
459
+ * @param string $file_path Path to the file.
460
+ * @param string $sections A comma separated list of sections that need to be present in file to produce a result array. See exif_read_data() documentation for values: FILE, COMPUTED, ANY_TAG, IFD0, THUMBNAIL, COMMENT, EXIF.
461
+ * @param bool $arrays Specifies whether or not each section becomes an array. The sections COMPUTED, THUMBNAIL, and COMMENT always become arrays as they may contain values whose names conflict with other sections.
462
+ * @param bool $thumbnail When set to TRUE the thumbnail itself is read. Otherwise, only the tagged data is read.
463
+ * @return array The EXIF headers. An empty array on failure.
464
+ */
465
+ public function get_image_exif( $file_path, $sections = null, $arrays = false, $thumbnail = false ) {
466
+ if ( ! $file_path || ! $this->can_get_exif() ) {
467
+ return array();
468
+ }
469
+
470
+ $exif = @exif_read_data( $file_path, $sections, $arrays, $thumbnail );
471
+
472
+ return is_array( $exif ) ? $exif : array();
473
+ }
474
+
475
+
476
+ /** ----------------------------------------------------------------------------------------- */
477
+ /** WORK WITH PATHS ========================================================================= */
478
+ /** ----------------------------------------------------------------------------------------- */
479
+
480
+ /**
481
+ * Make an absolute path relative to WordPress' root folder.
482
+ * Also works for files from registered symlinked plugins.
483
+ *
484
+ * @since 1.7.1
485
+ * @access public
486
+ * @author Grégory Viguier
487
+ *
488
+ * @param string $file_path An absolute path.
489
+ * @param string $base A base path to use instead of ABSPATH.
490
+ * @return string|bool A relative path. Can return the absolute path or false in case of a failure.
491
+ */
492
+ public function make_path_relative( $file_path, $base = '' ) {
493
+ static $abspath;
494
+ global $wp_plugin_paths;
495
+
496
+ if ( ! $file_path ) {
497
+ return false;
498
+ }
499
+
500
+ if ( ! isset( $abspath ) ) {
501
+ $abspath = wp_normalize_path( ABSPATH );
502
+ }
503
+
504
+ $file_path = wp_normalize_path( $file_path );
505
+ $base = $base ? $this->normalize_dir_path( $base ) : $abspath;
506
+ $pos = strpos( $file_path, $base );
507
+
508
+ if ( false === $pos && $wp_plugin_paths && is_array( $wp_plugin_paths ) ) {
509
+ // The file is probably part of a symlinked plugin.
510
+ arsort( $wp_plugin_paths );
511
+
512
+ foreach ( $wp_plugin_paths as $dir => $real_dir ) {
513
+ if ( strpos( $file_path, $real_dir ) === 0 ) {
514
+ $file_path = wp_normalize_path( $dir . substr( $file_path, strlen( $real_dir ) ) );
515
+ }
516
+ }
517
+
518
+ $pos = strpos( $file_path, $base );
519
+ }
520
+
521
+ if ( false === $pos ) {
522
+ // We're in trouble.
523
+ return $file_path;
524
+ }
525
+
526
+ return substr_replace( $file_path, '', 0, $pos + strlen( $base ) );
527
+ }
528
+
529
+ /**
530
+ * Normalize a directory path.
531
+ * The path is normalized and a trailing slash is added.
532
+ *
533
+ * @since 1.7.1
534
+ * @access public
535
+ * @author Grégory Viguier
536
+ *
537
+ * @param string $file_path The file path.
538
+ * @return string The normalized dir path.
539
+ */
540
+ public function normalize_dir_path( $file_path ) {
541
+ return wp_normalize_path( trailingslashit( $file_path ) );
542
+ }
543
+
544
+ /**
545
+ * Normalize a file path, aiming for path comparison.
546
+ * The path is normalized, case-lowered, and a trailing slash is added.
547
+ *
548
+ * @since 1.7.1
549
+ * @access public
550
+ * @author Grégory Viguier
551
+ *
552
+ * @param string $file_path The file path.
553
+ * @return string The normalized file path.
554
+ */
555
+ public function normalize_path_for_comparison( $file_path ) {
556
+ return strtolower( $this->normalize_dir_path( $file_path ) );
557
+ }
558
+
559
+
560
+ /** ----------------------------------------------------------------------------------------- */
561
+ /** SOME WELL KNOWN PATHS AND URLS ========================================================== */
562
+ /** ----------------------------------------------------------------------------------------- */
563
+
564
+ /**
565
+ * The path to the server's root is not always '/', it can also be '//' or 'C://'.
566
+ * I am get_root.
567
+ *
568
+ * @since 1.7.1
569
+ * @access public
570
+ * @author Grégory Viguier
571
+ *
572
+ * @return string The path to the server's root.
573
+ */
574
+ public function get_root() {
575
+ static $groot;
576
+
577
+ if ( isset( $groot ) ) {
578
+ return $groot;
579
+ }
580
+
581
+ $groot = preg_replace( '@^((?:.:)?/+).*@', '$1', $this->get_abspath() );
582
+
583
+ return $groot;
584
+ }
585
+
586
+ /**
587
+ * Tell if a path is the server's root.
588
+ *
589
+ * @since 1.7.1
590
+ * @access public
591
+ * @author Grégory Viguier
592
+ *
593
+ * @param string $path The path.
594
+ * @return bool
595
+ */
596
+ public function is_root( $path ) {
597
+ $path = rtrim( $path, '/\\' );
598
+ return '.' === $path || '' === $path || preg_match( '@^.:$@', $path );
599
+ }
600
+
601
+ /**
602
+ * Get a clean value of ABSPATH that can be used in string replacements.
603
+ *
604
+ * @since 1.7.1
605
+ * @access public
606
+ * @author Grégory Viguier
607
+ *
608
+ * @return string The path to WordPress' root folder.
609
+ */
610
+ public function get_abspath() {
611
+ static $abspath;
612
+
613
+ if ( isset( $abspath ) ) {
614
+ return $abspath;
615
+ }
616
+
617
+ $abspath = wp_normalize_path( ABSPATH );
618
+
619
+ // Make sure ABSPATH is not messed up: it could be defined as a relative path for example (yeah, I know, but we've seen it).
620
+ $test_file = wp_normalize_path( IMAGIFY_FILE );
621
+ $pos = strpos( $test_file, $abspath );
622
+
623
+ if ( $pos > 0 ) {
624
+ // ABSPATH has a wrong value.
625
+ $abspath = substr( $test_file, 0, $pos ) . $abspath;
626
+
627
+ } elseif ( false === $pos && class_exists( 'ReflectionClass' ) ) {
628
+ // Imagify is symlinked (dude, you look for trouble).
629
+ $reflector = new ReflectionClass( 'Requests' );
630
+ $test_file = $reflector->getFileName();
631
+ $pos = strpos( $test_file, $abspath );
632
+
633
+ if ( 0 < $pos ) {
634
+ // ABSPATH has a wrong value.
635
+ $abspath = substr( $test_file, 0, $pos ) . $abspath;
636
+ }
637
+ }
638
+
639
+ $abspath = trailingslashit( $abspath );
640
+
641
+ if ( '/' !== substr( $abspath, 0, 1 ) && ':' !== substr( $path, 1, 1 ) ) {
642
+ $abspath = '/' . $abspath;
643
+ }
644
+
645
+ return $abspath;
646
+ }
647
+
648
+ /**
649
+ * Tell if a path is the site's root (ABSPATH).
650
+ *
651
+ * @since 1.7.1
652
+ * @access public
653
+ * @author Grégory Viguier
654
+ *
655
+ * @param string $path The path.
656
+ * @return bool
657
+ */
658
+ public function is_abspath( $path ) {
659
+ return $this->normalize_dir_path( $path ) === $this->get_abspath();
660
+ }
661
+
662
+ /**
663
+ * Get the upload basedir.
664
+ *
665
+ * @since 1.7.1
666
+ * @access public
667
+ * @author Grégory Viguier
668
+ *
669
+ * @param bool $bypass_error True to return the path even if there is an error. This is used when we want to display this path in a message for example.
670
+ * @return string|bool The path. False on failure.
671
+ */
672
+ public function get_upload_basedir( $bypass_error = false ) {
673
+ static $upload_basedir;
674
+ static $upload_basedir_or_error;
675
+
676
+ if ( isset( $upload_basedir ) ) {
677
+ return $bypass_error ? $upload_basedir : $upload_basedir_or_error;
678
+ }
679
+
680
+ $uploads = wp_upload_dir();
681
+ $upload_basedir = $this->normalize_dir_path( $uploads['basedir'] );
682
+
683
+ if ( false !== $uploads['error'] ) {
684
+ $upload_basedir_or_error = false;
685
+ } else {
686
+ $upload_basedir_or_error = $upload_basedir;
687
+ }
688
+
689
+ return $bypass_error ? $upload_basedir : $upload_basedir_or_error;
690
+ }
691
+
692
+ /**
693
+ * Get the upload baseurl.
694
+ *
695
+ * @since 1.7.1
696
+ * @access public
697
+ * @author Grégory Viguier
698
+ *
699
+ * @return string|bool The URL. False on failure.
700
+ */
701
+ public function get_upload_baseurl() {
702
+ static $upload_baseurl;
703
+
704
+ if ( isset( $upload_baseurl ) ) {
705
+ return $upload_baseurl;
706
+ }
707
+
708
+ $uploads = wp_upload_dir();
709
+
710
+ if ( false !== $uploads['error'] ) {
711
+ $upload_baseurl = false;
712
+ return $upload_baseurl;
713
+ }
714
+
715
+ $upload_baseurl = trailingslashit( $uploads['baseurl'] );
716
+
717
+ return $upload_baseurl;
718
+ }
719
+ }
inc/classes/class-imagify-notices.php CHANGED
@@ -359,7 +359,7 @@ class Imagify_Notices extends Imagify_Notices_Deprecated {
359
  return $display;
360
  }
361
 
362
- if ( self::notice_is_dismissed( 'wrong-api-key' ) || ! get_imagify_option( 'api_key' ) || imagify_valid_key() ) {
363
  return $display;
364
  }
365
 
@@ -416,7 +416,7 @@ class Imagify_Notices extends Imagify_Notices_Deprecated {
416
  return $display;
417
  }
418
 
419
- if ( self::notice_is_dismissed( 'http-block-external' ) || ! is_imagify_blocked() ) {
420
  return $display;
421
  }
422
 
@@ -457,7 +457,7 @@ class Imagify_Notices extends Imagify_Notices_Deprecated {
457
  }
458
 
459
  // Don't display the notice if the API key isn't valid.
460
- if ( ! imagify_valid_key() ) {
461
  return $display;
462
  }
463
 
@@ -536,7 +536,7 @@ class Imagify_Notices extends Imagify_Notices_Deprecated {
536
  return $display;
537
  }
538
 
539
- if ( imagify_backup_dir_is_writable() ) {
540
  return $display;
541
  }
542
 
359
  return $display;
360
  }
361
 
362
+ if ( self::notice_is_dismissed( 'wrong-api-key' ) || ! get_imagify_option( 'api_key' ) || Imagify_Requirements::is_api_key_valid() ) {
363
  return $display;
364
  }
365
 
416
  return $display;
417
  }
418
 
419
+ if ( self::notice_is_dismissed( 'http-block-external' ) || ! Imagify_Requirements::is_imagify_blocked() ) {
420
  return $display;
421
  }
422
 
457
  }
458
 
459
  // Don't display the notice if the API key isn't valid.
460
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
461
  return $display;
462
  }
463
 
536
  return $display;
537
  }
538
 
539
+ if ( Imagify_Requirements::attachments_backup_dir_is_writable() ) {
540
  return $display;
541
  }
542
 
inc/classes/class-imagify-requirements.php ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class used to check that Imagify has everything it needs.
6
+ *
7
+ * @since 1.7.1
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Requirements {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7.1
17
+ * @author Grégory Viguier
18
+ */
19
+ const VERSION = '1.0';
20
+
21
+ /**
22
+ * Cache the test results.
23
+ *
24
+ * @var object
25
+ * @access protected
26
+ * @since 1.7.1
27
+ * @author Grégory Viguier
28
+ */
29
+ protected static $supports = array();
30
+
31
+
32
+ /** ----------------------------------------------------------------------------------------- */
33
+ /** SERVER ================================================================================== */
34
+ /** ----------------------------------------------------------------------------------------- */
35
+
36
+ /**
37
+ * Test for cURL.
38
+ *
39
+ * @since 1.7.1
40
+ * @access public
41
+ * @author Grégory Viguier
42
+ *
43
+ * @param bool $reset_cache True to get a fresh value.
44
+ * @return bool
45
+ */
46
+ public static function supports_curl( $reset_cache = false ) {
47
+ if ( $reset_cache || ! isset( self::$supports['curl'] ) ) {
48
+ self::$supports['curl'] = function_exists( 'curl_init' ) && function_exists( 'curl_exec' );
49
+ }
50
+
51
+ return self::$supports['curl'];
52
+ }
53
+
54
+ /**
55
+ * Test for imageMagick and GD.
56
+ * Similar to _wp_image_editor_choose(), but allows to test for multiple mime types at once.
57
+ *
58
+ * @since 1.7.1
59
+ * @access public
60
+ * @see _wp_image_editor_choose()
61
+ * @author Grégory Viguier
62
+ *
63
+ * @param bool $reset_cache True to get a fresh value.
64
+ * @return bool
65
+ */
66
+ public static function supports_image_editor( $reset_cache = false ) {
67
+ if ( ! $reset_cache && isset( self::$supports['image_editor'] ) ) {
68
+ return self::$supports['image_editor'];
69
+ }
70
+
71
+ require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
72
+ require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
73
+ require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
74
+
75
+ self::$supports['image_editor'] = false;
76
+
77
+ $args = array(
78
+ 'path' => IMAGIFY_PATH . 'assets/images/imagify-logo.png',
79
+ 'mime_types' => imagify_get_mime_types(),
80
+ 'methods' => Imagify_Attachment::get_editor_methods(),
81
+ );
82
+
83
+ /** This filter is documented in /wp-includes/media.php. */
84
+ $implementations = apply_filters( 'wp_image_editors', array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
85
+
86
+ foreach ( $implementations as $implementation ) {
87
+ if ( ! call_user_func( array( $implementation, 'test' ), $args ) ) {
88
+ continue;
89
+ }
90
+
91
+ foreach ( $args['mime_types'] as $mime_type ) {
92
+ if ( ! call_user_func( array( $implementation, 'supports_mime_type' ), $mime_type ) ) {
93
+ continue 2;
94
+ }
95
+ }
96
+
97
+ if ( array_diff( $args['methods'], get_class_methods( $implementation ) ) ) {
98
+ continue;
99
+ }
100
+
101
+ self::$supports['image_editor'] = true;
102
+ break;
103
+ }
104
+
105
+ return self::$supports['image_editor'];
106
+ }
107
+
108
+
109
+ /** ----------------------------------------------------------------------------------------- */
110
+ /** WORDPRESS =============================================================================== */
111
+ /** ----------------------------------------------------------------------------------------- */
112
+
113
+ /**
114
+ * Test for the uploads directory.
115
+ *
116
+ * @since 1.7.1
117
+ * @access public
118
+ * @author Grégory Viguier
119
+ *
120
+ * @param bool $reset_cache True to get a fresh value.
121
+ * @return bool
122
+ */
123
+ public static function supports_uploads( $reset_cache = false ) {
124
+ if ( ! $reset_cache && isset( self::$supports['uploads'] ) ) {
125
+ return self::$supports['uploads'];
126
+ }
127
+
128
+ self::$supports['uploads'] = Imagify_Filesystem::get_instance()->get_upload_basedir();
129
+
130
+ if ( self::$supports['uploads'] ) {
131
+ self::$supports['uploads'] = Imagify_Filesystem::get_instance()->is_writable( self::$supports['uploads'] );
132
+ }
133
+
134
+ return self::$supports['uploads'];
135
+ }
136
+
137
+ /**
138
+ * Test if external requests are blocked for Imagify.
139
+ *
140
+ * @since 1.7.1
141
+ * @access public
142
+ * @author Grégory Viguier
143
+ *
144
+ * @param bool $reset_cache True to get a fresh value.
145
+ * @return bool
146
+ */
147
+ public static function is_imagify_blocked( $reset_cache = false ) {
148
+ if ( ! $reset_cache && isset( self::$supports['imagify_blocked'] ) ) {
149
+ return self::$supports['imagify_blocked'];
150
+ }
151
+
152
+ if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL ) {
153
+ self::$supports['imagify_blocked'] = false;
154
+ return self::$supports['imagify_blocked'];
155
+ }
156
+
157
+ if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
158
+ self::$supports['imagify_blocked'] = true;
159
+ return self::$supports['imagify_blocked'];
160
+ }
161
+
162
+ $accessible_hosts = explode( ',', WP_ACCESSIBLE_HOSTS );
163
+ $accessible_hosts = array_map( 'trim', $accessible_hosts );
164
+ $accessible_hosts = array_flip( $accessible_hosts );
165
+
166
+ if ( isset( $accessible_hosts['*.imagify.io'] ) ) {
167
+ self::$supports['imagify_blocked'] = false;
168
+ return self::$supports['imagify_blocked'];
169
+ }
170
+
171
+ if ( isset( $accessible_hosts['imagify.io'], $accessible_hosts['app.imagify.io'], $accessible_hosts['storage.imagify.io'] ) ) {
172
+ self::$supports['imagify_blocked'] = false;
173
+ return self::$supports['imagify_blocked'];
174
+ }
175
+
176
+ self::$supports['imagify_blocked'] = true;
177
+ return self::$supports['imagify_blocked'];
178
+ }
179
+
180
+
181
+ /** ----------------------------------------------------------------------------------------- */
182
+ /** IMAGIFY BACKUP DIRECTORIES ============================================================== */
183
+ /** ----------------------------------------------------------------------------------------- */
184
+
185
+ /**
186
+ * Test for the attachments backup directory.
187
+ *
188
+ * @since 1.7.1
189
+ * @access public
190
+ * @author Grégory Viguier
191
+ *
192
+ * @param bool $reset_cache True to get a fresh value.
193
+ * @return bool
194
+ */
195
+ public static function attachments_backup_dir_is_writable( $reset_cache = false ) {
196
+ if ( $reset_cache || ! isset( self::$supports['attachment_backups'] ) ) {
197
+ self::$supports['attachment_backups'] = imagify_backup_dir_is_writable();
198
+ }
199
+
200
+ return self::$supports['attachment_backups'];
201
+ }
202
+
203
+ /**
204
+ * Test for the custom folders backup directory.
205
+ *
206
+ * @since 1.7.1
207
+ * @access public
208
+ * @author Grégory Viguier
209
+ *
210
+ * @param bool $reset_cache True to get a fresh value.
211
+ * @return bool
212
+ */
213
+ public static function custom_folders_backup_dir_is_writable( $reset_cache = false ) {
214
+ if ( $reset_cache || ! isset( self::$supports['custom_folder_backups'] ) ) {
215
+ self::$supports['custom_folder_backups'] = Imagify_Custom_Folders::backup_dir_is_writable();
216
+ }
217
+
218
+ return self::$supports['custom_folder_backups'];
219
+ }
220
+
221
+
222
+ /** ----------------------------------------------------------------------------------------- */
223
+ /** IMAGIFY API ============================================================================= */
224
+ /** ----------------------------------------------------------------------------------------- */
225
+
226
+ /**
227
+ * Determine if the Imagify API is available by checking the API version.
228
+ * The result is cached for 3 minutes.
229
+ *
230
+ * @since 1.7.1
231
+ * @access public
232
+ * @author Grégory Viguier
233
+ *
234
+ * @param bool $reset_cache True to get a fresh value.
235
+ * @return bool
236
+ */
237
+ public static function is_api_up( $reset_cache = false ) {
238
+ if ( ! $reset_cache && isset( self::$supports['api_up'] ) ) {
239
+ return self::$supports['api_up'];
240
+ }
241
+
242
+ $transient_name = 'imagify_check_api_version';
243
+ $transient_expiration = 3 * MINUTE_IN_SECONDS;
244
+ $transient_value = $reset_cache ? false : get_site_transient( $transient_name );
245
+
246
+ if ( false !== $transient_value ) {
247
+ self::$supports['api_up'] = (bool) $transient_value;
248
+ return self::$supports['api_up'];
249
+ }
250
+
251
+ self::$supports['api_up'] = ! is_wp_error( get_imagify_api_version() );
252
+ $transient_value = (int) self::$supports['api_up'];
253
+
254
+ set_site_transient( $transient_name, $transient_value, $transient_expiration );
255
+
256
+ return self::$supports['api_up'];
257
+ }
258
+
259
+ /**
260
+ * Test for the Imagify API key validity.
261
+ * A positive result is cached for 1 year.
262
+ *
263
+ * @since 1.7.1
264
+ * @access public
265
+ * @author Grégory Viguier
266
+ *
267
+ * @param bool $reset_cache True to get a fresh value.
268
+ * @return bool
269
+ */
270
+ public static function is_api_key_valid( $reset_cache = false ) {
271
+ if ( ! $reset_cache && isset( self::$supports['api_key_valid'] ) ) {
272
+ return self::$supports['api_key_valid'];
273
+ }
274
+
275
+ if ( ! Imagify_Options::get_instance()->get( 'api_key' ) ) {
276
+ self::$supports['api_key_valid'] = false;
277
+ return self::$supports['api_key_valid'];
278
+ }
279
+
280
+ if ( ! $reset_cache && get_site_transient( 'imagify_check_licence_1' ) ) {
281
+ self::$supports['api_key_valid'] = true;
282
+ return self::$supports['api_key_valid'];
283
+ }
284
+
285
+ if ( is_wp_error( get_imagify_user() ) ) {
286
+ self::$supports['api_key_valid'] = false;
287
+ return self::$supports['api_key_valid'];
288
+ }
289
+
290
+ self::$supports['api_key_valid'] = true;
291
+ set_site_transient( 'imagify_check_licence_1', 1, YEAR_IN_SECONDS );
292
+
293
+ return self::$supports['api_key_valid'];
294
+ }
295
+
296
+ /**
297
+ * Test for the Imagify account quota.
298
+ *
299
+ * @since 1.7.1
300
+ * @access public
301
+ * @author Grégory Viguier
302
+ *
303
+ * @param bool $reset_cache True to get a fresh value.
304
+ * @return bool
305
+ */
306
+ public static function is_over_quota( $reset_cache = false ) {
307
+ if ( ! $reset_cache && isset( self::$supports['over_quota'] ) ) {
308
+ return self::$supports['over_quota'];
309
+ }
310
+
311
+ $user = new Imagify_User();
312
+
313
+ self::$supports['over_quota'] = $user->is_over_quota();
314
+
315
+ return self::$supports['over_quota'];
316
+ }
317
+
318
+
319
+ /** ----------------------------------------------------------------------------------------- */
320
+ /** CLASS CACHE ============================================================================= */
321
+ /** ----------------------------------------------------------------------------------------- */
322
+
323
+ /**
324
+ * Reset a test cache.
325
+ *
326
+ * @since 1.7.1
327
+ * @access public
328
+ * @author Grégory Viguier
329
+ *
330
+ * @param string $cache_key Cache key.
331
+ */
332
+ public static function reset_cache( $cache_key ) {
333
+ unset( self::$supports[ $cache_key ] );
334
+
335
+ if ( 'api_up' === $cache_key ) {
336
+ delete_site_transient( 'imagify_check_api_version' );
337
+ }
338
+ elseif ( 'api_key_valid' === $cache_key ) {
339
+ delete_site_transient( 'imagify_check_licence_1' );
340
+ }
341
+ }
342
+ }
inc/classes/class-imagify-user.php CHANGED
@@ -237,7 +237,7 @@ class Imagify_User {
237
  }
238
 
239
  /**
240
- * Check if the user has consumed its quota.
241
  *
242
  * @since 1.1.1
243
  *
@@ -245,6 +245,10 @@ class Imagify_User {
245
  * @return bool
246
  */
247
  public function is_over_quota() {
 
 
 
 
248
  if ( $this->is_free() && 100 === $this->get_percent_consumed_quota() ) {
249
  return true;
250
  }
237
  }
238
 
239
  /**
240
+ * Check if the user has consumed all his/her quota.
241
  *
242
  * @since 1.1.1
243
  *
245
  * @return bool
246
  */
247
  public function is_over_quota() {
248
+ if ( empty( $this->id ) ) {
249
+ return true;
250
+ }
251
+
252
  if ( $this->is_free() && 100 === $this->get_percent_consumed_quota() ) {
253
  return true;
254
  }
inc/classes/class-imagify-views.php CHANGED
@@ -15,7 +15,7 @@ class Imagify_Views {
15
  * @var string
16
  * @since 1.7
17
  */
18
- const VERSION = '1.0';
19
 
20
  /**
21
  * Slug used for the settings page URL.
@@ -53,6 +53,16 @@ class Imagify_Views {
53
  */
54
  protected $list_table;
55
 
 
 
 
 
 
 
 
 
 
 
56
  /**
57
  * The single instance of the class.
58
  *
@@ -78,6 +88,7 @@ class Imagify_Views {
78
  $this->slug_settings = IMAGIFY_SLUG;
79
  $this->slug_bulk = IMAGIFY_SLUG . '-bulk-optimization';
80
  $this->slug_files = IMAGIFY_SLUG . '-files';
 
81
  }
82
 
83
  /**
@@ -240,23 +251,14 @@ class Imagify_Views {
240
  * @access public
241
  */
242
  public function display_bulk_page() {
243
- $data = array(
244
- // Global chart.
245
- 'total_attachments' => 0,
246
- 'unoptimized_attachments' => 0,
247
- 'optimized_attachments' => 0,
248
- 'errors_attachments' => 0,
249
- // Stats block.
250
- 'already_optimized_attachments' => 0,
251
- 'original_size' => 0,
252
- 'optimized_size' => 0,
253
- 'optimized_percent' => 0,
254
  // Limits.
255
- 'unoptimized_attachment_limit' => 0,
256
  // What to optimize.
257
- 'icon' => 'images-alt2',
258
- 'title' => __( 'Optimize your images', 'imagify' ),
259
- 'groups' => array(),
260
  );
261
 
262
  if ( imagify_is_screen( 'bulk' ) ) {
@@ -264,88 +266,78 @@ class Imagify_Views {
264
  /**
265
  * Library: in each site.
266
  */
267
- $total_saving_data = imagify_count_saving_data();
268
-
269
- // Global chart.
270
- $data['total_attachments'] += imagify_count_attachments();
271
- $data['unoptimized_attachments'] += imagify_count_unoptimized_attachments();
272
- $data['optimized_attachments'] += imagify_count_optimized_attachments();
273
- $data['errors_attachments'] += imagify_count_error_attachments();
274
- // Stats block.
275
- $data['already_optimized_attachments'] += $total_saving_data['count'];
276
- $data['original_size'] += $total_saving_data['original_size'];
277
- $data['optimized_size'] += $total_saving_data['optimized_size'];
278
- // Limits.
279
- $data['unoptimized_attachment_limit'] += imagify_get_unoptimized_attachment_limit();
280
- // Group.
281
- $data['groups']['library'] = array(
282
- /**
283
- * The group_id corresponds to the file names like 'part-bulk-optimization-results-row-{$group_id}'.
284
- * It is also used in get_imagify_localize_script_translations() and imagify_get_folder_type_data().
285
- */
286
- 'group_id' => 'library',
287
- 'context' => 'wp',
288
- 'title' => __( 'Media Library', 'imagify' ),
289
- /* translators: 1 is the opening of a link, 2 is the closing of this link. */
290
- 'footer' => sprintf( __( 'You can also re-optimize your images from your %1$sMedia Library%2$s screen.', 'imagify' ), '<a href="' . esc_url( admin_url( 'upload.php' ) ) . '">', '</a>' ),
291
- );
292
  }
293
 
294
  if ( imagify_can_optimize_custom_folders() && ( imagify_is_active_for_network() && is_network_admin() || ! imagify_is_active_for_network() ) ) {
295
  /**
296
  * Custom folders: in network admin only if network activated, in each site otherwise.
297
  */
298
- // Global chart.
299
- $data['total_attachments'] += Imagify_Files_Stats::count_all_files();
300
- $data['unoptimized_attachments'] += Imagify_Files_Stats::count_no_status_files();
301
- $data['optimized_attachments'] += Imagify_Files_Stats::count_optimized_files();
302
- $data['errors_attachments'] += Imagify_Files_Stats::count_error_files();
303
- // Stats block.
304
- $data['already_optimized_attachments'] += Imagify_Files_Stats::count_success_files();
305
- $data['original_size'] += Imagify_Files_Stats::get_original_size();
306
- $data['optimized_size'] += Imagify_Files_Stats::get_optimized_size();
307
-
308
- if ( ! Imagify_Folders_DB::get_instance()->has_items() ) {
309
- // New Feature!
310
- $data['no-custom-folders'] = true;
311
- } elseif ( Imagify_Folders_DB::get_instance()->has_active_folders() ) {
312
- // Group.
313
- $data['groups']['custom-folders'] = array(
314
- 'group_id' => 'custom-folders',
315
- 'context' => 'File',
316
- 'title' => __( 'Custom folders', 'imagify' ),
317
- /* translators: 1 is the opening of a link, 2 is the closing of this link. */
318
- 'footer' => sprintf( __( 'You can re-optimize your images more finely directly in the %1$simages management%2$s.', 'imagify' ), '<a href="' . esc_url( get_imagify_admin_url( 'files-list' ) ) . '">', '</a>' ),
319
- );
320
- }
321
  }
322
  }
323
 
324
  /**
325
- * Filter the data to use on the bulk optimization page.
326
  *
327
- * @since 1.7
328
  * @author Grégory Viguier
329
  *
330
- * @param array $data The data to use.
331
  */
332
- $data = apply_filters( 'imagify_bulk_page_data', $data );
 
333
 
334
- /**
335
- * Percentages.
336
- */
337
- if ( $data['total_attachments'] && $data['optimized_attachments'] ) {
338
- $data['optimized_attachments_percent'] = round( 100 * $data['optimized_attachments'] / $data['total_attachments'] );
339
- } else {
340
- $data['optimized_attachments_percent'] = 0;
 
 
 
 
 
 
 
 
341
  }
342
 
343
- if ( $data['original_size'] && $data['optimized_size'] ) {
344
- $data['optimized_percent'] = ceil( 100 - ( 100 * $data['optimized_size'] / $data['original_size'] ) );
345
- } else {
346
- $data['optimized_percent'] = 0;
 
 
 
 
 
 
 
 
 
 
347
  }
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  $this->print_template( 'page-bulk', $data );
350
  }
351
 
@@ -441,7 +433,7 @@ class Imagify_Views {
441
  $path = str_replace( '_', '-', $template );
442
  $path = IMAGIFY_PATH . 'views/' . $template . '.php';
443
 
444
- if ( ! imagify_get_filesystem()->exists( $path ) ) {
445
  return false;
446
  }
447
 
15
  * @var string
16
  * @since 1.7
17
  */
18
+ const VERSION = '1.0.1';
19
 
20
  /**
21
  * Slug used for the settings page URL.
53
  */
54
  protected $list_table;
55
 
56
+ /**
57
+ * Filesystem object.
58
+ *
59
+ * @var object Imagify_Filesystem
60
+ * @since 1.7.1
61
+ * @access protected
62
+ * @author Grégory Viguier
63
+ */
64
+ protected $filesystem;
65
+
66
  /**
67
  * The single instance of the class.
68
  *
88
  $this->slug_settings = IMAGIFY_SLUG;
89
  $this->slug_bulk = IMAGIFY_SLUG . '-bulk-optimization';
90
  $this->slug_files = IMAGIFY_SLUG . '-files';
91
+ $this->filesystem = Imagify_Filesystem::get_instance();
92
  }
93
 
94
  /**
251
  * @access public
252
  */
253
  public function display_bulk_page() {
254
+ $types = array();
255
+ $data = array(
 
 
 
 
 
 
 
 
 
256
  // Limits.
257
+ 'unoptimized_attachment_limit' => 0,
258
  // What to optimize.
259
+ 'icon' => 'images-alt2',
260
+ 'title' => __( 'Optimize your images', 'imagify' ),
261
+ 'groups' => array(),
262
  );
263
 
264
  if ( imagify_is_screen( 'bulk' ) ) {
266
  /**
267
  * Library: in each site.
268
  */
269
+ $types['library|wp'] = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  }
271
 
272
  if ( imagify_can_optimize_custom_folders() && ( imagify_is_active_for_network() && is_network_admin() || ! imagify_is_active_for_network() ) ) {
273
  /**
274
  * Custom folders: in network admin only if network activated, in each site otherwise.
275
  */
276
+ $types['custom-folders'] = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
  }
279
 
280
  /**
281
+ * Filter the types to display in the bulk optimization page.
282
  *
283
+ * @since 1.7.1
284
  * @author Grégory Viguier
285
  *
286
+ * @param array $types The folder types displayed on the page. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
287
  */
288
+ $types = apply_filters( 'imagify_bulk_page_types', $types );
289
+ $types = array_filter( (array) $types );
290
 
291
+ if ( isset( $types['library|wp'] ) ) {
292
+ // Limits.
293
+ $data['unoptimized_attachment_limit'] += imagify_get_unoptimized_attachment_limit();
294
+ // Group.
295
+ $data['groups']['library'] = array(
296
+ /**
297
+ * The group_id corresponds to the file names like 'part-bulk-optimization-results-row-{$group_id}'.
298
+ * It is also used in get_imagify_localize_script_translations() and imagify_get_folder_type_data().
299
+ */
300
+ 'group_id' => 'library',
301
+ 'context' => 'wp',
302
+ 'title' => __( 'Media Library', 'imagify' ),
303
+ /* translators: 1 is the opening of a link, 2 is the closing of this link. */
304
+ 'footer' => sprintf( __( 'You can also re-optimize your images from your %1$sMedia Library%2$s screen.', 'imagify' ), '<a href="' . esc_url( admin_url( 'upload.php' ) ) . '">', '</a>' ),
305
+ );
306
  }
307
 
308
+ if ( isset( $types['custom-folders'] ) ) {
309
+ if ( ! Imagify_Folders_DB::get_instance()->has_items() ) {
310
+ // New Feature!
311
+ $data['no-custom-folders'] = true;
312
+ } elseif ( Imagify_Folders_DB::get_instance()->has_active_folders() ) {
313
+ // Group.
314
+ $data['groups']['custom-folders'] = array(
315
+ 'group_id' => 'custom-folders',
316
+ 'context' => 'File',
317
+ 'title' => __( 'Custom folders', 'imagify' ),
318
+ /* translators: 1 is the opening of a link, 2 is the closing of this link. */
319
+ 'footer' => sprintf( __( 'You can re-optimize your images more finely directly in the %1$simages management%2$s.', 'imagify' ), '<a href="' . esc_url( get_imagify_admin_url( 'files-list' ) ) . '">', '</a>' ),
320
+ );
321
+ }
322
  }
323
 
324
+ // Add generic stats.
325
+ $data = array_merge( $data, imagify_get_bulk_stats( $types, array(
326
+ 'fullset' => true,
327
+ ) ) );
328
+
329
+ /**
330
+ * Filter the data to use on the bulk optimization page.
331
+ *
332
+ * @since 1.7
333
+ * @since 1.7.1 Added the $types parameter.
334
+ * @author Grégory Viguier
335
+ *
336
+ * @param array $data The data to use.
337
+ * @param array $types The folder types displayed on the page. They are passed as array keys.
338
+ */
339
+ $data = apply_filters( 'imagify_bulk_page_data', $data, $types );
340
+
341
  $this->print_template( 'page-bulk', $data );
342
  }
343
 
433
  $path = str_replace( '_', '-', $template );
434
  $path = IMAGIFY_PATH . 'views/' . $template . '.php';
435
 
436
+ if ( ! $this->filesystem->exists( $path ) ) {
437
  return false;
438
  }
439
 
inc/classes/class-imagify.php CHANGED
@@ -11,7 +11,7 @@ class Imagify extends Imagify_Deprecated {
11
  *
12
  * @var string
13
  */
14
- const VERSION = '1.1';
15
  /**
16
  * The Imagify API endpoint.
17
  *
@@ -40,6 +40,16 @@ class Imagify extends Imagify_Deprecated {
40
  */
41
  private $all_headers = array();
42
 
 
 
 
 
 
 
 
 
 
 
43
  /**
44
  * The single instance of the class.
45
  *
@@ -53,7 +63,8 @@ class Imagify extends Imagify_Deprecated {
53
  * The constructor.
54
  */
55
  protected function __construct() {
56
- $this->api_key = get_imagify_option( 'api_key' );
 
57
 
58
  $this->all_headers['Accept'] = 'Accept: application/json';
59
  $this->all_headers['Content-Type'] = 'Content-Type: application/json';
@@ -408,8 +419,8 @@ class Imagify extends Imagify_Deprecated {
408
  * @return object
409
  */
410
  private function curl_http_call( $url, $args = array() ) {
411
- // Check if php-curl is enabled.
412
- if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) {
413
  return new WP_Error( 'curl', 'cURL isn\'t installed on the server.' );
414
  }
415
 
@@ -418,10 +429,24 @@ class Imagify extends Imagify_Deprecated {
418
  try {
419
  $ch = curl_init();
420
 
421
- if ( isset( $args['post_data']['image'] ) && is_string( $args['post_data']['image'] ) && file_exists( $args['post_data']['image'] ) ) {
422
  $args['post_data']['image'] = curl_file_create( $args['post_data']['image'] );
423
  }
424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  if ( 'POST' === $args['method'] ) {
426
  curl_setopt( $ch, CURLOPT_POST, true );
427
  curl_setopt( $ch, CURLOPT_POSTFIELDS, $args['post_data'] );
11
  *
12
  * @var string
13
  */
14
+ const VERSION = '1.1.1';
15
  /**
16
  * The Imagify API endpoint.
17
  *
40
  */
41
  private $all_headers = array();
42
 
43
+ /**
44
+ * Filesystem object.
45
+ *
46
+ * @var object Imagify_Filesystem
47
+ * @since 1.7.1
48
+ * @access protected
49
+ * @author Grégory Viguier
50
+ */
51
+ protected $filesystem;
52
+
53
  /**
54
  * The single instance of the class.
55
  *
63
  * The constructor.
64
  */
65
  protected function __construct() {
66
+ $this->api_key = get_imagify_option( 'api_key' );
67
+ $this->filesystem = Imagify_Filesystem::get_instance();
68
 
69
  $this->all_headers['Accept'] = 'Accept: application/json';
70
  $this->all_headers['Content-Type'] = 'Content-Type: application/json';
419
  * @return object
420
  */
421
  private function curl_http_call( $url, $args = array() ) {
422
+ // Check if curl is available.
423
+ if ( ! Imagify_Requirements::supports_curl() ) {
424
  return new WP_Error( 'curl', 'cURL isn\'t installed on the server.' );
425
  }
426
 
429
  try {
430
  $ch = curl_init();
431
 
432
+ if ( isset( $args['post_data']['image'] ) && is_string( $args['post_data']['image'] ) && $this->filesystem->exists( $args['post_data']['image'] ) ) {
433
  $args['post_data']['image'] = curl_file_create( $args['post_data']['image'] );
434
  }
435
 
436
+ // Handle proxies.
437
+ $proxy = new WP_HTTP_Proxy();
438
+
439
+ if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
440
+ curl_setopt( $ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
441
+ curl_setopt( $ch, CURLOPT_PROXY, $proxy->host() );
442
+ curl_setopt( $ch, CURLOPT_PROXYPORT, $proxy->port() );
443
+
444
+ if ( $proxy->use_authentication() ) {
445
+ curl_setopt( $ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
446
+ curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
447
+ }
448
+ }
449
+
450
  if ( 'POST' === $args['method'] ) {
451
  curl_setopt( $ch, CURLOPT_POST, true );
452
  curl_setopt( $ch, CURLOPT_POSTFIELDS, $args['post_data'] );
inc/common/attachments.php CHANGED
@@ -15,7 +15,7 @@ add_filter( 'wp_generate_attachment_metadata', '_imagify_optimize_attachment', I
15
  */
16
  function _imagify_optimize_attachment( $metadata, $attachment_id ) {
17
 
18
- if ( ! imagify_valid_key() || ! get_imagify_option( 'auto_optimize' ) ) {
19
  return $metadata;
20
  }
21
 
@@ -70,7 +70,7 @@ function _imagify_optimize_save_image_editor_file() {
70
 
71
  $attachment_id = absint( $_POST['postid'] );
72
 
73
- if ( ! $attachment_id || ! imagify_valid_key() ) {
74
  return;
75
  }
76
 
15
  */
16
  function _imagify_optimize_attachment( $metadata, $attachment_id ) {
17
 
18
+ if ( ! Imagify_Requirements::is_api_key_valid() || ! get_imagify_option( 'auto_optimize' ) ) {
19
  return $metadata;
20
  }
21
 
70
 
71
  $attachment_id = absint( $_POST['postid'] );
72
 
73
+ if ( ! $attachment_id || ! Imagify_Requirements::is_api_key_valid() ) {
74
  return;
75
  }
76
 
inc/functions/admin-stats.php CHANGED
@@ -560,6 +560,7 @@ function imagify_calculate_total_image_size( $image_ids, $partial_total_images,
560
  // Total number of thumbnails.
561
  $partial_total_intermediate_images = 0;
562
 
 
563
  $is_active_for_network = imagify_is_active_for_network();
564
  $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
565
 
@@ -603,7 +604,7 @@ function imagify_calculate_total_image_size( $image_ids, $partial_total_images,
603
  }
604
 
605
  if ( $sizes ) {
606
- $full_dirname = dirname( $files['full'] );
607
 
608
  foreach ( $sizes as $size_key => $size_data ) {
609
  $files[ $size_key ] = $full_dirname . '/' . $size_data['file'];
@@ -630,8 +631,8 @@ function imagify_calculate_total_image_size( $image_ids, $partial_total_images,
630
  $partial_total_intermediate_images += $size_and_count['thumbnails'];
631
  } else {
632
  foreach ( $files as $file ) {
633
- if ( file_exists( $file ) ) {
634
- $partial_size_images += filesize( $file );
635
  }
636
  }
637
 
@@ -665,3 +666,122 @@ function imagify_calculate_total_image_size( $image_ids, $partial_total_images,
665
 
666
  return $total_size_images;
667
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
  // Total number of thumbnails.
561
  $partial_total_intermediate_images = 0;
562
 
563
+ $filesystem = imagify_get_filesystem();
564
  $is_active_for_network = imagify_is_active_for_network();
565
  $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
566
 
604
  }
605
 
606
  if ( $sizes ) {
607
+ $full_dirname = $filesystem->dir_path( $files['full'] );
608
 
609
  foreach ( $sizes as $size_key => $size_data ) {
610
  $files[ $size_key ] = $full_dirname . '/' . $size_data['file'];
631
  $partial_total_intermediate_images += $size_and_count['thumbnails'];
632
  } else {
633
  foreach ( $files as $file ) {
634
+ if ( $filesystem->exists( $file ) ) {
635
+ $partial_size_images += $filesystem->size( $file );
636
  }
637
  }
638
 
666
 
667
  return $total_size_images;
668
  }
669
+
670
+ /**
671
+ * Get all generic stats to be used in the bulk optimization page.
672
+ *
673
+ * @since 1.7.1
674
+ * @author Grégory Viguier
675
+ *
676
+ * @param array $types The folder types. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
677
+ * @param array $args {
678
+ * Optional. An array of arguments.
679
+ *
680
+ * @type bool $fullset True to return the full set of data. False to return only the main data.
681
+ * @type bool $formatting Some of the data is returned formatted.
682
+ * }
683
+ * @return array
684
+ */
685
+ function imagify_get_bulk_stats( $types, $args = array() ) {
686
+ $types = $types && is_array( $types ) ? $types : array();
687
+ $args = array_merge( array(
688
+ 'fullset' => false,
689
+ 'formatting' => true,
690
+ ), (array) $args );
691
+
692
+ $data = array(
693
+ // Global chart.
694
+ 'total_attachments' => 0,
695
+ 'unoptimized_attachments' => 0,
696
+ 'optimized_attachments' => 0,
697
+ 'errors_attachments' => 0,
698
+ // Stats block.
699
+ 'already_optimized_attachments' => 0,
700
+ 'original_human' => 0,
701
+ 'optimized_human' => 0,
702
+ );
703
+
704
+ if ( isset( $types['library|wp'] ) ) {
705
+ /**
706
+ * Library.
707
+ */
708
+ $saving_data = imagify_count_saving_data();
709
+
710
+ // Global chart.
711
+ $data['total_attachments'] += imagify_count_attachments();
712
+ $data['unoptimized_attachments'] += imagify_count_unoptimized_attachments();
713
+ $data['optimized_attachments'] += imagify_count_optimized_attachments();
714
+ $data['errors_attachments'] += imagify_count_error_attachments();
715
+ // Stats block.
716
+ $data['already_optimized_attachments'] += $saving_data['count'];
717
+ $data['original_human'] += $saving_data['original_size'];
718
+ $data['optimized_human'] += $saving_data['optimized_size'];
719
+ }
720
+
721
+ if ( isset( $types['custom-folders'] ) ) {
722
+ /**
723
+ * Custom folders.
724
+ */
725
+ // Global chart.
726
+ $data['total_attachments'] += Imagify_Files_Stats::count_all_files();
727
+ $data['unoptimized_attachments'] += Imagify_Files_Stats::count_no_status_files();
728
+ $data['optimized_attachments'] += Imagify_Files_Stats::count_optimized_files();
729
+ $data['errors_attachments'] += Imagify_Files_Stats::count_error_files();
730
+ // Stats block.
731
+ $data['already_optimized_attachments'] += Imagify_Files_Stats::count_success_files();
732
+ $data['original_human'] += Imagify_Files_Stats::get_original_size();
733
+ $data['optimized_human'] += Imagify_Files_Stats::get_optimized_size();
734
+ }
735
+
736
+ /**
737
+ * Full set of data.
738
+ */
739
+ if ( $args['fullset'] ) {
740
+ // User account.
741
+ $user = new Imagify_User();
742
+ $data['unconsumed_quota'] = is_wp_error( $user ) ? 0 : $user->get_percent_unconsumed_quota();
743
+ }
744
+
745
+ /**
746
+ * Filter the generic stats used in the bulk optimization page.
747
+ *
748
+ * @since 1.7.1
749
+ * @author Grégory Viguier
750
+ *
751
+ * @param array $data The data.
752
+ * @param array $types The folder types. They are passed as array keys.
753
+ * @param array $args {
754
+ * Optional. An array of arguments.
755
+ *
756
+ * @type bool $fullset True to return the full set of data. False to return only the main data.
757
+ * @type bool $formatting Some of the data is returned formatted.
758
+ * }
759
+ */
760
+ $data = apply_filters( 'imagify_bulk_stats', $data, $types, $args );
761
+
762
+ /**
763
+ * Percentages.
764
+ */
765
+ if ( $data['total_attachments'] && $data['optimized_attachments'] ) {
766
+ $data['optimized_attachments_percent'] = round( 100 * $data['optimized_attachments'] / $data['total_attachments'] );
767
+ } else {
768
+ $data['optimized_attachments_percent'] = 0;
769
+ }
770
+
771
+ if ( $data['original_human'] && $data['optimized_human'] ) {
772
+ $data['optimized_percent'] = ceil( 100 - ( 100 * $data['optimized_human'] / $data['original_human'] ) );
773
+ } else {
774
+ $data['optimized_percent'] = 0;
775
+ }
776
+
777
+ /**
778
+ * Formating.
779
+ */
780
+ if ( $args['formatting'] ) {
781
+ $data['already_optimized_attachments'] = number_format_i18n( $data['already_optimized_attachments'] );
782
+ $data['original_human'] = imagify_size_format( $data['original_human'], 1 );
783
+ $data['optimized_human'] = imagify_size_format( $data['optimized_human'], 1 );
784
+ }
785
+
786
+ return $data;
787
+ }
inc/functions/admin-ui.php CHANGED
@@ -171,7 +171,7 @@ function get_imagify_attachment_reoptimize_link( $attachment, $context = 'wp' )
171
  global $pagenow, $typenow;
172
 
173
  // Stop the process if the API key isn't valid.
174
- if ( ! imagify_valid_key() ) {
175
  return '';
176
  }
177
 
@@ -247,7 +247,7 @@ function get_imagify_attachment_optimize_missing_thumbnails_link( $attachment, $
247
  $display = apply_filters( 'imagify_display_missing_thumbnails_link', true, $attachment, $context );
248
 
249
  // Stop the process if the filter is false, or if the API key isn't valid, or if there is no backup file.
250
- if ( ! $display || ! imagify_valid_key() || ! $attachment->has_backup() ) {
251
  return '';
252
  }
253
 
@@ -303,17 +303,14 @@ function get_imagify_media_column_content( $attachment, $context = 'wp' ) {
303
  }
304
 
305
  // Check if the API key is valid.
306
- if ( ! imagify_valid_key() && ! $attachment->is_optimized() ) {
307
  $output = __( 'Invalid API key', 'imagify' );
308
  $output .= '<br/>';
309
  $output .= '<a href="' . esc_url( get_imagify_admin_url() ) . '">' . __( 'Check your Settings', 'imagify' ) . '</a>';
310
  return $output;
311
  }
312
 
313
- $transient_context = ( 'wp' !== $context ) ? strtolower( $context ) . '-' : '';
314
- $transient_name = 'imagify-' . $transient_context . 'async-in-progress-' . $attachment->id;
315
-
316
- if ( false !== get_transient( $transient_name ) ) {
317
  return '<div class="button"><span class="imagify-spinner"></span>' . __( 'Optimizing...', 'imagify' ) . '</div>';
318
  }
319
 
171
  global $pagenow, $typenow;
172
 
173
  // Stop the process if the API key isn't valid.
174
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
175
  return '';
176
  }
177
 
247
  $display = apply_filters( 'imagify_display_missing_thumbnails_link', true, $attachment, $context );
248
 
249
  // Stop the process if the filter is false, or if the API key isn't valid, or if there is no backup file.
250
+ if ( ! $display || ! Imagify_Requirements::is_api_key_valid() || ! $attachment->has_backup() ) {
251
  return '';
252
  }
253
 
303
  }
304
 
305
  // Check if the API key is valid.
306
+ if ( ! Imagify_Requirements::is_api_key_valid() && ! $attachment->is_optimized() ) {
307
  $output = __( 'Invalid API key', 'imagify' );
308
  $output .= '<br/>';
309
  $output .= '<a href="' . esc_url( get_imagify_admin_url() ) . '">' . __( 'Check your Settings', 'imagify' ) . '</a>';
310
  return $output;
311
  }
312
 
313
+ if ( $attachment->is_running() ) {
 
 
 
314
  return '<div class="button"><span class="imagify-spinner"></span>' . __( 'Optimizing...', 'imagify' ) . '</div>';
315
  }
316
 
inc/functions/admin.php CHANGED
@@ -430,7 +430,7 @@ function imagify_maybe_redirect( $message = false, $args_or_url = array() ) {
430
  * @return object|bool An object on success. False otherwise.
431
  */
432
  function imagify_get_cached_user() {
433
- if ( ! imagify_valid_key() ) {
434
  return false;
435
  }
436
 
@@ -453,7 +453,7 @@ function imagify_get_cached_user() {
453
  * @return object|bool An object on success. False otherwise.
454
  */
455
  function imagify_cache_user() {
456
- if ( ! imagify_valid_key() ) {
457
  return false;
458
  }
459
 
430
  * @return object|bool An object on success. False otherwise.
431
  */
432
  function imagify_get_cached_user() {
433
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
434
  return false;
435
  }
436
 
453
  * @return object|bool An object on success. False otherwise.
454
  */
455
  function imagify_cache_user() {
456
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
457
  return false;
458
  }
459
 
inc/functions/api.php CHANGED
@@ -167,63 +167,6 @@ function get_imagify_max_image_size() {
167
  return $max_image_size;
168
  }
169
 
170
- /**
171
- * Check if external requests are blocked for Imagify.
172
- *
173
- * @since 1.0
174
- *
175
- * return bool True if Imagify API can't be called.
176
- */
177
- function is_imagify_blocked() {
178
- if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL ) {
179
- return false;
180
- }
181
-
182
- if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
183
- return true;
184
- }
185
-
186
- $accessible_hosts = explode( ',', WP_ACCESSIBLE_HOSTS );
187
- $accessible_hosts = array_map( 'trim', $accessible_hosts );
188
-
189
- return ! in_array( '*.imagify.io', $accessible_hosts, true );
190
- }
191
-
192
- /**
193
- * Determine if the Imagify API is available by checking the API version.
194
- *
195
- * @since 1.0
196
- *
197
- * @return bool True if the Imagify API is available.
198
- */
199
- function is_imagify_servers_up() {
200
- static $is_up;
201
-
202
- if ( isset( $is_up ) ) {
203
- return $is_up;
204
- }
205
-
206
- $transient_name = 'imagify_check_api_version';
207
- $transient_expiration = 3 * MINUTE_IN_SECONDS;
208
-
209
- if ( get_site_transient( $transient_name ) ) {
210
- $is_up = true;
211
- return $is_up;
212
- }
213
-
214
- if ( is_wp_error( get_imagify_api_version() ) ) {
215
- set_site_transient( $transient_name, 0, $transient_expiration );
216
-
217
- $is_up = false;
218
- return $is_up;
219
- }
220
-
221
- set_site_transient( $transient_name, 1, $transient_expiration );
222
-
223
- $is_up = true;
224
- return $is_up;
225
- }
226
-
227
  /**
228
  * Translate a message from our servers.
229
  *
@@ -266,7 +209,7 @@ function imagify_translate_api_message( $message ) {
266
  // Local messages from Imagify::curl_http_call() and Imagify::handle_response().
267
  'Unknown error occurred' => __( 'Unknown error occurred.', 'imagify' ),
268
  'Your image is too big to be uploaded on our server' => __( 'Your image is too big to be uploaded on our server.', 'imagify' ),
269
- 'cURL isn\'t installed on the server' => __( 'cURL is not installed on the server.', 'imagify' ),
270
  // API messages.
271
  'Authentification not provided' => __( 'Authentication not provided.', 'imagify' ),
272
  'Cannot create client token' => __( 'Cannot create client token.', 'imagify' ),
167
  return $max_image_size;
168
  }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  /**
171
  * Translate a message from our servers.
172
  *
209
  // Local messages from Imagify::curl_http_call() and Imagify::handle_response().
210
  'Unknown error occurred' => __( 'Unknown error occurred.', 'imagify' ),
211
  'Your image is too big to be uploaded on our server' => __( 'Your image is too big to be uploaded on our server.', 'imagify' ),
212
+ 'cURL isn\'t installed on the server' => __( 'cURL is not available on the server.', 'imagify' ),
213
  // API messages.
214
  'Authentification not provided' => __( 'Authentication not provided.', 'imagify' ),
215
  'Cannot create client token' => __( 'Cannot create client token.', 'imagify' ),
inc/functions/attachments.php CHANGED
@@ -172,7 +172,7 @@ function get_imagify_backup_dir_path( $bypass_error = false ) {
172
  * @param string $backup_dir The backup directory path.
173
  */
174
  $backup_dir = apply_filters( 'imagify_backup_directory', $backup_dir );
175
- $backup_dir = trailingslashit( wp_normalize_path( $backup_dir ) );
176
 
177
  return $backup_dir;
178
  }
@@ -186,14 +186,7 @@ function get_imagify_backup_dir_path( $bypass_error = false ) {
186
  * @return bool
187
  */
188
  function imagify_backup_dir_is_writable() {
189
- if ( ! get_imagify_backup_dir_path() ) {
190
- return false;
191
- }
192
-
193
- $filesystem = imagify_get_filesystem();
194
- $has_backup_dir = wp_mkdir_p( get_imagify_backup_dir_path() );
195
-
196
- return $has_backup_dir && $filesystem->is_writable( get_imagify_backup_dir_path() );
197
  }
198
 
199
  /**
@@ -267,7 +260,7 @@ function get_imagify_attachment_url( $file_path ) {
267
 
268
  if ( false !== strpos( '/' . $file_path, '/wp-content/uploads/' ) ) {
269
  // Get the directory name relative to the basedir (back compat for pre-2.7 uploads).
270
- return trailingslashit( $upload_baseurl . _wp_get_attachment_relative_path( $file_path ) ) . basename( $file_path );
271
  }
272
 
273
  // It's a newly-uploaded file, therefore $file is relative to the basedir.
@@ -339,23 +332,7 @@ function get_imagify_thumbnail_sizes() {
339
  * @return string|bool The path. False on failure.
340
  */
341
  function get_imagify_upload_basedir( $bypass_error = false ) {
342
- static $upload_basedir;
343
- static $upload_basedir_or_error;
344
-
345
- if ( isset( $upload_basedir ) ) {
346
- return $bypass_error ? $upload_basedir : $upload_basedir_or_error;
347
- }
348
-
349
- $uploads = wp_upload_dir();
350
- $upload_basedir = trailingslashit( wp_normalize_path( $uploads['basedir'] ) );
351
-
352
- if ( false !== $uploads['error'] ) {
353
- $upload_basedir_or_error = false;
354
- } else {
355
- $upload_basedir_or_error = $upload_basedir;
356
- }
357
-
358
- return $bypass_error ? $upload_basedir : $upload_basedir_or_error;
359
  }
360
 
361
  /**
@@ -364,25 +341,10 @@ function get_imagify_upload_basedir( $bypass_error = false ) {
364
  * @since 1.6.7
365
  * @author Grégory Viguier
366
  *
367
- * @return string|bool The path. False on failure.
368
  */
369
  function get_imagify_upload_baseurl() {
370
- static $upload_baseurl;
371
-
372
- if ( isset( $upload_baseurl ) ) {
373
- return $upload_baseurl;
374
- }
375
-
376
- $uploads = wp_upload_dir();
377
-
378
- if ( false !== $uploads['error'] ) {
379
- $upload_baseurl = false;
380
- return $upload_baseurl;
381
- }
382
-
383
- $upload_baseurl = trailingslashit( $uploads['baseurl'] );
384
-
385
- return $upload_baseurl;
386
  }
387
 
388
  /**
172
  * @param string $backup_dir The backup directory path.
173
  */
174
  $backup_dir = apply_filters( 'imagify_backup_directory', $backup_dir );
175
+ $backup_dir = imagify_get_filesystem()->normalize_dir_path( $backup_dir );
176
 
177
  return $backup_dir;
178
  }
186
  * @return bool
187
  */
188
  function imagify_backup_dir_is_writable() {
189
+ return imagify_get_filesystem()->make_dir( get_imagify_backup_dir_path() );
 
 
 
 
 
 
 
190
  }
191
 
192
  /**
260
 
261
  if ( false !== strpos( '/' . $file_path, '/wp-content/uploads/' ) ) {
262
  // Get the directory name relative to the basedir (back compat for pre-2.7 uploads).
263
+ return trailingslashit( $upload_baseurl . _wp_get_attachment_relative_path( $file_path ) ) . imagify_get_filesystem()->file_name( $file_path );
264
  }
265
 
266
  // It's a newly-uploaded file, therefore $file is relative to the basedir.
332
  * @return string|bool The path. False on failure.
333
  */
334
  function get_imagify_upload_basedir( $bypass_error = false ) {
335
+ return imagify_get_filesystem()->get_upload_basedir( $bypass_error );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  }
337
 
338
  /**
341
  * @since 1.6.7
342
  * @author Grégory Viguier
343
  *
344
+ * @return string|bool The URL. False on failure.
345
  */
346
  function get_imagify_upload_baseurl() {
347
+ return imagify_get_filesystem()->get_upload_baseurl();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
 
350
  /**
inc/functions/common.php CHANGED
@@ -115,6 +115,18 @@ function imagify_current_user_can( $describer = 'manage', $post_id = null ) {
115
  return apply_filters( 'imagify_current_user_can', $user_can, $capacity, $describer, $post_id );
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  /**
119
  * Tell if the current user can optimize custom folders.
120
  *
@@ -191,9 +203,11 @@ function imagify_autoload( $class ) {
191
  'Imagify_Files_Recursive_Iterator' => 1,
192
  'Imagify_Files_Scan' => 1,
193
  'Imagify_Files_Stats' => 1,
 
194
  'Imagify_Folders_DB' => 1,
195
  'Imagify_Notices' => 1,
196
  'Imagify_Options' => 1,
 
197
  'Imagify_Settings' => 1,
198
  'Imagify_User' => 1,
199
  'Imagify_Views' => 1,
@@ -208,14 +222,15 @@ function imagify_autoload( $class ) {
208
 
209
  // Third party classes.
210
  $classes = array(
211
- 'Imagify_AS3CF_Attachment' => 'amazon-s3-and-cloudfront',
212
- 'Imagify_AS3CF' => 'amazon-s3-and-cloudfront',
213
- 'Imagify_Enable_Media_Replace' => 'enable-media-replace',
214
- 'Imagify_Formidable_Pro' => 'formidable-pro',
215
- 'Imagify_NGG_Attachment' => 'nextgen-gallery',
216
- 'Imagify_NGG_DB' => 'nextgen-gallery',
217
- 'Imagify_NGG_Storage' => 'nextgen-gallery',
218
- 'Imagify_NGG' => 'nextgen-gallery',
 
219
  );
220
 
221
  if ( isset( $classes[ $class ] ) ) {
115
  return apply_filters( 'imagify_current_user_can', $user_can, $capacity, $describer, $post_id );
116
  }
117
 
118
+ /**
119
+ * Get WP Direct filesystem object. Also define chmod constants if not done yet.
120
+ *
121
+ * @since 1.6.5
122
+ * @author Grégory Viguier
123
+ *
124
+ * @return object A Imagify_Filesystem object.
125
+ */
126
+ function imagify_get_filesystem() {
127
+ return Imagify_Filesystem::get_instance();
128
+ }
129
+
130
  /**
131
  * Tell if the current user can optimize custom folders.
132
  *
203
  'Imagify_Files_Recursive_Iterator' => 1,
204
  'Imagify_Files_Scan' => 1,
205
  'Imagify_Files_Stats' => 1,
206
+ 'Imagify_Filesystem' => 1,
207
  'Imagify_Folders_DB' => 1,
208
  'Imagify_Notices' => 1,
209
  'Imagify_Options' => 1,
210
+ 'Imagify_Requirements' => 1,
211
  'Imagify_Settings' => 1,
212
  'Imagify_User' => 1,
213
  'Imagify_Views' => 1,
222
 
223
  // Third party classes.
224
  $classes = array(
225
+ 'Imagify_AS3CF_Attachment' => 'amazon-s3-and-cloudfront',
226
+ 'Imagify_AS3CF' => 'amazon-s3-and-cloudfront',
227
+ 'Imagify_Enable_Media_Replace' => 'enable-media-replace',
228
+ 'Imagify_Formidable_Pro' => 'formidable-pro',
229
+ 'Imagify_NGG_Attachment' => 'nextgen-gallery',
230
+ 'Imagify_NGG_DB' => 'nextgen-gallery',
231
+ 'Imagify_NGG_Storage' => 'nextgen-gallery',
232
+ 'Imagify_NGG' => 'nextgen-gallery',
233
+ 'Imagify_Regenerate_Thumbnails' => 'regenerate-thumbnails',
234
  );
235
 
236
  if ( isset( $classes[ $class ] ) ) {
inc/functions/compat.php CHANGED
@@ -313,17 +313,31 @@ if ( ! function_exists( 'wp_normalize_path' ) ) :
313
  * @since WP 3.9.0
314
  * @since WP 4.4.0 Ensures upper-case drive letters on Windows systems.
315
  * @since WP 4.5.0 Allows for Windows network shares.
 
316
  *
317
  * @param string $path Path to normalize.
318
  * @return string Normalized path.
319
  */
320
  function wp_normalize_path( $path ) {
 
 
 
 
 
 
 
 
321
  $path = str_replace( '\\', '/', $path );
 
 
322
  $path = preg_replace( '|(?<=.)/+|', '/', $path );
 
 
323
  if ( ':' === substr( $path, 1, 1 ) ) {
324
  $path = ucfirst( $path );
325
  }
326
- return $path;
 
327
  }
328
  endif;
329
 
313
  * @since WP 3.9.0
314
  * @since WP 4.4.0 Ensures upper-case drive letters on Windows systems.
315
  * @since WP 4.5.0 Allows for Windows network shares.
316
+ * @since WP 5.0 Allows for PHP file wrappers.
317
  *
318
  * @param string $path Path to normalize.
319
  * @return string Normalized path.
320
  */
321
  function wp_normalize_path( $path ) {
322
+ $wrapper = '';
323
+
324
+ if ( wp_is_stream( $path ) ) {
325
+ list( $wrapper, $path ) = explode( '://', $path, 2 );
326
+ $wrapper .= '://';
327
+ }
328
+
329
+ // Standardise all paths to use /.
330
  $path = str_replace( '\\', '/', $path );
331
+
332
+ // Replace multiple slashes down to a singular, allowing for network shares having two slashes.
333
  $path = preg_replace( '|(?<=.)/+|', '/', $path );
334
+
335
+ // Windows paths should uppercase the drive letter.
336
  if ( ':' === substr( $path, 1, 1 ) ) {
337
  $path = ucfirst( $path );
338
  }
339
+
340
+ return $wrapper . $path;
341
  }
342
  endif;
343
 
inc/functions/deprecated.php CHANGED
@@ -454,9 +454,9 @@ function _imagify_admin_bar_styles() {
454
  * @return string A relative path. Can return the absolute path in case of a failure.
455
  */
456
  function imagify_make_file_path_replative( $file_path ) {
457
- _deprecated_function( __FUNCTION__ . '()', '1.6.10', 'imagify_make_file_path_relative( $file_path )' );
458
 
459
- return imagify_make_file_path_relative( $file_path );
460
  }
461
 
462
  if ( is_admin() && ( function_exists( 'as3cf_init' ) || function_exists( 'as3cf_pro_init' ) ) ) :
@@ -586,6 +586,46 @@ if ( class_exists( 'C_NextGEN_Bootstrap' ) && class_exists( 'Mixin' ) && get_sit
586
  add_filter( 'imagify_count_saving_data' , 'imagify_ngg_count_saving_data', 8 );
587
  }
588
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
589
  endif;
590
 
591
  /**
@@ -726,6 +766,156 @@ function _do_imagify_update_library_size_calculations() {
726
  Imagify_Cron_Library_Size::get_instance()->do_event();
727
  }
728
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729
  if ( is_admin() ) :
730
 
731
  /**
454
  * @return string A relative path. Can return the absolute path in case of a failure.
455
  */
456
  function imagify_make_file_path_replative( $file_path ) {
457
+ _deprecated_function( __FUNCTION__ . '()', '1.6.10', 'imagify_get_filesystem()->make_path_relative( $file_path )' );
458
 
459
+ return imagify_get_filesystem()->make_path_relative( $file_path );
460
  }
461
 
462
  if ( is_admin() && ( function_exists( 'as3cf_init' ) || function_exists( 'as3cf_pro_init' ) ) ) :
586
  add_filter( 'imagify_count_saving_data' , 'imagify_ngg_count_saving_data', 8 );
587
  }
588
 
589
+ /**
590
+ * Prepare the data that goes back with the Heartbeat API.
591
+ *
592
+ * @since 1.5
593
+ * @since 1.7.1 Deprecated.
594
+ * @deprecated
595
+ *
596
+ * @param array $response The Heartbeat response.
597
+ * @param array $data The $_POST data sent.
598
+ * @return array
599
+ */
600
+ function _imagify_ngg_heartbeat_received( $response, $data ) {
601
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1' );
602
+
603
+ if ( ! isset( $data['imagify_heartbeat'] ) || 'update_ngg_bulk_data' !== $data['imagify_heartbeat'] ) {
604
+ return $response;
605
+ }
606
+
607
+ add_filter( 'imagify_count_saving_data', 'imagify_ngg_count_saving_data', 8 );
608
+ $saving_data = imagify_count_saving_data();
609
+ $user = new Imagify_User();
610
+
611
+ $response['imagify_bulk_data'] = array(
612
+ // User account.
613
+ 'unconsumed_quota' => is_wp_error( $user ) ? 0 : $user->get_percent_unconsumed_quota(),
614
+ // Global chart.
615
+ 'optimized_attachments_percent' => imagify_ngg_percent_optimized_attachments(),
616
+ 'unoptimized_attachments' => imagify_ngg_count_unoptimized_attachments(),
617
+ 'optimized_attachments' => imagify_ngg_count_optimized_attachments(),
618
+ 'errors_attachments' => imagify_ngg_count_error_attachments(),
619
+ // Stats block.
620
+ 'already_optimized_attachments' => number_format_i18n( $saving_data['count'] ),
621
+ 'original_human' => imagify_size_format( $saving_data['original_size'], 1 ),
622
+ 'optimized_human' => imagify_size_format( $saving_data['optimized_size'], 1 ),
623
+ 'optimized_percent' => $saving_data['percent'],
624
+ );
625
+
626
+ return $response;
627
+ }
628
+
629
  endif;
630
 
631
  /**
766
  Imagify_Cron_Library_Size::get_instance()->do_event();
767
  }
768
 
769
+ /**
770
+ * Set a file permissions using FS_CHMOD_FILE.
771
+ *
772
+ * @since 1.2
773
+ * @since 1.6.5 Use WP Filesystem.
774
+ * @since 1.7.1 Deprecated.
775
+ * @deprecated
776
+ *
777
+ * @param string $file_path Path to the file.
778
+ * @return bool True on success, false on failure.
779
+ */
780
+ function imagify_chmod_file( $file_path ) {
781
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'imagify_get_filesystem()->chmod_file( $file_path )' );
782
+
783
+ return imagify_get_filesystem()->chmod_file( $file_path );
784
+ }
785
+
786
+ /**
787
+ * Get a file mime type.
788
+ *
789
+ * @since 1.6.9
790
+ * @since 1.7 Doesn't use exif_imagetype() nor getimagesize() anymore.
791
+ * @since 1.7.1 Deprecated.
792
+ * @author Grégory Viguier
793
+ * @deprecated
794
+ *
795
+ * @param string $file_path A file path (prefered) or a filename.
796
+ * @return string|bool A mime type. False on failure: the test is limited to mime types supported by Imagify.
797
+ */
798
+ function imagify_get_mime_type_from_file( $file_path ) {
799
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'imagify_get_filesystem()->get_mime_type( $file_path )' );
800
+
801
+ return imagify_get_filesystem()->get_mime_type( $file_path );
802
+ }
803
+
804
+ /**
805
+ * Get a file modification date, formated as "mysql". Fallback to current date.
806
+ *
807
+ * @since 1.7
808
+ * @since 1.7.1 Deprecated.
809
+ * @author Grégory Viguier
810
+ * @deprecated
811
+ *
812
+ * @param string $file_path The file path.
813
+ * @return string The date.
814
+ */
815
+ function imagify_get_file_date( $file_path ) {
816
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'imagify_get_filesystem()->get_date( $file_path )' );
817
+
818
+ return imagify_get_filesystem()->get_date( $file_path );
819
+ }
820
+
821
+ /**
822
+ * Get a clean value of ABSPATH that can be used in string replacements.
823
+ *
824
+ * @since 1.6.8
825
+ * @since 1.7.1 Deprecated.
826
+ * @author Grégory Viguier
827
+ * @deprecated
828
+ *
829
+ * @return string The path to WordPress' root folder.
830
+ */
831
+ function imagify_get_abspath() {
832
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'imagify_get_filesystem()->get_abspath()' );
833
+
834
+ return imagify_get_filesystem()->get_abspath();
835
+ }
836
+
837
+ /**
838
+ * Make an absolute path relative to WordPress' root folder.
839
+ * Also works for files from registered symlinked plugins.
840
+ *
841
+ * @since 1.6.10
842
+ * @since 1.7 The parameter $base is added.
843
+ * @since 1.7.1 Deprecated.
844
+ * @author Grégory Viguier
845
+ * @deprecated
846
+ *
847
+ * @param string $file_path An absolute path.
848
+ * @param string $base A base path to use instead of ABSPATH.
849
+ * @return string|bool A relative path. Can return the absolute path or false in case of a failure.
850
+ */
851
+ function imagify_make_file_path_relative( $file_path, $base = '' ) {
852
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'imagify_get_filesystem()->make_path_relative( $file_path, $base )' );
853
+
854
+ return imagify_get_filesystem()->make_path_relative( $file_path, $base );
855
+ }
856
+
857
+ /**
858
+ * Tell if a file is symlinked.
859
+ *
860
+ * @since 1.7
861
+ * @since 1.7.1 Deprecated.
862
+ * @author Grégory Viguier
863
+ * @deprecated
864
+ *
865
+ * @param string $file_path An absolute path.
866
+ * @return bool
867
+ */
868
+ function imagify_file_is_symlinked( $file_path ) {
869
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'imagify_get_filesystem()->is_symlinked( $file_path )' );
870
+
871
+ return imagify_get_filesystem()->is_symlinked( $file_path );
872
+ }
873
+
874
+ /**
875
+ * Determine if the Imagify API key is valid.
876
+ *
877
+ * @since 1.0
878
+ * @since 1.7.1 Deprecated.
879
+ * @deprecated
880
+ *
881
+ * @return bool True if the API key is valid.
882
+ */
883
+ function imagify_valid_key() {
884
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'Imagify_Requirements::is_api_key_valid()' );
885
+
886
+ return Imagify_Requirements::is_api_key_valid();
887
+ }
888
+
889
+ /**
890
+ * Check if external requests are blocked for Imagify.
891
+ *
892
+ * @since 1.0
893
+ * @since 1.7.1 Deprecated.
894
+ * @deprecated
895
+ *
896
+ * @return bool True if Imagify API can't be called.
897
+ */
898
+ function is_imagify_blocked() {
899
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'Imagify_Requirements::is_imagify_blocked()' );
900
+
901
+ return Imagify_Requirements::is_imagify_blocked();
902
+ }
903
+
904
+ /**
905
+ * Determine if the Imagify API is available by checking the API version.
906
+ *
907
+ * @since 1.0
908
+ * @since 1.7.1 Deprecated.
909
+ * @deprecated
910
+ *
911
+ * @return bool True if the Imagify API is available.
912
+ */
913
+ function is_imagify_servers_up() {
914
+ _deprecated_function( __FUNCTION__ . '()', '1.7.1', 'Imagify_Requirements::is_api_up()' );
915
+
916
+ return Imagify_Requirements::is_api_up();
917
+ }
918
+
919
  if ( is_admin() ) :
920
 
921
  /**
inc/functions/files.php DELETED
@@ -1,233 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- /**
5
- * Get WP Direct filesystem object. Also define chmod constants if not done yet.
6
- *
7
- * @since 1.6.5
8
- * @author Grégory Viguier
9
- *
10
- * @return object A `$wp_filesystem` object.
11
- */
12
- function imagify_get_filesystem() {
13
- static $filesystem;
14
-
15
- if ( $filesystem ) {
16
- return $filesystem;
17
- }
18
-
19
- require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php' );
20
- require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php' );
21
-
22
- $filesystem = new WP_Filesystem_Direct( new StdClass() );
23
-
24
- // Set the permission constants if not already set.
25
- if ( ! defined( 'FS_CHMOD_DIR' ) ) {
26
- define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
27
- }
28
- if ( ! defined( 'FS_CHMOD_FILE' ) ) {
29
- define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
30
- }
31
-
32
- return $filesystem;
33
- }
34
-
35
-
36
- /**
37
- * Set the default file permissions using FS_CHMOD_FILE from WP.
38
- *
39
- * @since 1.2
40
- * @since 1.6.5 Use WP Filesystem.
41
- *
42
- * @param string $file_path The path to file.
43
- * @return bool
44
- */
45
- function imagify_chmod_file( $file_path ) {
46
- return imagify_get_filesystem()->chmod( $file_path, FS_CHMOD_FILE );
47
- }
48
-
49
- /**
50
- * Get a file mime type.
51
- *
52
- * @since 1.6.9
53
- * @since 1.7 Doesn't use exif_imagetype() nor getimagesize() anymore.
54
- * @author Grégory Viguier
55
- *
56
- * @param string $file_path A file path (prefered) or a filename.
57
- * @return string|bool A mime type. False on failure: the last test is limited to mime types supported by Imagify.
58
- */
59
- function imagify_get_mime_type_from_file( $file_path ) {
60
- if ( ! $file_path ) {
61
- return false;
62
- }
63
-
64
- $file_type = wp_check_filetype( $file_path, imagify_get_mime_types() );
65
-
66
- return $file_type['type'];
67
- }
68
-
69
- /**
70
- * Get a file modification date, formated as "mysql". Fallback to current date.
71
- *
72
- * @since 1.7
73
- * @author Grégory Viguier
74
- *
75
- * @param string $file_path The file path.
76
- * @return string The date.
77
- */
78
- function imagify_get_file_date( $file_path ) {
79
- static $offset;
80
-
81
- if ( ! $file_path ) {
82
- return current_time( 'mysql' );
83
- }
84
-
85
- $date = imagify_get_filesystem()->mtime( $file_path );
86
-
87
- if ( ! $date ) {
88
- return current_time( 'mysql' );
89
- }
90
-
91
- if ( ! isset( $offset ) ) {
92
- $offset = get_option( 'gmt_offset' ) * HOUR_IN_SECONDS;
93
- }
94
-
95
- return gmdate( 'Y-m-d H:i:s', $date + $offset );
96
- }
97
-
98
-
99
- /**
100
- * Get a clean value of ABSPATH that can be used in string replacements.
101
- *
102
- * @since 1.6.8
103
- * @author Grégory Viguier
104
- *
105
- * @return string The path to WordPress' root folder.
106
- */
107
- function imagify_get_abspath() {
108
- static $abspath;
109
-
110
- if ( isset( $abspath ) ) {
111
- return $abspath;
112
- }
113
-
114
- $abspath = wp_normalize_path( ABSPATH );
115
-
116
- // Make sure ABSPATH is not messed up: it could be defined as a relative path for example (yeah, I know, but we've seen it).
117
- $test_file = wp_normalize_path( IMAGIFY_FILE );
118
- $pos = strpos( $test_file, $abspath );
119
-
120
- if ( 0 < $pos ) {
121
- // ABSPATH has a wrong value.
122
- $abspath = substr( $test_file, 0, $pos ) . $abspath;
123
-
124
- } elseif ( false === $pos && class_exists( 'ReflectionClass' ) ) {
125
- // Imagify is symlinked (dude, you look for trouble).
126
- $reflector = new ReflectionClass( 'Requests' );
127
- $test_file = $reflector->getFileName();
128
- $pos = strpos( $test_file, $abspath );
129
-
130
- if ( 0 < $pos ) {
131
- // ABSPATH has a wrong value.
132
- $abspath = substr( $test_file, 0, $pos ) . $abspath;
133
- }
134
- }
135
-
136
- $abspath = '/' . trim( $abspath, '/' ) . '/';
137
-
138
- return $abspath;
139
- }
140
-
141
-
142
- /**
143
- * Make an absolute path relative to WordPress' root folder.
144
- * Also works for files from registered symlinked plugins.
145
- *
146
- * @since 1.6.10
147
- * @since 1.7 The parameter $base is added.
148
- * @author Grégory Viguier
149
- *
150
- * @param string $file_path An absolute path.
151
- * @param string $base A base path to use instead of ABSPATH.
152
- * @return string|bool A relative path. Can return the absolute path or false in case of a failure.
153
- */
154
- function imagify_make_file_path_relative( $file_path, $base = '' ) {
155
- static $abspath;
156
- global $wp_plugin_paths;
157
-
158
- if ( ! $file_path ) {
159
- return false;
160
- }
161
-
162
- if ( ! isset( $abspath ) ) {
163
- $abspath = wp_normalize_path( ABSPATH );
164
- }
165
-
166
- $file_path = wp_normalize_path( $file_path );
167
- $base = $base ? trailingslashit( wp_normalize_path( $base ) ) : $abspath;
168
- $pos = strpos( $file_path, $base );
169
-
170
- if ( false === $pos && $wp_plugin_paths && is_array( $wp_plugin_paths ) ) {
171
- // The file is probably part of a symlinked plugin.
172
- arsort( $wp_plugin_paths );
173
-
174
- foreach ( $wp_plugin_paths as $dir => $realdir ) {
175
- if ( strpos( $file_path, $realdir ) === 0 ) {
176
- $file_path = wp_normalize_path( $dir . substr( $file_path, strlen( $realdir ) ) );
177
- }
178
- }
179
-
180
- $pos = strpos( $file_path, $base );
181
- }
182
-
183
- if ( false === $pos ) {
184
- // We're in trouble.
185
- return $file_path;
186
- }
187
-
188
- return substr_replace( $file_path, '', 0, $pos + strlen( $base ) );
189
- }
190
-
191
- /**
192
- * Tell if a file is symlinked.
193
- *
194
- * @since 1.7
195
- * @author Grégory Viguier
196
- *
197
- * @param string $file_path An absolute path.
198
- * @return bool
199
- */
200
- function imagify_file_is_symlinked( $file_path ) {
201
- static $abspath;
202
- static $plugin_paths = array();
203
- global $wp_plugin_paths;
204
-
205
- if ( ! isset( $abspath ) ) {
206
- $abspath = strtolower( wp_normalize_path( trailingslashit( imagify_get_abspath() ) ) );
207
- }
208
-
209
- $lower_file_path = strtolower( wp_normalize_path( trailingslashit( realpath( $file_path ) ) ) );
210
-
211
- if ( strpos( $lower_file_path, $abspath ) !== 0 ) {
212
- return true;
213
- }
214
-
215
- if ( $wp_plugin_paths && is_array( $wp_plugin_paths ) ) {
216
- if ( ! $plugin_paths ) {
217
- foreach ( $wp_plugin_paths as $dir => $realdir ) {
218
- $dir = strtolower( wp_normalize_path( trailingslashit( $dir ) ) );
219
- $plugin_paths[ $dir ] = strtolower( wp_normalize_path( trailingslashit( $realdir ) ) );
220
- }
221
- }
222
-
223
- $lower_file_path = strtolower( wp_normalize_path( trailingslashit( $file_path ) ) );
224
-
225
- foreach ( $plugin_paths as $dir => $realdir ) {
226
- if ( strpos( $lower_file_path, $dir ) === 0 ) {
227
- return true;
228
- }
229
- }
230
- }
231
-
232
- return false;
233
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/functions/i18n.php CHANGED
@@ -118,11 +118,16 @@ function get_imagify_localize_script_translations( $context ) {
118
 
119
  case 'bulk':
120
  $translations = array(
121
- 'keyIsValid' => imagify_valid_key(),
122
- 'curlMissing' => ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ),
123
- 'heartbeatId' => 'update_bulk_data',
124
- 'waitImageUrl' => IMAGIFY_ASSETS_IMG_URL . 'popin-loader.svg',
125
- 'ajaxActions' => array(
 
 
 
 
 
126
  'libraryFetch' => 'imagify_get_unoptimized_attachment_ids',
127
  'customFoldersFetch' => 'imagify_get_unoptimized_file_ids',
128
  'libraryOptimize' => 'imagify_bulk_upload',
@@ -130,22 +135,26 @@ function get_imagify_localize_script_translations( $context ) {
130
  'getFolderData' => 'imagify_get_folder_type_data',
131
  'bulkInfoSeen' => 'imagify_bulk_info_seen',
132
  ),
133
- 'ajaxNonce' => wp_create_nonce( 'imagify-bulk-upload' ),
134
- 'bufferSizes' => array(
135
  'wp' => get_imagify_bulk_buffer_size(),
136
  'File' => get_imagify_bulk_buffer_size( 1 ),
137
  ),
138
- 'labels' => array(
139
  'overviewChartLabels' => array(
140
  'unoptimized' => __( 'Unoptimized', 'imagify' ),
141
  'optimized' => __( 'Optimized', 'imagify' ),
142
  'error' => __( 'Error', 'imagify' ),
143
  ),
 
 
 
 
 
 
144
  'processing' => __( 'Imagify is still processing. Are you sure you want to leave this page?', 'imagify' ),
145
  'waitTitle' => __( 'Please wait...', 'imagify' ),
146
  'waitText' => __( 'We are trying to get your unoptimized images, it may take time depending on the number of images.', 'imagify' ),
147
- 'invalidAPIKeyTitle' => __( 'Your API key isn\'t valid!', 'imagify' ),
148
- 'overQuotaTitle' => __( 'You have used all your credits!', 'imagify' ),
149
  'noAttachmentToOptimizeTitle' => __( 'Hold on!', 'imagify' ),
150
  'noAttachmentToOptimizeText' => __( 'All your images have been optimized by Imagify. Congratulations!', 'imagify' ),
151
  'optimizing' => __( 'Optimizing', 'imagify' ),
@@ -170,16 +179,12 @@ function get_imagify_localize_script_translations( $context ) {
170
  'imagesErrorText' => __( '%s Error(s)', 'imagify' ),
171
  'bulkInfoTitle' => __( 'Information', 'imagify' ),
172
  'confirmBulk' => __( 'Start the optimization', 'imagify' ),
173
- 'curlMissing' => __( 'cURL is not installed on the server.', 'imagify' ),
174
  ),
175
  );
176
 
177
- if ( $translations['keyIsValid'] ) {
178
- $user = new Imagify_User();
179
-
180
- if ( $user->is_over_quota() ) {
181
- $translations['isOverQuota'] = 1;
182
- }
183
  }
184
 
185
  if ( isset( $translations['bufferSizes']['wp'] ) ) {
118
 
119
  case 'bulk':
120
  $translations = array(
121
+ 'curlMissing' => ! Imagify_Requirements::supports_curl(),
122
+ 'editorMissing' => ! Imagify_Requirements::supports_image_editor(),
123
+ 'extHttpBlocked' => Imagify_Requirements::is_imagify_blocked(),
124
+ 'apiDown' => Imagify_Requirements::is_imagify_blocked() || ! Imagify_Requirements::is_api_up(),
125
+ 'keyIsValid' => ! Imagify_Requirements::is_imagify_blocked() && Imagify_Requirements::is_api_up() && Imagify_Requirements::is_api_key_valid(),
126
+ 'isOverQuota' => ! Imagify_Requirements::is_imagify_blocked() && Imagify_Requirements::is_api_up() && Imagify_Requirements::is_api_key_valid() && Imagify_Requirements::is_over_quota(),
127
+ 'heartbeatId' => 'update_bulk_data',
128
+ 'reqsHeartbeatId' => 'update_bulk_requirements',
129
+ 'waitImageUrl' => IMAGIFY_ASSETS_IMG_URL . 'popin-loader.svg',
130
+ 'ajaxActions' => array(
131
  'libraryFetch' => 'imagify_get_unoptimized_attachment_ids',
132
  'customFoldersFetch' => 'imagify_get_unoptimized_file_ids',
133
  'libraryOptimize' => 'imagify_bulk_upload',
135
  'getFolderData' => 'imagify_get_folder_type_data',
136
  'bulkInfoSeen' => 'imagify_bulk_info_seen',
137
  ),
138
+ 'ajaxNonce' => wp_create_nonce( 'imagify-bulk-upload' ),
139
+ 'bufferSizes' => array(
140
  'wp' => get_imagify_bulk_buffer_size(),
141
  'File' => get_imagify_bulk_buffer_size( 1 ),
142
  ),
143
+ 'labels' => array(
144
  'overviewChartLabels' => array(
145
  'unoptimized' => __( 'Unoptimized', 'imagify' ),
146
  'optimized' => __( 'Optimized', 'imagify' ),
147
  'error' => __( 'Error', 'imagify' ),
148
  ),
149
+ 'curlMissing' => __( 'cURL is not available on the server.', 'imagify' ),
150
+ 'editorMissing' => __( 'No php extensions are available to edit images on the server.', 'imagify' ),
151
+ 'extHttpBlocked' => __( 'External HTTP requests are blocked.', 'imagify' ),
152
+ 'apiDown' => __( 'Sorry, our servers are temporarily unavailable. Please, try again in a couple of minutes.', 'imagify' ),
153
+ 'invalidAPIKeyTitle' => __( 'Your API key isn\'t valid!', 'imagify' ),
154
+ 'overQuotaTitle' => __( 'You have used all your credits!', 'imagify' ),
155
  'processing' => __( 'Imagify is still processing. Are you sure you want to leave this page?', 'imagify' ),
156
  'waitTitle' => __( 'Please wait...', 'imagify' ),
157
  'waitText' => __( 'We are trying to get your unoptimized images, it may take time depending on the number of images.', 'imagify' ),
 
 
158
  'noAttachmentToOptimizeTitle' => __( 'Hold on!', 'imagify' ),
159
  'noAttachmentToOptimizeText' => __( 'All your images have been optimized by Imagify. Congratulations!', 'imagify' ),
160
  'optimizing' => __( 'Optimizing', 'imagify' ),
179
  'imagesErrorText' => __( '%s Error(s)', 'imagify' ),
180
  'bulkInfoTitle' => __( 'Information', 'imagify' ),
181
  'confirmBulk' => __( 'Start the optimization', 'imagify' ),
 
182
  ),
183
  );
184
 
185
+ if ( get_transient( 'imagify_large_library' ) ) {
186
+ // On huge media libraries, don't use heartbeat, and fetch stats only when the process ends.
187
+ $translations['ajaxActions']['getStats'] = 'imagify_bulk_get_stats';
 
 
 
188
  }
189
 
190
  if ( isset( $translations['bufferSizes']['wp'] ) ) {
inc/functions/options.php CHANGED
@@ -27,41 +27,6 @@ function update_imagify_option( $key, $value ) {
27
  Imagify_Options::get_instance()->set( $key, $value );
28
  }
29
 
30
- /**
31
- * Determine if the Imagify API key is valid.
32
- *
33
- * @since 1.0
34
- *
35
- * @return bool True if the API key is valid.
36
- */
37
- function imagify_valid_key() {
38
- static $is_valid;
39
-
40
- if ( isset( $is_valid ) ) {
41
- return $is_valid;
42
- }
43
-
44
- if ( ! Imagify_Options::get_instance()->get( 'api_key' ) ) {
45
- $is_valid = false;
46
- return $is_valid;
47
- }
48
-
49
- if ( get_site_transient( 'imagify_check_licence_1' ) ) {
50
- $is_valid = true;
51
- return $is_valid;
52
- }
53
-
54
- if ( is_wp_error( get_imagify_user() ) ) {
55
- $is_valid = false;
56
- return $is_valid;
57
- }
58
-
59
- $is_valid = true;
60
- set_site_transient( 'imagify_check_licence_1', $is_valid, YEAR_IN_SECONDS );
61
-
62
- return $is_valid;
63
- }
64
-
65
  /**
66
  * Autoload network options and put them in cache.
67
  *
27
  Imagify_Options::get_instance()->set( $key, $value );
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  /**
31
  * Autoload network options and put them in cache.
32
  *
inc/functions/process.php CHANGED
@@ -14,8 +14,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
14
  * @return obj|array Error message | Optimized image data.
15
  */
16
  function do_imagify( $file_path, $args = array() ) {
17
- $errors = new WP_Error();
18
- $args = array_merge( array(
19
  'backup' => get_imagify_option( 'backup' ),
20
  'optimization_level' => get_imagify_option( 'optimization_level' ),
21
  'keep_exif' => get_imagify_option( 'exif' ),
@@ -34,48 +33,43 @@ function do_imagify( $file_path, $args = array() ) {
34
  */
35
  $file_path = apply_filters( 'imagify_file_path', $file_path );
36
 
37
- // Check if the Imagify servers & the API are accessible.
38
- if ( ! is_imagify_servers_up() ) {
39
- $errors->add( 'api_server_down', __( 'Sorry, our servers are temporarily unaccessible. Please, try again in a couple of minutes.', 'imagify' ) );
40
- return $errors;
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
  // Check if external HTTP requests are blocked.
44
- if ( is_imagify_blocked() ) {
45
- $errors->add( 'http_block_external', __( 'External HTTP requests are blocked', 'imagify' ) );
46
- return $errors;
47
  }
48
 
49
- // Check that file path isn't empty.
50
- if ( empty( $file_path ) ) {
51
- $errors->add( 'empty_path', __( 'File path is empty', 'imagify' ) );
52
- return $errors;
53
  }
54
 
55
  $filesystem = imagify_get_filesystem();
56
 
57
  // Check that the file exists.
58
- if ( ! $filesystem->exists( $file_path ) || ! $filesystem->is_file( $file_path ) ) {
59
- /* translators: %s is a file path. */
60
- $errors->add( 'file_not_found', sprintf( __( 'Could not find %s', 'imagify' ), $file_path ) );
61
- return $errors;
62
- }
63
-
64
- // Check that the file is writable.
65
- if ( ! wp_is_writable( dirname( $file_path ) ) ) {
66
  /* translators: %s is a file path. */
67
- $errors->add( 'not_writable', sprintf( __( '%s is not writable', 'imagify' ), dirname( $file_path ) ) );
68
- return $errors;
69
  }
70
 
71
- // Get file size.
72
- $file_size = $filesystem->size( $file_path );
73
-
74
- // Check that file exists.
75
- if ( 0 === $file_size ) {
76
  /* translators: %s is a file path. */
77
- $errors->add( 'image_not_found', sprintf( __( 'Skipped (%s), image not found.', 'imagify' ), '<code>' . imagify_make_file_path_relative( $file_path ) . '</code>' ) );
78
- return $errors;
79
  }
80
 
81
  /**
@@ -102,8 +96,7 @@ function do_imagify( $file_path, $args = array() ) {
102
 
103
  // Check status code.
104
  if ( is_wp_error( $response ) ) {
105
- $errors->add( 'api_error', $response->get_error_message() );
106
- return $errors;
107
  }
108
 
109
  // Create a backup file.
@@ -119,12 +112,11 @@ function do_imagify( $file_path, $args = array() ) {
119
  $temp_file = download_url( $response->image );
120
 
121
  if ( is_wp_error( $temp_file ) ) {
122
- $errors->add( 'temp_file_not_found', $temp_file->get_error_message() );
123
- return $errors;
124
  }
125
 
126
  $filesystem->move( $temp_file, $file_path, true );
127
- imagify_chmod_file( $file_path );
128
 
129
  // If temp file still exists, delete it.
130
  if ( $filesystem->exists( $temp_file ) ) {
@@ -225,13 +217,13 @@ function imagify_backup_file( $file_path, $backup_path = null ) {
225
  // Make sure the source file exists.
226
  if ( ! $filesystem->exists( $file_path ) ) {
227
  return new WP_Error( 'source_doesnt_exist', __( 'The file to backup does not exist.', 'imagify' ), array(
228
- 'file_path' => imagify_make_file_path_relative( $file_path ),
229
  ) );
230
  }
231
 
232
  if ( ! isset( $backup_path ) ) {
233
  // Make sure the backup directory is writable.
234
- if ( ! imagify_backup_dir_is_writable() ) {
235
  return new WP_Error( 'backup_dir_not_writable', __( 'The backup directory is not writable.', 'imagify' ) );
236
  }
237
 
@@ -244,7 +236,7 @@ function imagify_backup_file( $file_path, $backup_path = null ) {
244
  }
245
 
246
  // Create sub-directories.
247
- wp_mkdir_p( dirname( $backup_path ) );
248
 
249
  /**
250
  * Allow to overwrite the backup file if it already exists.
@@ -264,8 +256,8 @@ function imagify_backup_file( $file_path, $backup_path = null ) {
264
  // Make sure the backup copy exists.
265
  if ( ! $filesystem->exists( $backup_path ) ) {
266
  return new WP_Error( 'backup_doesnt_exist', __( 'The file could not be saved.', 'imagify' ), array(
267
- 'file_path' => imagify_make_file_path_relative( $file_path ),
268
- 'backup_path' => imagify_make_file_path_relative( $backup_path ),
269
  ) );
270
  }
271
 
14
  * @return obj|array Error message | Optimized image data.
15
  */
16
  function do_imagify( $file_path, $args = array() ) {
17
+ $args = array_merge( array(
 
18
  'backup' => get_imagify_option( 'backup' ),
19
  'optimization_level' => get_imagify_option( 'optimization_level' ),
20
  'keep_exif' => get_imagify_option( 'exif' ),
33
  */
34
  $file_path = apply_filters( 'imagify_file_path', $file_path );
35
 
36
+ // Check that file path isn't empty.
37
+ if ( ! $file_path ) {
38
+ return new WP_Error( 'empty_path', __( 'File path is empty.', 'imagify' ) );
39
+ }
40
+
41
+ // Check if curl is available.
42
+ if ( ! Imagify_Requirements::supports_curl() ) {
43
+ return new WP_Error( 'curl', __( 'cURL is not available on the server.', 'imagify' ) );
44
+ }
45
+
46
+ // Check if imageMagick or GD is available.
47
+ if ( ! Imagify_Requirements::supports_image_editor() ) {
48
+ return new WP_Error( 'image_editor', __( 'No php extensions are available to edit images on the server.', 'imagify' ) );
49
  }
50
 
51
  // Check if external HTTP requests are blocked.
52
+ if ( Imagify_Requirements::is_imagify_blocked() ) {
53
+ return new WP_Error( 'http_block_external', __( 'External HTTP requests are blocked.', 'imagify' ) );
 
54
  }
55
 
56
+ // Check if the Imagify servers & the API are accessible.
57
+ if ( ! Imagify_Requirements::is_api_up() ) {
58
+ return new WP_Error( 'api_server_down', __( 'Sorry, our servers are temporarily unavailable. Please, try again in a couple of minutes.', 'imagify' ) );
 
59
  }
60
 
61
  $filesystem = imagify_get_filesystem();
62
 
63
  // Check that the file exists.
64
+ if ( ! $filesystem->is_writable( $file_path ) || ! $filesystem->is_file( $file_path ) ) {
 
 
 
 
 
 
 
65
  /* translators: %s is a file path. */
66
+ return new WP_Error( 'file_not_found', sprintf( __( 'Could not find %s.', 'imagify' ), $filesystem->make_path_relative( $file_path ) ) );
 
67
  }
68
 
69
+ // Check that the file directory is writable.
70
+ if ( ! $filesystem->is_writable( $filesystem->dir_path( $file_path ) ) ) {
 
 
 
71
  /* translators: %s is a file path. */
72
+ return new WP_Error( 'not_writable', sprintf( __( '%s is not writable.', 'imagify' ), $filesystem->make_path_relative( $filesystem->dir_path( $file_path ) ) ) );
 
73
  }
74
 
75
  /**
96
 
97
  // Check status code.
98
  if ( is_wp_error( $response ) ) {
99
+ return new WP_Error( 'api_error', $response->get_error_message() );
 
100
  }
101
 
102
  // Create a backup file.
112
  $temp_file = download_url( $response->image );
113
 
114
  if ( is_wp_error( $temp_file ) ) {
115
+ return new WP_Error( 'temp_file_not_found', $temp_file->get_error_message() );
 
116
  }
117
 
118
  $filesystem->move( $temp_file, $file_path, true );
119
+ $filesystem->chmod_file( $file_path );
120
 
121
  // If temp file still exists, delete it.
122
  if ( $filesystem->exists( $temp_file ) ) {
217
  // Make sure the source file exists.
218
  if ( ! $filesystem->exists( $file_path ) ) {
219
  return new WP_Error( 'source_doesnt_exist', __( 'The file to backup does not exist.', 'imagify' ), array(
220
+ 'file_path' => $filesystem->make_path_relative( $file_path ),
221
  ) );
222
  }
223
 
224
  if ( ! isset( $backup_path ) ) {
225
  // Make sure the backup directory is writable.
226
+ if ( ! Imagify_Requirements::attachments_backup_dir_is_writable() ) {
227
  return new WP_Error( 'backup_dir_not_writable', __( 'The backup directory is not writable.', 'imagify' ) );
228
  }
229
 
236
  }
237
 
238
  // Create sub-directories.
239
+ $filesystem->make_dir( $filesystem->dir_path( $backup_path ) );
240
 
241
  /**
242
  * Allow to overwrite the backup file if it already exists.
256
  // Make sure the backup copy exists.
257
  if ( ! $filesystem->exists( $backup_path ) ) {
258
  return new WP_Error( 'backup_doesnt_exist', __( 'The file could not be saved.', 'imagify' ), array(
259
+ 'file_path' => $filesystem->make_path_relative( $file_path ),
260
+ 'backup_path' => $filesystem->make_path_relative( $backup_path ),
261
  ) );
262
  }
263
 
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: wp_media, GregLone
3
  Tags: compress image, images, performance, optimization, photos, upload, resize, gif, png, jpg, reduce image size, retina
4
  Requires at least: 3.7.0
5
- Tested up to: 4.9.4
6
- Stable tag: 1.7
7
 
8
  Dramatically reduce image file sizes without losing quality, make your website load faster, boost your SEO and save money on your bandwidth.
9
 
@@ -138,6 +138,14 @@ When the plugin is disabled, your existing images remain optimized. Backups of t
138
  4. Other Media Page
139
 
140
  == Changelog ==
 
 
 
 
 
 
 
 
141
  = 1.7 - 2018/03/13 =
142
  * New: you can now optimize the images from your themes and plugins, or from any other folder in your site!
143
  * Improvement: compatibility with old and new versions of WP Offload S3 plugins.
2
  Contributors: wp_media, GregLone
3
  Tags: compress image, images, performance, optimization, photos, upload, resize, gif, png, jpg, reduce image size, retina
4
  Requires at least: 3.7.0
5
+ Tested up to: 4.9.5
6
+ Stable tag: 1.7.1
7
 
8
  Dramatically reduce image file sizes without losing quality, make your website load faster, boost your SEO and save money on your bandwidth.
9
 
138
  4. Other Media Page
139
 
140
  == Changelog ==
141
+ = 1.7.1 - 2018/04/10 =
142
+ * New: compatibility with Regenerate Thumbnails (v3) plugin.
143
+ * Improvement: better performance of the bulk optimization on sites with huge media library. This is done by not updating the statistics display periodically, but only when the job is done.
144
+ * Improvement: SiteGround cache testing is not blocked anymore.
145
+ * Improvement: proxies are now handled.
146
+ * Improvement: test for ImageMagick or GD availability.
147
+ * Dev stuff: improved the way we use the filesystem. This should solve few edge cases.
148
+
149
  = 1.7 - 2018/03/13 =
150
  * New: you can now optimize the images from your themes and plugins, or from any other folder in your site!
151
  * Improvement: compatibility with old and new versions of WP Offload S3 plugins.
views/notice-backup-folder-not-writable.php CHANGED
@@ -5,10 +5,9 @@ $this->print_template( 'notice-header', array(
5
  'classes' => array( 'error' ),
6
  ) );
7
 
8
- $filesystem = imagify_get_filesystem();
9
- $backup_path = imagify_make_file_path_relative( get_imagify_backup_dir_path( true ) );
10
 
11
- if ( $filesystem->exists( get_imagify_backup_dir_path() ) ) {
12
  /* translators: %s is a file path. */
13
  $message = __( 'The backup folder %s is not writable by the server, original images cannot be saved!', 'imagify' );
14
  } else {
5
  'classes' => array( 'error' ),
6
  ) );
7
 
8
+ $backup_path = $this->filesystem->make_path_relative( get_imagify_backup_dir_path( true ) );
 
9
 
10
+ if ( $this->filesystem->exists( get_imagify_backup_dir_path() ) ) {
11
  /* translators: %s is a file path. */
12
  $message = __( 'The backup folder %s is not writable by the server, original images cannot be saved!', 'imagify' );
13
  } else {
views/page-bulk.php CHANGED
@@ -20,7 +20,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
20
 
21
  <div class="imagify-number-you-optimized">
22
  <p>
23
- <span id="imagify-total-optimized-attachments" class="number"><?php echo esc_html( number_format_i18n( $data['already_optimized_attachments'] ) ); ?></span>
24
  <span class="text">
25
  <?php
26
  printf(
@@ -36,12 +36,12 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
36
  <div class="imagify-bars">
37
  <p><?php esc_html_e( 'Original size', 'imagify' ); ?></p>
38
  <div class="imagify-bar-negative base-transparent right-outside-number">
39
- <div id="imagify-original-bar" class="imagify-progress" style="width: 100%"><span class="imagify-barnb"><?php echo esc_html( imagify_size_format( $data['original_size'], 1 ) ); ?></span></div>
40
  </div>
41
 
42
  <p><?php esc_html_e( 'Optimized size', 'imagify' ); ?></p>
43
  <div class="imagify-bar-primary base-transparent right-outside-number">
44
- <div id="imagify-optimized-bar" class="imagify-progress" style="width: <?php echo max( 100 - $data['optimized_percent'], 0 ); ?>%"><span class="imagify-barnb"><?php echo esc_html( imagify_size_format( $data['optimized_size'], 1 ) ); ?></span></div>
45
  </div>
46
 
47
  </div>
@@ -85,7 +85,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
85
  <div class="imagify-col imagify-account-info-col">
86
 
87
  <?php
88
- if ( ( ! defined( 'IMAGIFY_HIDDEN_ACCOUNT' ) || ! IMAGIFY_HIDDEN_ACCOUNT ) && imagify_valid_key() ) {
89
  $user = new Imagify_User();
90
  ?>
91
  <div class="imagify-options-title">
@@ -214,7 +214,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
214
  <?php
215
  $this->print_template( 'modal-payment' );
216
 
217
- if ( imagify_valid_key() ) {
218
  $display_infos = get_transient( 'imagify_bulk_optimization_infos' );
219
 
220
  ?>
20
 
21
  <div class="imagify-number-you-optimized">
22
  <p>
23
+ <span id="imagify-total-optimized-attachments" class="number"><?php echo esc_html( $data['already_optimized_attachments'] ); ?></span>
24
  <span class="text">
25
  <?php
26
  printf(
36
  <div class="imagify-bars">
37
  <p><?php esc_html_e( 'Original size', 'imagify' ); ?></p>
38
  <div class="imagify-bar-negative base-transparent right-outside-number">
39
+ <div id="imagify-original-bar" class="imagify-progress" style="width: 100%"><span class="imagify-barnb"><?php echo esc_html( $data['original_human'] ); ?></span></div>
40
  </div>
41
 
42
  <p><?php esc_html_e( 'Optimized size', 'imagify' ); ?></p>
43
  <div class="imagify-bar-primary base-transparent right-outside-number">
44
+ <div id="imagify-optimized-bar" class="imagify-progress" style="width: <?php echo max( 100 - $data['optimized_percent'], 0 ); ?>%"><span class="imagify-barnb"><?php echo esc_html( $data['optimized_human'] ); ?></span></div>
45
  </div>
46
 
47
  </div>
85
  <div class="imagify-col imagify-account-info-col">
86
 
87
  <?php
88
+ if ( ( ! defined( 'IMAGIFY_HIDDEN_ACCOUNT' ) || ! IMAGIFY_HIDDEN_ACCOUNT ) && Imagify_Requirements::is_api_key_valid() ) {
89
  $user = new Imagify_User();
90
  ?>
91
  <div class="imagify-options-title">
214
  <?php
215
  $this->print_template( 'modal-payment' );
216
 
217
+ if ( Imagify_Requirements::is_api_key_valid() ) {
218
  $display_infos = get_transient( 'imagify_bulk_optimization_infos' );
219
 
220
  ?>
views/page-settings.php CHANGED
@@ -4,7 +4,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
4
  $settings = Imagify_Settings::get_instance();
5
  $options = Imagify_Options::get_instance();
6
  $option_name = $options->get_option_name();
7
- $hidden_class = imagify_valid_key() ? '' : ' hidden';
8
 
9
  /* Ads notice */
10
  $notice = 'wp-rocket';
@@ -21,14 +21,14 @@ $wrapper_class = isset( $notices[ $notice ] ) || defined( 'WP_ROCKET_VERSION' )
21
 
22
  <form action="<?php echo esc_url( $settings->get_form_action() ); ?>" id="imagify-settings" method="post">
23
 
24
- <div class="imagify-settings-main-content<?php echo imagify_valid_key() ? '' : ' imagify-no-api-key'; ?>">
25
 
26
  <?php settings_fields( $settings->get_settings_group() ); ?>
27
  <?php wp_nonce_field( 'imagify-signup', 'imagifysignupnonce', false ); ?>
28
  <?php wp_nonce_field( 'imagify-check-api-key', 'imagifycheckapikeynonce', false ); ?>
29
 
30
  <?php
31
- if ( ! imagify_valid_key() ) {
32
  $this->print_template( 'part-settings-account' );
33
  $this->print_template( 'part-settings-footer' );
34
  }
@@ -96,11 +96,11 @@ $wrapper_class = isset( $notices[ $notice ] ) || defined( 'WP_ROCKET_VERSION' )
96
  'info' => __( 'Keep your original images in a separate folder before optimization process.', 'imagify' ),
97
  ) );
98
 
99
- $backup_error_class = $options->get( 'backup' ) && ! imagify_backup_dir_is_writable() ? '' : ' hidden';
100
  ?>
101
  <br/><strong id="backup-dir-is-writable" class="imagify-error<?php echo $backup_error_class; ?>" data-nonce="<?php echo esc_attr( wp_create_nonce( 'imagify_check_backup_dir_is_writable' ) ); ?>">
102
  <?php
103
- $backup_path = imagify_make_file_path_relative( get_imagify_backup_dir_path( true ) );
104
  /* translators: %s is a file path. */
105
  printf( __( 'The backup folder %s cannot be created or is not writable by the server, original images cannot be saved!', 'imagify' ), "<code>$backup_path</code>" );
106
  ?>
@@ -123,7 +123,7 @@ $wrapper_class = isset( $notices[ $notice ] ) || defined( 'WP_ROCKET_VERSION' )
123
  </div>
124
  </div>
125
 
126
- <?php if ( imagify_valid_key() ) { ?>
127
  <div class="imagify-col imagify-account-info-col">
128
  <?php $this->print_template( 'part-settings-account' ); ?>
129
  </div>
@@ -167,7 +167,7 @@ $wrapper_class = isset( $notices[ $notice ] ) || defined( 'WP_ROCKET_VERSION' )
167
  </div>
168
 
169
  <?php
170
- if ( imagify_valid_key() ) {
171
  $this->print_template( 'part-settings-footer' );
172
  }
173
  ?>
4
  $settings = Imagify_Settings::get_instance();
5
  $options = Imagify_Options::get_instance();
6
  $option_name = $options->get_option_name();
7
+ $hidden_class = Imagify_Requirements::is_api_key_valid() ? '' : ' hidden';
8
 
9
  /* Ads notice */
10
  $notice = 'wp-rocket';
21
 
22
  <form action="<?php echo esc_url( $settings->get_form_action() ); ?>" id="imagify-settings" method="post">
23
 
24
+ <div class="imagify-settings-main-content<?php echo Imagify_Requirements::is_api_key_valid() ? '' : ' imagify-no-api-key'; ?>">
25
 
26
  <?php settings_fields( $settings->get_settings_group() ); ?>
27
  <?php wp_nonce_field( 'imagify-signup', 'imagifysignupnonce', false ); ?>
28
  <?php wp_nonce_field( 'imagify-check-api-key', 'imagifycheckapikeynonce', false ); ?>
29
 
30
  <?php
31
+ if ( ! Imagify_Requirements::is_api_key_valid() ) {
32
  $this->print_template( 'part-settings-account' );
33
  $this->print_template( 'part-settings-footer' );
34
  }
96
  'info' => __( 'Keep your original images in a separate folder before optimization process.', 'imagify' ),
97
  ) );
98
 
99
+ $backup_error_class = $options->get( 'backup' ) && ! Imagify_Requirements::attachments_backup_dir_is_writable() ? '' : ' hidden';
100
  ?>
101
  <br/><strong id="backup-dir-is-writable" class="imagify-error<?php echo $backup_error_class; ?>" data-nonce="<?php echo esc_attr( wp_create_nonce( 'imagify_check_backup_dir_is_writable' ) ); ?>">
102
  <?php
103
+ $backup_path = $this->filesystem->make_path_relative( get_imagify_backup_dir_path( true ) );
104
  /* translators: %s is a file path. */
105
  printf( __( 'The backup folder %s cannot be created or is not writable by the server, original images cannot be saved!', 'imagify' ), "<code>$backup_path</code>" );
106
  ?>
123
  </div>
124
  </div>
125
 
126
+ <?php if ( Imagify_Requirements::is_api_key_valid() ) { ?>
127
  <div class="imagify-col imagify-account-info-col">
128
  <?php $this->print_template( 'part-settings-account' ); ?>
129
  </div>
167
  </div>
168
 
169
  <?php
170
+ if ( Imagify_Requirements::is_api_key_valid() ) {
171
  $this->print_template( 'part-settings-footer' );
172
  }
173
  ?>
views/part-bulk-optimization-overquota-alert.php CHANGED
@@ -9,7 +9,7 @@ $overquota_url = imagify_get_external_url( 'subscription', array(
9
  ?>
10
  <div class="imagify-swal-subtitle"><?php esc_html_e( 'Upgrade your account to continue optimizing your images.', 'imagify' ); ?></div>
11
  <div class="imagify-swal-content imagify-txt-start">
12
- <?php if ( imagify_valid_key() ) { ?>
13
  <strong><?php esc_html_e( 'To continue optimizing your images, you can:', 'imagify' ); ?></strong>
14
  <ul class="imagify-count-list">
15
  <li>
9
  ?>
10
  <div class="imagify-swal-subtitle"><?php esc_html_e( 'Upgrade your account to continue optimizing your images.', 'imagify' ); ?></div>
11
  <div class="imagify-swal-content imagify-txt-start">
12
+ <?php if ( Imagify_Requirements::is_api_key_valid() ) { ?>
13
  <strong><?php esc_html_e( 'To continue optimizing your images, you can:', 'imagify' ); ?></strong>
14
  <ul class="imagify-count-list">
15
  <li>
views/part-settings-account.php CHANGED
@@ -11,7 +11,7 @@ if ( defined( 'IMAGIFY_HIDDEN_ACCOUNT' ) && IMAGIFY_HIDDEN_ACCOUNT ) {
11
  return;
12
  }
13
 
14
- if ( imagify_valid_key() ) {
15
  $user = imagify_get_cached_user();
16
  $unconsumed_quota = $user ? $user->get_percent_unconsumed_quota : false;
17
  $hidden_class = '';
@@ -30,7 +30,7 @@ if ( imagify_valid_key() ) {
30
  <div class="imagify-settings-section">
31
 
32
  <?php
33
- if ( imagify_valid_key() ) {
34
  ?>
35
  <div class="imagify-col-content imagify-block-secondary imagify-mt2">
36
  <?php
@@ -65,7 +65,7 @@ if ( imagify_valid_key() ) {
65
 
66
  <?php
67
  if ( ! defined( 'IMAGIFY_API_KEY' ) || ! IMAGIFY_API_KEY ) {
68
- if ( imagify_valid_key() ) {
69
  ?>
70
  <h2 class="imagify-options-title">
71
  <?php esc_html_e( 'Account Type', 'imagify' ); ?>
@@ -99,7 +99,7 @@ if ( imagify_valid_key() ) {
99
  <label for="api_key"><?php echo $options->get( 'api_key' ) ? esc_html__( 'API Key', 'imagify' ) : esc_html__( 'Enter Your API Key Below', 'imagify' ); ?></label>
100
  <input type="text" size="35" value="<?php echo esc_attr( $options->get( 'api_key' ) ); ?>" name="<?php echo $options->get_option_name(); ?>[api_key]" id="api_key">
101
  <?php
102
- if ( imagify_valid_key() ) {
103
  ?>
104
 
105
  <span id="imagify-check-api-container" class="imagify-valid">
@@ -107,7 +107,7 @@ if ( imagify_valid_key() ) {
107
  </span>
108
 
109
  <?php
110
- } elseif ( ! imagify_valid_key() && $options->get( 'api_key' ) ) {
111
  ?>
112
 
113
  <span id="imagify-check-api-container">
11
  return;
12
  }
13
 
14
+ if ( Imagify_Requirements::is_api_key_valid() ) {
15
  $user = imagify_get_cached_user();
16
  $unconsumed_quota = $user ? $user->get_percent_unconsumed_quota : false;
17
  $hidden_class = '';
30
  <div class="imagify-settings-section">
31
 
32
  <?php
33
+ if ( Imagify_Requirements::is_api_key_valid() ) {
34
  ?>
35
  <div class="imagify-col-content imagify-block-secondary imagify-mt2">
36
  <?php
65
 
66
  <?php
67
  if ( ! defined( 'IMAGIFY_API_KEY' ) || ! IMAGIFY_API_KEY ) {
68
+ if ( Imagify_Requirements::is_api_key_valid() ) {
69
  ?>
70
  <h2 class="imagify-options-title">
71
  <?php esc_html_e( 'Account Type', 'imagify' ); ?>
99
  <label for="api_key"><?php echo $options->get( 'api_key' ) ? esc_html__( 'API Key', 'imagify' ) : esc_html__( 'Enter Your API Key Below', 'imagify' ); ?></label>
100
  <input type="text" size="35" value="<?php echo esc_attr( $options->get( 'api_key' ) ); ?>" name="<?php echo $options->get_option_name(); ?>[api_key]" id="api_key">
101
  <?php
102
+ if ( Imagify_Requirements::is_api_key_valid() ) {
103
  ?>
104
 
105
  <span id="imagify-check-api-container" class="imagify-valid">
107
  </span>
108
 
109
  <?php
110
+ } elseif ( ! Imagify_Requirements::is_api_key_valid() && $options->get( 'api_key' ) ) {
111
  ?>
112
 
113
  <span id="imagify-check-api-container">
views/part-settings-custom-folders.php CHANGED
@@ -6,16 +6,17 @@ if ( ! imagify_can_optimize_custom_folders() ) {
6
  }
7
 
8
  // Get folders, remove excluded ones, sort them, add labels.
9
- $disabled_values = Imagify_Files_Scan::get_forbidden_folders();
10
- $disabled_values = array_map( array( 'Imagify_Files_Scan', 'add_placeholder' ), $disabled_values );
11
- $custom_folders = Imagify_Folders_DB::get_instance()->get_active_folders_column_not_in( 'path', 'path', $disabled_values );
12
- $custom_folders = array_filter( $custom_folders, array( 'Imagify_Files_Scan', 'is_path_forbidden' ) );
13
- $themes = array();
14
 
15
  if ( $custom_folders ) {
16
  $custom_folders = array_combine( $custom_folders, $custom_folders );
17
  $custom_folders = array_map( array( 'Imagify_Files_Scan', 'remove_placeholder' ), $custom_folders );
18
- $custom_folders = array_map( 'imagify_make_file_path_relative', $custom_folders );
 
 
 
 
19
  $custom_folders = array_map( 'untrailingslashit', $custom_folders );
20
  natcasesort( $custom_folders );
21
  $custom_folders = array_map( 'trailingslashit', $custom_folders );
@@ -41,7 +42,7 @@ if ( ! is_network_admin() ) {
41
  $theme = array(
42
  'name' => $theme->display( 'Name' ),
43
  'path' => Imagify_Files_Scan::add_placeholder( $theme_path ),
44
- 'label' => imagify_make_file_path_relative( $theme_path ),
45
  );
46
 
47
  $themes[ $theme['path'] ] = $theme;
6
  }
7
 
8
  // Get folders, remove excluded ones, sort them, add labels.
9
+ $custom_folders = Imagify_Folders_DB::get_instance()->get_active_folders_column( 'path' );
10
+ $themes = array();
 
 
 
11
 
12
  if ( $custom_folders ) {
13
  $custom_folders = array_combine( $custom_folders, $custom_folders );
14
  $custom_folders = array_map( array( 'Imagify_Files_Scan', 'remove_placeholder' ), $custom_folders );
15
+ $custom_folders = array_filter( $custom_folders, array( 'Imagify_Files_Scan', 'is_path_autorized' ) );
16
+ }
17
+
18
+ if ( $custom_folders ) {
19
+ $custom_folders = array_map( array( $this->filesystem, 'make_path_relative' ), $custom_folders );
20
  $custom_folders = array_map( 'untrailingslashit', $custom_folders );
21
  natcasesort( $custom_folders );
22
  $custom_folders = array_map( 'trailingslashit', $custom_folders );
42
  $theme = array(
43
  'name' => $theme->display( 'Name' ),
44
  'path' => Imagify_Files_Scan::add_placeholder( $theme_path ),
45
+ 'label' => $this->filesystem->make_path_relative( $theme_path ),
46
  );
47
 
48
  $themes[ $theme['path'] ] = $theme;
views/part-settings-footer.php CHANGED
@@ -8,7 +8,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
8
  // Classical submit.
9
  submit_button();
10
 
11
- if ( imagify_valid_key() ) {
12
  // Submit and go to bulk page.
13
  submit_button(
14
  esc_html__( 'Save & Go to Bulk Optimizer', 'imagify' ),
8
  // Classical submit.
9
  submit_button();
10
 
11
+ if ( Imagify_Requirements::is_api_key_valid() ) {
12
  // Submit and go to bulk page.
13
  submit_button(
14
  esc_html__( 'Save & Go to Bulk Optimizer', 'imagify' ),