Imagify Image Optimizer - Version 1.7

Version Description

  • 2018/03/13 =
  • New: you can now optimize the images from your themes and plugins, or from any other folder in your site!
  • Improvement: compatibility with old and new versions of WP Offload S3 plugins.
  • Improvement: don't start the bulk optimization process if cURL is not available.
  • Bug Fix: image dimensions not being stored sometimes after it is resized.
  • Bug Fix: the comparison tool could display multiple handles.
  • Bug Fix: issue with php 7.2.
  • Dev stuff: lots of internal changes, many things have been rewritten.
  • Dev stuff: the default options can now be filtered.
Download this release

Release Info

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

Code changes from version 1.6.14.2 to 1.7

Files changed (133) hide show
  1. assets/css/admin-bar.css +0 -8
  2. assets/css/admin-bar.min.css +1 -1
  3. assets/css/admin.css +377 -31
  4. assets/css/admin.min.css +1 -1
  5. assets/css/bulk.css +542 -194
  6. assets/css/bulk.min.css +1 -1
  7. assets/css/files-list.css +162 -0
  8. assets/css/files-list.min.css +1 -0
  9. assets/css/notices.css +41 -9
  10. assets/css/notices.min.css +1 -1
  11. assets/css/options.css +599 -144
  12. assets/css/options.min.css +1 -1
  13. assets/css/sweetalert-custom.css +140 -21
  14. assets/css/sweetalert-custom.min.css +1 -1
  15. assets/css/twentytwenty.css +0 -6
  16. assets/css/twentytwenty.min.css +1 -1
  17. assets/images/big-blue-check.png +0 -0
  18. assets/images/icon-alert.svg +1 -0
  19. assets/images/icon-arrow-choice.png +0 -0
  20. assets/images/icon-doc-image.svg +1 -0
  21. assets/images/icon-external.svg +1 -0
  22. assets/images/icon-level.svg +7 -0
  23. assets/images/icon-load.svg +1 -0
  24. assets/images/icon-lock.png +0 -0
  25. assets/images/icon-pack.png +0 -0
  26. assets/images/icon-time.svg +1 -0
  27. assets/images/imagify-logo.png +0 -0
  28. assets/images/imagify-menu-bar.jpg +0 -0
  29. assets/images/lazyload.png +0 -0
  30. assets/images/logo-wprocket.png +0 -0
  31. assets/images/logo-wprocket.svg +47 -0
  32. assets/images/logo-wprocket2x.png +0 -0
  33. assets/images/logo-wprocket@2x.png +0 -0
  34. assets/images/pic-ericwaltr.jpg +0 -0
  35. assets/images/pic-srhdesign.jpg +0 -0
  36. assets/images/sidebar-wp-rocket.jpg +0 -0
  37. assets/images/spinner.gif +0 -0
  38. assets/images/stormy.svg +1 -1
  39. assets/images/sun.svg +1 -1
  40. assets/images/upload-image.png +0 -0
  41. assets/js/admin.js +34 -0
  42. assets/js/admin.min.js +1 -1
  43. assets/js/bulk.js +1142 -334
  44. assets/js/bulk.min.js +1 -1
  45. assets/js/files-list.js +241 -0
  46. assets/js/files-list.min.js +1 -0
  47. assets/js/imagify-gulp.js +29 -50
  48. assets/js/imagify-gulp.min.js +243 -1
  49. assets/js/jquery.twentytwenty.js +57 -2
  50. assets/js/jquery.twentytwenty.min.js +1 -1
  51. assets/js/notices.js +4 -0
  52. assets/js/notices.min.js +1 -1
  53. assets/js/options.js +309 -24
  54. assets/js/options.min.js +1 -1
  55. assets/js/pricing-modal.js +1 -1
  56. imagify.php +15 -13
  57. inc/3rd-party/amazon-s3-and-cloudfront/amazon-s3-and-cloudfront.php +45 -1
  58. inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf-attachment.php +15 -37
  59. inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf.php +2 -24
  60. inc/3rd-party/hosting/siteground.php +7 -1
  61. inc/3rd-party/nextgen-gallery/inc/admin/ajax.php +19 -17
  62. inc/3rd-party/nextgen-gallery/inc/admin/bulk.php +66 -7
  63. inc/3rd-party/nextgen-gallery/inc/admin/db.php +0 -17
  64. inc/3rd-party/nextgen-gallery/inc/admin/enqueue.php +8 -4
  65. inc/3rd-party/nextgen-gallery/inc/admin/heartbeat.php +10 -7
  66. inc/3rd-party/nextgen-gallery/inc/admin/menu.php +1 -1
  67. inc/3rd-party/nextgen-gallery/inc/classes/class-imagify-ngg-attachment.php +151 -100
  68. inc/3rd-party/nextgen-gallery/inc/classes/class-imagify-ngg-db.php +48 -55
  69. inc/3rd-party/nextgen-gallery/inc/common/attachments.php +2 -2
  70. inc/3rd-party/nextgen-gallery/inc/functions/admin-stats.php +6 -2
  71. inc/3rd-party/nextgen-gallery/inc/functions/common.php +15 -2
  72. inc/3rd-party/nextgen-gallery/nextgen-gallery.php +1 -2
  73. inc/3rd-party/wp-retina-2x.php +1 -1
  74. inc/3rd-party/yoast-seo.php +1 -1
  75. inc/admin/custom-folders.php +66 -0
  76. inc/admin/heartbeat.php +71 -14
  77. inc/admin/media.php +1 -1
  78. inc/admin/menu.php +0 -24
  79. inc/admin/meta-boxes.php +2 -2
  80. inc/admin/options.php +0 -207
  81. inc/admin/plugins.php +0 -19
  82. inc/admin/ui/bulk.php +0 -393
  83. inc/admin/ui/notice-free-over-quota.php +0 -30
  84. inc/admin/ui/options.php +0 -598
  85. inc/admin/upgrader.php +168 -39
  86. inc/admin/upload.php +1 -1
  87. inc/classes/class-imagify-abstract-attachment.php +391 -91
  88. inc/classes/class-imagify-abstract-cron.php +286 -0
  89. inc/classes/class-imagify-abstract-db.php +652 -77
  90. inc/classes/class-imagify-abstract-options.php +623 -0
  91. inc/classes/class-imagify-admin-ajax-post.php +693 -79
  92. inc/classes/class-imagify-assets.php +17 -8
  93. inc/classes/class-imagify-attachment.php +39 -22
  94. inc/classes/class-imagify-cron-library-size.php +91 -0
  95. inc/classes/class-imagify-cron-rating.php +115 -0
  96. inc/classes/class-imagify-cron-sync-files.php +118 -0
  97. inc/classes/class-imagify-custom-folders.php +1227 -0
  98. inc/classes/class-imagify-data.php +103 -0
  99. inc/classes/class-imagify-db.php +245 -2
  100. inc/classes/class-imagify-file-attachment.php +746 -0
  101. inc/classes/class-imagify-files-db.php +174 -0
  102. inc/classes/class-imagify-files-iterator.php +93 -0
  103. inc/classes/class-imagify-files-list-table.php +1028 -0
  104. inc/classes/class-imagify-files-recursive-iterator.php +70 -0
  105. inc/classes/class-imagify-files-scan.php +544 -0
  106. inc/classes/class-imagify-files-stats.php +474 -0
  107. inc/classes/class-imagify-folders-db.php +238 -0
  108. inc/classes/class-imagify-notices.php +206 -41
  109. inc/classes/class-imagify-options.php +195 -0
  110. inc/classes/class-imagify-settings.php +662 -0
  111. inc/classes/class-imagify-user.php +45 -5
  112. inc/classes/class-imagify-views.php +468 -0
  113. inc/classes/class-imagify.php +11 -13
  114. inc/common/admin-bar.php +6 -8
  115. inc/common/cron.php +0 -90
  116. inc/common/partners.php +2 -2
  117. inc/functions/admin-stats.php +152 -140
  118. inc/functions/admin-ui.php +85 -530
  119. inc/functions/admin.php +137 -16
  120. inc/functions/api.php +30 -22
  121. inc/functions/attachments.php +89 -42
  122. inc/functions/common.php +129 -15
  123. inc/functions/compat-spl-autoload.php +109 -0
  124. inc/functions/compat.php +142 -86
  125. inc/functions/deprecated.php +546 -41
  126. inc/functions/files.php +107 -11
  127. inc/functions/formatting.php +16 -0
  128. inc/functions/i18n.php +74 -45
  129. inc/functions/options.php +80 -49
  130. inc/functions/process.php +2 -2
  131. readme.txt +15 -3
  132. uninstall.php +15 -0
  133. views/modal-payment.php +410 -0
assets/css/admin-bar.css CHANGED
@@ -15,16 +15,9 @@
15
  color: #FFF;
16
  font-size: 17px;
17
  }
18
- .imagify-space-left {
19
- display: inline-block;
20
- min-height: 38px;
21
- min-width: 245px;
22
- vertical-align: middle;
23
- }
24
  .imagify-space-left > p {
25
  color: #FFF;
26
  }
27
- [class^="imagify-bar-"],
28
  #wp-admin-bar-imagify-profile [class^="imagify-bar-"] {
29
  position: relative;
30
  height: 1.5em;
@@ -33,7 +26,6 @@
33
  color: #FFF;
34
  font-size: 10px;
35
  }
36
- .imagify-progress,
37
  #wp-admin-bar-imagify-profile .imagify-progress {
38
  height: 1.5em;
39
  }
15
  color: #FFF;
16
  font-size: 17px;
17
  }
 
 
 
 
 
 
18
  .imagify-space-left > p {
19
  color: #FFF;
20
  }
 
21
  #wp-admin-bar-imagify-profile [class^="imagify-bar-"] {
22
  position: relative;
23
  height: 1.5em;
26
  color: #FFF;
27
  font-size: 10px;
28
  }
 
29
  #wp-admin-bar-imagify-profile .imagify-progress {
30
  height: 1.5em;
31
  }
assets/css/admin-bar.min.css CHANGED
@@ -1 +1 @@
1
- #wp-admin-bar-imagify .button-text,.imagify-meteo-icon,.imagify-space-left{display:inline-block;vertical-align:middle}.imagify-account,.imagify-account-link{padding-right:15px}.imagify-meteo-icon{height:38px;margin-right:10px}.imagify-user-plan{color:#40b1d0}.imagify-meteo-title.imagify-meteo-title{color:#FFF;font-size:17px}.imagify-space-left{min-height:38px;min-width:245px}.imagify-space-left>p{color:#FFF}#wp-admin-bar-imagify-profile [class^=imagify-bar-],[class^=imagify-bar-]{position:relative;height:1.5em;width:100%;background:#60758D;color:#FFF;font-size:10px}#wp-admin-bar-imagify-profile .imagify-progress,.imagify-progress{height:1.5em}.imagify-progress{-webkit-transition:width .3s;-o-transition:width .3s;transition:width .3s}.imagify-bar-positive .imagify-progress{background:#8CC152}.imagify-bar-positive .imagify-barnb{color:#8CC152}.imagify-bar-negative .imagify-progress{background:#73818C}.imagify-bar-negative .imagify-barnb{color:#73818C}.imagify-bar-neutral .imagify-progress{background:#F5A623}.imagify-space-left .imagify-bar-negative .imagify-progress{background:#D0021B}#wpadminbar #wp-admin-bar-imagify-profile *{line-height:1.5;white-space:initial}#wpadminbar #wp-admin-bar-imagify .ab-submenu{padding-bottom:0}#wpadminbar #wp-admin-bar-imagify-profile .ab-item{height:auto;padding:0 13px}#wpadminbar #wp-admin-bar-imagify-profile{min-width:200px;padding:15px 0 10px;margin-top:.7em;background:#222}#wp-admin-bar-imagify .dashicons{font-family:dashicons;font-size:18px;vertical-align:middle;margin:0 5px 0 0}#wp-admin-bar-imagify .imagify-abq-row{display:table;width:100%}#wp-admin-bar-imagify .imagify-abq-row+.imagify-abq-row{margin-top:.75em}#wp-admin-bar-imagify .imagify-abq-row>*{display:table-cell}#wp-admin-bar-imagify-profile .imagify-meteo-icon{padding-right:7px}#wp-admin-bar-imagify-profile .imagify-meteo-icon img{width:37px}#wp-admin-bar-imagify-profile .imagify-meteo-title{font-size:17px}#wp-admin-bar-imagify-profile .imagify-meteo-subs{color:#72889F}#wpadminbar #wp-admin-bar-imagify-profile strong{font-weight:700}#wpadminbar #wp-admin-bar-imagify-profile .imagify-user-plan,#wpadminbar #wp-admin-bar-imagify-profile a{padding:0;color:#40B1D0}#wpadminbar #wp-admin-bar-imagify-profile .imagify-account-link{display:table}#wpadminbar #wp-admin-bar-imagify-profile .imagify-account-link>*{display:table-cell}#wpadminbar #wp-admin-bar-imagify-profile .imagify-space-left{max-width:210px;min-width:210px;width:210px}#wpadminbar #wp-admin-bar-imagify-profile .imagify-space-left p{font-size:12px}#wp-admin-bar-imagify-profile .imagify-error,#wp-admin-bar-imagify-profile .imagify-warning{padding:10px;margin:0 -13px -13px}#wp-admin-bar-imagify-profile .imagify-error p+p,#wp-admin-bar-imagify-profile .imagify-warning p+p{margin-top:.5em}#wp-admin-bar-imagify-profile .imagify-error p+p+p,#wp-admin-bar-imagify-profile .imagify-warning p+p+p{margin-top:1em}#wpadminbar #wp-admin-bar-imagify-profile .imagify-btn-ghost{display:inline-block;height:auto;padding:7px 10px;border:1px solid #FFF;text-align:center;background:0 0;color:#FFF;border-radius:3px;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}#wpadminbar #wp-admin-bar-imagify-profile .imagify-btn-ghost:focus,#wpadminbar #wp-admin-bar-imagify-profile .imagify-btn-ghost:hover{background:#FFF;color:#888}#wpadminbar .imagify-warning *{background:#f5a623;color:#FFF;text-shadow:0 0 2px rgba(0,0,0,.2)}
1
+ #wp-admin-bar-imagify .button-text,.imagify-meteo-icon{display:inline-block;vertical-align:middle}.imagify-account,.imagify-account-link{padding-right:15px}.imagify-meteo-icon{height:38px;margin-right:10px}.imagify-user-plan{color:#40b1d0}.imagify-meteo-title.imagify-meteo-title{color:#FFF;font-size:17px}.imagify-space-left>p{color:#FFF}#wp-admin-bar-imagify-profile [class^=imagify-bar-]{position:relative;height:1.5em;width:100%;background:#60758D;color:#FFF;font-size:10px}#wp-admin-bar-imagify-profile .imagify-progress{height:1.5em}.imagify-progress{-webkit-transition:width .3s;-o-transition:width .3s;transition:width .3s}.imagify-bar-positive .imagify-progress{background:#8CC152}.imagify-bar-positive .imagify-barnb{color:#8CC152}.imagify-bar-negative .imagify-progress{background:#73818C}.imagify-bar-negative .imagify-barnb{color:#73818C}.imagify-bar-neutral .imagify-progress{background:#F5A623}.imagify-space-left .imagify-bar-negative .imagify-progress{background:#D0021B}#wpadminbar #wp-admin-bar-imagify-profile *{line-height:1.5;white-space:initial}#wpadminbar #wp-admin-bar-imagify .ab-submenu{padding-bottom:0}#wpadminbar #wp-admin-bar-imagify-profile .ab-item{height:auto;padding:0 13px}#wpadminbar #wp-admin-bar-imagify-profile{min-width:200px;padding:15px 0 10px;margin-top:.7em;background:#222}#wp-admin-bar-imagify .dashicons{font-family:dashicons;font-size:18px;vertical-align:middle;margin:0 5px 0 0}#wp-admin-bar-imagify .imagify-abq-row{display:table;width:100%}#wp-admin-bar-imagify .imagify-abq-row+.imagify-abq-row{margin-top:.75em}#wp-admin-bar-imagify .imagify-abq-row>*{display:table-cell}#wp-admin-bar-imagify-profile .imagify-meteo-icon{padding-right:7px}#wp-admin-bar-imagify-profile .imagify-meteo-icon img{width:37px}#wp-admin-bar-imagify-profile .imagify-meteo-title{font-size:17px}#wp-admin-bar-imagify-profile .imagify-meteo-subs{color:#72889F}#wpadminbar #wp-admin-bar-imagify-profile strong{font-weight:700}#wpadminbar #wp-admin-bar-imagify-profile .imagify-user-plan,#wpadminbar #wp-admin-bar-imagify-profile a{padding:0;color:#40B1D0}#wpadminbar #wp-admin-bar-imagify-profile .imagify-account-link{display:table}#wpadminbar #wp-admin-bar-imagify-profile .imagify-account-link>*{display:table-cell}#wpadminbar #wp-admin-bar-imagify-profile .imagify-space-left{max-width:210px;min-width:210px;width:210px}#wpadminbar #wp-admin-bar-imagify-profile .imagify-space-left p{font-size:12px}#wp-admin-bar-imagify-profile .imagify-error,#wp-admin-bar-imagify-profile .imagify-warning{padding:10px;margin:0 -13px -13px}#wp-admin-bar-imagify-profile .imagify-error p+p,#wp-admin-bar-imagify-profile .imagify-warning p+p{margin-top:.5em}#wp-admin-bar-imagify-profile .imagify-error p+p+p,#wp-admin-bar-imagify-profile .imagify-warning p+p+p{margin-top:1em}#wpadminbar #wp-admin-bar-imagify-profile .imagify-btn-ghost{display:inline-block;height:auto;padding:7px 10px;border:1px solid #FFF;text-align:center;background:0 0;color:#FFF;border-radius:3px;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}#wpadminbar #wp-admin-bar-imagify-profile .imagify-btn-ghost:focus,#wpadminbar #wp-admin-bar-imagify-profile .imagify-btn-ghost:hover{background:#FFF;color:#888}#wpadminbar .imagify-warning *{background:#f5a623;color:#FFF;text-shadow:0 0 2px rgba(0,0,0,.2)}
assets/css/admin.css CHANGED
@@ -2,6 +2,31 @@
2
  * == Utilities Classes
3
  */
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  /* Util: float */
6
  .imagify-start {
7
  float: left;
@@ -22,9 +47,21 @@
22
  }
23
 
24
  /* Util: margin/padding */
 
 
 
25
  .imagify-mt1.imagify-mt1 {
26
  margin-top: 1em;
27
  }
 
 
 
 
 
 
 
 
 
28
  .imagify-mb1.imagify-mb1 {
29
  margin-bottom: 1em;
30
  }
@@ -41,9 +78,15 @@
41
  .imagify-pl0.imagify-pl0.imagify-pl0 {
42
  padding-left: 0;
43
  }
 
 
 
44
  .imagify-pr1.imagify-pr1 {
45
  padding-right: 1em;
46
  }
 
 
 
47
 
48
  /* Util: Overflow */
49
  .imagify-oh {
@@ -53,11 +96,45 @@
53
  clear: both;
54
  }
55
  .imagify-clearfix:after,
56
- .imagify-inline-options:after {
 
 
57
  content: "";
58
  display: table;
 
59
  clear: both;
60
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  /* Default counter */
63
  .imagify-count.imagify-count {
@@ -75,6 +152,34 @@
75
  content: counter(num) ". ";
76
  }
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  /* Table layout */
79
  .imagify-table {
80
  display: table;
@@ -181,10 +286,13 @@ body.imagify-modal-is-open {
181
  .imagify-button-primary.imagify-button-primary,
182
  .imagify-button-secondary.imagify-button-secondary {
183
  height: auto;
184
- padding: 8px 20px;
185
  border: 0 none;
186
  font-size: 14px;
187
  font-weight: 600;
 
 
 
188
  box-shadow: 0 3px 0 rgba(0, 0, 0, .15);
189
  border-radius: 3px;
190
  cursor: pointer;
@@ -275,12 +383,64 @@ body.imagify-modal-is-open {
275
  color: #4a4a4a;
276
  box-shadow: 0 2px 0 rgba(0, 0, 0, .2);
277
  }
 
 
 
278
  .imagify-button-light.imagify-button-light:hover,
279
  .imagify-button-light.imagify-button-light:focus {
280
  color: #FFF;
281
  background: rgba(0, 0, 0, .2);
282
  }
283
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  /* Modifier */
285
  .imagify-section-positive .imagify-button-light {
286
  color: #709A41;
@@ -309,7 +469,7 @@ body.imagify-modal-is-open {
309
  vertical-align: middle;
310
  }
311
 
312
- /* Exception in Medias edition page and post Edition pages (insert media popin) */
313
  .wp_attachment_image .imagify-button-primary,
314
  .media-frame-content .imagify-button-primary {
315
  padding: 0 10px 1px;
@@ -329,20 +489,34 @@ body.imagify-modal-is-open {
329
  */
330
  .imagify-title.imagify-title {
331
  position: relative;
332
- padding: 30px 50px;
333
  font-size: 23px;
334
- background: #2E3243;
335
  color: #FFF;
336
  }
 
 
 
 
 
 
 
337
  .imagify-settings .imagify-title { /* (options and bulk) */
338
  display: flex;
339
  justify-content: space-between;
340
  align-items: center;
341
  }
342
  .imagify-settings .imagify-logo-block {
 
 
343
  flex-shrink: 0;
344
  flex-grow: 0;
 
345
  margin-right: 35px;
 
 
 
 
346
  }
347
  .imagify-settings .imagify-title + .imagify-notice {
348
  margin: 0;
@@ -350,8 +524,26 @@ body.imagify-modal-is-open {
350
  padding-top: 15px;
351
  padding-bottom: 15px;
352
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  .imagify-logo {
 
354
  vertical-align: top;
 
355
  }
356
  .imagify-sub-header,
357
  .imagify-sub-title.imagify-sub-title, /* heavier is better */
@@ -385,6 +577,28 @@ body.imagify-modal-is-open {
385
  margin-bottom: 0;
386
  }
387
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  @media (max-width: 1120px) {
389
  .imagify-settings .imagify-title {
390
  flex-wrap: wrap;
@@ -394,12 +608,28 @@ body.imagify-modal-is-open {
394
  .imagify-settings-section {
395
  padding: 10px 20px;
396
  }
397
- [id="imagify-settings"],
 
 
 
398
  .imagify-welcome .imagify-settings-section {
399
  border: 1px solid #D9D9D9;
400
- border-top: 0 none;
401
  background: #FFF;
402
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
403
  .imagify-br {
404
  line-height: 2;
405
  }
@@ -457,20 +687,24 @@ p.imagify-section-title.imagify-section-title {
457
  .imagify-important {
458
  color: #F5A623;
459
  }
 
 
 
 
460
  .imagify-info,
461
  .imagify-info a {
462
- color: #40B1D0;
463
  font-size: 12px;
464
  }
465
  .imagify-info {
466
  position: relative;
467
  display: inline-block;
468
  padding-left: 25px;
469
- vertical-align: top;
470
  }
471
  .imagify-info .dashicons {
472
  position: absolute;
473
- left: 0; top: -1px;
 
474
  }
475
 
476
  /* Custom checkboxes in CSS */
@@ -478,6 +712,7 @@ p.imagify-section-title.imagify-section-title {
478
  .imagify-settings.imagify-settings [type="checkbox"]:checked,
479
  .imagify-checkbox.imagify-checkbox:not(:checked),
480
  .imagify-checkbox.imagify-checkbox:checked {
 
481
  opacity: 0.01;
482
  }
483
  .imagify-settings.imagify-settings [type="checkbox"]:not(:checked):focus,
@@ -494,9 +729,14 @@ p.imagify-section-title.imagify-section-title {
494
  .imagify-checkbox.imagify-checkbox:not(:checked) + label,
495
  .imagify-checkbox.imagify-checkbox:checked + label {
496
  position: relative;
497
- padding-left: 6px;
 
 
 
498
  cursor: pointer;
499
- vertical-align: top;
 
 
500
  }
501
 
502
  /* checkbox aspect */
@@ -507,11 +747,10 @@ p.imagify-section-title.imagify-section-title {
507
  content: '';
508
  position: absolute;
509
  left: 0; top: 0;
510
- width: 28px; height: 28px;
511
- margin: 0 0 0 -24px;
512
  border: 2px solid #8BA6B4;
513
  background: #FFFFFF;
514
- border-radius: 4px;
515
  }
516
  /* checked mark aspect */
517
  .imagify-settings [type="checkbox"]:not(:checked) + label:after,
@@ -521,7 +760,9 @@ p.imagify-section-title.imagify-section-title {
521
  content: "✓";
522
  position: absolute;
523
  font-size: 1.4em;
524
- top: 3px; left: -16px;
 
 
525
  -webkit-transition: all .2s;
526
  -moz-transition: all .2s;
527
  -ms-transition: all .2s;
@@ -597,6 +838,32 @@ p.imagify-section-title.imagify-section-title {
597
  border-color: #40b1d0;
598
  }
599
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
  /* Custom radio in CSS */
601
  .imagify-inline-options {
602
  position: relative;
@@ -626,8 +893,7 @@ p.imagify-section-title.imagify-section-title {
626
  letter-spacing: 0.1em;
627
  color: #FFF;
628
  background: #2E3243;
629
- border-left: 1px solid rgba(255, 255, 255, 0.2);
630
- box-shadow: 0 -3px 0 rgba(0, 0, 0, 0.1) inset, inset -1px 0 0 rgba(255, 255, 255, 0.2);
631
  z-index: 2;
632
  -webkit-transition: all .275s;
633
  transition: all .275s;
@@ -651,6 +917,67 @@ p.imagify-section-title.imagify-section-title {
651
  margin-top: 15px;
652
  }
653
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
  /**
655
  * == Columns
656
  */
@@ -930,18 +1257,12 @@ ul.imagify-datas-details.imagify-datas-details {
930
  color: #FFF;
931
  font-size: 17px;
932
  }
933
- .imagify-space-left {
934
- display: inline-block;
935
- min-height: 38px;
936
- min-width: 245px;
937
- vertical-align: middle;
938
- }
939
  .imagify-space-left > p {
940
  color: #FFF;
941
  }
942
  [class^="imagify-bar-"] {
943
  position: relative;
944
- height: 1.5em;
945
  width: 100%;
946
  background: #60758D;
947
  color: #FFF;
@@ -949,7 +1270,7 @@ ul.imagify-datas-details.imagify-datas-details {
949
  }
950
 
951
  .imagify-progress {
952
- height: 1.5em;
953
  }
954
  .imagify-progress {
955
  transition: width .3s;
@@ -960,17 +1281,23 @@ ul.imagify-datas-details.imagify-datas-details {
960
  .imagify-bar-positive .imagify-barnb {
961
  color: #8CC152;
962
  }
 
 
 
 
 
 
963
  .imagify-bar-negative .imagify-progress {
964
- background: #73818C;
965
  }
966
  .imagify-bar-negative .imagify-barnb {
967
- color: #73818C;
968
  }
969
  .imagify-bar-neutral .imagify-progress {
970
  background: #F5A623;
971
  }
972
  .imagify-space-left .imagify-bar-negative .imagify-progress {
973
- background: #D0021B;
974
  }
975
 
976
  .imagify-btn-ghost {
@@ -1114,9 +1441,28 @@ ul.imagify-datas-details.imagify-datas-details {
1114
  .imagify-col {
1115
  float: left;
1116
  width: 50%;
1117
- }
1118
- .imagify-col {
1119
  -webkit-flex-basis: 50%;
1120
  -ms-flex-preferred-size: 50%;
1121
  flex-basis: 50%;
1122
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  * == Utilities Classes
3
  */
4
 
5
+ /* Util: Flexbox */
6
+ .imagify-flex {
7
+ display: flex;
8
+ }
9
+ .imagify-vcenter {
10
+ align-items: center;
11
+ }
12
+ .imagify-noshrink {
13
+ flex-shrink: 0;
14
+ }
15
+ .imagify-nogrow {
16
+ flex-grow: 0;
17
+ }
18
+
19
+ /* Util: dimension */
20
+ .imagify-wauto {
21
+ width: auto;
22
+ }
23
+ .imagify-hauto {
24
+ height: auto;
25
+ }
26
+ .imagify-full-width {
27
+ width: 100%;
28
+ }
29
+
30
  /* Util: float */
31
  .imagify-start {
32
  float: left;
47
  }
48
 
49
  /* Util: margin/padding */
50
+ .imagify-mt0.imagify-mt0 {
51
+ margin-top: 0;
52
+ }
53
  .imagify-mt1.imagify-mt1 {
54
  margin-top: 1em;
55
  }
56
+ .imagify-mt2.imagify-mt2 {
57
+ margin-top: 2em;
58
+ }
59
+ .imagify-mt3.imagify-mt3 {
60
+ margin-top: 3em;
61
+ }
62
+ .imagify-mb0.imagify-mb0 {
63
+ margin-bottom: 0;
64
+ }
65
  .imagify-mb1.imagify-mb1 {
66
  margin-bottom: 1em;
67
  }
78
  .imagify-pl0.imagify-pl0.imagify-pl0 {
79
  padding-left: 0;
80
  }
81
+ .imagify-pb0.imagify-pb0 {
82
+ padding-bottom: 0;
83
+ }
84
  .imagify-pr1.imagify-pr1 {
85
  padding-right: 1em;
86
  }
87
+ .imagify-pr2.imagify-pr2 {
88
+ padding-right: 2em;
89
+ }
90
 
91
  /* Util: Overflow */
92
  .imagify-oh {
96
  clear: both;
97
  }
98
  .imagify-clearfix:after,
99
+ .imagify-inline-options:after,
100
+ .imagify-settings-main-content:after,
101
+ .imagify-settings-section:after {
102
  content: "";
103
  display: table;
104
+ width: 100%;
105
  clear: both;
106
  }
107
+ .imagify-setting-optim-level .imagify-inline-options:after {
108
+ display: none;
109
+ }
110
+
111
+ /* Util: Dividers */
112
+ .imagify-divider {
113
+ height: 1px;
114
+ margin: 20px 0;
115
+ background: #D2D3D6;
116
+ }
117
+ .imagify-pipe {
118
+ display: inline-block;
119
+ margin: 0 .75em;
120
+ vertical-align: middle;
121
+ height: 15px;
122
+ width: 1px;
123
+ background: #979797;
124
+ }
125
+
126
+ /* Titles */
127
+ .imagify-h3-like.imagify-h3-like.imagify-h3-like {
128
+ margin-bottom: 0;
129
+ font-size: 19px;
130
+ font-weight: 500;
131
+ color: #1F2332;
132
+ }
133
+ .imagify-h4-like.imagify-h4-like.imagify-h4-like {
134
+ font-size: 14px;
135
+ font-weight: bold;
136
+ color: #2E3243;
137
+ }
138
 
139
  /* Default counter */
140
  .imagify-count.imagify-count {
152
  content: counter(num) ". ";
153
  }
154
 
155
+ /* List counter */
156
+ .imagify-count-list {
157
+ counter-reset: listcount;
158
+ }
159
+ .imagify-count-list li {
160
+ display: flex;
161
+ align-items: center;
162
+ }
163
+ .imagify-count-list li + li {
164
+ margin-top: .5em;
165
+ }
166
+ .imagify-count-list li:before {
167
+ display: flex;
168
+ flex-basis: 24px;
169
+ flex-shrink: 0;
170
+ flex-grow: 0;
171
+ align-items: center;
172
+ justify-content: center;
173
+ margin-right: 16px;
174
+ border: 2px solid #40b1d0;
175
+ width: 24px;
176
+ height: 24px;
177
+ counter-increment: listcount 1;
178
+ content: counter(listcount);
179
+ color: #40b1d0;
180
+ border-radius: 50%;
181
+ }
182
+
183
  /* Table layout */
184
  .imagify-table {
185
  display: table;
286
  .imagify-button-primary.imagify-button-primary,
287
  .imagify-button-secondary.imagify-button-secondary {
288
  height: auto;
289
+ padding: 11px 22px;
290
  border: 0 none;
291
  font-size: 14px;
292
  font-weight: 600;
293
+ text-transform: uppercase;
294
+ letter-spacing: 0.01em;
295
+ word-spacing: 0.01em;
296
  box-shadow: 0 3px 0 rgba(0, 0, 0, .15);
297
  border-radius: 3px;
298
  cursor: pointer;
383
  color: #4a4a4a;
384
  box-shadow: 0 2px 0 rgba(0, 0, 0, .2);
385
  }
386
+ .imagify-block-secondary .imagify-button-light.imagify-button-light {
387
+ color: #6F9C3B;
388
+ }
389
  .imagify-button-light.imagify-button-light:hover,
390
  .imagify-button-light.imagify-button-light:focus {
391
  color: #FFF;
392
  background: rgba(0, 0, 0, .2);
393
  }
394
 
395
+ /* Buttons clean */
396
+ .button.imagify-button-clean,
397
+ .imagify-button-clean {
398
+ padding: 0;
399
+ background: transparent;
400
+ box-shadow: none;
401
+ }
402
+ .imagify-button-clean .dashicons-plus {
403
+ width: 32px;
404
+ height: 25px;
405
+ }
406
+ .imagify-button-clean .dashicons-plus:before {
407
+ display: flex;
408
+ align-items: center;
409
+ justify-content: center;
410
+ width: 25px;
411
+ height: 22px;
412
+ margin-left: 2px;
413
+ padding-top: 3px;
414
+ font-size: 17px;
415
+ background: #40B1D0;
416
+ color: #FFF;
417
+ transition: all .275s;
418
+ }
419
+ .button.imagify-button-clean:hover,
420
+ .button.imagify-button-clean:focus,
421
+ .button.imagify-button-clean:active,
422
+ .button.imagify-button-clean[disabled] {
423
+ background: transparent!important;
424
+ color: #343A49;
425
+ box-shadow: none;
426
+ }
427
+ .button.imagify-button-clean:hover .dashicons-plus:before,
428
+ .button.imagify-button-clean:focus .dashicons-plus:before {
429
+ background: #343A49;
430
+ }
431
+
432
+ /* Buttons link-like */
433
+ button.imagify-link-like {
434
+ border: 0;
435
+ padding: 0;
436
+ color: inherit;
437
+ text-decoration: underline;
438
+ font-size: 13px;
439
+ box-shadow: none;
440
+ background: transparent;
441
+ cursor: pointer;
442
+ }
443
+
444
  /* Modifier */
445
  .imagify-section-positive .imagify-button-light {
446
  color: #709A41;
469
  vertical-align: middle;
470
  }
471
 
472
+ /* Exception in Media edition page and post Edition pages (insert media popin) */
473
  .wp_attachment_image .imagify-button-primary,
474
  .media-frame-content .imagify-button-primary {
475
  padding: 0 10px 1px;
489
  */
490
  .imagify-title.imagify-title {
491
  position: relative;
492
+ padding: 10px 30px;
493
  font-size: 23px;
494
+ background: #1F2332;
495
  color: #FFF;
496
  }
497
+ .imagify-welcome .imagify-logo {
498
+ opacity: 1;
499
+ }
500
+ .imagify-welcome .imagify-title {
501
+ display: flex;
502
+ padding: 20px 30px;
503
+ }
504
  .imagify-settings .imagify-title { /* (options and bulk) */
505
  display: flex;
506
  justify-content: space-between;
507
  align-items: center;
508
  }
509
  .imagify-settings .imagify-logo-block {
510
+ display: flex;
511
+ align-items: center;
512
  flex-shrink: 0;
513
  flex-grow: 0;
514
+ padding: 0;
515
  margin-right: 35px;
516
+ color: inherit;
517
+ }
518
+ .imagify-logo-block sup {
519
+ color: #1F2332;
520
  }
521
  .imagify-settings .imagify-title + .imagify-notice {
522
  margin: 0;
524
  padding-top: 15px;
525
  padding-bottom: 15px;
526
  }
527
+ .imagify-title .title-text {
528
+ font-size: 28px;
529
+ font-weight: bold;
530
+ color: #FFF;
531
+ }
532
+ .imagify-lb-icon {
533
+ padding-right: 18px;
534
+ }
535
+ .imagify-lb-text img {
536
+ margin-bottom: .15em;
537
+ }
538
+ .imagify-lb-text {
539
+ font-size: 23px;
540
+ font-weight: bold;
541
+ color: #FFF;
542
+ }
543
  .imagify-logo {
544
+ display: block;
545
  vertical-align: top;
546
+ opacity: .4;
547
  }
548
  .imagify-sub-header,
549
  .imagify-sub-title.imagify-sub-title, /* heavier is better */
577
  margin-bottom: 0;
578
  }
579
 
580
+ /* Documentation link */
581
+ .imagify-settings .imagify-documentation-link-box {
582
+ display: flex;
583
+ padding: 12px 13px 14px;
584
+ border: 1px solid #40b1d0;
585
+ color: #E5EBEF;
586
+ border-radius: 3px;
587
+ }
588
+ .imagify-documentation-link-icon {
589
+ width: 23px;
590
+ height: 31px;
591
+ font-size: 2.6em;
592
+ margin-right: 15px;
593
+ line-height: 1.3;
594
+ }
595
+ .imagify-documentation-link-box span {
596
+ font-size: 12px;
597
+ }
598
+ .imagify-documentation-link-box a {
599
+ font-weight: bold;
600
+ }
601
+
602
  @media (max-width: 1120px) {
603
  .imagify-settings .imagify-title {
604
  flex-wrap: wrap;
608
  .imagify-settings-section {
609
  padding: 10px 20px;
610
  }
611
+ .imagify-account-info-col .imagify-settings-section {
612
+ padding-right: 0;
613
+ }
614
+ .imagify-settings-main-content,
615
  .imagify-welcome .imagify-settings-section {
616
  border: 1px solid #D9D9D9;
617
+ border-top-width: 0;
618
  background: #FFF;
619
  }
620
+
621
+ .imagify-settings-main-content {
622
+ padding-bottom: 20px;
623
+ }
624
+ .imagify-settings-main-content p {
625
+ font-size: 14px;
626
+ }
627
+
628
+ .imagify-settings-main-content + .imagify-settings-main-content {
629
+ margin-top: 20px;
630
+ border-top-width: 1px;
631
+ }
632
+
633
  .imagify-br {
634
  line-height: 2;
635
  }
687
  .imagify-important {
688
  color: #F5A623;
689
  }
690
+ .imagify-success,
691
+ .imagify-settings .imagify-success {
692
+ color: #8BC34A;
693
+ }
694
  .imagify-info,
695
  .imagify-info a {
696
+ color: #7A8996;
697
  font-size: 12px;
698
  }
699
  .imagify-info {
700
  position: relative;
701
  display: inline-block;
702
  padding-left: 25px;
 
703
  }
704
  .imagify-info .dashicons {
705
  position: absolute;
706
+ left: 0; top: 0;
707
+ color: #40B1D0;
708
  }
709
 
710
  /* Custom checkboxes in CSS */
712
  .imagify-settings.imagify-settings [type="checkbox"]:checked,
713
  .imagify-checkbox.imagify-checkbox:not(:checked),
714
  .imagify-checkbox.imagify-checkbox:checked {
715
+ position: absolute;
716
  opacity: 0.01;
717
  }
718
  .imagify-settings.imagify-settings [type="checkbox"]:not(:checked):focus,
729
  .imagify-checkbox.imagify-checkbox:not(:checked) + label,
730
  .imagify-checkbox.imagify-checkbox:checked + label {
731
  position: relative;
732
+ display: flex;
733
+ align-items: center;
734
+ min-height: 24px;
735
+ padding-left: 40px;
736
  cursor: pointer;
737
+ font-size: 14px;
738
+ font-weight: bold;
739
+ color: #2E3243;
740
  }
741
 
742
  /* checkbox aspect */
747
  content: '';
748
  position: absolute;
749
  left: 0; top: 0;
750
+ width: 22px; height: 22px;
 
751
  border: 2px solid #8BA6B4;
752
  background: #FFFFFF;
753
+ border-radius: 3px;
754
  }
755
  /* checked mark aspect */
756
  .imagify-settings [type="checkbox"]:not(:checked) + label:after,
760
  content: "✓";
761
  position: absolute;
762
  font-size: 1.4em;
763
+ top: -2px; left: 4.5px;
764
+ color: #8BA6B4;
765
+ font-weight: normal;
766
  -webkit-transition: all .2s;
767
  -moz-transition: all .2s;
768
  -ms-transition: all .2s;
838
  border-color: #40b1d0;
839
  }
840
 
841
+ /* Checkbox groups */
842
+ .imagify-check-group {
843
+ padding-left: 2px;
844
+ margin-bottom: 0;
845
+ }
846
+ .imagify-check-group.imagify-is-scrollable {
847
+ max-height: 15em;
848
+ overflow-y: auto;
849
+ padding: 8px;
850
+ margin: 1.5em 0 0 -8px;
851
+ background: #F4F7F9;
852
+ border: 1px solid #D2D3D6;
853
+ border-radius: 3px;
854
+ }
855
+ .imagify-is-scrollable legend + p {
856
+ margin-top: 0;
857
+ }
858
+ .imagify-is-scrollable [type="checkbox"]:not(:checked) + label:before,
859
+ .imagify-is-scrollable [type="checkbox"]:checked + label:before {
860
+ background: #F4F7F9;
861
+ }
862
+ .imagify-settings .imagify-check-group.imagify-check-group label {
863
+ color: #338EA6;
864
+ font-weight: 500;
865
+ }
866
+
867
  /* Custom radio in CSS */
868
  .imagify-inline-options {
869
  position: relative;
893
  letter-spacing: 0.1em;
894
  color: #FFF;
895
  background: #2E3243;
896
+ box-shadow: 0 -3px 0 rgba(0, 0, 0, 0.1) inset;
 
897
  z-index: 2;
898
  -webkit-transition: all .275s;
899
  transition: all .275s;
917
  margin-top: 15px;
918
  }
919
 
920
+ /**
921
+ * Account information columns (Settings & Bulk)
922
+ */
923
+ .imagify-col.imagify-col.imagify-account-info-col,
924
+ .imagify-account-info-col {
925
+ width: 380px;
926
+ max-width: 100%;
927
+ padding: 0 20px 0 0;
928
+ }
929
+ .imagify-col.imagify-col.imagify-shared-with-account-col,
930
+ .imagify-shared-with-account-col {
931
+ width: calc(100% - 380px);
932
+ padding: 0;
933
+ }
934
+
935
+ .imagify-account-info-col .imagify-options-title {
936
+ padding: 24px 26px;
937
+ color: #FFF;
938
+ background: #1F2332;
939
+ }
940
+ .imagify-block-secondary {
941
+ padding: 26px 26px 35px;
942
+ border: 1px solid #75A345;
943
+ background: #8BC34A;
944
+ border-radius: 3px;
945
+ color: #FFF;
946
+ }
947
+ .imagify-block-secondary.imagify-block-secondary p,
948
+ .imagify-account-info-col .imagify-block-secondary.imagify-block-secondary h3 {
949
+ color: inherit;
950
+ }
951
+ .imagify-account-info-col .imagify-col-content h3:first-child {
952
+ margin-top: 0;
953
+ }
954
+ .imagify-account-info-col .imagify-col-content h3 {
955
+ font-size: 19px;
956
+ }
957
+ .imagify-account-info-col .imagify-col-content p {
958
+ margin: 1.5em 0;
959
+ }
960
+ .imagify-account-info-col .imagify-col-content p:first-child {
961
+ margin-top: 0;
962
+ }
963
+
964
+ .imagify-user-plan-label {
965
+ float: right;
966
+ margin-top: -4px;
967
+ padding: 2px 10px;
968
+ border: 2px solid #40B1D0;
969
+ font-size: 14px;
970
+ text-transform: uppercase;
971
+ letter-spacing: 0.02em;
972
+ color: #40B1D0;
973
+ border-radius: 3px;
974
+ }
975
+
976
+ /* Content given after remote answer, avoid "flash" effect on content */
977
+ .imagify-user-plan-label:empty {
978
+ display: none;
979
+ }
980
+
981
  /**
982
  * == Columns
983
  */
1257
  color: #FFF;
1258
  font-size: 17px;
1259
  }
 
 
 
 
 
 
1260
  .imagify-space-left > p {
1261
  color: #FFF;
1262
  }
1263
  [class^="imagify-bar-"] {
1264
  position: relative;
1265
+ height: 8px;
1266
  width: 100%;
1267
  background: #60758D;
1268
  color: #FFF;
1270
  }
1271
 
1272
  .imagify-progress {
1273
+ height: 8px;
1274
  }
1275
  .imagify-progress {
1276
  transition: width .3s;
1281
  .imagify-bar-positive .imagify-barnb {
1282
  color: #8CC152;
1283
  }
1284
+ .imagify-bar-primary .imagify-progress {
1285
+ background: #40B1D0;
1286
+ }
1287
+ .imagify-bar-primary .imagify-barnb {
1288
+ color: #40B1D0;
1289
+ }
1290
  .imagify-bar-negative .imagify-progress {
1291
+ background: #D2D3D6;
1292
  }
1293
  .imagify-bar-negative .imagify-barnb {
1294
+ color: #7A8996;
1295
  }
1296
  .imagify-bar-neutral .imagify-progress {
1297
  background: #F5A623;
1298
  }
1299
  .imagify-space-left .imagify-bar-negative .imagify-progress {
1300
+ background: #C51162;
1301
  }
1302
 
1303
  .imagify-btn-ghost {
1441
  .imagify-col {
1442
  float: left;
1443
  width: 50%;
1444
+ box-sizing: border-box;
 
1445
  -webkit-flex-basis: 50%;
1446
  -ms-flex-preferred-size: 50%;
1447
  flex-basis: 50%;
1448
  }
1449
+ .imagify-col {
1450
+ padding-right: 20px;
1451
+ }
1452
+ .imagify-col + .imagify-col {
1453
+ padding-right: 0;
1454
+ padding-left: 20px;
1455
+ }
1456
+
1457
+ .imagify-col:target {
1458
+ animation: hello 1s 3 linear backwards;
1459
+ }
1460
+
1461
+ @keyframes hello {
1462
+ 0%, 100% {
1463
+ background: #FFF;
1464
+ }
1465
+ 50% {
1466
+ background: #F4F7F9;
1467
+ }
1468
+ }
assets/css/admin.min.css CHANGED
@@ -1 +1 @@
1
- .imagify-cell,.imagify-cell.va-top,.va-top .imagify-cell{vertical-align:top}.imagify-columns,.imagify-data-item,.imagify-oh,body.imagify-modal-is-open{overflow:hidden}.imagify-start{float:left}.imagify-end{float:right}.imagify-txt-start.imagify-txt-start.imagify-txt-start{text-align:left}.imagify-txt-center.imagify-txt-center.imagify-txt-center{text-align:center}.imagify-txt-end.imagify-txt-end.imagify-txt-end{text-align:right}.imagify-mt1.imagify-mt1{margin-top:1em}.imagify-mb1.imagify-mb1{margin-bottom:1em}.imagify-mr1.imagify-mr1{margin-right:1em}.imagify-ml2.imagify-ml2{margin-left:2em}.imagify-mr2.imagify-mr2{margin-right:2em}.imagify-pl0.imagify-pl0.imagify-pl0{padding-left:0}.imagify-pr1.imagify-pr1{padding-right:1em}.imagify-clear{clear:both}.imagify-clearfix:after,.imagify-inline-options:after{content:"";display:table;clear:both}.imagify-count.imagify-count{counter-reset:num}.imagify-count .imagify-count-title{font-weight:700}.imagify-default-settings{color:#73818c;font-weight:400}.imagify-count .imagify-count-title:before{counter-increment:num 1;content:counter(num) ". "}.imagify-table{display:table;width:100%}.imagify-cell{display:table-cell;padding:10px}.imagify-bulk-submit .imagify-cell{padding-top:0}.imagify-spinner{display:inline-block;width:20px;height:20px;margin-right:5px;vertical-align:middle;background:url(../images/spinner.gif) 0 0/20px 20px no-repeat rgba(0,0,0,0);opacity:.7}.spinner.imagify-hidden{width:0;margin:4px 0 0}.imagify-primary.imagify-primary.imagify-primary{color:#40b1d0}.imagify-secondary.imagify-secondary.imagify-secondary,.imagify-valid{color:#8BC34A}.misc-pub-section.misc-pub-imagify h4{font-size:14px;margin-top:5px;margin-bottom:0}.imagify-chart{position:relative;top:1px;display:inline-block;vertical-align:middle}.imagify-chart-container{position:relative;display:inline-block;margin-right:5px}.imagify-chart-container canvas{display:block}.imagify-settings .button,.imagify-settings a,.imagify-settings input,.imagify-welcome .button,.imagify-welcome a,.imagify-weolcome input{-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-settings a{color:#40b1d0}.imagify-settings,.imagify-settings p,.imagify-settings th{color:#5F758E}.imagify-button-primary.imagify-button-primary,.imagify-button-secondary.imagify-button-secondary,.imagify-button.imagify-button,.imagify-notice .button,.imagify-settings .button,.imagify-welcome .button{height:auto;padding:8px 20px;border:0;font-size:14px;font-weight:600;-webkit-box-shadow:0 3px 0 rgba(0,0,0,.15);box-shadow:0 3px 0 rgba(0,0,0,.15);border-radius:3px;cursor:pointer;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.button-primary.button-mini{padding:2px 10px}.imagify-settings .button.button-mini-flat{padding:3px 6px 5px;font-size:12px;-webkit-box-shadow:none!important;box-shadow:none!important;line-height:1.2}.imagify-settings .button.button-mini-flat:focus,.imagify-settings .button.button-mini-flat:hover{-webkit-box-shadow:none!important;box-shadow:none!important}.imagify-button-ghost.imagify-button-ghost,.imagify-title .button-ghost.button-ghost{padding:2px 9px;border:1px solid #40B1D0;font-size:12px;font-weight:400;color:#40B1D0;background:0 0;-webkit-box-shadow:none;box-shadow:none}.imagify-button-ghost.imagify-button-ghost:focus,.imagify-button-ghost.imagify-button-ghost:hover,.imagify-title .button-ghost.button-ghost:focus,.imagify-title .button-ghost.button-ghost:hover{border-color:transparent;color:#000;background:#40B1D0}.imagify-button-ghost.imagify-button-ghost:focus,.imagify-button-ghost.imagify-button-ghost:hover{color:#FFF}.imagify-button-medium.imagify-button-medium{text-transform:uppercase;letter-spacing:.1em;padding:3px 10px;font-weight:700}.imagify-button-medium.imagify-button-ghost{border-width:2px}[class*=imagify-] .button .dashicons{margin-right:5px;vertical-align:middle}.imagify-button-primary.imagify-button-primary,.imagify-settings .button-primary.button-primary,.imagify-welcome .button-primary.button-primary{background:#40B1D0;color:#FFF;-webkit-box-shadow:0 3px 0 rgba(51,142,166,1);box-shadow:0 3px 0 rgba(51,142,166,1);text-shadow:0 -1px 1px #006799,1px 0 1px #006799,0 1px 1px #006799!important}.imagify-button-secondary.imagify-button-secondary{background:#8BC34A;color:#FFF;-webkit-box-shadow:0 3px 0 #6F9C3B;box-shadow:0 3px 0 #6F9C3B;text-shadow:0 -1px 1px #6F9C3B,1px 0 1px #6F9C3B,0 1px 1px #6F9C3B!important}.imagify-button-primary.imagify-button-primary:focus,.imagify-button-primary.imagify-button-primary:hover,.imagify-settings .button-primary:focus,.imagify-settings .button-primary:hover,.imagify-welcome .button-primary:focus,.imagify-welcome .button-primary:hover{background:#338ea6;-webkit-box-shadow:0 3px 0 #1f7a92;box-shadow:0 3px 0 #1f7a92}.imagify-button-secondary.imagify-button-secondary:focus,.imagify-button-secondary.imagify-button-secondary:hover{background:#6F9C3B;color:#FFF}.imagify-button-light.imagify-button-light{background:#FFF;color:#4a4a4a;-webkit-box-shadow:0 2px 0 rgba(0,0,0,.2);box-shadow:0 2px 0 rgba(0,0,0,.2)}.imagify-button-light.imagify-button-light:focus,.imagify-button-light.imagify-button-light:hover{color:#FFF;background:rgba(0,0,0,.2)}.imagify-section-positive .imagify-button-light{color:#709A41}.imagify-button.imagify-button-big{font-size:15px;padding:11px 30px}.imagify-button-big .dashicons{font-size:1.45em;margin-right:6px;margin-left:-4px}.imagify-button-primary.imagify-button-primary .dashicons,.imagify-button-secondary.imagify-button-secondary .dashicons,.imagify-button.imagify-button .dashicons,.imagify-notice .button .dashicons,.imagify-settings .button .dashicons,.imagify-welcome .button .dashicons{vertical-align:middle}[class*=imagify-] .button-text{display:inline-block;vertical-align:middle}.media-frame-content .imagify-button-primary,.wp_attachment_image .imagify-button-primary{padding:0 10px 1px;margin:0 5px 2px 0;font-size:13px;line-height:26px;-webkit-box-shadow:0 3px 0 rgba(51,142,166,1);box-shadow:0 3px 0 rgba(51,142,166,1)}.wp_attachment_image .imagify-button-primary{float:left}.imagify-title.imagify-title{position:relative;padding:30px 50px;font-size:23px;background:#2E3243;color:#FFF}.imagify-settings .imagify-title{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.imagify-settings .imagify-logo-block{-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;margin-right:35px}.imagify-settings .imagify-title+.imagify-notice{margin:0;border-right:1px solid #D9D9D9;padding-top:15px;padding-bottom:15px}.imagify-logo{vertical-align:top}.imagify-section,.imagify-settings div.submit,.imagify-sub-header,.imagify-sub-title.imagify-sub-title{margin:0;padding:20px;background:#F2F5F7}.imagify-section-positive,.imagify-sub-title.imagify-sub-title{padding-left:40px}.imagify-section-positive{background:#8cc152;color:#FFF}.imagify-section-positive p{color:#FFF}.imagify-section-gray{background:#D9E4EB}.imagify-section-gray .imagify-count-title{color:#4a4a4a}.imagify-section p:first-child{margin-top:0}.imagify-section p:last-child{margin-bottom:0}@media (max-width:1120px){.imagify-settings .imagify-title{-ms-flex-wrap:wrap;flex-wrap:wrap}}.imagify-settings-section{padding:10px 20px}.imagify-welcome .imagify-settings-section,[id=imagify-settings]{border:1px solid #D9D9D9;border-top:0 none;background:#FFF}.imagify-br{line-height:2}p.imagify-section-title.imagify-section-title{font-size:20px;margin-top:-.3em;margin-bottom:-.6em}.imagify-rate-us.imagify-rate-us{text-align:right;margin:-1em -2.4em -1em 0;color:#FFF}.imagify-rate-us a{color:#40B1D0}.imagify-rate-us .stars{display:inline-block;margin:2px 0 0 10px;text-decoration:none;letter-spacing:.2em;vertical-align:-1px}.imagify-rate-us .stars .dashicons:before{font-size:18px}.imagify-rate-us a:focus,.imagify-rate-us a:hover{color:#FEE102}@media (max-width:1220px){.imagify-rate-us.imagify-rate-us{position:static;margin-bottom:0;text-align:left}.imagify-rate-us.imagify-rate-us br{display:none}.imagify-rate-us .stars{display:block;margin-left:0}}.imagify-important{color:#F5A623}.imagify-info,.imagify-info a{color:#40B1D0;font-size:12px}.imagify-info{position:relative;display:inline-block;padding-left:25px;vertical-align:top}.imagify-info .dashicons{position:absolute;left:0;top:-1px}.imagify-checkbox.imagify-checkbox:checked,.imagify-checkbox.imagify-checkbox:not(:checked),.imagify-settings.imagify-settings [type=checkbox]:checked,.imagify-settings.imagify-settings [type=checkbox]:not(:checked){opacity:.01}.imagify-checkbox.imagify-checkbox:checked:focus,.imagify-checkbox.imagify-checkbox:not(:checked):focus,.imagify-settings.imagify-settings [type=checkbox]:checked:focus,.imagify-settings.imagify-settings [type=checkbox]:not(:checked):focus{-webkit-box-shadow:none!important;box-shadow:none!important;outline:0!important;border:0!important}.imagify-checkbox.imagify-checkbox:checked+label,.imagify-checkbox.imagify-checkbox:not(:checked)+label,.imagify-settings [type=checkbox]:checked+label,.imagify-settings [type=checkbox]:not(:checked)+label{position:relative;padding-left:6px;cursor:pointer;vertical-align:top}.imagify-checkbox.imagify-checkbox:checked+label:before,.imagify-checkbox.imagify-checkbox:not(:checked)+label:before,.imagify-settings [type=checkbox]:checked+label:before,.imagify-settings [type=checkbox]:not(:checked)+label:before{content:'';position:absolute;left:0;top:0;width:28px;height:28px;margin:0 0 0 -24px;border:2px solid #8BA6B4;background:#FFF;border-radius:4px}.imagify-checkbox.imagify-checkbox:checked+label:after,.imagify-checkbox.imagify-checkbox:not(:checked)+label:after,.imagify-settings [type=checkbox]:checked+label:after,.imagify-settings [type=checkbox]:not(:checked)+label:after{content:"✓";position:absolute;font-size:1.4em;top:3px;left:-16px;-webkit-transition:all .2s;-o-transition:all .2s;transition:all .2s}.imagify-checkbox.imagify-checkbox[disabled]:checked+label:before,.imagify-checkbox.imagify-checkbox[disabled]:not(:checked)+label:before,.imagify-settings [type=checkbox][disabled]:checked+label:before,.imagify-settings [type=checkbox][disabled]:not(:checked)+label:before{border-color:#ccc;background:#ddd}.imagify-checkbox.imagify-checkbox:not(:checked)+label:after,.imagify-settings [type=checkbox]:not(:checked)+label:after{opacity:0;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0)}.imagify-checkbox.imagify-checkbox:checked+label:after,.imagify-settings [type=checkbox]:checked+label:after{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.medium.imagify-checkbox:checked+label:before,.medium.imagify-checkbox:not(:checked)+label:before{width:22px;height:22px;border-width:1.5px;border-radius:2px;margin-top:0}.medium.imagify-checkbox:checked+label:after,.medium.imagify-checkbox:not(:checked)+label:after{font-size:1.1em;left:-17px;top:3px}.imagify-settings .mini[type=checkbox]:checked+label:before,.imagify-settings .mini[type=checkbox]:not(:checked)+label:before,.mini.imagify-checkbox:checked+label:before,.mini.imagify-checkbox:not(:checked)+label:before{width:15px;height:15px;border-width:1px;border-radius:2px;margin-top:0}.imagify-settings .mini[type=checkbox]:checked+label:after,.imagify-settings .mini[type=checkbox]:not(:checked)+label:after,.mini.imagify-checkbox:checked+label:after,.mini.imagify-checkbox:not(:checked)+label:after{font-size:.9em;left:-21px;top:-.5px}.imagify-checkbox.imagify-checkbox:checked:focus+label:before,.imagify-checkbox.imagify-checkbox:not(:checked):focus+label:before,.imagify-settings [type=checkbox]:checked:focus+label:before,.imagify-settings [type=checkbox]:not(:checked):focus+label:before{border-style:dotted;border-color:#40b1d0}.imagify-inline-options{position:relative;display:table;width:100%;max-width:600px;border-collapse:collapse}.imagify-inline-options input[type=radio]:checked,.imagify-inline-options input[type=radio]:not(:checked){position:absolute;left:5px;top:5px;display:none}.imagify-inline-options input[type=radio]:checked+label,.imagify-inline-options input[type=radio]:not(:checked)+label{position:relative;display:table-cell;padding:13px 10px;text-align:center;font-weight:600;font-size:16px;text-transform:uppercase;letter-spacing:.1em;color:#FFF;background:#2E3243;border-left:1px solid rgba(255,255,255,.2);-webkit-box-shadow:0 -3px 0 rgba(0,0,0,.1) inset,inset -1px 0 0 rgba(255,255,255,.2);box-shadow:0 -3px 0 rgba(0,0,0,.1) inset,inset -1px 0 0 rgba(255,255,255,.2);z-index:2;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-modal .h2,.imagify-modal .h3{letter-spacing:.075em;text-align:center;font-weight:400}.imagify-inline-options input[type=radio]:checked+label:first-of-type,.imagify-inline-options input[type=radio]:not(:checked)+label:first-of-type{border-radius:3px 0 0 3px}.imagify-inline-options input[type=radio]:checked+label:last-of-type,.imagify-inline-options input[type=radio]:not(:checked)+label:last-of-type{border-radius:0 3px 3px 0}.imagify-inline-options input[type=radio]:checked+label{background:#8BC34A}.imagify-inline-options .imagify-info{margin-top:15px}.imagify-columns{padding:15px 0;counter-reset:cols}.imagify-columns [class^=col-]{float:left;-webkit-box-sizing:border-box;box-sizing:border-box}.imagify-columns .col-1-3{width:33.333%;padding-left:28px}.imagify-columns .col-2-3{width:66.666%;padding-left:28px}.imagify-columns .col-1-2{width:50%;padding:0 20px}@media (max-width:830px){.imagify-columns [class^=col-]{float:none;margin-bottom:1.5em}.imagify-columns .col-1-2,.imagify-columns .col-1-3{width:auto;padding:0 28px;clear:both;padding-top:1em}}.column-imagify_optimized_file.column-imagify_optimized_file{width:300px;text-align:center;vertical-align:middle}.column-imagify_optimized_file>*{max-width:235px;margin:0 auto}@media (min-width:1151px) and (max-width:1800px){.column-imagify_optimized_file.column-imagify_optimized_file{width:235px}}@media (min-width:783px) and (max-width:1150px){.column-imagify_optimized_file.column-imagify_optimized_file{width:13em}table.media .column-title .has-media-icon~.row-actions.row-actions{margin-left:0}}@media (max-width:782px){table.media .column-imagify_optimized_file.column-imagify_optimized_file{text-align:left}table.media .imagify-datas-actions-links,table.media .imagify-datas-more-action{text-align:center}table.media .column-imagify_optimized_file .imagify-datas-actions-links a,table.media .column-imagify_optimized_file>*{max-width:100%;margin-left:0}}@media (min-width:783px) and (max-width:1150px),(max-width:360px){table.media .imagify-hide-if-small{position:absolute;margin:-1px;padding:0;height:1px;width:1px;overflow:hidden;clip:rect(0 0 0 0);border:0;word-wrap:normal!important}}.compat-field-imagify .label{vertical-align:top}.compat-field-imagify ul.imagify-datas-list{margin-top:7px;font-size:11px}ul.imagify-datas-list.imagify-datas-list{margin:0 auto;color:#555;font-size:10px}ul.imagify-datas-list .big{font-size:12px;color:#40B1D0}li.imagify-data-item{clear:both;margin-bottom:2px}ul.imagify-datas-list .imagify-data-item span.data,ul.imagify-datas-list .imagify-data-item strong{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box}ul.imagify-datas-list .imagify-data-item span.data{text-align:left;padding-right:5px}.compat-field-imagify .imagify-datas-list .imagify-data-item .data{width:130px;text-align:left;font-weight:700}ul.imagify-datas-list .imagify-data-item strong{text-align:left;padding-left:5px}.media-sidebar .imagify-datas-list .imagify-data-item .data{width:auto;float:none}.media-sidebar .imagify-datas-list .imagify-data-item strong{display:inline-block;width:auto;float:none}.media-sidebar .imagify-datas-list .imagify-data-item .imagify-chart{float:left}.imagify-datas-more-action.imagify-datas-more-action{margin:.4em auto;background:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(49%,transparent),color-stop(50%,rgba(0,0,0,.075)),color-stop(58%,rgba(0,0,0,.075)),color-stop(58%,transparent),to(transparent));background:-o-linear-gradient(top,transparent,transparent 49%,rgba(0,0,0,.075) 50%,rgba(0,0,0,.075) 58%,transparent 58%,transparent);background:linear-gradient(to bottom,transparent,transparent 49%,rgba(0,0,0,.075) 50%,rgba(0,0,0,.075) 58%,transparent 58%,transparent)}.imagify-datas-more-action a{display:inline-block;padding:0 5px;background:#40B1D0;color:#FFF;text-transform:uppercase;font-size:9px;font-weight:700;line-height:1.9;text-decoration:none}.imagify-datas-more-action a.is-open{background:#555}.imagify-datas-more-action a.is-open .dashicons{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.imagify-datas-more-action a .dashicons{font-size:14px;vertical-align:middle;line-height:.8}.imagify-datas-more-action a .dashicons:before{vertical-align:middle;line-height:20px}.imagify-datas-more-action .the-text{display:inline-block;vertical-align:middle;height:auto;line-height:inherit}ul.imagify-datas-details.imagify-datas-details{margin:.7em auto}.imagify-datas-details strong{color:#40B1D0}.imagify-datas-details .original{color:#555}.imagify-datas-actions-links{overflow:hidden;border-top:2px solid transparent;padding-top:5px;font-size:8px}.nggform .imagify-datas-actions-links{position:relative;z-index:2}.nggform .row-actions{z-index:1}.imagify-datas-actions-links a{position:relative;display:inline-block;padding-left:17px;text-decoration:none;font-weight:600}.compat-field-imagify .imagify-datas-actions-links{max-width:300px}.misc-pub-imagify .imagify-datas-actions-links{border-top:2px solid #f2f2f2;padding-bottom:5px}.column-imagify_optimized_file .imagify-datas-actions-links a{margin:0 .7em;padding-left:15px}.compat-field-imagify .imagify-datas-actions-links a,.misc-pub-imagify .imagify-datas-actions-links a{font-size:10px;float:left;width:50%}.media-sidebar .compat-field-imagify .imagify-datas-actions-links a,.submitbox .misc-pub-imagify .imagify-datas-actions-links a{display:block;width:auto;float:none}.imagify-datas-actions-links a:only-child{float:none;width:auto}.imagify-datas-details.is-open+.imagify-datas-actions-links{border-top-color:rgba(0,0,0,.075)}.imagify-datas-actions-links .dashicons{position:absolute;left:0;top:4px;width:12px;margin-right:2px;font-size:11px}.imagify-account,.imagify-account-link{padding-right:15px}.imagify-meteo-icon{display:inline-block;height:38px;vertical-align:middle;margin-right:10px}.imagify-user-plan{color:#40b1d0}.imagify-meteo-title.imagify-meteo-title{color:#FFF;font-size:17px}.imagify-space-left{display:inline-block;min-height:38px;min-width:245px;vertical-align:middle}.imagify-space-left>p{color:#FFF}[class^=imagify-bar-]{position:relative;height:1.5em;width:100%;background:#60758D;color:#FFF;font-size:10px}.imagify-progress{height:1.5em;-webkit-transition:width .3s;-o-transition:width .3s;transition:width .3s}.imagify-bar-positive .imagify-progress{background:#8CC152}.imagify-bar-positive .imagify-barnb{color:#8CC152}.imagify-bar-negative .imagify-progress{background:#73818C}.imagify-bar-negative .imagify-barnb{color:#73818C}.imagify-bar-neutral .imagify-progress{background:#F5A623}.imagify-space-left .imagify-bar-negative .imagify-progress{background:#D0021B}.imagify-btn-ghost{display:inline-block;height:auto;padding:7px 10px;border:1px solid #FFF;text-align:center;background:0 0;color:#FFF;border-radius:3px;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-btn-ghost:focus,.imagify-btn-ghost:hover{background:#FFF;color:#888}.imagify-error{background:#D0021B;color:#FFF}.imagify-settings-section .imagify-error{display:inline-block;padding:7px 10px;margin:10px 0 0 45px;border-radius:3px}.imagify-settings-section .imagify-error code{font-weight:400}.imagify-settings-section .imagify-error.hidden{display:none}.imagify-warning{background:#f5a623;color:#FFF;text-shadow:0 0 2px rgba(0,0,0,.2)}.imagify-modal{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.js .imagify-modal{display:none;position:fixed;top:0;right:0;bottom:0;left:0;background-color:#1F2332;background-color:rgba(31,35,50,.95);z-index:99999}.imagify-modal-content{-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;width:800px;max-width:95%;max-height:90vw;overflow:auto;padding:20px 25px;margin:1em auto;background:#FFF;-webkit-box-shadow:1px 1px 4px rgba(0,0,0,.7);box-shadow:1px 1px 4px rgba(0,0,0,.7);border-radius:3px}#imagify-visual-comparison .imagify-modal-content,.imagify-visual-comparison .imagify-modal-content{max-width:1400px;background:0 0;padding:5px;-webkit-box-shadow:none;box-shadow:none;border-radius:0}.imagify-modal .h2{margin:.5em 0;color:#8ba6b4;font-size:24px}.imagify-modal .h3{color:#40b1d0;font-size:18px}.imagify-modal .close-btn{display:none;visibility:hidden;position:absolute;right:20px;top:20px;font-size:1.2em;border:0;background:0 0;border-radius:0;cursor:pointer}.imagify-modal .close-btn i{margin-left:-2px}.imagify-modal .close-btn:focus,.imagify-modal .close-btn:hover{color:#40b1d0}.js .imagify-modal .close-btn{display:block;visibility:visible}.imagify-visual-comparison .close-btn,.wp_attachment_image #imagify-visual-comparison .close-btn{top:0}.imagify-visual-comparison .imagify-modal-content,.wp_attachment_image #imagify-visual-comparison .imagify-modal-content{padding-top:40px}.imagify-col{float:left;width:50%;-ms-flex-preferred-size:50%;flex-basis:50%}
1
+ .imagify-count-list li,.imagify-flex{display:-webkit-box;display:-ms-flexbox}.imagify-oh,body.imagify-modal-is-open{overflow:hidden}.imagify-flex{display:flex}.imagify-vcenter{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.imagify-noshrink{-ms-flex-negative:0;flex-shrink:0}.imagify-nogrow{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}.imagify-wauto{width:auto}.imagify-hauto{height:auto}.imagify-full-width{width:100%}.imagify-start{float:left}.imagify-end{float:right}.imagify-txt-start.imagify-txt-start.imagify-txt-start{text-align:left}.imagify-txt-center.imagify-txt-center.imagify-txt-center{text-align:center}.imagify-txt-end.imagify-txt-end.imagify-txt-end{text-align:right}.imagify-mt0.imagify-mt0{margin-top:0}.imagify-mt1.imagify-mt1{margin-top:1em}.imagify-mt2.imagify-mt2{margin-top:2em}.imagify-mt3.imagify-mt3{margin-top:3em}.imagify-mb0.imagify-mb0{margin-bottom:0}.imagify-mb1.imagify-mb1{margin-bottom:1em}.imagify-mr1.imagify-mr1{margin-right:1em}.imagify-ml2.imagify-ml2{margin-left:2em}.imagify-mr2.imagify-mr2{margin-right:2em}.imagify-pl0.imagify-pl0.imagify-pl0{padding-left:0}.imagify-pb0.imagify-pb0{padding-bottom:0}.imagify-pr1.imagify-pr1{padding-right:1em}.imagify-pr2.imagify-pr2{padding-right:2em}.imagify-clear{clear:both}.imagify-clearfix:after,.imagify-inline-options:after,.imagify-settings-main-content:after,.imagify-settings-section:after{content:"";display:table;width:100%;clear:both}.imagify-setting-optim-level .imagify-inline-options:after{display:none}.imagify-divider{height:1px;margin:20px 0;background:#D2D3D6}.imagify-pipe{display:inline-block;margin:0 .75em;vertical-align:middle;height:15px;width:1px;background:#979797}.imagify-cell,.imagify-cell.va-top,.va-top .imagify-cell{vertical-align:top}.imagify-h3-like.imagify-h3-like.imagify-h3-like{margin-bottom:0;font-size:19px;font-weight:500;color:#1F2332}.imagify-h4-like.imagify-h4-like.imagify-h4-like{font-size:14px;font-weight:700;color:#2E3243}.imagify-count.imagify-count{counter-reset:num}.imagify-count .imagify-count-title{font-weight:700}.imagify-default-settings{color:#73818c;font-weight:400}.imagify-count .imagify-count-title:before{counter-increment:num 1;content:counter(num) ". "}.imagify-count-list{counter-reset:listcount}.imagify-count-list li{display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.imagify-count-list li+li{margin-top:.5em}.imagify-count-list li:before{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-preferred-size:24px;flex-basis:24px;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-right:16px;border:2px solid #40b1d0;width:24px;height:24px;counter-increment:listcount 1;content:counter(listcount);color:#40b1d0;border-radius:50%}.imagify-table{display:table;width:100%}.imagify-cell{display:table-cell;padding:10px}.imagify-bulk-submit .imagify-cell{padding-top:0}.imagify-spinner{display:inline-block;width:20px;height:20px;margin-right:5px;vertical-align:middle;background:url(../images/spinner.gif) 0 0/20px 20px no-repeat rgba(0,0,0,0);opacity:.7}.spinner.imagify-hidden{width:0;margin:4px 0 0}.imagify-primary.imagify-primary.imagify-primary{color:#40b1d0}.imagify-secondary.imagify-secondary.imagify-secondary,.imagify-valid{color:#8BC34A}.misc-pub-section.misc-pub-imagify h4{font-size:14px;margin-top:5px;margin-bottom:0}.imagify-chart{position:relative;top:1px;display:inline-block;vertical-align:middle}.imagify-chart-container{position:relative;display:inline-block;margin-right:5px}.imagify-chart-container canvas{display:block}.imagify-settings .button,.imagify-settings a,.imagify-settings input,.imagify-welcome .button,.imagify-welcome a,.imagify-weolcome input{-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-settings a{color:#40b1d0}.imagify-settings,.imagify-settings p,.imagify-settings th{color:#5F758E}.imagify-button-primary.imagify-button-primary,.imagify-button-secondary.imagify-button-secondary,.imagify-button.imagify-button,.imagify-notice .button,.imagify-settings .button,.imagify-welcome .button{height:auto;padding:11px 22px;border:0;font-size:14px;font-weight:600;text-transform:uppercase;letter-spacing:.01em;word-spacing:.01em;-webkit-box-shadow:0 3px 0 rgba(0,0,0,.15);box-shadow:0 3px 0 rgba(0,0,0,.15);border-radius:3px;cursor:pointer;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.button-primary.button-mini{padding:2px 10px}.imagify-settings .button.button-mini-flat{padding:3px 6px 5px;font-size:12px;-webkit-box-shadow:none!important;box-shadow:none!important;line-height:1.2}.imagify-settings .button.button-mini-flat:focus,.imagify-settings .button.button-mini-flat:hover{-webkit-box-shadow:none!important;box-shadow:none!important}.imagify-button-ghost.imagify-button-ghost,.imagify-title .button-ghost.button-ghost{padding:2px 9px;border:1px solid #40B1D0;font-size:12px;font-weight:400;color:#40B1D0;background:0 0;-webkit-box-shadow:none;box-shadow:none}.imagify-button-ghost.imagify-button-ghost:focus,.imagify-button-ghost.imagify-button-ghost:hover,.imagify-title .button-ghost.button-ghost:focus,.imagify-title .button-ghost.button-ghost:hover{border-color:transparent;color:#000;background:#40B1D0}.imagify-button-ghost.imagify-button-ghost:focus,.imagify-button-ghost.imagify-button-ghost:hover{color:#FFF}.imagify-button-medium.imagify-button-medium{text-transform:uppercase;letter-spacing:.1em;padding:3px 10px;font-weight:700}.imagify-button-medium.imagify-button-ghost{border-width:2px}[class*=imagify-] .button .dashicons{margin-right:5px;vertical-align:middle}.imagify-button-primary.imagify-button-primary,.imagify-settings .button-primary.button-primary,.imagify-welcome .button-primary.button-primary{background:#40B1D0;color:#FFF;-webkit-box-shadow:0 3px 0 rgba(51,142,166,1);box-shadow:0 3px 0 rgba(51,142,166,1);text-shadow:0 -1px 1px #006799,1px 0 1px #006799,0 1px 1px #006799!important}.imagify-button-secondary.imagify-button-secondary{background:#8BC34A;color:#FFF;-webkit-box-shadow:0 3px 0 #6F9C3B;box-shadow:0 3px 0 #6F9C3B;text-shadow:0 -1px 1px #6F9C3B,1px 0 1px #6F9C3B,0 1px 1px #6F9C3B!important}.imagify-button-primary.imagify-button-primary:focus,.imagify-button-primary.imagify-button-primary:hover,.imagify-settings .button-primary:focus,.imagify-settings .button-primary:hover,.imagify-welcome .button-primary:focus,.imagify-welcome .button-primary:hover{background:#338ea6;-webkit-box-shadow:0 3px 0 #1f7a92;box-shadow:0 3px 0 #1f7a92}.imagify-button-secondary.imagify-button-secondary:focus,.imagify-button-secondary.imagify-button-secondary:hover{background:#6F9C3B;color:#FFF}.imagify-button-light.imagify-button-light{background:#FFF;color:#4a4a4a;-webkit-box-shadow:0 2px 0 rgba(0,0,0,.2);box-shadow:0 2px 0 rgba(0,0,0,.2)}.imagify-block-secondary .imagify-button-light.imagify-button-light{color:#6F9C3B}.imagify-button-light.imagify-button-light:focus,.imagify-button-light.imagify-button-light:hover{color:#FFF;background:rgba(0,0,0,.2)}.button.imagify-button-clean,.imagify-button-clean{padding:0;background:0 0;-webkit-box-shadow:none;box-shadow:none}.imagify-button-clean .dashicons-plus{width:32px;height:25px}.imagify-button-clean .dashicons-plus:before{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:25px;height:22px;margin-left:2px;padding-top:3px;font-size:17px;background:#40B1D0;color:#FFF;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.button.imagify-button-clean:active,.button.imagify-button-clean:focus,.button.imagify-button-clean:hover,.button.imagify-button-clean[disabled]{background:0 0!important;color:#343A49;-webkit-box-shadow:none;box-shadow:none}.button.imagify-button-clean:focus .dashicons-plus:before,.button.imagify-button-clean:hover .dashicons-plus:before{background:#343A49}button.imagify-link-like{border:0;padding:0;color:inherit;text-decoration:underline;font-size:13px;-webkit-box-shadow:none;box-shadow:none;background:0 0;cursor:pointer}.imagify-section-positive .imagify-button-light{color:#709A41}.imagify-button.imagify-button-big{font-size:15px;padding:11px 30px}.imagify-button-big .dashicons{font-size:1.45em;margin-right:6px;margin-left:-4px}.imagify-button-primary.imagify-button-primary .dashicons,.imagify-button-secondary.imagify-button-secondary .dashicons,.imagify-button.imagify-button .dashicons,.imagify-notice .button .dashicons,.imagify-settings .button .dashicons,.imagify-welcome .button .dashicons{vertical-align:middle}[class*=imagify-] .button-text{display:inline-block;vertical-align:middle}.media-frame-content .imagify-button-primary,.wp_attachment_image .imagify-button-primary{padding:0 10px 1px;margin:0 5px 2px 0;font-size:13px;line-height:26px;-webkit-box-shadow:0 3px 0 rgba(51,142,166,1);box-shadow:0 3px 0 rgba(51,142,166,1)}.wp_attachment_image .imagify-button-primary{float:left}.imagify-title.imagify-title{position:relative;padding:10px 30px;font-size:23px;background:#1F2332;color:#FFF}.imagify-welcome .imagify-logo{opacity:1}.imagify-welcome .imagify-title{display:-webkit-box;display:-ms-flexbox;display:flex;padding:20px 30px}.imagify-settings .imagify-title{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.imagify-settings .imagify-logo-block{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;padding:0;margin-right:35px;color:inherit}.imagify-logo-block sup{color:#1F2332}.imagify-settings .imagify-title+.imagify-notice{margin:0;border-right:1px solid #D9D9D9;padding-top:15px;padding-bottom:15px}.imagify-title .title-text{font-size:28px;font-weight:700;color:#FFF}.imagify-lb-icon{padding-right:18px}.imagify-lb-text img{margin-bottom:.15em}.imagify-lb-text{font-size:23px;font-weight:700;color:#FFF}.imagify-logo{display:block;vertical-align:top;opacity:.4}.imagify-section,.imagify-settings div.submit,.imagify-sub-header,.imagify-sub-title.imagify-sub-title{margin:0;padding:20px;background:#F2F5F7}.imagify-section-positive,.imagify-sub-title.imagify-sub-title{padding-left:40px}.imagify-section-positive{background:#8cc152;color:#FFF}.imagify-section-positive p{color:#FFF}.imagify-section-gray{background:#D9E4EB}.imagify-section-gray .imagify-count-title{color:#4a4a4a}.imagify-section p:first-child{margin-top:0}.imagify-section p:last-child{margin-bottom:0}.imagify-settings .imagify-documentation-link-box{display:-webkit-box;display:-ms-flexbox;display:flex;padding:12px 13px 14px;border:1px solid #40b1d0;color:#E5EBEF;border-radius:3px}.imagify-documentation-link-icon{width:23px;height:31px;font-size:2.6em;margin-right:15px;line-height:1.3}.imagify-documentation-link-box span{font-size:12px}.imagify-documentation-link-box a{font-weight:700}@media (max-width:1120px){.imagify-settings .imagify-title{-ms-flex-wrap:wrap;flex-wrap:wrap}}.imagify-settings-section{padding:10px 20px}.imagify-account-info-col .imagify-settings-section{padding-right:0}.imagify-settings-main-content,.imagify-welcome .imagify-settings-section{border:1px solid #D9D9D9;border-top-width:0;background:#FFF}.imagify-settings-main-content{padding-bottom:20px}.imagify-settings-main-content p{font-size:14px}.imagify-settings-main-content+.imagify-settings-main-content{margin-top:20px;border-top-width:1px}.imagify-br{line-height:2}p.imagify-section-title.imagify-section-title{font-size:20px;margin-top:-.3em;margin-bottom:-.6em}.imagify-rate-us.imagify-rate-us{text-align:right;margin:-1em -2.4em -1em 0;color:#FFF}.imagify-rate-us a{color:#40B1D0}.imagify-rate-us .stars{display:inline-block;margin:2px 0 0 10px;text-decoration:none;letter-spacing:.2em;vertical-align:-1px}.imagify-rate-us .stars .dashicons:before{font-size:18px}.imagify-rate-us a:focus,.imagify-rate-us a:hover{color:#FEE102}@media (max-width:1220px){.imagify-rate-us.imagify-rate-us{position:static;margin-bottom:0;text-align:left}.imagify-rate-us.imagify-rate-us br{display:none}.imagify-rate-us .stars{display:block;margin-left:0}}.imagify-important{color:#F5A623}.imagify-settings .imagify-success,.imagify-success{color:#8BC34A}.imagify-info,.imagify-info a{color:#7A8996;font-size:12px}.imagify-info{position:relative;display:inline-block;padding-left:25px}.imagify-info .dashicons{position:absolute;left:0;top:0;color:#40B1D0}.imagify-checkbox.imagify-checkbox:checked,.imagify-checkbox.imagify-checkbox:not(:checked),.imagify-settings.imagify-settings [type=checkbox]:checked,.imagify-settings.imagify-settings [type=checkbox]:not(:checked){position:absolute;opacity:.01}.imagify-checkbox.imagify-checkbox:checked:focus,.imagify-checkbox.imagify-checkbox:not(:checked):focus,.imagify-settings.imagify-settings [type=checkbox]:checked:focus,.imagify-settings.imagify-settings [type=checkbox]:not(:checked):focus{-webkit-box-shadow:none!important;box-shadow:none!important;outline:0!important;border:0!important}.imagify-checkbox.imagify-checkbox:checked+label,.imagify-checkbox.imagify-checkbox:not(:checked)+label,.imagify-settings [type=checkbox]:checked+label,.imagify-settings [type=checkbox]:not(:checked)+label{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:24px;padding-left:40px;cursor:pointer;font-size:14px;font-weight:700;color:#2E3243}.imagify-checkbox.imagify-checkbox:checked+label:before,.imagify-checkbox.imagify-checkbox:not(:checked)+label:before,.imagify-settings [type=checkbox]:checked+label:before,.imagify-settings [type=checkbox]:not(:checked)+label:before{content:'';position:absolute;left:0;top:0;width:22px;height:22px;border:2px solid #8BA6B4;background:#FFF;border-radius:3px}.imagify-checkbox.imagify-checkbox:checked+label:after,.imagify-checkbox.imagify-checkbox:not(:checked)+label:after,.imagify-settings [type=checkbox]:checked+label:after,.imagify-settings [type=checkbox]:not(:checked)+label:after{content:"✓";position:absolute;font-size:1.4em;top:-2px;left:4.5px;color:#8BA6B4;font-weight:400;-webkit-transition:all .2s;-o-transition:all .2s;transition:all .2s}.imagify-checkbox.imagify-checkbox[disabled]:checked+label:before,.imagify-checkbox.imagify-checkbox[disabled]:not(:checked)+label:before,.imagify-settings [type=checkbox][disabled]:checked+label:before,.imagify-settings [type=checkbox][disabled]:not(:checked)+label:before{border-color:#ccc;background:#ddd}.imagify-checkbox.imagify-checkbox:not(:checked)+label:after,.imagify-settings [type=checkbox]:not(:checked)+label:after{opacity:0;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0)}.imagify-checkbox.imagify-checkbox:checked+label:after,.imagify-settings [type=checkbox]:checked+label:after{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.medium.imagify-checkbox:checked+label:before,.medium.imagify-checkbox:not(:checked)+label:before{width:22px;height:22px;border-width:1.5px;border-radius:2px;margin-top:0}.medium.imagify-checkbox:checked+label:after,.medium.imagify-checkbox:not(:checked)+label:after{font-size:1.1em;left:-17px;top:3px}.imagify-settings .mini[type=checkbox]:checked+label:before,.imagify-settings .mini[type=checkbox]:not(:checked)+label:before,.mini.imagify-checkbox:checked+label:before,.mini.imagify-checkbox:not(:checked)+label:before{width:15px;height:15px;border-width:1px;border-radius:2px;margin-top:0}.imagify-settings .mini[type=checkbox]:checked+label:after,.imagify-settings .mini[type=checkbox]:not(:checked)+label:after,.mini.imagify-checkbox:checked+label:after,.mini.imagify-checkbox:not(:checked)+label:after{font-size:.9em;left:-21px;top:-.5px}.imagify-checkbox.imagify-checkbox:checked:focus+label:before,.imagify-checkbox.imagify-checkbox:not(:checked):focus+label:before,.imagify-settings [type=checkbox]:checked:focus+label:before,.imagify-settings [type=checkbox]:not(:checked):focus+label:before{border-style:dotted;border-color:#40b1d0}.imagify-check-group{padding-left:2px;margin-bottom:0}.imagify-check-group.imagify-is-scrollable{max-height:15em;overflow-y:auto;padding:8px;margin:1.5em 0 0 -8px;background:#F4F7F9;border:1px solid #D2D3D6;border-radius:3px}.imagify-is-scrollable legend+p{margin-top:0}.imagify-is-scrollable [type=checkbox]:checked+label:before,.imagify-is-scrollable [type=checkbox]:not(:checked)+label:before{background:#F4F7F9}.imagify-settings .imagify-check-group.imagify-check-group label{color:#338EA6;font-weight:500}.imagify-inline-options{position:relative;display:table;width:100%;max-width:600px;border-collapse:collapse}.imagify-inline-options input[type=radio]:checked,.imagify-inline-options input[type=radio]:not(:checked){position:absolute;left:5px;top:5px;display:none}.imagify-inline-options input[type=radio]:checked+label,.imagify-inline-options input[type=radio]:not(:checked)+label{position:relative;display:table-cell;padding:13px 10px;text-align:center;font-weight:600;font-size:16px;text-transform:uppercase;letter-spacing:.1em;color:#FFF;background:#2E3243;-webkit-box-shadow:0 -3px 0 rgba(0,0,0,.1) inset;box-shadow:0 -3px 0 rgba(0,0,0,.1) inset;z-index:2;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-inline-options input[type=radio]:checked+label:first-of-type,.imagify-inline-options input[type=radio]:not(:checked)+label:first-of-type{border-radius:3px 0 0 3px}.imagify-inline-options input[type=radio]:checked+label:last-of-type,.imagify-inline-options input[type=radio]:not(:checked)+label:last-of-type{border-radius:0 3px 3px 0}.imagify-inline-options input[type=radio]:checked+label{background:#8BC34A}.imagify-inline-options .imagify-info{margin-top:15px}.imagify-account-info-col,.imagify-col.imagify-col.imagify-account-info-col{width:380px;max-width:100%;padding:0 20px 0 0}.imagify-col.imagify-col.imagify-shared-with-account-col,.imagify-shared-with-account-col{width:calc(100% - 380px);padding:0}.imagify-account-info-col .imagify-options-title{padding:24px 26px;color:#FFF;background:#1F2332}.imagify-block-secondary{padding:26px 26px 35px;border:1px solid #75A345;background:#8BC34A;border-radius:3px;color:#FFF}.imagify-account-info-col .imagify-block-secondary.imagify-block-secondary h3,.imagify-block-secondary.imagify-block-secondary p{color:inherit}.imagify-account-info-col .imagify-col-content h3:first-child{margin-top:0}.imagify-account-info-col .imagify-col-content h3{font-size:19px}.imagify-account-info-col .imagify-col-content p{margin:1.5em 0}.imagify-account-info-col .imagify-col-content p:first-child{margin-top:0}.imagify-user-plan-label{float:right;margin-top:-4px;padding:2px 10px;border:2px solid #40B1D0;font-size:14px;text-transform:uppercase;letter-spacing:.02em;color:#40B1D0;border-radius:3px}.imagify-modal .h2,.imagify-modal .h3{letter-spacing:.075em;text-align:center;font-weight:400}.imagify-user-plan-label:empty{display:none}.imagify-columns{overflow:hidden;padding:15px 0;counter-reset:cols}.imagify-columns [class^=col-]{float:left;-webkit-box-sizing:border-box;box-sizing:border-box}.imagify-columns .col-1-3{width:33.333%;padding-left:28px}.imagify-columns .col-2-3{width:66.666%;padding-left:28px}.imagify-columns .col-1-2{width:50%;padding:0 20px}@media (max-width:830px){.imagify-columns [class^=col-]{float:none;margin-bottom:1.5em}.imagify-columns .col-1-2,.imagify-columns .col-1-3{width:auto;padding:0 28px;clear:both;padding-top:1em}}.column-imagify_optimized_file.column-imagify_optimized_file{width:300px;text-align:center;vertical-align:middle}.column-imagify_optimized_file>*{max-width:235px;margin:0 auto}@media (min-width:1151px) and (max-width:1800px){.column-imagify_optimized_file.column-imagify_optimized_file{width:235px}}@media (min-width:783px) and (max-width:1150px){.column-imagify_optimized_file.column-imagify_optimized_file{width:13em}table.media .column-title .has-media-icon~.row-actions.row-actions{margin-left:0}}@media (max-width:782px){table.media .column-imagify_optimized_file.column-imagify_optimized_file{text-align:left}table.media .imagify-datas-actions-links,table.media .imagify-datas-more-action{text-align:center}table.media .column-imagify_optimized_file .imagify-datas-actions-links a,table.media .column-imagify_optimized_file>*{max-width:100%;margin-left:0}}@media (min-width:783px) and (max-width:1150px),(max-width:360px){table.media .imagify-hide-if-small{position:absolute;margin:-1px;padding:0;height:1px;width:1px;overflow:hidden;clip:rect(0 0 0 0);border:0;word-wrap:normal!important}}.compat-field-imagify .label{vertical-align:top}.compat-field-imagify ul.imagify-datas-list{margin-top:7px;font-size:11px}ul.imagify-datas-list.imagify-datas-list{margin:0 auto;color:#555;font-size:10px}ul.imagify-datas-list .big{font-size:12px;color:#40B1D0}.imagify-data-item{overflow:hidden}li.imagify-data-item{clear:both;margin-bottom:2px}ul.imagify-datas-list .imagify-data-item span.data,ul.imagify-datas-list .imagify-data-item strong{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box}ul.imagify-datas-list .imagify-data-item span.data{text-align:left;padding-right:5px}.compat-field-imagify .imagify-datas-list .imagify-data-item .data{width:130px;text-align:left;font-weight:700}ul.imagify-datas-list .imagify-data-item strong{text-align:left;padding-left:5px}.media-sidebar .imagify-datas-list .imagify-data-item .data{width:auto;float:none}.media-sidebar .imagify-datas-list .imagify-data-item strong{display:inline-block;width:auto;float:none}.media-sidebar .imagify-datas-list .imagify-data-item .imagify-chart{float:left}.imagify-datas-more-action.imagify-datas-more-action{margin:.4em auto;background:-webkit-gradient(linear,left top,left bottom,from(transparent),color-stop(49%,transparent),color-stop(50%,rgba(0,0,0,.075)),color-stop(58%,rgba(0,0,0,.075)),color-stop(58%,transparent),to(transparent));background:-o-linear-gradient(top,transparent,transparent 49%,rgba(0,0,0,.075) 50%,rgba(0,0,0,.075) 58%,transparent 58%,transparent);background:linear-gradient(to bottom,transparent,transparent 49%,rgba(0,0,0,.075) 50%,rgba(0,0,0,.075) 58%,transparent 58%,transparent)}.imagify-datas-more-action a{display:inline-block;padding:0 5px;background:#40B1D0;color:#FFF;text-transform:uppercase;font-size:9px;font-weight:700;line-height:1.9;text-decoration:none}.imagify-datas-more-action a.is-open{background:#555}.imagify-datas-more-action a.is-open .dashicons{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.imagify-datas-more-action a .dashicons{font-size:14px;vertical-align:middle;line-height:.8}.imagify-datas-more-action a .dashicons:before{vertical-align:middle;line-height:20px}.imagify-datas-more-action .the-text{display:inline-block;vertical-align:middle;height:auto;line-height:inherit}ul.imagify-datas-details.imagify-datas-details{margin:.7em auto}.imagify-datas-details strong{color:#40B1D0}.imagify-datas-details .original{color:#555}.imagify-datas-actions-links{overflow:hidden;border-top:2px solid transparent;padding-top:5px;font-size:8px}.nggform .imagify-datas-actions-links{position:relative;z-index:2}.nggform .row-actions{z-index:1}.imagify-datas-actions-links a{position:relative;display:inline-block;padding-left:17px;text-decoration:none;font-weight:600}.compat-field-imagify .imagify-datas-actions-links{max-width:300px}.misc-pub-imagify .imagify-datas-actions-links{border-top:2px solid #f2f2f2;padding-bottom:5px}.column-imagify_optimized_file .imagify-datas-actions-links a{margin:0 .7em;padding-left:15px}.compat-field-imagify .imagify-datas-actions-links a,.misc-pub-imagify .imagify-datas-actions-links a{font-size:10px;float:left;width:50%}.media-sidebar .compat-field-imagify .imagify-datas-actions-links a,.submitbox .misc-pub-imagify .imagify-datas-actions-links a{display:block;width:auto;float:none}.imagify-datas-actions-links a:only-child{float:none;width:auto}.imagify-datas-details.is-open+.imagify-datas-actions-links{border-top-color:rgba(0,0,0,.075)}.imagify-datas-actions-links .dashicons{position:absolute;left:0;top:4px;width:12px;margin-right:2px;font-size:11px}.imagify-account,.imagify-account-link{padding-right:15px}.imagify-meteo-icon{display:inline-block;height:38px;vertical-align:middle;margin-right:10px}.imagify-user-plan{color:#40b1d0}.imagify-meteo-title.imagify-meteo-title{color:#FFF;font-size:17px}.imagify-space-left>p{color:#FFF}[class^=imagify-bar-]{position:relative;height:8px;width:100%;background:#60758D;color:#FFF;font-size:10px}.imagify-progress{height:8px;-webkit-transition:width .3s;-o-transition:width .3s;transition:width .3s}.imagify-bar-positive .imagify-progress{background:#8CC152}.imagify-bar-positive .imagify-barnb{color:#8CC152}.imagify-bar-primary .imagify-progress{background:#40B1D0}.imagify-bar-primary .imagify-barnb{color:#40B1D0}.imagify-bar-negative .imagify-progress{background:#D2D3D6}.imagify-bar-negative .imagify-barnb{color:#7A8996}.imagify-bar-neutral .imagify-progress{background:#F5A623}.imagify-space-left .imagify-bar-negative .imagify-progress{background:#C51162}.imagify-btn-ghost{display:inline-block;height:auto;padding:7px 10px;border:1px solid #FFF;text-align:center;background:0 0;color:#FFF;border-radius:3px;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-btn-ghost:focus,.imagify-btn-ghost:hover{background:#FFF;color:#888}.imagify-error{background:#D0021B;color:#FFF}.imagify-settings-section .imagify-error{display:inline-block;padding:7px 10px;margin:10px 0 0 45px;border-radius:3px}.imagify-settings-section .imagify-error code{font-weight:400}.imagify-settings-section .imagify-error.hidden{display:none}.imagify-warning{background:#f5a623;color:#FFF;text-shadow:0 0 2px rgba(0,0,0,.2)}.imagify-modal{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.js .imagify-modal{display:none;position:fixed;top:0;right:0;bottom:0;left:0;background-color:#1F2332;background-color:rgba(31,35,50,.95);z-index:99999}.imagify-modal-content{-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;width:800px;max-width:95%;max-height:90vw;overflow:auto;padding:20px 25px;margin:1em auto;background:#FFF;-webkit-box-shadow:1px 1px 4px rgba(0,0,0,.7);box-shadow:1px 1px 4px rgba(0,0,0,.7);border-radius:3px}#imagify-visual-comparison .imagify-modal-content,.imagify-visual-comparison .imagify-modal-content{max-width:1400px;background:0 0;padding:5px;-webkit-box-shadow:none;box-shadow:none;border-radius:0}.imagify-modal .h2{margin:.5em 0;color:#8ba6b4;font-size:24px}.imagify-modal .h3{color:#40b1d0;font-size:18px}.imagify-modal .close-btn{display:none;visibility:hidden;position:absolute;right:20px;top:20px;font-size:1.2em;border:0;background:0 0;border-radius:0;cursor:pointer}.imagify-modal .close-btn i{margin-left:-2px}.imagify-modal .close-btn:focus,.imagify-modal .close-btn:hover{color:#40b1d0}.js .imagify-modal .close-btn{display:block;visibility:visible}.imagify-visual-comparison .close-btn,.wp_attachment_image #imagify-visual-comparison .close-btn{top:0}.imagify-visual-comparison .imagify-modal-content,.wp_attachment_image #imagify-visual-comparison .imagify-modal-content{padding-top:40px}.imagify-col{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-preferred-size:50%;flex-basis:50%;padding-right:20px}.imagify-col+.imagify-col{padding-right:0;padding-left:20px}.imagify-col:target{-webkit-animation:hello 1s 3 linear backwards;animation:hello 1s 3 linear backwards}@-webkit-keyframes hello{0%,100%{background:#FFF}50%{background:#F4F7F9}}@keyframes hello{0%,100%{background:#FFF}50%{background:#F4F7F9}}
assets/css/bulk.css CHANGED
@@ -15,8 +15,7 @@
15
  margin-bottom: 15px;
16
  border-radius: 5px;
17
  padding: 3px 8px 2px 31px;
18
- font-size: 14px;
19
- font-weight: 600;
20
  cursor: default;
21
  -webkit-transition: background-color 200ms ease-in-out;
22
  -moz-transition: background-color 200ms ease-in-out;
@@ -37,7 +36,6 @@
37
  width: 180px;
38
  padding-top: 20px;
39
  font-size: 14px;
40
- font-weight: bold;
41
  text-align: center;
42
  }
43
 
@@ -74,7 +72,9 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item {
74
  body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress {
75
  float: none;
76
  width: 100%;
 
77
  margin: 0;
 
78
  background: #1F2331;
79
  box-shadow: 0;
80
  border-radius: 0;
@@ -82,10 +82,16 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress {
82
 
83
  .media_page_imagify-bulk-optimization .media-item .percent,
84
  body[class*="_imagify-ngg-bulk-optimization"] .media-item .percent {
 
 
 
 
85
  width: auto;
86
  padding: 0 5px;
87
  line-height: 1.85;
88
- font-size: 12px;
 
 
89
  }
90
 
91
  .media_page_imagify-bulk-optimization .media-item .progress,
@@ -96,12 +102,12 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .percent {
96
  }
97
  .media_page_imagify-bulk-optimization .media-item .progress .bar,
98
  body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
 
99
  width: 1px;
100
- height: 22px;
101
  margin-top: 0;
102
  background: #46B1CE;
103
  border-radius: 0;
104
- overflow: visible;
105
  -webkit-transition: width .5s;
106
  transition: width .5s;
107
  }
@@ -110,94 +116,56 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
110
  padding: 11px 20px;
111
  }
112
 
 
113
  .imagify-columns .col-overview.col-overview {
 
114
  padding-left: 20px;
115
  }
116
- .imagify-columns .col-informations.col-informations {
117
- width: 36.6%;
118
- padding-right: 30px;
119
- }
120
  .imagify-columns .col-statistics.col-statistics {
121
- width: 30%;
122
  }
123
-
124
- .imagify-bulk-submit .imagify-inline-options input[type="radio"]:not(:checked) + label,
125
- .imagify-bulk-submit .imagify-inline-options input[type="radio"]:checked + label {
126
- margin-bottom: 1.75em;
127
  }
128
 
129
- /* Header */
130
- .imagify-title-right {
131
- display: table;
132
- float: right; /* fallback old browsers */
133
- margin-top: -10px;
134
- -webkit-box-ordinal-group: 3;
135
- -ms-flex-order: 2;
136
- order: 2;
137
- }
138
- .imagify-title-right p {
139
- margin: 0;
140
- }
141
- .imagify-title-right a {
142
- font-weight: bold;
143
- text-decoration: none;
144
- }
145
- .imagify-title-right > div {
146
- display: table-cell;
147
- vertical-align: middle;
148
- }
149
- .imagify-title-right .dashicons-arrow-down-alt2 {
150
- vertical-align: -4px;
151
- margin-left: 2px;
152
- }
153
- @media (max-width: 1200px) {
154
- .imagify-bulk .imagify-title {
155
- display: -webkit-box;
156
- display: -webkit-flex;
157
- display: -ms-flexbox;
158
- display: flex;
159
- -webkit-box-orient: vertical;
160
- -webkit-box-direction: normal;
161
- -webkit-flex-direction: column;
162
- -ms-flex-direction: column;
163
- flex-direction: column;
164
- }
165
- .imagify-title-right {
166
- -webkit-box-ordinal-group: 3;
167
- -webkit-order: 2;
168
- -ms-flex-order: 2;
169
- order: 2;
170
- margin-top: 20px;
171
  }
172
  }
173
 
174
- .imagify-sep-v {
175
- width: 1px;
176
- background: rgba(255, 255, 255, .2);
177
- }
178
-
179
- .imagify-credit-left {
180
- position: relative;
181
- min-width: 280px;
182
- padding-left: 15px;
183
- }
184
-
185
- @media (max-width: 630px) {
186
- .imagify-title-right {
187
- display: block;
188
  width: auto;
189
  }
190
- .imagify-title-right > div {
191
- display: block;
 
 
 
192
  width: auto;
193
- margin-top: 10px;
194
- max-width: 100%;
195
  }
196
- .imagify-credit-left {
197
- padding-left: 0;
198
  }
199
  }
200
 
 
 
 
 
 
201
  .base-transparent {
202
  background: transparent;
203
  }
@@ -209,17 +177,29 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
209
  }
210
  .right-outside-number .imagify-barnb {
211
  display: block;
212
- margin-right: -4.5em;
213
  text-align: right;
214
  font-weight: bold;
215
- line-height: 1.5;
216
  }
217
 
218
- .imagify-list-infos {
219
- max-width: 100%;
220
- width: 415px;
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  }
222
- .imagify-list-infos li,
223
  .imagify-info-block {
224
  position: relative;
225
  padding: 10px;
@@ -228,96 +208,114 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
228
  border-radius: 4px;
229
  line-height: 1.6;
230
  }
231
- /* documentation exception */
232
- .imagify-documentation-link-box:after,
233
- .imagify-documentation-link-box:before {
234
- display: none;
235
  }
236
- .imagify-list-infos .imagify-documentation-link-box {
237
  display: flex;
238
  align-items: center;
239
- padding-left: 12px;
240
- border: 1px solid #338EA6;
241
- background: #D9E4EC;
 
 
242
  }
243
- .imagify-documentation-icon {
244
- width: 15px;
245
- height: 20px;
246
- margin-right: 15px;
247
  }
248
- .imagify-list-infos .imagify-alt.imagify-alt {
249
- background: #8BA7B5;
250
- color: #FFF;
251
- font-weight: bold;
252
  }
253
  .imagify-list-infos li + li {
254
- margin-top: 1em;
255
- }
256
- .imagify-list-infos li:before,
257
- .imagify-info-block:before {
258
- content: "";
259
- position: absolute;
260
- left: 13px; top: 14px;
261
- height: 16px; width: 16px;
262
- border: 1px solid #46b1ce;
263
- border-radius: 10px;
264
- }
265
- .imagify-list-infos li:after,
266
- .imagify-info-block:after {
267
- content: "i";
268
- position: absolute;
269
- left: 20px; top: 13px;
270
- font-style: italic;
271
- color: #46b1ce;
272
- }
273
- .imagify-cell .imagify-info-block {
274
- padding-top: 0;
275
  }
276
- .imagify-cell .imagify-info-block:after {
277
- top: 6px;
 
 
278
  }
279
- .imagify-cell .imagify-info-block:before {
280
- top: 7px;
281
  }
282
- .imagify-list-infos .imagify-alt:before,
283
- .imagify-list-infos .imagify-alt:after {
284
- color: #fff;
285
- border-color: #fff;
286
  }
287
 
288
  /* Some main sections/content */
289
- .imagify-sub-title.imagify-sub-title {
290
- font-size: 23px;
291
- background: #FFF;
292
- color: #2E3243;
293
- border-left: 1px solid #D9D9D9;
294
- border-right: 1px solid #D9D9D9;
295
- font-weight: lighter;
296
- }
297
- .imagify-sub-title .icon-bulk {
298
- margin-right: 10px;
299
- vertical-align: middle;
300
- }
301
- .imagify-sub-title .title-text {
302
- display: inline-block;
303
- line-height: 1;
304
- vertical-align: middle;
305
- }
306
  .imagify-bulk .imagify-settings-section {
307
  border: 1px solid #D9D9D9;
308
  border-top: 0;
309
- background: #F2F5F7;
310
  color: #4A4A4A;
311
  }
312
- .imagify-bulk .imagify-settings-section p,
313
- .imagify-bulk .imagify-settings-section li,
314
- .imagify-bulk .imagify-settings-section h3 {
315
  color: #4A4A4A;
316
  }
317
  .imagify-bulk .imagify-settings-section h3 {
318
  margin-bottom: 2em;
319
  }
320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  /* Tooltips */
322
  .imagify-title .imagify-tooltips {
323
  position: absolute;
@@ -433,39 +431,41 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
433
  }
434
 
435
  /* Number display */
 
 
 
 
436
  .imagify-number-you-optimized .number {
437
  display: table-cell;
438
  padding-right: 15px;
439
- font-size: 36px;
440
  font-weight: bold;
441
- line-height: 1.1;
442
  vertical-align: middle;
443
  white-space: nowrap;
 
 
 
 
444
  }
445
  .imagify-number-you-optimized .text {
446
  display: table-cell;
447
  vertical-align: middle;
448
  overflow: hidden;
449
  font-size: 12px;
450
- }
451
- .imagify-number-you-optimized .text br {
452
- display: none;
453
  }
454
  .imagify-number-you-optimized > p {
455
  display: table;
456
  }
457
- .imagify-number-you-optimized {
458
- padding-bottom: 0.85em;
459
- margin-bottom: 1.35em;
460
- overflow: hidden;
461
- border-bottom: 1px solid rgba(0,0,0, 0.05);
462
- }
463
 
464
  /* Number and bars */
 
 
 
465
  .imagify-bars p {
466
- font-weight: bold;
467
  font-size: 12px;
468
- margin-bottom: 0;
469
  }
470
  .imagify-bars + .imagify-number-you-optimized {
471
  border-bottom: 0;
@@ -478,55 +478,125 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
478
  /* Table */
479
  .imagify-bulk-table {
480
  margin-top: 2em;
481
- max-height: 600px;
482
- max-height: 60vh;
483
- overflow: auto;
484
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
  .imagify-bulk-table table {
486
  width: 100%;
487
  border-spacing: 0;
488
  border-collapse: collapse;
489
- border: 1px solid #D3D3D3;
490
  }
491
  .imagify-bulk-table td {
492
- padding: 8px 15px;
493
  }
494
- .imagify-bulk-table thead tr,
495
- .imagify-bulk-table thead th {
496
- background: #2E3242;
497
  }
498
- .imagify-bulk-table tfoot tr,
499
- .imagify-bulk-table tfoot th {
500
- background: #73818C;
501
  }
502
- .imagify-bulk-table thead th {
503
- padding: 14px 15px;
504
  text-align: left;
505
- color: #F2F5F7;
506
  font-weight: bold;
507
- font-size: 14px;
 
 
 
 
 
 
 
 
508
  }
509
- .imagify-bulk-table tfoot td {
510
- padding: 14px 15px;
511
- color: #F9FAFA;
 
512
  }
513
 
 
 
 
514
  .imagify-bulk-table tbody tr,
515
  .imagify-bulk-table tbody td {
516
  background: #FFF;
517
  }
518
- .imagify-bulk-table tbody tr:nth-child(odd),
519
- .imagify-bulk-table tbody tr:nth-child(odd) td {
520
- background: #F2F5F7;
521
- }
522
 
523
  .imagify-bulk-table .imagify-row-progress {
524
  display: none;
525
  }
526
 
527
- .imagify-bulk-table .imagify-row-progress,
528
- .imagify-bulk-table .imagify-row-progress td {
529
- height: 15px;
530
  padding: 0;
531
  }
532
  .imagify-bulk-table .imagify-no-uploaded-yet td {
@@ -542,6 +612,104 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
542
  background-color: #FFF;
543
  }
544
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  .imagify-row-complete {
546
  padding: 35px 20px;
547
  margin-top: 2em;
@@ -552,7 +720,7 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
552
  .imagify-row-complete .imagify-ac-chart {
553
  margin-top: 3px;
554
  }
555
- .imagify-row-complete p {
556
  color: #FFF;
557
  margin: 0;
558
  }
@@ -566,7 +734,7 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
566
  -webkit-transform: scale(1.05);
567
  opacity: 1;
568
  }
569
- 100% {
570
  -webkit-transform: scale(1);
571
  opacity: 1;
572
  }
@@ -602,6 +770,7 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
602
  }
603
  .imagify-ac-report {
604
  min-width: 310px;
 
605
  }
606
  .imagify-ac-chart {
607
  width: 46px;
@@ -653,6 +822,78 @@ body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
653
  }
654
 
655
  /* TD's width */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
  .imagify-cell-filename {
657
  max-width: 200px;
658
  }
@@ -679,20 +920,32 @@ td.imagify-cell-filename {
679
  color: #46B1CE;
680
  font-weight: bold;
681
  }
682
- .imagify-cell-optimized {
683
- font-weight: bold;
684
- }
685
  .imagify-bulk-table td.imagify-cell-totaloriginal {
686
  padding-right: 78px;
687
  }
688
  .imagify-cell-totaloriginal {
689
  text-align: right;
690
  }
 
 
 
 
 
 
 
 
 
 
 
 
691
 
692
  .imagiuploaded,
693
  .imagifilename {
694
  display: inline-block;
695
  vertical-align: middle;
 
 
 
696
  }
697
  .imagifilename {
698
  font-size: 12px;
@@ -707,7 +960,7 @@ td.imagify-cell-filename {
707
  background-size: cover;
708
  }
709
  .imagiuploaded img {
710
- max-widht: 100%;
711
  height: auto;
712
  }
713
 
@@ -716,6 +969,9 @@ td.imagify-cell-filename {
716
  text-transform: uppercase;
717
  font-weight: bold;
718
  }
 
 
 
719
  .status-compressing {
720
  color: #46B1CE;
721
  }
@@ -729,8 +985,19 @@ td.imagify-cell-filename {
729
  color: #8CC152;
730
  }
731
 
 
 
 
 
 
 
 
 
 
 
 
 
732
  /* Icon rotation */
733
- /* icon rotate */
734
  .dashicons.rotate {
735
  -webkit-animation: icon-rotate 2.6s infinite linear;
736
  animation: icon-rotate 2.6s infinite linear;
@@ -757,3 +1024,84 @@ td.imagify-cell-filename {
757
  transform: rotate(360deg);
758
  }
759
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  margin-bottom: 15px;
16
  border-radius: 5px;
17
  padding: 3px 8px 2px 31px;
18
+ font-size: 13px;
 
19
  cursor: default;
20
  -webkit-transition: background-color 200ms ease-in-out;
21
  -moz-transition: background-color 200ms ease-in-out;
36
  width: 180px;
37
  padding-top: 20px;
38
  font-size: 14px;
 
39
  text-align: center;
40
  }
41
 
72
  body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress {
73
  float: none;
74
  width: 100%;
75
+ height: 8px;
76
  margin: 0;
77
+ overflow: visible;
78
  background: #1F2331;
79
  box-shadow: 0;
80
  border-radius: 0;
82
 
83
  .media_page_imagify-bulk-optimization .media-item .percent,
84
  body[class*="_imagify-ngg-bulk-optimization"] .media-item .percent {
85
+ position: absolute;
86
+ top: 6px;
87
+ right: 0;
88
+ text-shadow: none;
89
  width: auto;
90
  padding: 0 5px;
91
  line-height: 1.85;
92
+ font-size: 14px;
93
+ font-weight: bold;
94
+ color: #40B1D0;
95
  }
96
 
97
  .media_page_imagify-bulk-optimization .media-item .progress,
102
  }
103
  .media_page_imagify-bulk-optimization .media-item .progress .bar,
104
  body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar {
105
+ position: relative;
106
  width: 1px;
107
+ height: 8px;
108
  margin-top: 0;
109
  background: #46B1CE;
110
  border-radius: 0;
 
111
  -webkit-transition: width .5s;
112
  transition: width .5s;
113
  }
116
  padding: 11px 20px;
117
  }
118
 
119
+ /* Bulk overview columns */
120
  .imagify-columns .col-overview.col-overview {
121
+ width: calc(100% - 465px);
122
  padding-left: 20px;
123
  }
 
 
 
 
124
  .imagify-columns .col-statistics.col-statistics {
125
+ width: 60%;
126
  }
127
+ .imagify-columns .col-chart.col-chart {
128
+ width: 40%;
 
 
129
  }
130
 
131
+ @media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
132
+ .imagify-columns .col-statistics.col-statistics,
133
+ .imagify-columns .col-chart.col-chart {
134
+ width: 50%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  }
136
  }
137
 
138
+ @media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
139
+ .imagify-overview-chart-container {
140
+ float: none;
141
+ margin-right: 0;
142
+ }
143
+ .imagify-doughnut-legend {
144
+ margin-top: 18px;
145
+ }
146
+ .imagify-global-optim-phrase {
147
+ padding-top: 0;
 
 
 
 
148
  width: auto;
149
  }
150
+ }
151
+
152
+ @media (max-width: 808px) {
153
+ .imagify-columns .col-statistics.col-statistics,
154
+ .imagify-columns .col-chart.col-chart {
155
  width: auto;
156
+ float: none;
157
+ padding: 0;
158
  }
159
+ .imagify-columns .col-chart.col-chart {
160
+ margin-top: 3em;
161
  }
162
  }
163
 
164
+ /* Header */
165
+ .imagify-sep-v {
166
+ width: 1px;
167
+ background: rgba(255, 255, 255, .2);
168
+ }
169
  .base-transparent {
170
  background: transparent;
171
  }
177
  }
178
  .right-outside-number .imagify-barnb {
179
  display: block;
180
+ margin-right: -5.25em;
181
  text-align: right;
182
  font-weight: bold;
183
+ line-height: .8;
184
  }
185
 
186
+ .imagify-h2-like {
187
+ margin: 0 0 .5em 0;
188
+ padding-bottom: .5em;
189
+ border-bottom: 1px solid #E9EFF2;
190
+ font-size: 24px;
191
+ color: #000;
192
+ font-weight: bold;
193
+ }
194
+ .imagify-h2-like .dashicons,
195
+ .imagify-h2-like .dashicons:before {
196
+ font-size: 38px;
197
+ height: 38px;
198
+ width: 38px;
199
+ margin-right: 12px;
200
+ vertical-align: -5px;
201
+ color: #40B1D0;
202
  }
 
203
  .imagify-info-block {
204
  position: relative;
205
  padding: 10px;
208
  border-radius: 4px;
209
  line-height: 1.6;
210
  }
211
+ .imagify-list-infos {
212
+ margin: 0;
213
+ padding: 0;
 
214
  }
215
+ .imagify-list-infos li {
216
  display: flex;
217
  align-items: center;
218
+ padding: 15px 5px;
219
+ text-align: left;
220
+ font-size: 14px;
221
+ line-height: 1.5;
222
+ color: #626E7B;
223
  }
224
+ .imagify-list-infos li:first-child {
225
+ padding-top: 5px;
 
 
226
  }
227
+ .imagify-list-infos li:last-child {
228
+ padding-bottom: 5px;
 
 
229
  }
230
  .imagify-list-infos li + li {
231
+ border-top: 1px solid #E9EFF2;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  }
233
+
234
+ .imagify-info-icon {
235
+ flex-grow: 0;
236
+ flex-basis: 50px;
237
  }
238
+ .imagify-info-icon + span {
239
+ padding-left: 20px;
240
  }
241
+
242
+ .imagify-list-infos a:before {
243
+ content: '';
244
+ display: block;
245
  }
246
 
247
  /* Some main sections/content */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  .imagify-bulk .imagify-settings-section {
249
  border: 1px solid #D9D9D9;
250
  border-top: 0;
251
+ background: #FFF;
252
  color: #4A4A4A;
253
  }
254
+ .imagify-bulk p,
255
+ .imagify-bulk li,
256
+ .imagify-bulk h3 {
257
  color: #4A4A4A;
258
  }
259
  .imagify-bulk .imagify-settings-section h3 {
260
  margin-bottom: 2em;
261
  }
262
 
263
+ /* Account information col */
264
+ .imagify-account-info-col .imagify-options-title {
265
+ display: flex;
266
+ align-items: center;
267
+ }
268
+ .imagify-account-info-col p.imagify-meteo-title {
269
+ margin: 0;
270
+ font-size: 24px;
271
+ font-weight: bold;
272
+ color: #FFF;
273
+ }
274
+ .imagify-account-info-col .imagify-options-title > a {
275
+ flex-basis: 100px;
276
+ margin-left: auto;
277
+ margin-right: 10px;
278
+ text-decoration: underline;
279
+ font-size: 12px;
280
+ }
281
+ .imagify-account-info-col .imagify-meteo-title .dashicons,
282
+ .imagify-account-info-col .imagify-meteo-title .dashicons:before {
283
+ font-size: 38px;
284
+ width: 38px;
285
+ height: 38px;
286
+ margin-right: 4px;
287
+ color: #40B1D0;
288
+ }
289
+ .imagify-col-content {
290
+ background: #F4F7F9;
291
+ border-left: 1px solid #ddd;
292
+ border-right: 1px solid #ddd;
293
+ }
294
+ .imagify-col-content .imagify-block-secondary {
295
+ margin-left: -1px;
296
+ margin-right: -1px;
297
+ }
298
+ .imagify-col-content .imagify-space-left {
299
+ margin: 15px 30px 15px 0;
300
+ }
301
+ .imagify-col-content .imagify-space-left p {
302
+ margin: 0 0 10px 0;
303
+ font-size: 19px;
304
+ font-weight: 500;
305
+ color: #343A49;
306
+ }
307
+ .imagify-col-content .imagify-meteo-icon {
308
+ height: 64px;
309
+ margin: 15px 15px 15px 20px;
310
+ }
311
+ .imagify-col-content .imagify-section-title + p {
312
+ margin-top: 10px;
313
+ }
314
+
315
+ .imagify-account-info-col .imagify-h3-like.imagify-h3-like {
316
+ color: inherit;
317
+ }
318
+
319
  /* Tooltips */
320
  .imagify-title .imagify-tooltips {
321
  position: absolute;
431
  }
432
 
433
  /* Number display */
434
+ .imagify-number-you-optimized {
435
+ margin-bottom: 1.35em;
436
+ overflow: hidden;
437
+ }
438
  .imagify-number-you-optimized .number {
439
  display: table-cell;
440
  padding-right: 15px;
441
+ font-size: 48px;
442
  font-weight: bold;
443
+ line-height: 1;
444
  vertical-align: middle;
445
  white-space: nowrap;
446
+ color: #000;
447
+ }
448
+ .imagify-number-you-optimized [id="imagify-total-optimized-attachments-pct"] {
449
+ color: #40B1D0;
450
  }
451
  .imagify-number-you-optimized .text {
452
  display: table-cell;
453
  vertical-align: middle;
454
  overflow: hidden;
455
  font-size: 12px;
456
+ color: #626E7B;
 
 
457
  }
458
  .imagify-number-you-optimized > p {
459
  display: table;
460
  }
 
 
 
 
 
 
461
 
462
  /* Number and bars */
463
+ .imagify-bars {
464
+ padding-right: 15px;
465
+ }
466
  .imagify-bars p {
 
467
  font-size: 12px;
468
+ margin-bottom: 5px;
469
  }
470
  .imagify-bars + .imagify-number-you-optimized {
471
  border-bottom: 0;
478
  /* Table */
479
  .imagify-bulk-table {
480
  margin-top: 2em;
 
 
 
481
  }
482
+
483
+ .imagify-table-header {
484
+ justify-content: space-between;
485
+ padding: 15px 25px;
486
+ background: #343A49;
487
+ color: #FFF;
488
+ }
489
+ .imagify-newbie {
490
+ margin-top: 4em;
491
+ position: relative;
492
+ overflow: visible;
493
+ }
494
+ .imagify-newbie .imagify-new-feature.imagify-new-feature {
495
+ position: absolute;
496
+ top: 0;
497
+ left: 25px;
498
+ transform: translateY(-50%);
499
+ margin: 0;
500
+ padding: 8px 20px;
501
+ font-size: 14px;
502
+ letter-spacing: .02em;
503
+ text-transform: uppercase;
504
+ font-weight: bold;
505
+ color: #FFF;
506
+ background: #8BC34A;
507
+ }
508
+ .imagify-newbie .imagify-table-header {
509
+ padding: 30px 25px;
510
+ border: 2px solid #8BC34A;
511
+ background: #F3F9EC;
512
+ }
513
+ .imagify-th-titles .dashicons,
514
+ .imagify-th-titles .dashicons:before {
515
+ width: 38px;
516
+ height: 38px;
517
+ margin-right: 20px;
518
+ font-size: 38px;
519
+ color: #40B1D0;
520
+ }
521
+ .imagify-newbie .imagify-th-titles .dashicons:before {
522
+ color: #8BC34A;
523
+ }
524
+ .imagify-th-title.imagify-th-title.imagify-th-title {
525
+ margin: 0;
526
+ font-size: 24px;
527
+ font-weight: 500;
528
+ color: #FFF;
529
+ }
530
+ .imagify-newbie .imagify-th-title.imagify-th-title {
531
+ color: #343A49;
532
+ }
533
+ .imagify-th-subtitle.imagify-th-subtitle.imagify-th-subtitle {
534
+ margin: 0 0 5px;
535
+ font-size: 14px;
536
+ color: #7A8996;
537
+ font-weight: 500;
538
+ }
539
+ .imagify-th-action .imagify-button-clean {
540
+ font-size: 12px;
541
+ color: #7A8996;
542
+ }
543
+ .imagify-th-action .imagify-is-active {
544
+ color: #FFF;
545
+ }
546
+ .imagify-th-action .button:hover,
547
+ .imagify-th-action .button:focus {
548
+ color: #FFF;
549
+ }
550
+
551
  .imagify-bulk-table table {
552
  width: 100%;
553
  border-spacing: 0;
554
  border-collapse: collapse;
 
555
  }
556
  .imagify-bulk-table td {
557
+ padding: 20px;
558
  }
559
+ .imagify-bulk-table-details {
560
+ border-bottom: 2px solid #E5EBEF;
 
561
  }
562
+ .imagify-bulk-table-details thead tr,
563
+ .imagify-bulk-table-details thead th {
564
+ background: #4A5362;
565
  }
566
+ .imagify-bulk-table-details thead th {
567
+ padding: 12px 20px;
568
  text-align: left;
 
569
  font-weight: bold;
570
+ color: #E5EBEF;
571
+ font-size: 12px;
572
+ }
573
+ .imagify-bulk-table-details tbody tr:nth-child(odd) td {
574
+ background: #F2F5F7;
575
+ }
576
+ .imagify-bulk-table-content {
577
+ border: 1px solid #D3D3D3;
578
+ border-top: 0;
579
  }
580
+ .imagify-bulk-table-footer {
581
+ padding: 20px;
582
+ color: #626E7B;
583
+ background: #F2F5F7;
584
  }
585
 
586
+ .imagify-bulk-table tbody tr + tr {
587
+ border-top: 3px solid #F2F5F7;
588
+ }
589
  .imagify-bulk-table tbody tr,
590
  .imagify-bulk-table tbody td {
591
  background: #FFF;
592
  }
 
 
 
 
593
 
594
  .imagify-bulk-table .imagify-row-progress {
595
  display: none;
596
  }
597
 
598
+ .imagify-bulk-table .imagify-row-progress {
599
+ height: 8px;
 
600
  padding: 0;
601
  }
602
  .imagify-bulk-table .imagify-no-uploaded-yet td {
612
  background-color: #FFF;
613
  }
614
 
615
+ /* Custom Level Optimization Select */
616
+ .imagify-level-selector {
617
+ position: relative;
618
+ }
619
+ .imagify-level-selector-list {
620
+ background: #FFF;
621
+ border: 1px solid #F4F7F9;
622
+ box-shadow: 0 6px 12px rgba(0, 0, 0, .1);
623
+ border-radius: 3px;
624
+ font-weight: bold;
625
+ text-transform: uppercase;
626
+ letter-spacing: .02em;
627
+ }
628
+ .imagify-level-selector-list li:first-child label {
629
+ border-radius: 3px 3px 0 0;
630
+ }
631
+ .imagify-level-selector-list li:last-child label {
632
+ border-radius: 0 0 3px 3px;
633
+ }
634
+ .imagify-level-selector-list li {
635
+ margin: 0;
636
+ }
637
+ .imagify-level-selector-list li + li {
638
+ border-top: 1px solid #F4F7F9;
639
+ }
640
+ .imagify-level-selector-list svg {
641
+ margin-right: 5px;
642
+ }
643
+ .imagify-level-selector-list input:checked + label,
644
+ .imagify-level-selector-list .imagify-current-level label {
645
+ background: #343A49;
646
+ color: #FFF;
647
+ }
648
+ .imagify-level-selector-list input:checked + label:hover,
649
+ .imagify-level-selector-list .imagify-current-level label:hover,
650
+ .imagify-level-selector-list label:hover,
651
+ .imagify-level-selector-list input:focus + label,
652
+ .imagify-level-selector-list .imagify-current-level input:focus + label {
653
+ background: #40B1D0;
654
+ color: #F4F7F9;
655
+ }
656
+ .imagify-level-selector-list input:checked + label:hover polygon,
657
+ .imagify-level-selector-list .imagify-current-level label:hover polygon,
658
+ .imagify-level-selector-list label:hover polygon,
659
+ .imagify-level-selector-list input:focus + label polygon,
660
+ .imagify-level-selector-list .imagify-current-level input:focus + label polygon {
661
+ fill: #FFF;
662
+ }
663
+ .imagify-level-selector-list input:checked + label:hover polygon[fill="#CCD1D6"],
664
+ .imagify-level-selector-list .imagify-current-level label:hover polygon[fill="#CCD1D6"],
665
+ .imagify-level-selector-list label:hover polygon[fill="#CCD1D6"],
666
+ .imagify-level-selector-list input:focus + label polygon[fill="#CCD1D6"],
667
+ .imagify-level-selector-list .imagify-current-level input:focus + label polygon[fill="#CCD1D6"] {
668
+ fill: #3694AE;
669
+ }
670
+ .imagify-level-selector-list li label {
671
+ display: block;
672
+ padding: 10px;
673
+ transition: all .275s;
674
+ }
675
+ .imagify-level-selector-list polygon {
676
+ transition: all .275s;
677
+ }
678
+ .imagify-level-selector-list {
679
+ position: absolute;
680
+ top: 0;
681
+ left: 0;
682
+ right: 0;
683
+ transition: all .275s;
684
+ transform: translateY(-50%);
685
+ }
686
+ .imagify-level-selector-list[aria-hidden="true"] {
687
+ opacity: 0;
688
+ visibility: hidden;
689
+ transform: translateY(-50%) scale(0);
690
+ }
691
+ .imagify-level-selector-list[aria-hidden="false"] {
692
+ opacity: 1;
693
+ visibility: visible;
694
+ transform: translateY(-50%) scale(1);
695
+ }
696
+ .button .imagify-current-level-info {
697
+ position: relative;
698
+ padding-right: 20px;
699
+ }
700
+ .button .imagify-current-level-info:after {
701
+ content: '';
702
+ position: absolute;
703
+ right: 0;
704
+ top: 50%;
705
+ margin-top: -3px;
706
+ border-top: 6px solid #7A8996;
707
+ border-left: 6px solid transparent;
708
+ border-right: 6px solid transparent;
709
+ }
710
+
711
+
712
+ /* Complete row / success */
713
  .imagify-row-complete {
714
  padding: 35px 20px;
715
  margin-top: 2em;
720
  .imagify-row-complete .imagify-ac-chart {
721
  margin-top: 3px;
722
  }
723
+ .imagify-row-complete.imagify-row-complete p {
724
  color: #FFF;
725
  margin: 0;
726
  }
734
  -webkit-transform: scale(1.05);
735
  opacity: 1;
736
  }
737
+ 100% {
738
  -webkit-transform: scale(1);
739
  opacity: 1;
740
  }
770
  }
771
  .imagify-ac-report {
772
  min-width: 310px;
773
+ margin-right: 20px;
774
  }
775
  .imagify-ac-chart {
776
  width: 46px;
822
  }
823
 
824
  /* TD's width */
825
+ .imagify-cell-checkbox {
826
+ width: 35px;
827
+ }
828
+ .imagify-cell-checkbox p {
829
+ margin: 0;
830
+ }
831
+ .imagify-cell-checkbox-loader {
832
+ display: block;
833
+ width: 27px;
834
+ height: 28px;
835
+ line-height: 0;
836
+ animation: loading 4s infinite linear;
837
+ }
838
+ @keyframes loading {
839
+ 0% {
840
+ transform: rotate(0);
841
+ }
842
+ 100% {
843
+ transform: rotate(360deg);
844
+ }
845
+ }
846
+ .imagify-cell-checkbox-loader.hidden {
847
+ display: none;
848
+ animation: none;
849
+ }
850
+ .imagify-cell-title label,
851
+ .imagify-cell-label {
852
+ font-size: 14px;
853
+ text-transform: uppercase;
854
+ letter-spacing: .02em;
855
+ font-weight: bold;
856
+ }
857
+ .imagify-cell-label {
858
+ margin-right: 10px;
859
+ }
860
+ .imagify-cell-value {
861
+ font-size: 12px;
862
+ font-weight: 500;
863
+ color: #7A8996;
864
+ }
865
+ td.imagify-cell-title {
866
+ padding-left: 0;
867
+ }
868
+ .imagify-cell-title label,
869
+ .imagify-cell-original .imagify-cell-label {
870
+ color: #1F2332;
871
+ }
872
+ .imagify-cell-optimized,
873
+ .imagify-cell-original {
874
+ font-weight: 500;
875
+ color: #7A8996;
876
+ font-size: 12px;
877
+ }
878
+ .imagify-cell-optimized .imagify-cell-label {
879
+ color: #338EA6;
880
+ }
881
+ .imagify-cell-images-optimized {
882
+ font-size: 14px;
883
+ font-weight: bold;
884
+ color: #338EA6;
885
+ }
886
+ .imagify-cell-errors {
887
+ color: #C51162;
888
+ font-weight: bold;
889
+ font-size: 14px;
890
+ }
891
+ .imagify-cell-errors a {
892
+ margin-left: 5px;
893
+ color: #7A8996;
894
+ font-weight: normal;
895
+ font-size: 12px;
896
+ }
897
  .imagify-cell-filename {
898
  max-width: 200px;
899
  }
920
  color: #46B1CE;
921
  font-weight: bold;
922
  }
 
 
 
923
  .imagify-bulk-table td.imagify-cell-totaloriginal {
924
  padding-right: 78px;
925
  }
926
  .imagify-cell-totaloriginal {
927
  text-align: right;
928
  }
929
+ .imagify-cell-level {
930
+ width: 145px;
931
+ }
932
+ .imagify-level-selector-button.imagify-level-selector-button {
933
+ border: 1px solid #FFF;
934
+ padding: 2px 10px;
935
+ }
936
+ .imagify-level-selector-button.imagify-level-selector-button:hover,
937
+ .imagify-level-selector-button.imagify-level-selector-button:focus {
938
+ border-color: #EEE;
939
+ box-shadow: 0 4px 8px rgba(0,0,0,.1);
940
+ }
941
 
942
  .imagiuploaded,
943
  .imagifilename {
944
  display: inline-block;
945
  vertical-align: middle;
946
+ margin-left: 5px;
947
+ color: #626E7B;
948
+ font-weight: 500;
949
  }
950
  .imagifilename {
951
  font-size: 12px;
960
  background-size: cover;
961
  }
962
  .imagiuploaded img {
963
+ max-width: 100%;
964
  height: auto;
965
  }
966
 
969
  text-transform: uppercase;
970
  font-weight: bold;
971
  }
972
+ .imagistatus .dashicons {
973
+ margin-right: 5px;
974
+ }
975
  .status-compressing {
976
  color: #46B1CE;
977
  }
985
  color: #8CC152;
986
  }
987
 
988
+ /* Submit Bulk */
989
+ .imagify-bulk-submit {
990
+ padding: 15px 0 8px 0;
991
+ }
992
+
993
+ .imagify-settings .button-primary.button-primary[disabled] {
994
+ color: #4A4A4A!important;
995
+ background: #D9E4EB!important;
996
+ text-shadow: none!important;
997
+ cursor: not-allowed;
998
+ }
999
+
1000
  /* Icon rotation */
 
1001
  .dashicons.rotate {
1002
  -webkit-animation: icon-rotate 2.6s infinite linear;
1003
  animation: icon-rotate 2.6s infinite linear;
1024
  transform: rotate(360deg);
1025
  }
1026
  }
1027
+
1028
+ .imagify-col.imagify-col.imagify-account-info-col {
1029
+ width: 465px;
1030
+ }
1031
+
1032
+ @media (max-width: 1245px) {
1033
+ .imagify-col.imagify-col.imagify-account-info-col {
1034
+ width: auto;
1035
+ max-width: none;
1036
+ float: none;
1037
+ }
1038
+ .imagify-columns .col-overview.col-overview {
1039
+ float: none;
1040
+ width: auto;
1041
+ padding-left: 0;
1042
+ padding-right: 0;
1043
+ }
1044
+ }
1045
+
1046
+ @media (max-width: 1200px) {
1047
+ .imagify-settings .imagify-title .imagify-logo {
1048
+ display: none;
1049
+ }
1050
+ }
1051
+
1052
+ @media (max-width: 940px) {
1053
+ .imagify-bulk-table-container tbody,
1054
+ .imagify-bulk-table-container tr {
1055
+ text-align: left;
1056
+ }
1057
+ .imagify-bulk-table-container tbody,
1058
+ .imagify-bulk-table-container tbody tr,
1059
+ .imagify-bulk-table-container tbody td {
1060
+ display: block;
1061
+ }
1062
+ .imagify-bulk-table-container tbody td {
1063
+ padding: 20px;
1064
+ }
1065
+ .imagify-cell-checkbox,
1066
+ .imagify-cell-title {
1067
+ float: left;
1068
+ }
1069
+ .imagify-cell-checkbox {
1070
+ width: 26px;
1071
+ }
1072
+ .imagify-bulk-table-container .imagify-cell-title {
1073
+ padding-left: 10px;
1074
+ width: calc(100% - 96px);
1075
+ }
1076
+ .imagify-cell-title:after,
1077
+ .imagify-cell-images-optimized:before {
1078
+ content: '';
1079
+ display: table;
1080
+ clear: both;
1081
+ width: 100%;
1082
+ }
1083
+ .imagify-cell-images-optimized {
1084
+ clear: both;
1085
+ }
1086
+
1087
+ .imagify-bulk-table-container .imagify-cell-title ~ td {
1088
+ display: inline-block;
1089
+ }
1090
+
1091
+ .imagify-bulk-table-container td.imagify-cell-level {
1092
+ display: block
1093
+ }
1094
+ }
1095
+
1096
+ @media (max-width: 918px) {
1097
+ .imagify-settings .imagify-title {
1098
+ display: block;
1099
+ }
1100
+ .imagify-settings .imagify-documentation-link-box {
1101
+ display: inline-flex;
1102
+ }
1103
+ }
1104
+
1105
+ .hidden {
1106
+ display: none;
1107
+ }
assets/css/bulk.min.css CHANGED
@@ -1 +1 @@
1
- #imagify-overview-chart-legend{overflow:hidden}.imagify-doughnut-legend{margin-top:38px;list-style:none}.imagify-doughnut-legend li{display:block;padding-left:30px;position:relative;margin-bottom:15px;border-radius:5px;padding:3px 8px 2px 31px;font-size:14px;font-weight:600;cursor:default;-webkit-transition:background-color .2s ease-in-out;-o-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out}.imagify-doughnut-legend li span{display:block;position:absolute;left:0;top:0;width:25px;height:25px;border-radius:50%}.imagify-global-optim-phrase{width:180px;padding-top:20px;font-size:14px;font-weight:700;text-align:center}.imagify-total-percent{color:#46b1ce}.imagify-overview-chart-container{float:left;margin-right:20px}.imagify-chart-percent{position:absolute;left:0;right:0;top:50%;margin-top:-.5em;line-height:1;text-align:center;font-size:55px;font-weight:700;color:#46B1CE}.imagify-chart-percent span{font-size:20px;vertical-align:super}.media_page_imagify-bulk-optimization .media-item,body[class*="_imagify-ngg-bulk-optimization"] .media-item{margin:0}.media_page_imagify-bulk-optimization .media-item .progress,body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress{float:none;width:100%;margin:0;background:#1F2331;-webkit-box-shadow:0;box-shadow:0;border-radius:0}.media_page_imagify-bulk-optimization .media-item .percent,body[class*="_imagify-ngg-bulk-optimization"] .media-item .percent{width:auto;padding:0 5px;line-height:1.85;font-size:12px}.media_page_imagify-bulk-optimization .media-item .percent,.media_page_imagify-bulk-optimization .media-item .progress,body[class*="_imagify-ngg-bulk-optimization"] .media-item .percent,body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress{text-align:right}.media_page_imagify-bulk-optimization .media-item .progress .bar,body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar{width:1px;height:22px;margin-top:0;background:#46B1CE;border-radius:0;overflow:visible;-webkit-transition:width .5s;-o-transition:width .5s;transition:width .5s}#imagify-bulk-action{padding:11px 20px}.imagify-columns .col-overview.col-overview{padding-left:20px}.imagify-columns .col-informations.col-informations{width:36.6%;padding-right:30px}.imagify-columns .col-statistics.col-statistics{width:30%}.imagify-bulk-submit .imagify-inline-options input[type=radio]:checked+label,.imagify-bulk-submit .imagify-inline-options input[type=radio]:not(:checked)+label{margin-bottom:1.75em}.imagify-title-right{display:table;float:right;margin-top:-10px;-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.imagify-title-right p{margin:0}.imagify-title-right a{font-weight:700;text-decoration:none}.imagify-title-right>div{display:table-cell;vertical-align:middle}.imagify-title-right .dashicons-arrow-down-alt2{vertical-align:-4px;margin-left:2px}@media (max-width:1200px){.imagify-bulk .imagify-title{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.imagify-title-right{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2;margin-top:20px}}.imagify-sep-v{width:1px;background:rgba(255,255,255,.2)}.imagify-credit-left{position:relative;min-width:280px;padding-left:15px}@media (max-width:630px){.imagify-title-right,.imagify-title-right>div{display:block;width:auto}.imagify-title-right>div{margin-top:10px;max-width:100%}.imagify-credit-left{padding-left:0}}.base-transparent{background:0 0}[class^=imagify-bar-].right-outside-number{-webkit-box-sizing:border-box;box-sizing:border-box;padding-right:4.5em}.right-outside-number .imagify-barnb{display:block;margin-right:-4.5em;text-align:right;font-weight:700;line-height:1.5}.imagify-list-infos{max-width:100%;width:415px}.imagify-info-block,.imagify-list-infos li{position:relative;padding:10px;padding-left:42px;background:#D9E4EB;border-radius:4px;line-height:1.6}.imagify-documentation-link-box:after,.imagify-documentation-link-box:before{display:none}.imagify-list-infos .imagify-documentation-link-box{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:12px;border:1px solid #338EA6;background:#D9E4EC}.imagify-documentation-icon{width:15px;height:20px;margin-right:15px}.imagify-list-infos .imagify-alt.imagify-alt{background:#8BA7B5;color:#FFF;font-weight:700}.imagify-list-infos li+li{margin-top:1em}.imagify-info-block:before,.imagify-list-infos li:before{content:"";position:absolute;left:13px;top:14px;height:16px;width:16px;border:1px solid #46b1ce;border-radius:10px}.imagify-info-block:after,.imagify-list-infos li:after{content:"i";position:absolute;left:20px;top:13px;font-style:italic;color:#46b1ce}.imagify-cell .imagify-info-block{padding-top:0}.imagify-cell .imagify-info-block:after{top:6px}.imagify-cell .imagify-info-block:before{top:7px}.imagify-list-infos .imagify-alt:after,.imagify-list-infos .imagify-alt:before{color:#fff;border-color:#fff}.imagify-sub-title.imagify-sub-title{font-size:23px;background:#FFF;color:#2E3243;border-left:1px solid #D9D9D9;border-right:1px solid #D9D9D9;font-weight:lighter}.imagify-sub-title .icon-bulk{margin-right:10px;vertical-align:middle}.imagify-sub-title .title-text{display:inline-block;line-height:1;vertical-align:middle}.imagify-bulk .imagify-settings-section{border:1px solid #D9D9D9;border-top:0;background:#F2F5F7;color:#4A4A4A}.imagify-bulk .imagify-settings-section h3,.imagify-bulk .imagify-settings-section li,.imagify-bulk .imagify-settings-section p{color:#4A4A4A}.imagify-bulk .imagify-settings-section h3{margin-bottom:2em}.imagify-title .imagify-tooltips{position:absolute;top:100%;left:0}.imagify-tooltips .icon-round{float:left;display:inline-block;width:28px;height:28px;border:1px solid #FFF;margin-right:8px;margin-bottom:8px;font-size:17px;font-style:italic;line-height:29px;font-weight:700;text-align:center;border-radius:50%}.imagify-tooltips .tooltip-content{display:block;position:relative;max-width:250px;padding:7px 15px 8px;background:#2e3242;color:#FFF;font-size:10px;border-radius:3px}.imagify-tooltips.right .tooltip-content{margin-left:12px}.imagify-tooltips.bottom .tooltip-content{margin-top:4px}.imagify-inline-options label .tooltip-content{position:absolute;left:0;right:0;top:100%;text-transform:none;font-size:10px;letter-spacing:0;text-align:center}.imagify-ac-rt-big,.imagistatus{text-transform:uppercase;font-weight:700}.imagify-tooltips .tooltip-content:after{content:"";position:absolute}.imagify-tooltips.right .tooltip-content:after{top:16px;left:-6px;border-right:8px solid #2e3242;border-top:6px solid transparent;border-bottom:6px solid transparent}.imagify-tooltips.bottom .tooltip-content:after{top:-5px;left:50%;margin-left:-3px;border-bottom:6px solid #2e3242;border-left:6px solid transparent;border-right:6px solid transparent}.imagify-space-tooltips .tooltip-content{max-width:280px;margin-top:20px;margin-left:0;padding:5px 15px;font-size:13px;background:#40B1D0;-webkit-box-shadow:0 3px 0 #338EA6;box-shadow:0 3px 0 #338EA6}.imagify-space-tooltips .tooltip-content:after{top:-14px;left:50%;margin-left:-7px;border:0;border-bottom:15px solid #40B1D0;border-left:15px solid transparent;border-right:15px solid transparent}.tooltip-content.tooltip-table{display:table;width:100%}.tooltip-content.tooltip-table>*{display:table-cell;vertical-align:middle}.tooltip-content .cell-icon{width:28px}.tooltip-content .cell-icon .icon{margin-bottom:0}.tooltip-content .cell-text{padding:5px 10px 5px 0;line-height:1.3}.tooltip-content .cell-sep{width:1px;background:rgba(255,255,255,.4)}.tooltip-content .cell-cta{padding-left:10px}.tooltip-content .cell-cta a{display:block;color:#FFF;width:100%;height:100%;white-space:nowrap}.imagify-number-you-optimized .number{display:table-cell;padding-right:15px;font-size:36px;font-weight:700;line-height:1.1;vertical-align:middle;white-space:nowrap}.imagify-number-you-optimized .text{display:table-cell;vertical-align:middle;overflow:hidden;font-size:12px}.imagify-number-you-optimized .text br{display:none}.imagify-number-you-optimized>p{display:table}.imagify-number-you-optimized{padding-bottom:.85em;margin-bottom:1.35em;overflow:hidden;border-bottom:1px solid rgba(0,0,0,.05)}.imagify-bars p{font-weight:700;font-size:12px;margin-bottom:0}.imagify-bars+.imagify-number-you-optimized{border-bottom:0;padding-top:.85em}.imagify-bars+.imagify-number-you-optimized p{color:#46b1ce}.imagify-bulk-table{margin-top:2em;max-height:600px;max-height:60vh;overflow:auto}.imagify-ac-report-text,.imagiuploaded{overflow:hidden}.imagify-bulk-table table{width:100%;border-spacing:0;border-collapse:collapse;border:1px solid #D3D3D3}.imagify-bulk-table td{padding:8px 15px}.imagify-bulk-table thead th,.imagify-bulk-table thead tr{background:#2E3242}.imagify-bulk-table tfoot th,.imagify-bulk-table tfoot tr{background:#73818C}.imagify-bulk-table thead th{padding:14px 15px;text-align:left;color:#F2F5F7;font-weight:700;font-size:14px}.imagify-bulk-table tfoot td{padding:14px 15px;color:#F9FAFA}.imagify-bulk-table tbody td,.imagify-bulk-table tbody tr{background:#FFF}.imagify-bulk-table tbody tr:nth-child(odd),.imagify-bulk-table tbody tr:nth-child(odd) td{background:#F2F5F7}.imagify-bulk-table .imagify-row-progress{display:none}.imagify-bulk-table .imagify-row-progress,.imagify-bulk-table .imagify-row-progress td{height:15px;padding:0}.imagify-bulk-table .imagify-no-uploaded-yet td{height:200px;font-size:17px;letter-spacing:.1em;word-spacing:.12em;vertical-align:middle;text-transform:uppercase;font-weight:700;text-align:center;color:#999;background-color:#FFF}.imagify-row-complete{padding:35px 20px;margin-top:2em;background:#8BC34A;color:#FFF;text-shadow:0 0 2px rgba(0,0,0,.1)}.imagify-row-complete .imagify-ac-chart{margin-top:3px}.imagify-row-complete p{color:#FFF;margin:0}@-webkit-keyframes congrate{0%{opacity:0;-webkit-transform:scale(1)}50%{-webkit-transform:scale(1.05);opacity:1}100%{-webkit-transform:scale(1);opacity:1}}@keyframes congrate{0%{opacity:0;-webkit-transform:scale(1);transform:scale(1)}50%{-webkit-transform:scale(1.05);transform:scale(1.05);opacity:1}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.imagify-row-complete.done{-webkit-animation:congrate .5s ease-in-out;animation:congrate .5s ease-in-out}.imagify-all-complete{margin:1.5em 0}.imagify-all-complete>div{display:inline-block;vertical-align:middle}.imagify-ac-report{min-width:310px}.imagify-ac-chart{width:46px;height:46px;float:left;margin:0 20px 0 10px}.imagify-ac-report-text p{line-height:1.3}.imagify-ac-rt-big{font-size:24px;letter-spacing:.15em;word-spacing:.15em}.imagify-ac-share{text-align:right}.imagify-ac-share-content{display:inline-block;padding:10px 15px;background:rgba(255,255,255,.2)}.imagify-ac-share-content>*{display:inline-block;vertical-align:middle}.imagify-bulk-table .imagify-ac-share-content p{margin-right:5px}.imagify-share-networks,.imagify-share-networks li{margin:0}.imagify-share-networks li{display:inline-block}.imagify-share-networks a{display:inline-block;vertical-align:-7px;margin:0 5px;text-decoration:none;color:#FFF}.imagify-cell-filename{max-width:200px}.imagify-cell-status{max-width:145px}.imagify-cell-status .dashicons-warning{margin-right:2px}.imagify-cell-thumbnails{max-width:120px}td.imagify-cell-filename{-o-text-overflow:clip;text-overflow:clip;white-space:nowrap;overflow:hidden}.imagify-bulk-table .imagify-cell-thumbnails{text-align:center}.imagify-cell-percentage,.imagify-cell-savings{color:#46B1CE;font-weight:700}.imagify-cell-optimized{font-weight:700}.imagify-bulk-table td.imagify-cell-totaloriginal{padding-right:78px}.imagify-cell-totaloriginal{text-align:right}.imagifilename,.imagiuploaded{display:inline-block;vertical-align:middle}.imagifilename{font-size:12px}.imagiuploaded{width:33px;height:33px;margin-right:5px;margin-left:-8px;background:url(../images/upload-image.png) no-repeat;background-size:cover}.imagiuploaded img{max-widht:100%;height:auto}.imagistatus{color:#8CA6B3}.status-compressing{color:#46B1CE}.status-error{color:#CE0B24}.status-warning{color:#f5a623}.status-complete{color:#8CC152}.dashicons.rotate{-webkit-animation:icon-rotate 2.6s infinite linear;animation:icon-rotate 2.6s infinite linear}.imagify-cell-status .dashicons-admin-generic{-webkit-transform-origin:48.75% 51.75%;-ms-transform-origin:48.75% 51.75%;transform-origin:48.75% 51.75%}@-webkit-keyframes icon-rotate{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes icon-rotate{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}
1
+ #imagify-overview-chart-legend{overflow:hidden}.imagify-doughnut-legend{margin-top:38px;list-style:none}.imagify-doughnut-legend li{display:block;padding-left:30px;position:relative;margin-bottom:15px;border-radius:5px;padding:3px 8px 2px 31px;font-size:13px;cursor:default;-webkit-transition:background-color .2s ease-in-out;-o-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out}.imagify-doughnut-legend li span{display:block;position:absolute;left:0;top:0;width:25px;height:25px;border-radius:50%}.imagify-global-optim-phrase{width:180px;padding-top:20px;font-size:14px;text-align:center}.imagify-total-percent{color:#46b1ce}.imagify-overview-chart-container{float:left;margin-right:20px}.imagify-chart-percent{position:absolute;left:0;right:0;top:50%;margin-top:-.5em;line-height:1;text-align:center;font-size:55px;font-weight:700;color:#46B1CE}.imagify-chart-percent span{font-size:20px;vertical-align:super}.media_page_imagify-bulk-optimization .media-item,body[class*="_imagify-ngg-bulk-optimization"] .media-item{margin:0}.media_page_imagify-bulk-optimization .media-item .progress,body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress{float:none;width:100%;height:8px;margin:0;overflow:visible;background:#1F2331;-webkit-box-shadow:0;box-shadow:0;border-radius:0}.media_page_imagify-bulk-optimization .media-item .percent,body[class*="_imagify-ngg-bulk-optimization"] .media-item .percent{position:absolute;top:6px;right:0;text-shadow:none;width:auto;padding:0 5px;line-height:1.85;font-size:14px;font-weight:700;color:#40B1D0}.media_page_imagify-bulk-optimization .media-item .percent,.media_page_imagify-bulk-optimization .media-item .progress,body[class*="_imagify-ngg-bulk-optimization"] .media-item .percent,body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress{text-align:right}.media_page_imagify-bulk-optimization .media-item .progress .bar,body[class*="_imagify-ngg-bulk-optimization"] .media-item .progress .bar{position:relative;width:1px;height:8px;margin-top:0;background:#46B1CE;border-radius:0;-webkit-transition:width .5s;-o-transition:width .5s;transition:width .5s}#imagify-bulk-action{padding:11px 20px}.imagify-columns .col-overview.col-overview{width:calc(100% - 465px);padding-left:20px}.imagify-columns .col-statistics.col-statistics{width:60%}.imagify-columns .col-chart.col-chart{width:40%}@media (max-width:1520px) and (min-width:1381px),(max-width:1086px){.imagify-columns .col-chart.col-chart,.imagify-columns .col-statistics.col-statistics{width:50%}}@media (max-width:1380px) and (min-width:1246px),(max-width:380px){.imagify-overview-chart-container{float:none;margin-right:0}.imagify-doughnut-legend{margin-top:18px}.imagify-global-optim-phrase{padding-top:0;width:auto}}@media (max-width:808px){.imagify-columns .col-chart.col-chart,.imagify-columns .col-statistics.col-statistics{width:auto;float:none;padding:0}.imagify-columns .col-chart.col-chart{margin-top:3em}}.imagify-sep-v{width:1px;background:rgba(255,255,255,.2)}.base-transparent{background:0 0}[class^=imagify-bar-].right-outside-number{-webkit-box-sizing:border-box;box-sizing:border-box;padding-right:4.5em}.right-outside-number .imagify-barnb{display:block;margin-right:-5.25em;text-align:right;font-weight:700;line-height:.8}.imagify-h2-like{margin:0 0 .5em;padding-bottom:.5em;border-bottom:1px solid #E9EFF2;font-size:24px;color:#000;font-weight:700}.imagify-h2-like .dashicons,.imagify-h2-like .dashicons:before{font-size:38px;height:38px;width:38px;margin-right:12px;vertical-align:-5px;color:#40B1D0}.imagify-info-block{position:relative;padding:10px;padding-left:42px;background:#D9E4EB;border-radius:4px;line-height:1.6}.imagify-list-infos{margin:0;padding:0}.imagify-list-infos li{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:15px 5px;text-align:left;font-size:14px;line-height:1.5;color:#626E7B}.imagify-list-infos li:first-child{padding-top:5px}.imagify-list-infos li:last-child{padding-bottom:5px}.imagify-list-infos li+li{border-top:1px solid #E9EFF2}.imagify-info-icon{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-preferred-size:50px;flex-basis:50px}.imagify-info-icon+span{padding-left:20px}.imagify-list-infos a:before{content:'';display:block}.imagify-bulk .imagify-settings-section{border:1px solid #D9D9D9;border-top:0;background:#FFF;color:#4A4A4A}.imagify-bulk h3,.imagify-bulk li,.imagify-bulk p{color:#4A4A4A}.imagify-bulk .imagify-settings-section h3{margin-bottom:2em}.imagify-account-info-col .imagify-options-title{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.imagify-account-info-col p.imagify-meteo-title{margin:0;font-size:24px;font-weight:700;color:#FFF}.imagify-account-info-col .imagify-options-title>a{-ms-flex-preferred-size:100px;flex-basis:100px;margin-left:auto;margin-right:10px;text-decoration:underline;font-size:12px}.imagify-account-info-col .imagify-meteo-title .dashicons,.imagify-account-info-col .imagify-meteo-title .dashicons:before{font-size:38px;width:38px;height:38px;margin-right:4px;color:#40B1D0}.imagify-col-content{background:#F4F7F9;border-left:1px solid #ddd;border-right:1px solid #ddd}.imagify-col-content .imagify-block-secondary{margin-left:-1px;margin-right:-1px}.imagify-col-content .imagify-space-left{margin:15px 30px 15px 0}.imagify-col-content .imagify-space-left p{margin:0 0 10px;font-size:19px;font-weight:500;color:#343A49}.imagify-col-content .imagify-meteo-icon{height:64px;margin:15px 15px 15px 20px}.imagify-col-content .imagify-section-title+p{margin-top:10px}.imagify-account-info-col .imagify-h3-like.imagify-h3-like{color:inherit}.imagify-title .imagify-tooltips{position:absolute;top:100%;left:0}.imagify-tooltips .icon-round{float:left;display:inline-block;width:28px;height:28px;border:1px solid #FFF;margin-right:8px;margin-bottom:8px;font-size:17px;font-style:italic;line-height:29px;font-weight:700;text-align:center;border-radius:50%}.imagify-tooltips .tooltip-content{display:block;position:relative;max-width:250px;padding:7px 15px 8px;background:#2e3242;color:#FFF;font-size:10px;border-radius:3px}.imagify-tooltips.right .tooltip-content{margin-left:12px}.imagify-tooltips.bottom .tooltip-content{margin-top:4px}.imagify-inline-options label .tooltip-content{position:absolute;left:0;right:0;top:100%;text-transform:none;font-size:10px;letter-spacing:0;text-align:center}.imagify-tooltips .tooltip-content:after{content:"";position:absolute}.imagify-tooltips.right .tooltip-content:after{top:16px;left:-6px;border-right:8px solid #2e3242;border-top:6px solid transparent;border-bottom:6px solid transparent}.imagify-tooltips.bottom .tooltip-content:after{top:-5px;left:50%;margin-left:-3px;border-bottom:6px solid #2e3242;border-left:6px solid transparent;border-right:6px solid transparent}.imagify-space-tooltips .tooltip-content{max-width:280px;margin-top:20px;margin-left:0;padding:5px 15px;font-size:13px;background:#40B1D0;-webkit-box-shadow:0 3px 0 #338EA6;box-shadow:0 3px 0 #338EA6}.imagify-space-tooltips .tooltip-content:after{top:-14px;left:50%;margin-left:-7px;border:0;border-bottom:15px solid #40B1D0;border-left:15px solid transparent;border-right:15px solid transparent}.tooltip-content.tooltip-table{display:table;width:100%}.tooltip-content.tooltip-table>*{display:table-cell;vertical-align:middle}.tooltip-content .cell-icon{width:28px}.tooltip-content .cell-icon .icon{margin-bottom:0}.tooltip-content .cell-text{padding:5px 10px 5px 0;line-height:1.3}.tooltip-content .cell-sep{width:1px;background:rgba(255,255,255,.4)}.tooltip-content .cell-cta{padding-left:10px}.tooltip-content .cell-cta a{display:block;color:#FFF;width:100%;height:100%;white-space:nowrap}.imagify-number-you-optimized{margin-bottom:1.35em;overflow:hidden}.imagify-number-you-optimized .number{display:table-cell;padding-right:15px;font-size:48px;font-weight:700;line-height:1;vertical-align:middle;white-space:nowrap;color:#000}.imagify-number-you-optimized [id=imagify-total-optimized-attachments-pct]{color:#40B1D0}.imagify-number-you-optimized .text{display:table-cell;vertical-align:middle;overflow:hidden;font-size:12px;color:#626E7B}.imagify-number-you-optimized>p{display:table}.imagify-bars{padding-right:15px}.imagify-bars p{font-size:12px;margin-bottom:5px}.imagify-bars+.imagify-number-you-optimized{border-bottom:0;padding-top:.85em}.imagify-bars+.imagify-number-you-optimized p{color:#46b1ce}.imagify-bulk-table{margin-top:2em}.imagify-table-header{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:15px 25px;background:#343A49;color:#FFF}.imagify-newbie{margin-top:4em;position:relative;overflow:visible}.imagify-ac-report-text,.imagiuploaded{overflow:hidden}.imagify-newbie .imagify-new-feature.imagify-new-feature{position:absolute;top:0;left:25px;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);margin:0;padding:8px 20px;font-size:14px;letter-spacing:.02em;text-transform:uppercase;font-weight:700;color:#FFF;background:#8BC34A}.imagify-newbie .imagify-table-header{padding:30px 25px;border:2px solid #8BC34A;background:#F3F9EC}.imagify-th-titles .dashicons,.imagify-th-titles .dashicons:before{width:38px;height:38px;margin-right:20px;font-size:38px;color:#40B1D0}.imagify-newbie .imagify-th-titles .dashicons:before{color:#8BC34A}.imagify-th-title.imagify-th-title.imagify-th-title{margin:0;font-size:24px;font-weight:500;color:#FFF}.imagify-newbie .imagify-th-title.imagify-th-title{color:#343A49}.imagify-th-subtitle.imagify-th-subtitle.imagify-th-subtitle{margin:0 0 5px;font-size:14px;color:#7A8996;font-weight:500}.imagify-th-action .imagify-button-clean{font-size:12px;color:#7A8996}.imagify-th-action .button:focus,.imagify-th-action .button:hover,.imagify-th-action .imagify-is-active{color:#FFF}.imagify-bulk-table table{width:100%;border-spacing:0;border-collapse:collapse}.imagify-bulk-table td{padding:20px}.imagify-bulk-table-details{border-bottom:2px solid #E5EBEF}.imagify-bulk-table-details thead th,.imagify-bulk-table-details thead tr{background:#4A5362}.imagify-bulk-table-details thead th{padding:12px 20px;text-align:left;font-weight:700;color:#E5EBEF;font-size:12px}.imagify-bulk-table-details tbody tr:nth-child(odd) td{background:#F2F5F7}.imagify-bulk-table-content{border:1px solid #D3D3D3;border-top:0}.imagify-bulk-table-footer{padding:20px;color:#626E7B;background:#F2F5F7}.imagify-bulk-table tbody tr+tr{border-top:3px solid #F2F5F7}.imagify-bulk-table tbody td,.imagify-bulk-table tbody tr{background:#FFF}.imagify-bulk-table .imagify-row-progress{display:none;height:8px;padding:0}.imagify-bulk-table .imagify-no-uploaded-yet td{height:200px;font-size:17px;letter-spacing:.1em;word-spacing:.12em;vertical-align:middle;text-transform:uppercase;font-weight:700;text-align:center;color:#999;background-color:#FFF}.imagify-level-selector{position:relative}.imagify-level-selector-list{background:#FFF;border:1px solid #F4F7F9;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.1);box-shadow:0 6px 12px rgba(0,0,0,.1);border-radius:3px;font-weight:700;text-transform:uppercase;letter-spacing:.02em}.imagify-level-selector-list li:first-child label{border-radius:3px 3px 0 0}.imagify-level-selector-list li:last-child label{border-radius:0 0 3px 3px}.imagify-level-selector-list li{margin:0}.imagify-level-selector-list li+li{border-top:1px solid #F4F7F9}.imagify-level-selector-list svg{margin-right:5px}.imagify-level-selector-list .imagify-current-level label,.imagify-level-selector-list input:checked+label{background:#343A49;color:#FFF}.imagify-level-selector-list .imagify-current-level input:focus+label,.imagify-level-selector-list .imagify-current-level label:hover,.imagify-level-selector-list input:checked+label:hover,.imagify-level-selector-list input:focus+label,.imagify-level-selector-list label:hover{background:#40B1D0;color:#F4F7F9}.imagify-level-selector-list .imagify-current-level input:focus+label polygon,.imagify-level-selector-list .imagify-current-level label:hover polygon,.imagify-level-selector-list input:checked+label:hover polygon,.imagify-level-selector-list input:focus+label polygon,.imagify-level-selector-list label:hover polygon{fill:#FFF}.imagify-level-selector-list .imagify-current-level input:focus+label polygon[fill="#CCD1D6"],.imagify-level-selector-list .imagify-current-level label:hover polygon[fill="#CCD1D6"],.imagify-level-selector-list input:checked+label:hover polygon[fill="#CCD1D6"],.imagify-level-selector-list input:focus+label polygon[fill="#CCD1D6"],.imagify-level-selector-list label:hover polygon[fill="#CCD1D6"]{fill:#3694AE}.imagify-level-selector-list li label{display:block;padding:10px;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-level-selector-list polygon{-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-level-selector-list{position:absolute;top:0;left:0;right:0;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}.imagify-level-selector-list[aria-hidden=true]{opacity:0;visibility:hidden;-webkit-transform:translateY(-50%) scale(0);-ms-transform:translateY(-50%) scale(0);transform:translateY(-50%) scale(0)}.imagify-level-selector-list[aria-hidden=false]{opacity:1;visibility:visible;-webkit-transform:translateY(-50%) scale(1);-ms-transform:translateY(-50%) scale(1);transform:translateY(-50%) scale(1)}.button .imagify-current-level-info{position:relative;padding-right:20px}.button .imagify-current-level-info:after{content:'';position:absolute;right:0;top:50%;margin-top:-3px;border-top:6px solid #7A8996;border-left:6px solid transparent;border-right:6px solid transparent}.imagify-row-complete{padding:35px 20px;margin-top:2em;background:#8BC34A;color:#FFF;text-shadow:0 0 2px rgba(0,0,0,.1)}.imagify-row-complete .imagify-ac-chart{margin-top:3px}.imagify-row-complete.imagify-row-complete p{color:#FFF;margin:0}@-webkit-keyframes congrate{0%{opacity:0;-webkit-transform:scale(1)}50%{-webkit-transform:scale(1.05);opacity:1}100%{-webkit-transform:scale(1);opacity:1}}@keyframes congrate{0%{opacity:0;-webkit-transform:scale(1);transform:scale(1)}50%{-webkit-transform:scale(1.05);transform:scale(1.05);opacity:1}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.imagify-row-complete.done{-webkit-animation:congrate .5s ease-in-out;animation:congrate .5s ease-in-out}.imagify-all-complete{margin:1.5em 0}.imagify-all-complete>div{display:inline-block;vertical-align:middle}.imagify-ac-report{min-width:310px;margin-right:20px}.imagify-ac-chart{width:46px;height:46px;float:left;margin:0 20px 0 10px}.imagify-ac-report-text p{line-height:1.3}.imagify-ac-rt-big{font-weight:700;font-size:24px;letter-spacing:.15em;word-spacing:.15em;text-transform:uppercase}.imagify-ac-share{text-align:right}.imagify-ac-share-content{display:inline-block;padding:10px 15px;background:rgba(255,255,255,.2)}.imagify-ac-share-content>*{display:inline-block;vertical-align:middle}.imagify-bulk-table .imagify-ac-share-content p{margin-right:5px}.imagify-share-networks,.imagify-share-networks li{margin:0}.imagify-share-networks li{display:inline-block}.imagify-share-networks a{display:inline-block;vertical-align:-7px;margin:0 5px;text-decoration:none;color:#FFF}.imagify-cell-checkbox{width:35px}.imagify-cell-checkbox p{margin:0}.imagify-cell-checkbox-loader{display:block;width:27px;height:28px;line-height:0;-webkit-animation:loading 4s infinite linear;animation:loading 4s infinite linear}@-webkit-keyframes loading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.imagify-cell-checkbox-loader.hidden{display:none;-webkit-animation:none;animation:none}.imagify-cell-label,.imagify-cell-title label{font-size:14px;text-transform:uppercase;letter-spacing:.02em;font-weight:700}.imagify-cell-label{margin-right:10px}.imagify-cell-value{font-size:12px;font-weight:500;color:#7A8996}td.imagify-cell-title{padding-left:0}.imagify-cell-original .imagify-cell-label,.imagify-cell-title label{color:#1F2332}.imagify-cell-optimized,.imagify-cell-original{font-weight:500;color:#7A8996;font-size:12px}.imagify-cell-optimized .imagify-cell-label{color:#338EA6}.imagify-cell-images-optimized{font-size:14px;font-weight:700;color:#338EA6}.imagify-cell-errors{color:#C51162;font-weight:700;font-size:14px}.imagify-cell-errors a{margin-left:5px;color:#7A8996;font-weight:400;font-size:12px}.imagify-cell-filename{max-width:200px}.imagify-cell-status{max-width:145px}.imagify-cell-status .dashicons-warning{margin-right:2px}.imagistatus .dashicons,.imagiuploaded{margin-right:5px}.imagify-cell-thumbnails{max-width:120px}td.imagify-cell-filename{-o-text-overflow:clip;text-overflow:clip;white-space:nowrap;overflow:hidden}.imagify-bulk-table .imagify-cell-thumbnails{text-align:center}.imagify-cell-percentage,.imagify-cell-savings{color:#46B1CE;font-weight:700}.imagify-bulk-table td.imagify-cell-totaloriginal{padding-right:78px}.imagify-cell-totaloriginal{text-align:right}.imagify-cell-level{width:145px}.imagify-level-selector-button.imagify-level-selector-button{border:1px solid #FFF;padding:2px 10px}.imagify-level-selector-button.imagify-level-selector-button:focus,.imagify-level-selector-button.imagify-level-selector-button:hover{border-color:#EEE;-webkit-box-shadow:0 4px 8px rgba(0,0,0,.1);box-shadow:0 4px 8px rgba(0,0,0,.1)}.imagifilename,.imagiuploaded{display:inline-block;vertical-align:middle;margin-left:5px;color:#626E7B;font-weight:500}.imagifilename{font-size:12px}.imagiuploaded{width:33px;height:33px;margin-left:-8px;background:url(../images/upload-image.png) no-repeat;background-size:cover}.imagiuploaded img{max-width:100%;height:auto}.imagistatus{color:#8CA6B3;text-transform:uppercase;font-weight:700}.status-compressing{color:#46B1CE}.status-error{color:#CE0B24}.status-warning{color:#f5a623}.status-complete{color:#8CC152}.imagify-bulk-submit{padding:15px 0 8px}.imagify-settings .button-primary.button-primary[disabled]{color:#4A4A4A!important;background:#D9E4EB!important;text-shadow:none!important;cursor:not-allowed}.dashicons.rotate{-webkit-animation:icon-rotate 2.6s infinite linear;animation:icon-rotate 2.6s infinite linear}.imagify-cell-status .dashicons-admin-generic{-webkit-transform-origin:48.75% 51.75%;-ms-transform-origin:48.75% 51.75%;transform-origin:48.75% 51.75%}@-webkit-keyframes icon-rotate{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes icon-rotate{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.imagify-col.imagify-col.imagify-account-info-col{width:465px}@media (max-width:1245px){.imagify-col.imagify-col.imagify-account-info-col{width:auto;max-width:none;float:none}.imagify-columns .col-overview.col-overview{float:none;width:auto;padding-left:0;padding-right:0}}@media (max-width:1200px){.imagify-settings .imagify-title .imagify-logo{display:none}}@media (max-width:940px){.imagify-bulk-table-container tbody,.imagify-bulk-table-container tr{text-align:left}.imagify-bulk-table-container tbody,.imagify-bulk-table-container tbody td,.imagify-bulk-table-container tbody tr{display:block}.imagify-bulk-table-container tbody td{padding:20px}.imagify-cell-checkbox,.imagify-cell-title{float:left}.imagify-cell-checkbox{width:26px}.imagify-bulk-table-container .imagify-cell-title{padding-left:10px;width:calc(100% - 96px)}.imagify-cell-images-optimized:before,.imagify-cell-title:after{content:'';display:table;clear:both;width:100%}.imagify-cell-images-optimized{clear:both}.imagify-bulk-table-container .imagify-cell-title~td{display:inline-block}.imagify-bulk-table-container td.imagify-cell-level{display:block}}@media (max-width:918px){.imagify-settings .imagify-title{display:block}.imagify-settings .imagify-documentation-link-box{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}}.hidden{display:none}
assets/css/files-list.css ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Filter block */
2
+ .imagify-files-list .wp-filter {
3
+ padding: 15px 20px;
4
+ }
5
+ .imagify-files-list .filter-items select {
6
+ height: auto;
7
+ padding: 6px;
8
+ margin-right: 12px;
9
+ }
10
+ .imagify-files-list .filter-items .button {
11
+ height: auto;
12
+ padding: 2px 12px 3px;
13
+ }
14
+
15
+ /* Empty table */
16
+ .imagify-files-list .no-items td {
17
+ padding: 35px;
18
+ text-align: center;
19
+ font-size: 18px;
20
+ }
21
+ .imagify-files-list .no-items td a {
22
+ text-decoration: underline;
23
+ }
24
+
25
+ /* Th sortable */
26
+ .imagify-files-list .sortable a {
27
+ color: #000;
28
+ }
29
+
30
+ /* Global links */
31
+ .imagify-files-list a {
32
+ color: #3694AE;
33
+ }
34
+
35
+ /* Global TDs */
36
+ .imagify-files-list tbody td,
37
+ .imagify-files-list tbody th,
38
+ .imagify-files-list.imagify-files-list tbody .check-column {
39
+ vertical-align: middle;
40
+ padding-top: 20px;
41
+ padding-bottom: 20px;
42
+ color: #626E7B;
43
+ }
44
+
45
+ /* Col Title */
46
+ .imagify-files-list .column-title strong {
47
+ font-weight: normal;
48
+ font-size: 14px;
49
+ }
50
+ .imagify-files-list .column-title strong a {
51
+ display: inline-flex;
52
+ align-items: center;
53
+ word-break: break-all;
54
+ word-wrap: break-word;
55
+ font-weight: normal;
56
+ }
57
+ .imagify-files-list .filename {
58
+ font-size: 12px;
59
+ font-weight: bold;
60
+ }
61
+ .imagify-files-list .media-icon {
62
+ position: relative;
63
+ width: 60px;
64
+ overflow: hidden;
65
+ flex-shrink: 0;
66
+ }
67
+ .media-icon .centered {
68
+ position: absolute;
69
+ left: 0;
70
+ top: 0;
71
+ width: 100%;
72
+ height: 100%;
73
+ transform: translate( 50%, 50% );
74
+ }
75
+ .media-icon .centered img {
76
+ position: absolute;
77
+ left: 0;
78
+ top: 0;
79
+ transform: translate( -50%, -50% );
80
+ }
81
+ table.media .column-title .media-icon.landscape img {
82
+ max-width: none;
83
+ width: auto;
84
+ height: 60px;
85
+ }
86
+ table.media .column-title .media-icon.portrait img {
87
+ width: 60px;
88
+ }
89
+
90
+ /* Optimization datas Col */
91
+ .imagify-files-list ul.imagify-datas-list {
92
+ font-size: 11px;
93
+ }
94
+ .imagify-files-list ul.imagify-datas-list .big {
95
+ font-size: 13px;
96
+ }
97
+ .imagify-files-list ul.imagify-datas-list span.imagify-chart-value {
98
+ font-size: 12px;
99
+ }
100
+ .imagify-files-list ul.imagify-datas-list .imagify-chart-container {
101
+ margin-right: 2px;
102
+ }
103
+ .imagify-files-list ul.imagify-datas-list canvas {
104
+ width: 18px!important;
105
+ height: 18px!important;
106
+ }
107
+
108
+ /* Optimization Level Col */
109
+ .imagify-files-list .optimization_level {
110
+ text-align: center;
111
+ font-weight: bold;
112
+ font-size: 14px;
113
+ text-transform: uppercase;
114
+ letter-spacing: 0.02em;
115
+ }
116
+ .imagify-files-list .column-optimization_level,
117
+ .imagify-files-list .column-optimization_level a {
118
+ text-align: center;
119
+ }
120
+ .imagify-files-list .column-optimization_level a span {
121
+ float: none;
122
+ display: inline-block;
123
+ vertical-align: middle;
124
+ }
125
+ .imagify-files-list .column-optimization_level .sorting-indicator {
126
+ vertical-align: -10px;
127
+ }
128
+
129
+ /* Actions col */
130
+ .imagify-files-list .column-actions .button,
131
+ .imagify-files-list .column-actions .button-primary {
132
+ padding: 5px 20px;
133
+ font-size: 14px;
134
+ height: auto;
135
+ }
136
+ .imagify-files-list .column-actions .button-primary {
137
+ background: #3694AE;
138
+ color: #FFF;
139
+ border: 0;
140
+ box-shadow: none;
141
+ text-shadow: none;
142
+ }
143
+ .imagify-files-list .column-actions a,
144
+ .status a.button-imagify-refresh-status {
145
+ display: inline-block;
146
+ margin: .3em 0;
147
+ font-size: 12px;
148
+ font-weight: bold;
149
+ }
150
+ .imagify-files-list .imagify-status-already_optimized {
151
+ font-weight: bold;
152
+ color: #8BC34A;
153
+ }
154
+ .imagify-files-list .column-actions a .dashicons,
155
+ .imagify-files-list .column-actions a .dashicons:before,
156
+ .status a.button-imagify-refresh-status .dashicons,
157
+ .status a.button-imagify-refresh-status .dashicons:before {
158
+ margin-right: 2px;
159
+ font-size: 17px;
160
+ height: 17px;
161
+ width: 17px;
162
+ }
assets/css/files-list.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .imagify-files-list .wp-filter{padding:15px 20px}.imagify-files-list .filter-items select{height:auto;padding:6px;margin-right:12px}.imagify-files-list .filter-items .button{height:auto;padding:2px 12px 3px}.imagify-files-list .no-items td{padding:35px;text-align:center;font-size:18px}.imagify-files-list .no-items td a{text-decoration:underline}.imagify-files-list .sortable a{color:#000}.imagify-files-list a{color:#3694AE}.imagify-files-list tbody td,.imagify-files-list tbody th,.imagify-files-list.imagify-files-list tbody .check-column{vertical-align:middle;padding-top:20px;padding-bottom:20px;color:#626E7B}.imagify-files-list .column-title strong{font-weight:400;font-size:14px}.imagify-files-list .column-title strong a{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;word-break:break-all;word-wrap:break-word;font-weight:400}.imagify-files-list .filename{font-size:12px;font-weight:700}.imagify-files-list .media-icon{position:relative;width:60px;overflow:hidden;-ms-flex-negative:0;flex-shrink:0}.media-icon .centered{position:absolute;left:0;top:0;width:100%;height:100%;-webkit-transform:translate(50%,50%);-ms-transform:translate(50%,50%);transform:translate(50%,50%)}.media-icon .centered img{position:absolute;left:0;top:0;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}table.media .column-title .media-icon.landscape img{max-width:none;width:auto;height:60px}table.media .column-title .media-icon.portrait img{width:60px}.imagify-files-list ul.imagify-datas-list{font-size:11px}.imagify-files-list ul.imagify-datas-list .big{font-size:13px}.imagify-files-list ul.imagify-datas-list span.imagify-chart-value{font-size:12px}.imagify-files-list ul.imagify-datas-list .imagify-chart-container{margin-right:2px}.imagify-files-list ul.imagify-datas-list canvas{width:18px!important;height:18px!important}.imagify-files-list .optimization_level{text-align:center;font-weight:700;font-size:14px;text-transform:uppercase;letter-spacing:.02em}.imagify-files-list .column-optimization_level,.imagify-files-list .column-optimization_level a{text-align:center}.imagify-files-list .column-optimization_level a span{float:none;display:inline-block;vertical-align:middle}.imagify-files-list .column-optimization_level .sorting-indicator{vertical-align:-10px}.imagify-files-list .column-actions .button,.imagify-files-list .column-actions .button-primary{padding:5px 20px;font-size:14px;height:auto}.imagify-files-list .column-actions .button-primary{background:#3694AE;color:#FFF;border:0;-webkit-box-shadow:none;box-shadow:none;text-shadow:none}.imagify-files-list .column-actions a,.status a.button-imagify-refresh-status{display:inline-block;margin:.3em 0;font-size:12px;font-weight:700}.imagify-files-list .imagify-status-already_optimized{font-weight:700;color:#8BC34A}.imagify-files-list .column-actions a .dashicons,.imagify-files-list .column-actions a .dashicons:before,.status a.button-imagify-refresh-status .dashicons,.status a.button-imagify-refresh-status .dashicons:before{margin-right:2px;font-size:17px;height:17px;width:17px}
assets/css/notices.css CHANGED
@@ -41,7 +41,7 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
41
  padding: 0;
42
  margin: 10px 20px 10px 2px;
43
  border: 0 none;
44
- background: #2E3243;
45
  box-shadow: none;
46
  color: #FFF;
47
  }
@@ -68,11 +68,18 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
68
  padding: 18px 23px;
69
  background: #40B1D0;
70
  }
 
 
 
 
 
 
 
71
  .updated .imagify-notice-logo {
72
  background: #8BC34A;
73
  }
74
  .error .imagify-notice-logo {
75
- background: #D0021B;
76
  }
77
 
78
  .imagify-notice-title {
@@ -85,6 +92,29 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
85
  .imagify-notice-content.imagify-notice-content p {
86
  margin: 0.65em 0;
87
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  .imagify-notice a {
89
  color: #40B1D0;
90
  }
@@ -164,7 +194,7 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
164
  border: 0 none;
165
  box-shadow: none;
166
  color: #FFF;
167
- background: #412355;
168
  }
169
  .media_page_imagify-bulk-optimization .imagify-rkt-notice {
170
  margin-left: 2px;
@@ -183,7 +213,8 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
183
  padding: 0;
184
  margin-top: -11px;
185
  background: transparent;
186
- color: #FD7300;
 
187
  border-radius: 50%;
188
  transition: all .275s;
189
  }
@@ -213,6 +244,7 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
213
  }
214
  .imagify-rkt-notice .imagify-rkt-msg {
215
  width: 100%!important; /* !important because of a dirtugly WP Engine code */
 
216
  padding: 0 15px;
217
  font-size: 14px;
218
  line-height: 1.6;
@@ -225,8 +257,8 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
225
  padding: 5px 10px;
226
  font-size: 23px;
227
  font-weight: bold;
228
- border: 1px dashed #F7A933;
229
- color: #F7A933;
230
  }
231
  .imagify-rkt-notice .imagify-rkt-cta {
232
  width: 250px!important; /* !important because of a dirtugly WP Engine code */
@@ -244,11 +276,11 @@ body[class*="_imagify-ngg-bulk-optimization"] .notice,
244
  height: auto;
245
  font-weight: 600;
246
  font-size: 14px;
247
- box-shadow: 0 4px 0 #B27A27;
248
  border: 0 none;
249
  padding: 9px 18px 9px;
250
- background: #F7A933;
251
- text-shadow: 1px 1px 1px rgba(0,0,0,.2);
 
252
  }
253
  @media (max-width: 880px) {
254
  .imagify-rkt-notice {
41
  padding: 0;
42
  margin: 10px 20px 10px 2px;
43
  border: 0 none;
44
+ background: #4A5362;
45
  box-shadow: none;
46
  color: #FFF;
47
  }
68
  padding: 18px 23px;
69
  background: #40B1D0;
70
  }
71
+ .imagify-notice-logo .imagify-logo {
72
+ opacity: 1;
73
+ }
74
+ .imagify-flex-notice-content .imagify-notice-logo {
75
+ display: flex;
76
+ align-items: center;
77
+ }
78
  .updated .imagify-notice-logo {
79
  background: #8BC34A;
80
  }
81
  .error .imagify-notice-logo {
82
+ background: #C51162;
83
  }
84
 
85
  .imagify-notice-title {
92
  .imagify-notice-content.imagify-notice-content p {
93
  margin: 0.65em 0;
94
  }
95
+ .imagify-flex-notice-content .imagify-notice-content {
96
+ display: flex;
97
+ flex-wrap: wrap;
98
+ padding: 0;
99
+ }
100
+ .imagify-flex-notice-content .imagify-notice-content > * {
101
+ padding: 10px 20px;
102
+ }
103
+ .imagify-flex-notice-content .imagify-meteo-icon img {
104
+ height: 100%;
105
+ margin-top: 6px;
106
+ }
107
+ .imagify-notice-quota [class^="imagify-bar-"] {
108
+ background: #1F2332;
109
+ }
110
+ .imagify-notice-quota .imagify-space-left p {
111
+ margin: 0;
112
+ }
113
+ .imagify-flex-notice-content .imagify-notice-content .imagify-notice-quota {
114
+ padding-right: 24px;
115
+ padding-left: 8px;
116
+ background: #343A49;
117
+ }
118
  .imagify-notice a {
119
  color: #40B1D0;
120
  }
194
  border: 0 none;
195
  box-shadow: none;
196
  color: #FFF;
197
+ background: #1F2332;
198
  }
199
  .media_page_imagify-bulk-optimization .imagify-rkt-notice {
200
  margin-left: 2px;
213
  padding: 0;
214
  margin-top: -11px;
215
  background: transparent;
216
+ color: rgba(255,255,255,.5);
217
+ text-decoration: none;
218
  border-radius: 50%;
219
  transition: all .275s;
220
  }
244
  }
245
  .imagify-rkt-notice .imagify-rkt-msg {
246
  width: 100%!important; /* !important because of a dirtugly WP Engine code */
247
+ color: #FFF;
248
  padding: 0 15px;
249
  font-size: 14px;
250
  line-height: 1.6;
257
  padding: 5px 10px;
258
  font-size: 23px;
259
  font-weight: bold;
260
+ border: 1px dashed #F56640;
261
+ color: #F56640;
262
  }
263
  .imagify-rkt-notice .imagify-rkt-cta {
264
  width: 250px!important; /* !important because of a dirtugly WP Engine code */
276
  height: auto;
277
  font-weight: 600;
278
  font-size: 14px;
 
279
  border: 0 none;
280
  padding: 9px 18px 9px;
281
+ background: #F56640;
282
+ box-shadow: none;
283
+ text-shadow: none!important;
284
  }
285
  @media (max-width: 880px) {
286
  .imagify-rkt-notice {
assets/css/notices.min.css CHANGED
@@ -1 +1 @@
1
- .imagify-plugins-error{overflow:hidden;padding-left:20px;list-style-type:disc}.imagify-plugins-error li{width:300px;line-height:30px}@media (max-width:570px){.imagify-plugins-error li{width:auto}}.imagify-notice-dismiss.notice-dismiss{text-decoration:none}.media_page_imagify-bulk-optimization .notice,.settings_page_imagify .notice,body[class*="_imagify-ngg-bulk-optimization"] .notice{margin-right:20px;margin-left:2px}.imagify-notice .button-mini{padding:2px 10px;font-size:13px}.imagify-notice.imagify-notice{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;padding:0;margin:10px 20px 10px 2px;border:0;background:#2E3243;-webkit-box-shadow:none;box-shadow:none;color:#FFF}@media (max-width:782px){.imagify-notice.imagify-notice,.imagify-welcome{margin-right:12px}}@media (max-width:450px){.imagify-notice.imagify-notice{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.wrap .imagify-notice{margin:5px 15px 2px;position:relative}.imagify-notice-logo{padding:18px 23px;background:#40B1D0}.updated .imagify-notice-logo{background:#8BC34A}.error .imagify-notice-logo{background:#D0021B}.imagify-notice-title{font-size:15px}.imagify-notice-content{padding:5px 23px}.imagify-notice-content.imagify-notice-content p{margin:.65em 0}.imagify-notice a{color:#40B1D0}.imagify-notice a:focus,.imagify-notice a:hover{color:#FEE102}.imagify-notice code{background:rgba(0,0,0,.4)}.imagify-notice .imagify-rate-us.imagify-rate-us{text-align:left}.imagify-notice .imagify-rate-us .stars{margin:0}.imagify-welcome{margin:30px 20px 0 0}.imagify-welcome .baseline{display:inline-block;margin:.2em 0 0 2em;font-size:17px}.imagify-welcome .imagify-logo{vertical-align:middle}.imagify-welcome-remove{position:absolute;top:50%;right:15px;margin-top:-8px;color:#FFF;text-decoration:none}.imagify-columns [class^=col-] img{float:left;margin-right:18px}.imagify-col-content{overflow:hidden}.imagify-col-title{margin:0 0 15px;font-size:23px}.counter .imagify-col-title:before{counter-increment:cols;content:counter(cols) ". ";color:#40B1D0}.imagify-col-desc{color:#5F758E;margin-bottom:2em}.imagify-rkt-notice.imagify-rkt-notice{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;padding:10px 45px 10px 0;border:0;-webkit-box-shadow:none;box-shadow:none;color:#FFF;background:#412355}.media_page_imagify-bulk-optimization .imagify-rkt-notice{margin-left:2px;margin-right:20px}@media (max-width:782px){.media_page_imagify-bulk-optimization .imagify-rkt-notice{margin-left:0;margin-right:12px}}.imagify-rkt-notice .imagify-cross{position:absolute;right:8px;top:50%;width:22px;height:22px;padding:0;margin-top:-11px;background:0 0;color:#FD7300;border-radius:50%;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-rkt-notice .imagify-cross .dashicons{position:relative;top:2px;left:1px;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-rkt-notice .imagify-cross:hover{background:#FFF}.imagify-rkt-notice .imagify-cross:hover .dashicons{color:#412355}.imagify-rkt-notice .imagify-rkt-coupon,.imagify-rkt-notice .imagify-rkt-cta,.imagify-rkt-notice .imagify-rkt-logo{-ms-flex-negative:0;flex-shrink:0}.imagify-rkt-notice .imagify-rkt-logo{width:150px!important;text-align:center;padding:0 25px 0 30px;line-height:.8}.imagify-rkt-notice .imagify-rkt-msg{width:100%!important;padding:0 15px;font-size:14px;line-height:1.6}.imagify-rkt-notice .imagify-rkt-coupon{width:150px!important;padding:0 15px}.imagify-rkt-notice .imagify-rkt-coupon-code{padding:5px 10px;font-size:23px;font-weight:700;border:1px dashed #F7A933;color:#F7A933}.imagify-rkt-notice .imagify-rkt-cta{width:250px!important;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:200px;flex-basis:200px}.imagify-rkt-notice .button.button{position:relative;top:-1px;height:auto;font-weight:600;font-size:14px;-webkit-box-shadow:0 4px 0 #B27A27;box-shadow:0 4px 0 #B27A27;border:0;padding:9px 18px;background:#F7A933;text-shadow:1px 1px 1px rgba(0,0,0,.2)}@media (max-width:880px){.imagify-rkt-notice{-ms-flex-wrap:wrap;flex-wrap:wrap}.imagify-rkt-notice .imagify-rkt-cta,.imagify-rkt-notice .imagify-rkt-logo,.imagify-rkt-notice .imagify-rkt-msg{text-align:left;padding:5px 15px}.imagify-cross.imagify-cross{top:8px;margin-top:0}.imagify-rkt-notice .imagify-cross .dashicons{top:1px}}
1
+ .imagify-plugins-error{overflow:hidden;padding-left:20px;list-style-type:disc}.imagify-plugins-error li{width:300px;line-height:30px}@media (max-width:570px){.imagify-plugins-error li{width:auto}}.imagify-notice-dismiss.notice-dismiss{text-decoration:none}.media_page_imagify-bulk-optimization .notice,.settings_page_imagify .notice,body[class*="_imagify-ngg-bulk-optimization"] .notice{margin-right:20px;margin-left:2px}.imagify-notice .button-mini{padding:2px 10px;font-size:13px}.imagify-notice.imagify-notice{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;padding:0;margin:10px 20px 10px 2px;border:0;background:#4A5362;-webkit-box-shadow:none;box-shadow:none;color:#FFF}@media (max-width:782px){.imagify-notice.imagify-notice,.imagify-welcome{margin-right:12px}}@media (max-width:450px){.imagify-notice.imagify-notice{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.wrap .imagify-notice{margin:5px 15px 2px;position:relative}.imagify-notice-logo{padding:18px 23px;background:#40B1D0}.imagify-notice-logo .imagify-logo{opacity:1}.imagify-flex-notice-content .imagify-notice-logo{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.updated .imagify-notice-logo{background:#8BC34A}.error .imagify-notice-logo{background:#C51162}.imagify-notice-title{font-size:15px}.imagify-notice-content{padding:5px 23px}.imagify-notice-content.imagify-notice-content p{margin:.65em 0}.imagify-flex-notice-content .imagify-notice-content{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0}.imagify-flex-notice-content .imagify-notice-content>*{padding:10px 20px}.imagify-flex-notice-content .imagify-meteo-icon img{height:100%;margin-top:6px}.imagify-notice .imagify-rate-us .stars,.imagify-notice-quota .imagify-space-left p{margin:0}.imagify-notice-quota [class^=imagify-bar-]{background:#1F2332}.imagify-flex-notice-content .imagify-notice-content .imagify-notice-quota{padding-right:24px;padding-left:8px;background:#343A49}.imagify-notice a{color:#40B1D0}.imagify-notice a:focus,.imagify-notice a:hover{color:#FEE102}.imagify-notice code{background:rgba(0,0,0,.4)}.imagify-notice .imagify-rate-us.imagify-rate-us{text-align:left}.imagify-welcome{margin:30px 20px 0 0}.imagify-welcome .baseline{display:inline-block;margin:.2em 0 0 2em;font-size:17px}.imagify-welcome .imagify-logo{vertical-align:middle}.imagify-welcome-remove{position:absolute;top:50%;right:15px;margin-top:-8px;color:#FFF;text-decoration:none}.imagify-columns [class^=col-] img{float:left;margin-right:18px}.imagify-col-content{overflow:hidden}.imagify-col-title{margin:0 0 15px;font-size:23px}.counter .imagify-col-title:before{counter-increment:cols;content:counter(cols) ". ";color:#40B1D0}.imagify-col-desc{color:#5F758E;margin-bottom:2em}.imagify-rkt-notice.imagify-rkt-notice{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center;padding:10px 45px 10px 0;border:0;-webkit-box-shadow:none;box-shadow:none;color:#FFF;background:#1F2332}.media_page_imagify-bulk-optimization .imagify-rkt-notice{margin-left:2px;margin-right:20px}@media (max-width:782px){.media_page_imagify-bulk-optimization .imagify-rkt-notice{margin-left:0;margin-right:12px}}.imagify-rkt-notice .imagify-cross{position:absolute;right:8px;top:50%;width:22px;height:22px;padding:0;margin-top:-11px;background:0 0;color:rgba(255,255,255,.5);text-decoration:none;border-radius:50%;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-rkt-notice .imagify-cross .dashicons{position:relative;top:2px;left:1px;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-rkt-notice .imagify-cross:hover{background:#FFF}.imagify-rkt-notice .imagify-cross:hover .dashicons{color:#412355}.imagify-rkt-notice .imagify-rkt-coupon,.imagify-rkt-notice .imagify-rkt-cta,.imagify-rkt-notice .imagify-rkt-logo{-ms-flex-negative:0;flex-shrink:0}.imagify-rkt-notice .imagify-rkt-logo{width:150px!important;text-align:center;padding:0 25px 0 30px;line-height:.8}.imagify-rkt-notice .imagify-rkt-msg{width:100%!important;color:#FFF;padding:0 15px;font-size:14px;line-height:1.6}.imagify-rkt-notice .imagify-rkt-coupon{width:150px!important;padding:0 15px}.imagify-rkt-notice .imagify-rkt-coupon-code{padding:5px 10px;font-size:23px;font-weight:700;border:1px dashed #F56640;color:#F56640}.imagify-rkt-notice .imagify-rkt-cta{width:250px!important;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-preferred-size:200px;flex-basis:200px}.imagify-rkt-notice .button.button{position:relative;top:-1px;height:auto;font-weight:600;font-size:14px;border:0;padding:9px 18px;background:#F56640;-webkit-box-shadow:none;box-shadow:none;text-shadow:none!important}@media (max-width:880px){.imagify-rkt-notice{-ms-flex-wrap:wrap;flex-wrap:wrap}.imagify-rkt-notice .imagify-rkt-cta,.imagify-rkt-notice .imagify-rkt-logo,.imagify-rkt-notice .imagify-rkt-msg{text-align:left;padding:5px 15px}.imagify-cross.imagify-cross{top:8px;margin-top:0}.imagify-rkt-notice .imagify-cross .dashicons{top:1px}}
assets/css/options.css CHANGED
@@ -1,5 +1,14 @@
 
 
 
 
 
 
 
1
  /* The field that checks the API Key */
2
  #imagify-check-api-container {
 
 
3
  font-weight: bold;
4
  }
5
 
@@ -12,21 +21,149 @@
12
  vertical-align: -1px;
13
  }
14
 
15
- #imagify-check-api-container .dashicons-yes:before {
 
 
 
16
  color: #8BC34A;
17
- vertical-align: -1px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
 
20
  .imagify-options-title {
21
- padding-bottom: .5em;
22
- border-bottom: 3px solid #F2F2F2;
23
- font-size: 13px;
24
- font-weight: 500;
 
 
 
 
 
 
 
 
 
 
25
  text-transform: uppercase;
26
- letter-spacing: 0.025em;
27
- color: #40b1d0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  @media (max-width: 782px) {
31
  .imagify-settings .form-table th {
32
  padding-top: 2em;
@@ -49,41 +186,13 @@
49
  box-shadow: none;
50
  }
51
 
52
- .imagify-bulk-info {
53
- margin: 1em 1em 1em 5px;
54
- transition: margin .3s;
55
- }
56
- @media (max-width: 850px) {
57
- .imagify-bulk-info {
58
- margin: 1em;
59
- }
60
- }
61
- .imagify-bulk-info p {
62
- display: inline-block;
63
- text-align: left;
64
- width: 400px;
65
- max-width: 100%;
66
- font-weight: bold;
67
- }
68
-
69
- @media (min-width: 1500px) {
70
- .imagify-settings div.submit {
71
- display: table;
72
- width: 100%;
73
- }
74
- .imagify-settings div.submit > * {
75
- display: table-cell;
76
- vertical-align: middle;
77
- }
78
- .imagify-bulk-info {
79
- padding: 0 25px;
80
- text-align: right;
81
- }
82
- }
83
-
84
  .imagify-settings div.submit.submit {
 
85
  margin-top: 2em;
86
- padding: 40px 0 20px 235px;
 
 
 
87
  }
88
  .imagify-settings p.submit {
89
  float: left;
@@ -92,14 +201,6 @@
92
  .imagify-settings p.submit .button {
93
  margin: 0 5px;
94
  }
95
- @media (max-width: 850px) {
96
- .imagify-settings div.submit {
97
- padding-left: 0;
98
- }
99
- .imagify-settings p.submit {
100
- text-align: center;
101
- }
102
- }
103
 
104
  .imagify-sub-header th {
105
  text-align: right;
@@ -130,35 +231,29 @@
130
  margin-right: 25px;
131
  margin-left: auto;
132
  }
133
- /* Documentation link */
134
- .imagify-settings .imagify-documentation-link-box {
135
- display: flex;
136
- padding: 10px 15px 12px;
137
- margin-right: -1em;
138
- border: 2px solid #40b1d0;
139
- color: #FFF;
140
- font-weight: bold;
141
- border-radius: 2px;
142
- }
143
- .imagify-documentation-link-icon {
144
- width: 23px;
145
- height: 31px;
146
- font-size: 2.6em;
147
- margin-right: 15px;
148
- line-height: 1.3;
149
- }
150
 
151
  /* Label & fake labels */
152
  label + .imagify-info,
153
- label + .imagify-options-line,
154
  .imagify-visual-label {
155
  display: inline-block;
156
- max-width: 70%;
157
- margin-left: 15px;
158
- margin-top: 2px!important;
 
 
 
 
 
 
159
  -webkit-transition: opacity .3s;
160
  transition: opacity .3s;
161
  }
 
 
 
 
 
 
162
  .imagify-visual-label {
163
  vertical-align: -5px;
164
  }
@@ -180,22 +275,31 @@ label[for="imagify_sizes_full"] + .imagify-info {
180
  margin-left: 45px;
181
  }
182
 
183
- input[id^="imagify_sizes_"]:checked + label {
184
- font-weight: bold;
185
- }
186
-
187
- #describe-resize-larger.imagify-options-line input {
188
  width: 5em;
 
 
 
 
 
 
 
 
189
  }
190
 
191
  .imagify-menu-bar-img {
192
- margin-top: 1em;
 
 
 
193
  }
194
 
195
  /* Layout */
196
- .imagify-main {
197
  float: left;
198
- width: 70%;
 
 
199
  }
200
  .imagify-have-rocket .imagify-main {
201
  float: none;
@@ -203,15 +307,15 @@ input[id^="imagify_sizes_"]:checked + label {
203
  max-width: 100%;
204
  }
205
  .imagify-sidebar {
206
- float: right;
207
- width: 28%;
208
- max-width: 300px;
209
  }
210
 
211
  /* Sidebar with Ads */
212
  .imagify-sidebar-section {
213
  border: 1px solid #BBB;
214
- background: #FFF;
215
  }
216
  .imagify-sidebar-section + .imagify-sidebar-section {
217
  margin-top: 2em;
@@ -253,92 +357,443 @@ input[id^="imagify_sizes_"]:checked + label {
253
  width: 74%;
254
  }
255
  }
256
- .imagify-sidebar-content {
 
257
  padding: 10px 20px;
 
 
258
  }
259
- .imagify-sidebar-title {
260
- display: block;
261
- padding: 20px 15px;
262
- border-bottom: 1px solid #BBB;
263
- font-size: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
264
  font-weight: bold;
265
- color: #23282d;
266
- background: #F2F5F7;
267
  }
268
- .imagify-sidebar-title img {
269
- vertical-align: top;
270
- margin-right: 5px;
 
 
271
  }
272
- .imagify-sidebar .wp-media-products {
273
- margin-top: 0;
274
- margin-bottom: -.5em;
275
  }
276
- .imagify-sidebar .wp-media-products .links {
277
  display: block;
278
- padding: 85px 25px 24px;
279
- text-align: center;
280
- background-color: #2E3243;
281
- background-size: contain;
282
- background-repeat: no-repeat;
283
- background-position: 50% 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  text-decoration: none;
285
  }
286
- .imagify-sidebar .wp-media-products .links p {
287
- margin: 1.5em auto;
288
- color: #FFF;
289
  }
290
- .imagify-sidebar .wp-media-products .wprocket-link {
291
- background-image: linear-gradient(to bottom, rgba(59, 30, 78 ,0), rgba(59, 30, 78 ,0) 130px, rgba(59 ,30 ,78 , 1) 210px), url("../images/sidebar-wp-rocket.jpg");
292
- background-color: #3B1E4E;
293
- background-repeat: repeat-x, no-repeat;
294
  }
295
 
296
- .imagify-mark-styled {
297
- display: inline-block;
298
- padding: 1px 2px 1px 4px;
299
- line-height: 1.3;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  font-weight: bold;
301
- background: #F7A933;
302
- transform-origin: 50% 50%;
303
- transform: rotateZ(-2deg) rotateX(-10deg) skewX(-2deg) skewY(-3deg);
304
- text-shadow: 1px 1px 0 rgba(0,0,0,0.2);
305
  }
306
- .imagify-mark-styled span {
307
- display: inline-block;
308
- transform: rotateZ(2deg) rotateX(10deg) skewX(2deg) skewY(3deg);
 
309
  }
310
 
311
- .imagify-discount-code {
312
- margin: 0.35em 0 .5em;
313
- display: block;
314
- font-size: 2em;
315
- letter-spacing: .05em;
 
 
 
 
 
 
 
 
 
 
 
 
316
  font-weight: bold;
317
- text-align: center;
318
- text-shadow: 1px 1px 0 rgba(0,0,0,0.2);
319
  }
320
- .imagify-big-text {
321
- font-size: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
322
  font-weight: bold;
323
  }
324
- .imagify-big-text strong {
325
- font-size: 20px;
326
  }
327
- a.btn-rocket {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  display: block;
329
- font-size: 1.15em;
330
- padding: 12px;
331
- background: #F7A933;
332
- box-shadow: 0 3px 0 #D69626;
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  border-radius: 3px;
 
 
 
 
334
  color: #FFF;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  text-transform: uppercase;
336
- font-weight: bold;
337
- text-shadow: 1px 1px 0 rgba(0,0,0,0.2);
338
- text-decoration: none;
 
 
 
 
 
 
 
 
 
 
 
339
  }
340
- a.btn-rocket:hover,
341
- a.btn-rocket:focus {
342
- background: darkorange;
343
- box-shadow: 0 3px 0 darkorange;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  }
1
+ .wrap.imagify-settings {
2
+ margin-right: 0;
3
+ }
4
+ .imagify-settings.imagify-have-rocket {
5
+ margin-right: 20px;
6
+ }
7
+
8
  /* The field that checks the API Key */
9
  #imagify-check-api-container {
10
+ display: block;
11
+ margin-top: 6px;
12
  font-weight: bold;
13
  }
14
 
21
  vertical-align: -1px;
22
  }
23
 
24
+ #imagify-check-api-container .imagify-icon {
25
+ font-size: 1.8em;
26
+ margin-right: 3px;
27
+ margin-left: 1px;
28
  color: #8BC34A;
29
+ vertical-align: -2px;
30
+ }
31
+
32
+ .imagify-account-info-col .imagify-api-line {
33
+ padding: 22px 26px;
34
+ background: #343A49
35
+ }
36
+
37
+ .imagify-api-line label,
38
+ p.imagify-api-key-invite-title {
39
+ display: block;
40
+ margin-bottom: 6px;
41
+ font-size: 14px;
42
+ text-transform: uppercase;
43
+ letter-spacing: 0.02em;
44
+ font-weight: bold;
45
+ color: #343A49;
46
+ }
47
+ .imagify-account-info-col .imagify-api-line label {
48
+ color: #E5EBEF;
49
+ display: inline-block;
50
+ }
51
+ .imagify-api-line.imagify-api-line input[type="text"] {
52
+ width: 100%;
53
+ padding: 6px 10px;
54
+ border: 1px solid #40B1D0;
55
+ font-family: "PT Mono", "Consolas", monospace;
56
+ font-size: 14px;
57
+ letter-spacing: 0.01em;
58
+ font-weight: bold;
59
+ color: #40B1D0;
60
+ background: transparent;
61
+ box-shadow: none;
62
+ }
63
+
64
+ .imagify-no-api-key .imagify-api-line {
65
+ margin: 3em 0 0 0;
66
+ padding: 2em 0 0;
67
+ border-top: 1px solid #D5D6D9;
68
+ }
69
+ .imagify-no-api-key .imagify-api-line input[type="text"] {
70
+ margin-top: 5px;
71
+ width: 400px;
72
+ max-width: 100%;
73
+ }
74
+ .imagify-settings .imagify-no-api-key div.submit.submit {
75
+ border: 0;
76
+ padding: 0 16px;
77
+ margin-top: 0;
78
+ background: #FFF;
79
+ }
80
+ .imagify-settings .imagify-no-api-key div.submit.submit p {
81
+ padding-bottom: 0;
82
  }
83
 
84
  .imagify-options-title {
85
+ margin: .75em 0 0 0;
86
+ font-size: 24px;
87
+ letter-spacing: 0.02em;
88
+ font-weight: bold;
89
+ color: #343A49;
90
+ }
91
+
92
+ .imagify-options-subtitle {
93
+ padding-bottom: .3em;
94
+ margin-bottom: 20px;
95
+ border-bottom: 1px solid #D2D3D6;
96
+ font-size: 14px;
97
+ letter-spacing: 0.01em;
98
+ font-weight: bold;
99
  text-transform: uppercase;
100
+ color: #626E7B;
101
+ }
102
+
103
+ .imagify-options-subtitle a {
104
+ font-size: 12px;
105
+ color: #338EA6;
106
+ text-transform: none;
107
+ letter-spacing: 0;
108
+ }
109
+
110
+ .imagify-options-subtitle .imagify-info {
111
+ margin-left: 15px;
112
+ }
113
+
114
+ .imagify-setting-line {
115
+ border-top: 1px solid #D2D3D6;
116
+ padding: 25px 0 13px;
117
+ }
118
+ .imagify-options-subtitle + .imagify-setting-line {
119
+ border-top: 0;
120
+ padding-top: 8px;
121
+ }
122
+
123
+ /* 3 inlined buttons + Visual comparison */
124
+ .imagify-setting-optim-level {
125
+ display: flex;
126
+ flex-wrap: wrap;
127
+ align-items: center;
128
  }
129
 
130
+ .imagify-setting-optim-level {
131
+ padding: 8px 0 18px;
132
+ }
133
+ .imagify-setting-optim-level > p {
134
+ margin: 0;
135
+ }
136
+ .imagify-setting-optim-level .imagify-inline-options {
137
+ flex-basis: 60%;
138
+ flex-grow: 1;
139
+ width: auto;
140
+ display: flex;
141
+ background: #2E3243;
142
+ border-radius: 3px;
143
+ }
144
+ .imagify-setting-optim-level .imagify-inline-options label {
145
+ display: block !important;
146
+ width: 100%;
147
+ font-size: 14px !important;
148
+ border-radius: 3px!important;
149
+ }
150
+ .imagify-setting-optim-level .imagify-visual-comparison-text {
151
+ flex-basis: 40%;
152
+ flex-shrink: 1;
153
+ padding-left: 20px;
154
+ color: #626E7B;
155
+ box-sizing: border-box;
156
+ }
157
+ .imagify-setting-optim-level.imagify-setting-optim-level .imagify-visual-comparison-btn {
158
+ padding-top: 5px;
159
+ margin-top: 2px;
160
+ border-radius: 2px;
161
+ text-transform: none;
162
+ letter-spacing: 0;
163
+ text-shadow: none!important;
164
+ }
165
+
166
+ /* TODO: maybe remove table lines, we don't use theme anymore… */
167
  @media (max-width: 782px) {
168
  .imagify-settings .form-table th {
169
  padding-top: 2em;
186
  box-shadow: none;
187
  }
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  .imagify-settings div.submit.submit {
190
+ border-top: 1px solid #D9D9D9;
191
  margin-top: 2em;
192
+ padding: 18px 0 7px 30px;
193
+ }
194
+ .imagify-settings .hidden + div.submit.submit {
195
+ margin-top: -1px;
196
  }
197
  .imagify-settings p.submit {
198
  float: left;
201
  .imagify-settings p.submit .button {
202
  margin: 0 5px;
203
  }
 
 
 
 
 
 
 
 
204
 
205
  .imagify-sub-header th {
206
  text-align: right;
231
  margin-right: 25px;
232
  margin-left: auto;
233
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
235
  /* Label & fake labels */
236
  label + .imagify-info,
 
237
  .imagify-visual-label {
238
  display: inline-block;
239
+ width: 550px;
240
+ max-width: calc(100% - 38px);
241
+ margin-left: 38px;
242
+ padding-right: 25px;
243
+ }
244
+ label + .imagify-options-line {
245
+ display: block;
246
+ margin: 8px 0 20px 40px;
247
+ font-size: 14px;
248
  -webkit-transition: opacity .3s;
249
  transition: opacity .3s;
250
  }
251
+ .imagify-options-line + .imagify-info {
252
+ margin-left: 38px;
253
+ }
254
+ label + .imagify-info {
255
+ margin-top: 10px;
256
+ }
257
  .imagify-visual-label {
258
  vertical-align: -5px;
259
  }
275
  margin-left: 45px;
276
  }
277
 
278
+ #imagify_resize_larger_w {
 
 
 
 
279
  width: 5em;
280
+ height: auto;
281
+ padding: 6px;
282
+ margin: 0 6px;
283
+ border: 1px solid #8BA6B4;
284
+ box-shadow: none;
285
+ border-radius: 2px;
286
+ color: #338EA6;
287
+ font-weight: bold;
288
  }
289
 
290
  .imagify-menu-bar-img {
291
+ max-width: 100%;
292
+ height: auto;
293
+ margin-top: 0;
294
+ border: 1px solid #8BA6B4;
295
  }
296
 
297
  /* Layout */
298
+ .imagify-col.imagify-main {
299
  float: left;
300
+ width: calc(100% - 320px);
301
+ padding-left: 0;
302
+ padding-right: 0;
303
  }
304
  .imagify-have-rocket .imagify-main {
305
  float: none;
307
  max-width: 100%;
308
  }
309
  .imagify-sidebar {
310
+ float: left;
311
+ width: 300px;
312
+ max-width: 100%;
313
  }
314
 
315
  /* Sidebar with Ads */
316
  .imagify-sidebar-section {
317
  border: 1px solid #BBB;
318
+ background: #1F2332;
319
  }
320
  .imagify-sidebar-section + .imagify-sidebar-section {
321
  margin-top: 2em;
357
  width: 74%;
358
  }
359
  }
360
+ .imagify-sidebar-section {
361
+ position: relative;
362
  padding: 10px 20px;
363
+ text-align: center;
364
+ color: #F2F2F2;
365
  }
366
+ .imagify-sidebar-close {
367
+ position: absolute;
368
+ top: 8px;
369
+ right: 12px;
370
+ text-decoration: none;
371
+ }
372
+ .imagify-sidebar-close i {
373
+ font-size: 2em;
374
+ color: rgba(255,255,255,.5);
375
+ }
376
+ p.imagify-sidebar-title {
377
+ margin: 1.2em 0 1.5em;
378
+ text-align: left;
379
+ color: #F56640;
380
+ text-transform: uppercase;
381
+ letter-spacing: 0.015em;
382
+ word-spacing: 0.015em;
383
  font-weight: bold;
 
 
384
  }
385
+ p.imagify-sidebar-description {
386
+ margin: 1.5em 0;
387
+ text-align: left;
388
+ font-weight: 500;
389
+ color: #F2F2F2;
390
  }
391
+ .imagify-sidebar-description strong {
392
+ color: #39CE9A;
 
393
  }
394
+ .imagify-rocket-cta-promo {
395
  display: block;
396
+ padding: 8px 10px;
397
+ margin: 1.3em 0 .5em 0;
398
+ border: 2px dashed #F56640;
399
+ border-radius: 3px;
400
+ font-size: 18px;
401
+ font-weight: bold;
402
+ color: #F56640;
403
+ }
404
+ .imagify-rocket-cta-promo strong {
405
+ color: #F2F2F2;
406
+ }
407
+ a.btn-rocket {
408
+ display: block;
409
+ font-size: 15px;
410
+ padding: 10px 12px;
411
+ margin: 0 0 1.5em;
412
+ background: #F56640;
413
+ border-radius: 3px;
414
+ color: #F2F2F2;
415
+ text-transform: uppercase;
416
+ font-weight: bold;
417
  text-decoration: none;
418
  }
419
+ a.btn-rocket:hover,
420
+ a.btn-rocket:focus {
421
+ background: #AC2B15;
422
  }
423
+
424
+ .imagify-sidebar-section ul {
425
+ margin-top: 20px;
 
426
  }
427
 
428
+ .imagify-sidebar-section li {
429
+ position: relative;
430
+ margin: 1.2em 0;
431
+ padding-left: 25px;
432
+ text-align: left;
433
+ }
434
+ .imagify-sidebar-section li:before {
435
+ content: "✓";
436
+ position: absolute;
437
+ left: 0; top: 0;
438
+ color: #39CE9A;
439
+ font-size: 18px;
440
+ }
441
+
442
+ /* Menu in admin bar label exception */
443
+ label[for="imagify_admin_bar_menu"] {
444
+ font-weight: normal !important;
445
+ color: #626E7B !important;
446
+ }
447
+
448
+ /* Select / Unselect all buttons */
449
+ .imagify-select-all-buttons {
450
+ margin-top: 8px;
451
+ }
452
+ .imagify-link-like.imagify-select-all {
453
  font-weight: bold;
454
+ font-size: 12px;
455
+ color: #3694AE;
 
 
456
  }
457
+ .imagify-select-all.imagify-is-inactive {
458
+ color: inherit;
459
+ text-decoration: none;
460
+ cursor: default;
461
  }
462
 
463
+ /* Add Themes box */
464
+ .imagify-fts-header {
465
+ padding: 10px 16px;
466
+ background: #343A49;
467
+ color: #FFF;
468
+ }
469
+ .imagify-fts-header i {
470
+ font-size: 1.8em;
471
+ margin-right: 12px;
472
+ }
473
+ .imagify-fts-header p {
474
+ margin: 0;
475
+ color: #FFF;
476
+ }
477
+ .imagify-fts-header strong,
478
+ #imagify-add-themes-to-custom-folder strong {
479
+ color: #40B1D0;
480
  font-weight: bold;
 
 
481
  }
482
+
483
+ .imagify-fts-content {
484
+ padding: 16px;
485
+ background: #F4F7F9;
486
+ border: 1px solid #CDD0D4;
487
+ border-top: 0;
488
+ }
489
+
490
+ .imagify-fts-content p {
491
+ margin-top: 0;
492
+ }
493
+
494
+ .imagify-kindof-title {
495
+ margin-top: 2em;
496
+ padding: 0 0 10px 0;
497
+ border-bottom: 1px solid #D2D3D6;
498
+ justify-content: space-between;
499
  font-weight: bold;
500
  }
501
+ .imagify-kindof-title .imagify-button-primary {
502
+ padding: 4px 13px 4px 10px;
503
  }
504
+ .imagify-kindof-title .imagify-button-primary .dashicons {
505
+ vertical-align: -7.5px;
506
+ }
507
+ .imagify-kindof-title .imagify-button-primary:hover,
508
+ .imagify-kindof-title .imagify-button-primary:focus {
509
+ color: #FFF;
510
+ }
511
+
512
+ p.imagify-custom-folder-line {
513
+ position: relative;
514
+ margin: 0;
515
+ padding: 12px 15px;
516
+ color: #4A5362;
517
+ font-weight: 500;
518
+ transition: all .75s;
519
+ }
520
+ .imagify-custom-folder-line.imagify-will-remove {
521
+ background: #C51162;
522
+ color: #FFF;
523
+ transform: translateX(-120px);
524
+ opacity: 0;
525
+ }
526
+ .imagify-custom-folder-line:first-child {
527
+ margin-top: -.5em;
528
+ }
529
+ .imagify-custom-folder-line + .imagify-custom-folder-line {
530
+ border-top: 1px solid #E9EFF2;
531
+ }
532
+
533
+ .imagify-custom-folders-remove {
534
+ position: absolute;
535
+ right: 0;
536
+ top: 6px;
537
+ border: 0;
538
+ padding: 5px 10px 4px;
539
+ box-shadow: none;
540
+ color: #7A8996;
541
+ border-radius: 16px;
542
+ font-size: 13px;
543
+ line-height: 18px;
544
+ background: #FFF;
545
+ transition: all .275s;
546
+ cursor: pointer;
547
+ }
548
+ .imagify-custom-folders-remove-text {
549
+ max-width: 0;
550
+ overflow: hidden;
551
+ white-space: nowrap;
552
+ display: inline-block;
553
+ transform: scale(0);
554
+ opacity: 0;
555
+ transition: all .275s;
556
+ }
557
+ .imagify-custom-folders-remove:hover,
558
+ .imagify-custom-folders-remove:focus {
559
+ background: #D9EFF6;
560
+ color: #225E6E;
561
+ }
562
+ .imagify-custom-folders-remove:hover .imagify-custom-folders-remove-text,
563
+ .imagify-custom-folders-remove:focus .imagify-custom-folders-remove-text {
564
+ max-width: 6em;
565
+ transform: scale(1);
566
+ opacity: 1;
567
+ }
568
+
569
+ /* Files tree Part */
570
+ .imagify-add-custom-folder + .imagify-loader {
571
+ display: none;
572
+ vertical-align: middle;
573
+ }
574
+ .imagify-add-custom-folder[disabled] + .imagify-loader {
575
+ display: inline-block;
576
+ }
577
+
578
+ .imagify-folders-information {
579
+ position: relative;
580
+ margin: -5px 0 20px 0;
581
+ padding: 10px 10px 10px 40px;
582
+ text-align: left;
583
+ background: #F2F2F2;
584
+ }
585
+ .imagify-folders-information i {
586
+ position: absolute;
587
+ left: 10px;
588
+ top: 50%;
589
+ margin-top: -10px;
590
+ }
591
+
592
+ /* Tree */
593
+ .imagify-folders-tree {
594
+ margin: 0;
595
+ text-align: left;
596
+ }
597
+ .imagify-folders-tree li {
598
+ clear: left;
599
+ }
600
+ .imagify-folders-tree .imagify-folder {
601
+ box-sizing: border-box;
602
+ position: relative;
603
+ width: 48px;
604
+ z-index: 2;
605
+ float: left;
606
+ margin-top: -3px;
607
+ padding: 0 8px 0 0;
608
+ border: 0;
609
+ background: transparent!important;
610
+ box-shadow: none;
611
+ cursor: pointer;
612
+ transition: all .275s;
613
+ }
614
+ .imagify-folders-tree span.imagify-folder {
615
+ padding-left: 1.5px;
616
+ }
617
+ .imagify-folders-tree .imagify-folder:before {
618
+ content: "+";
619
+ display: inline-block;
620
+ width: 13px;
621
+ height: 21px;
622
+ font-size: 1.5em;
623
+ vertical-align: .15em;
624
+ }
625
+ .imagify-folders-tree span.imagify-folder:before {
626
+ content: '';
627
+ }
628
+ .imagify-folders-tree .imagify-folder-icon path {
629
+ transition: all .275s;
630
+ }
631
+ .imagify-folders-tree .imagify-is-open .imagify-folder-icon path {
632
+ stroke: #7A8996;
633
+ }
634
+ .imagify-folders-tree .imagify-is-open.imagify-folder:before {
635
+ content: "-";
636
+ color: #7A8996;
637
+ }
638
+ .imagify-folders-tree .imagify-is-open ~ label {
639
+ color: #7A8996;
640
+ }
641
+
642
+ .imagify-folders-tree button.imagify-folder:hover,
643
+ .imagify-folders-tree button.imagify-folder:focus,
644
+ .imagify-folders-tree button.imagify-folder:hover path,
645
+ .imagify-folders-tree button.imagify-folder:focus path {
646
+ color: #3694AE;
647
+ stroke: #3694AE;
648
+ }
649
+
650
+ .imagify-folders-tree .imagify-folder:disabled,
651
+ .imagify-folders-tree .imagify-folder.disabled {
652
+ color: rgb(127, 127, 127);
653
+ }
654
+ .imagify-swal-content .imagify-folders-tree label {
655
+ position: relative;
656
  display: block;
657
+ width: 100%;
658
+ padding: 3px 0;
659
+ font-size: 15px;
660
+ font-weight: 500;
661
+ vertical-align: top;
662
+ transition: all .475s;
663
+ }
664
+ .imagify-swal-content .imagify-folders-tree label:hover,
665
+ .imagify-folders-tree input:focus + label {
666
+ background: #F4F7F9;
667
+ }
668
+
669
+ .imagify-folders-tree .imagify-folder-already-selected label,
670
+ .imagify-folders-tree .imagify-folder-already-selected label:hover,
671
+ .imagify-folders-tree .imagify-folder-already-selected input:focus + label {
672
+ background: #40B1D0;
673
+ color: #FFF;
674
  border-radius: 3px;
675
+ cursor: default;
676
+ }
677
+ .imagify-folders-tree .imagify-folder-already-selected button,
678
+ .imagify-folders-tree .imagify-folder-already-selected button path {
679
  color: #FFF;
680
+ stroke: #FFF;
681
+ cursor: default;
682
+ }
683
+ .imagify-folders-tree .imagify-folder-already-selected button:hover path,
684
+ .imagify-folders-tree .imagify-folder-already-selected button:focus path {
685
+ stroke: #FFF;
686
+ }
687
+ .imagify-folders-tree .imagify-folder-already-selected button:before {
688
+ content: '';
689
+ }
690
+
691
+ /* Add Folder fake checkbox */
692
+ .imagify-add-ed-folder {
693
+ position: absolute;
694
+ top: 0;
695
+ bottom: 0;
696
+ right: 0;
697
+ font-size: 11px;
698
  text-transform: uppercase;
699
+ letter-spacing: 0.02em;
700
+ word-spacing: 0.02em;
701
+ color: #3694AE;
702
+ background: #F4F7F9;
703
+ opacity: 0;
704
+ transform: translateX(15px);
705
+ transition: all .275s;
706
+ }
707
+ label:hover .imagify-add-ed-folder,
708
+ input:focus + label .imagify-add-ed-folder,
709
+ input:checked + label .imagify-add-ed-folder,
710
+ .imagify-folder-already-selected .imagify-add-ed-folder {
711
+ opacity: 1;
712
+ transform: translateX(0);
713
  }
714
+ input:checked + label .imagify-add-ed-folder {
715
+ background: #FFF;
716
+ }
717
+ input:checked:focus + label .imagify-add-ed-folder,
718
+ input:checked + label:hover .imagify-add-ed-folder {
719
+ background: #F4F7F9;
720
+ }
721
+ .imagify-folder-already-selected .imagify-add-ed-folder {
722
+ background: #40B1D0;
723
+ color: #FFF;
724
+ }
725
+ .imagify-fake-checkbox {
726
+ position: relative;
727
+ display: inline-block;
728
+ width: 14px;
729
+ height: 14px;
730
+ margin: 3.5px 15px 0 5px;
731
+ border: 1.5px solid #3694AE;
732
+ border-radius: 3px;
733
+ vertical-align: -4px;
734
+ }
735
+ .imagify-fake-checkbox:after {
736
+ position: absolute;
737
+ left: 1px;
738
+ top: 0;
739
+ content: "✓";
740
+ color: #3694AE;
741
+ font-size: 14px;
742
+ line-height: .9;
743
+ font-style: normal;
744
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
745
+ opacity: 0;
746
+ transform: scale(0);
747
+ transition: all .475s;
748
+ }
749
+ input:checked + label .imagify-fake-checkbox:after,
750
+ .imagify-folder-already-selected .imagify-fake-checkbox:after {
751
+ opacity: 1;
752
+ transform: scale(1);
753
+ }
754
+ .imagify-folder-already-selected .imagify-fake-checkbox {
755
+ border-color: #40B1D0;
756
+ }
757
+ .imagify-folder-already-selected .imagify-fake-checkbox:after {
758
+ color: #FFF;
759
+ }
760
+
761
+ /* Sub Trees */
762
+ .imagify-folders-sub-tree {
763
+ position: relative;
764
+ margin-left: .75em;
765
+ padding-top: .6em;
766
+ padding-left: 1em;
767
+ border-left: 1px dotted rgba(98, 110, 123, .3);
768
+ }
769
+ .imagify-folders-sub-tree li {
770
+ position: relative;
771
+ margin-bottom: 4px;
772
+ }
773
+ .imagify-folders-sub-tree li:before {
774
+ content: "";
775
+ position: absolute;
776
+ top: 12px;
777
+ left: -1em;
778
+ height: 1px;
779
+ width: 0.9em;
780
+ border-top: 1px dotted rgba(98, 110, 123, .3);
781
+ }
782
+ .imagify-folders-sub-tree li:last-child:after {
783
+ content: "";
784
+ position: absolute;
785
+ left: -1.1em;
786
+ bottom: 0;
787
+ height: 11px;
788
+ width: 3px;
789
+ background: #FFF;
790
+ }
791
+
792
+ .imagify-empty-folder {
793
+ margin-top: -.5em;
794
+ }
795
+ .imagify-empty-folder em {
796
+ font-size: 12px;
797
+ font-weight: 500;
798
+ color: #A2AFBC;
799
  }
assets/css/options.min.css CHANGED
@@ -1 +1 @@
1
- #imagify-check-api-container{font-weight:700}#imagify-check-api-container .dashicons{font-size:25px}#imagify-check-api-container .dashicons-no:before{color:#f06e57;vertical-align:-1px}#imagify-check-api-container .dashicons-yes:before{color:#8BC34A;vertical-align:-1px}.imagify-options-title{padding-bottom:.5em;border-bottom:3px solid #F2F2F2;font-size:13px;font-weight:500;text-transform:uppercase;letter-spacing:.025em;color:#40b1d0}@media (max-width:782px){.imagify-settings .form-table th{padding-top:2em;padding-bottom:.5em}}.imagify-settings .form-table td{vertical-align:top}.imagify-settings .form-table th span{cursor:pointer}.imagify-middle th{padding-top:35px}.imagify-settings input[type=text]{color:#4A4A4A;font-weight:600;-webkit-box-shadow:none;box-shadow:none}.imagify-bulk-info{margin:1em 1em 1em 5px;-webkit-transition:margin .3s;-o-transition:margin .3s;transition:margin .3s}@media (max-width:850px){.imagify-bulk-info{margin:1em}}.imagify-bulk-info p{display:inline-block;text-align:left;width:400px;max-width:100%;font-weight:700}@media (min-width:1500px){.imagify-settings div.submit{display:table;width:100%}.imagify-settings div.submit>*{display:table-cell;vertical-align:middle}.imagify-bulk-info{padding:0 25px;text-align:right}}.imagify-settings div.submit.submit{margin-top:2em;padding:40px 0 20px 235px}.imagify-settings p.submit{float:left;margin-top:0}.imagify-settings p.submit .button{margin:0 5px}@media (max-width:850px){.imagify-settings div.submit{padding-left:0}.imagify-settings p.submit{text-align:center}}.imagify-sub-header th{text-align:right}.imagify-sub-header .form-table{margin:0}.imagify-sub-header td,.imagify-sub-header th{padding-top:0;padding-bottom:0}.imagify-sub-header [for=api_key]{padding-top:5px}@media (max-width:1120px){.imagify-settings .imagify-logo-block{margin-right:0}.imagify-settings .imagify-rate-us.imagify-rate-us{margin:1em 0 -1em}}.imagify-settings .imagify-rate-us{margin-right:25px;margin-left:auto}.imagify-settings .imagify-documentation-link-box{display:-webkit-box;display:-ms-flexbox;display:flex;padding:10px 15px 12px;margin-right:-1em;border:2px solid #40b1d0;color:#FFF;font-weight:700;border-radius:2px}.imagify-documentation-link-icon{width:23px;height:31px;font-size:2.6em;margin-right:15px;line-height:1.3}.imagify-visual-label,label+.imagify-info,label+.imagify-options-line{display:inline-block;max-width:70%;margin-left:15px;margin-top:2px!important;-webkit-transition:opacity .3s;-o-transition:opacity .3s;transition:opacity .3s}.imagify-visual-label{vertical-align:-5px}label[for=imagify_sizes_full]+.imagify-info{vertical-align:middle}.imagify-settings.imagify-settings [type=checkbox]:not(:checked)+label .imagify-visual-label,.imagify-settings.imagify-settings [type=checkbox]:not(:checked)+label+.imagify-options-line{opacity:.5}.imagify-settings.imagify-settings [type=checkbox]:checked+label .imagify-visual-label,.imagify-settings.imagify-settings [type=checkbox]:checked+label+.imagify-options-line{opacity:1}.imagify-checkbox-marged{max-width:500px;margin-left:45px}input[id^=imagify_sizes_]:checked+label{font-weight:700}#describe-resize-larger.imagify-options-line input{width:5em}.imagify-menu-bar-img{margin-top:1em}.imagify-main{float:left;width:70%}.imagify-have-rocket .imagify-main{float:none;width:1265px;max-width:100%}.imagify-sidebar{float:right;width:28%;max-width:300px}.imagify-sidebar-section{border:1px solid #BBB;background:#FFF}.imagify-sidebar-section+.imagify-sidebar-section{margin-top:2em}@media (max-width:820px){.imagify-main,.imagify-sidebar{float:none;width:auto}.imagify-settings{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.imagify-sidebar{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2;max-width:none;margin-left:0;margin-top:25px}.wp-media-products{text-align:center}.wp-media-products li{display:inline-block;width:100%;max-width:276px}}@media (min-width:1400px){.imagify-main{width:74%}}.imagify-sidebar-content{padding:10px 20px}.imagify-sidebar-title{display:block;padding:20px 15px;border-bottom:1px solid #BBB;font-size:14px;font-weight:700;color:#23282d;background:#F2F5F7}.imagify-sidebar-title img{vertical-align:top;margin-right:5px}.imagify-sidebar .wp-media-products{margin-top:0;margin-bottom:-.5em}.imagify-sidebar .wp-media-products .links{display:block;padding:85px 25px 24px;text-align:center;background-color:#2E3243;background-size:contain;background-repeat:no-repeat;background-position:50% 0;text-decoration:none}.imagify-sidebar .wp-media-products .links p{margin:1.5em auto;color:#FFF}.imagify-sidebar .wp-media-products .wprocket-link{background-image:-o-linear-gradient(top,rgba(59,30,78 ,0),rgba(59,30,78 ,0) 130px,rgba(59 ,30 ,78 ,1) 210px),url(../images/sidebar-wp-rocket.jpg);background-image:linear-gradient(to bottom,rgba(59,30,78 ,0),rgba(59,30,78 ,0) 130px,rgba(59 ,30 ,78 ,1) 210px),url(../images/sidebar-wp-rocket.jpg);background-color:#3B1E4E;background-repeat:repeat-x,no-repeat}.imagify-mark-styled{display:inline-block;padding:1px 2px 1px 4px;line-height:1.3;font-weight:700;background:#F7A933;-webkit-transform-origin:50% 50%;-ms-transform-origin:50% 50%;transform-origin:50% 50%;-webkit-transform:rotateZ(-2deg) rotateX(-10deg) skewX(-2deg) skewY(-3deg);transform:rotateZ(-2deg) rotateX(-10deg) skewX(-2deg) skewY(-3deg);text-shadow:1px 1px 0 rgba(0,0,0,.2)}.imagify-mark-styled span{display:inline-block;-webkit-transform:rotateZ(2deg) rotateX(10deg) skewX(2deg) skewY(3deg);transform:rotateZ(2deg) rotateX(10deg) skewX(2deg) skewY(3deg)}.imagify-discount-code,a.btn-rocket{display:block;font-weight:700;text-shadow:1px 1px 0 rgba(0,0,0,.2)}.imagify-discount-code{margin:.35em 0 .5em;font-size:2em;letter-spacing:.05em;text-align:center}.imagify-big-text{font-size:15px;font-weight:700}.imagify-big-text strong{font-size:20px}a.btn-rocket{font-size:1.15em;padding:12px;background:#F7A933;-webkit-box-shadow:0 3px 0 #D69626;box-shadow:0 3px 0 #D69626;border-radius:3px;color:#FFF;text-transform:uppercase;text-decoration:none}a.btn-rocket:focus,a.btn-rocket:hover{background:#ff8c00;-webkit-box-shadow:0 3px 0 #ff8c00;box-shadow:0 3px 0 #ff8c00}
1
+ .wrap.imagify-settings{margin-right:0}.imagify-settings.imagify-have-rocket{margin-right:20px}#imagify-check-api-container{display:block;margin-top:6px;font-weight:700}#imagify-check-api-container .dashicons{font-size:25px}#imagify-check-api-container .dashicons-no:before{color:#f06e57;vertical-align:-1px}#imagify-check-api-container .imagify-icon{font-size:1.8em;margin-right:3px;margin-left:1px;color:#8BC34A;vertical-align:-2px}.imagify-account-info-col .imagify-api-line{padding:22px 26px;background:#343A49}.imagify-api-line label,p.imagify-api-key-invite-title{display:block;margin-bottom:6px;font-size:14px;text-transform:uppercase;letter-spacing:.02em;font-weight:700;color:#343A49}.imagify-account-info-col .imagify-api-line label{color:#E5EBEF;display:inline-block}.imagify-api-line.imagify-api-line input[type=text]{width:100%;padding:6px 10px;border:1px solid #40B1D0;font-family:"PT Mono",Consolas,monospace;font-size:14px;letter-spacing:.01em;font-weight:700;color:#40B1D0;background:0 0;-webkit-box-shadow:none;box-shadow:none}.imagify-no-api-key .imagify-api-line{margin:3em 0 0;padding:2em 0 0;border-top:1px solid #D5D6D9}.imagify-no-api-key .imagify-api-line input[type=text]{margin-top:5px;width:400px;max-width:100%}.imagify-settings .imagify-no-api-key div.submit.submit{border:0;padding:0 16px;margin-top:0;background:#FFF}.imagify-settings .imagify-no-api-key div.submit.submit p{padding-bottom:0}.imagify-options-title{margin:.75em 0 0;font-size:24px;letter-spacing:.02em;font-weight:700;color:#343A49}.imagify-options-subtitle{padding-bottom:.3em;margin-bottom:20px;border-bottom:1px solid #D2D3D6;font-size:14px;letter-spacing:.01em;font-weight:700;text-transform:uppercase;color:#626E7B}.imagify-options-subtitle a{font-size:12px;color:#338EA6;text-transform:none;letter-spacing:0}.imagify-options-subtitle .imagify-info{margin-left:15px}.imagify-setting-line{border-top:1px solid #D2D3D6;padding:25px 0 13px}.imagify-options-subtitle+.imagify-setting-line{border-top:0;padding-top:8px}.imagify-setting-optim-level{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:8px 0 18px}.imagify-setting-optim-level>p{margin:0}.imagify-setting-optim-level .imagify-inline-options{-ms-flex-preferred-size:60%;flex-basis:60%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;width:auto;display:-webkit-box;display:-ms-flexbox;display:flex;background:#2E3243;border-radius:3px}.imagify-setting-optim-level .imagify-inline-options label{display:block!important;width:100%;font-size:14px!important;border-radius:3px!important}.imagify-setting-optim-level .imagify-visual-comparison-text{-ms-flex-preferred-size:40%;flex-basis:40%;-ms-flex-negative:1;flex-shrink:1;padding-left:20px;color:#626E7B;-webkit-box-sizing:border-box;box-sizing:border-box}.imagify-setting-optim-level.imagify-setting-optim-level .imagify-visual-comparison-btn{padding-top:5px;margin-top:2px;border-radius:2px;text-transform:none;letter-spacing:0;text-shadow:none!important}@media (max-width:782px){.imagify-settings .form-table th{padding-top:2em;padding-bottom:.5em}}.imagify-settings .form-table td{vertical-align:top}.imagify-settings .form-table th span{cursor:pointer}.imagify-middle th{padding-top:35px}.imagify-settings input[type=text]{color:#4A4A4A;font-weight:600;-webkit-box-shadow:none;box-shadow:none}.imagify-settings div.submit.submit{border-top:1px solid #D9D9D9;margin-top:2em;padding:18px 0 7px 30px}.imagify-settings .hidden+div.submit.submit{margin-top:-1px}.imagify-settings p.submit{float:left;margin-top:0}.imagify-settings p.submit .button{margin:0 5px}.imagify-sub-header th{text-align:right}.imagify-sub-header .form-table{margin:0}.imagify-sub-header td,.imagify-sub-header th{padding-top:0;padding-bottom:0}.imagify-sub-header [for=api_key]{padding-top:5px}@media (max-width:1120px){.imagify-settings .imagify-logo-block{margin-right:0}.imagify-settings .imagify-rate-us.imagify-rate-us{margin:1em 0 -1em}}.imagify-settings .imagify-rate-us{margin-right:25px;margin-left:auto}.imagify-visual-label,label+.imagify-info{display:inline-block;width:550px;max-width:calc(100% - 38px);margin-left:38px;padding-right:25px}label+.imagify-options-line{display:block;margin:8px 0 20px 40px;font-size:14px;-webkit-transition:opacity .3s;-o-transition:opacity .3s;transition:opacity .3s}.imagify-options-line+.imagify-info{margin-left:38px}label+.imagify-info{margin-top:10px}.imagify-visual-label{vertical-align:-5px}label[for=imagify_sizes_full]+.imagify-info{vertical-align:middle}.imagify-settings.imagify-settings [type=checkbox]:not(:checked)+label .imagify-visual-label,.imagify-settings.imagify-settings [type=checkbox]:not(:checked)+label+.imagify-options-line{opacity:.5}.imagify-settings.imagify-settings [type=checkbox]:checked+label .imagify-visual-label,.imagify-settings.imagify-settings [type=checkbox]:checked+label+.imagify-options-line{opacity:1}.imagify-checkbox-marged{max-width:500px;margin-left:45px}#imagify_resize_larger_w{width:5em;height:auto;padding:6px;margin:0 6px;border:1px solid #8BA6B4;-webkit-box-shadow:none;box-shadow:none;border-radius:2px;color:#338EA6;font-weight:700}.imagify-menu-bar-img{max-width:100%;height:auto;margin-top:0;border:1px solid #8BA6B4}.imagify-col.imagify-main{float:left;width:calc(100% - 320px);padding-left:0;padding-right:0}.imagify-have-rocket .imagify-main{float:none;width:1265px;max-width:100%}.imagify-sidebar{float:left;width:300px;max-width:100%}.imagify-sidebar-section{border:1px solid #BBB;background:#1F2332;position:relative;padding:10px 20px;text-align:center;color:#F2F2F2}.imagify-sidebar-section+.imagify-sidebar-section{margin-top:2em}@media (max-width:820px){.imagify-main,.imagify-sidebar{float:none;width:auto}.imagify-settings{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.imagify-sidebar{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2;max-width:none;margin-left:0;margin-top:25px}.wp-media-products{text-align:center}.wp-media-products li{display:inline-block;width:100%;max-width:276px}}@media (min-width:1400px){.imagify-main{width:74%}}.imagify-sidebar-close{position:absolute;top:8px;right:12px;text-decoration:none}.imagify-sidebar-close i{font-size:2em;color:rgba(255,255,255,.5)}p.imagify-sidebar-title{margin:1.2em 0 1.5em;text-align:left;color:#F56640;text-transform:uppercase;letter-spacing:.015em;word-spacing:.015em;font-weight:700}p.imagify-sidebar-description{margin:1.5em 0;text-align:left;font-weight:500;color:#F2F2F2}.imagify-sidebar-description strong{color:#39CE9A}.imagify-rocket-cta-promo{display:block;padding:8px 10px;margin:1.3em 0 .5em;border:2px dashed #F56640;border-radius:3px;font-size:18px;font-weight:700;color:#F56640}.imagify-rocket-cta-promo strong,a.btn-rocket{color:#F2F2F2}a.btn-rocket{display:block;font-size:15px;padding:10px 12px;margin:0 0 1.5em;background:#F56640;border-radius:3px;text-transform:uppercase;font-weight:700;text-decoration:none}a.btn-rocket:focus,a.btn-rocket:hover{background:#AC2B15}.imagify-sidebar-section ul{margin-top:20px}.imagify-sidebar-section li{position:relative;margin:1.2em 0;padding-left:25px;text-align:left}.imagify-sidebar-section li:before{content:"✓";position:absolute;left:0;top:0;color:#39CE9A;font-size:18px}label[for=imagify_admin_bar_menu]{font-weight:400!important;color:#626E7B!important}.imagify-select-all-buttons{margin-top:8px}.imagify-link-like.imagify-select-all{font-weight:700;font-size:12px;color:#3694AE}.imagify-select-all.imagify-is-inactive{color:inherit;text-decoration:none;cursor:default}.imagify-fts-header{padding:10px 16px;background:#343A49;color:#FFF}.imagify-fts-header i{font-size:1.8em;margin-right:12px}.imagify-fts-header p{margin:0;color:#FFF}#imagify-add-themes-to-custom-folder strong,.imagify-fts-header strong{color:#40B1D0;font-weight:700}.imagify-fts-content{padding:16px;background:#F4F7F9;border:1px solid #CDD0D4;border-top:0}.imagify-fts-content p{margin-top:0}.imagify-kindof-title{margin-top:2em;padding:0 0 10px;border-bottom:1px solid #D2D3D6;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-weight:700}.imagify-kindof-title .imagify-button-primary{padding:4px 13px 4px 10px}.imagify-kindof-title .imagify-button-primary .dashicons{vertical-align:-7.5px}.imagify-kindof-title .imagify-button-primary:focus,.imagify-kindof-title .imagify-button-primary:hover{color:#FFF}p.imagify-custom-folder-line{position:relative;margin:0;padding:12px 15px;color:#4A5362;font-weight:500;-webkit-transition:all .75s;-o-transition:all .75s;transition:all .75s}.imagify-custom-folder-line.imagify-will-remove{background:#C51162;color:#FFF;-webkit-transform:translateX(-120px);-ms-transform:translateX(-120px);transform:translateX(-120px);opacity:0}.imagify-custom-folder-line:first-child{margin-top:-.5em}.imagify-custom-folder-line+.imagify-custom-folder-line{border-top:1px solid #E9EFF2}.imagify-custom-folders-remove{position:absolute;right:0;top:6px;border:0;padding:5px 10px 4px;-webkit-box-shadow:none;box-shadow:none;color:#7A8996;border-radius:16px;font-size:13px;line-height:18px;background:#FFF;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s;cursor:pointer}.imagify-custom-folders-remove-text{max-width:0;overflow:hidden;white-space:nowrap;display:inline-block;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);opacity:0;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-custom-folders-remove:focus,.imagify-custom-folders-remove:hover{background:#D9EFF6;color:#225E6E}.imagify-custom-folders-remove:focus .imagify-custom-folders-remove-text,.imagify-custom-folders-remove:hover .imagify-custom-folders-remove-text{max-width:6em;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);opacity:1}.imagify-add-custom-folder+.imagify-loader{display:none;vertical-align:middle}.imagify-add-custom-folder[disabled]+.imagify-loader{display:inline-block}.imagify-folders-information{position:relative;margin:-5px 0 20px;padding:10px 10px 10px 40px;text-align:left;background:#F2F2F2}.imagify-folders-information i{position:absolute;left:10px;top:50%;margin-top:-10px}.imagify-folders-tree{margin:0;text-align:left}.imagify-folders-tree li{clear:left}.imagify-folders-tree .imagify-folder{-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;width:48px;z-index:2;float:left;margin-top:-3px;padding:0 8px 0 0;border:0;background:0 0!important;-webkit-box-shadow:none;box-shadow:none;cursor:pointer;-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-folders-tree span.imagify-folder{padding-left:1.5px}.imagify-folders-tree .imagify-folder:before{content:"+";display:inline-block;width:13px;height:21px;font-size:1.5em;vertical-align:.15em}.imagify-folders-tree span.imagify-folder:before{content:''}.imagify-folders-tree .imagify-folder-icon path{-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-folders-tree .imagify-is-open .imagify-folder-icon path{stroke:#7A8996}.imagify-folders-tree .imagify-is-open.imagify-folder:before{content:"-";color:#7A8996}.imagify-folders-tree .imagify-is-open~label{color:#7A8996}.imagify-folders-tree button.imagify-folder:focus,.imagify-folders-tree button.imagify-folder:focus path,.imagify-folders-tree button.imagify-folder:hover,.imagify-folders-tree button.imagify-folder:hover path{color:#3694AE;stroke:#3694AE}.imagify-folders-tree .imagify-folder.disabled,.imagify-folders-tree .imagify-folder:disabled{color:#7f7f7f}.imagify-swal-content .imagify-folders-tree label{position:relative;display:block;width:100%;padding:3px 0;font-size:15px;font-weight:500;vertical-align:top;-webkit-transition:all .475s;-o-transition:all .475s;transition:all .475s}.imagify-folders-tree input:focus+label,.imagify-swal-content .imagify-folders-tree label:hover{background:#F4F7F9}.imagify-folders-tree .imagify-folder-already-selected input:focus+label,.imagify-folders-tree .imagify-folder-already-selected label,.imagify-folders-tree .imagify-folder-already-selected label:hover{background:#40B1D0;color:#FFF;border-radius:3px;cursor:default}.imagify-folders-tree .imagify-folder-already-selected button,.imagify-folders-tree .imagify-folder-already-selected button path{color:#FFF;stroke:#FFF;cursor:default}.imagify-folders-tree .imagify-folder-already-selected button:focus path,.imagify-folders-tree .imagify-folder-already-selected button:hover path{stroke:#FFF}.imagify-folders-tree .imagify-folder-already-selected button:before{content:''}.imagify-add-ed-folder{position:absolute;top:0;bottom:0;right:0;font-size:11px;text-transform:uppercase;letter-spacing:.02em;word-spacing:.02em;color:#3694AE;background:#F4F7F9;opacity:0;-webkit-transform:translateX(15px);-ms-transform:translateX(15px);transform:translateX(15px);-webkit-transition:all .275s;-o-transition:all .275s;transition:all .275s}.imagify-folder-already-selected .imagify-add-ed-folder,input:checked+label .imagify-add-ed-folder,input:focus+label .imagify-add-ed-folder,label:hover .imagify-add-ed-folder{opacity:1;-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}input:checked+label .imagify-add-ed-folder{background:#FFF}input:checked+label:hover .imagify-add-ed-folder,input:checked:focus+label .imagify-add-ed-folder{background:#F4F7F9}.imagify-folder-already-selected .imagify-add-ed-folder{background:#40B1D0;color:#FFF}.imagify-fake-checkbox{position:relative;display:inline-block;width:14px;height:14px;margin:3.5px 15px 0 5px;border:1.5px solid #3694AE;border-radius:3px;vertical-align:-4px}.imagify-fake-checkbox:after{position:absolute;left:1px;top:0;content:"✓";color:#3694AE;font-size:14px;line-height:.9;font-style:normal;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;opacity:0;-webkit-transform:scale(0);-ms-transform:scale(0);transform:scale(0);-webkit-transition:all .475s;-o-transition:all .475s;transition:all .475s}.imagify-folder-already-selected .imagify-fake-checkbox:after,input:checked+label .imagify-fake-checkbox:after{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.imagify-folder-already-selected .imagify-fake-checkbox{border-color:#40B1D0}.imagify-folder-already-selected .imagify-fake-checkbox:after{color:#FFF}.imagify-folders-sub-tree{position:relative;margin-left:.75em;padding-top:.6em;padding-left:1em;border-left:1px dotted rgba(98,110,123,.3)}.imagify-folders-sub-tree li{position:relative;margin-bottom:4px}.imagify-folders-sub-tree li:before{content:"";position:absolute;top:12px;left:-1em;height:1px;width:.9em;border-top:1px dotted rgba(98,110,123,.3)}.imagify-folders-sub-tree li:last-child:after{content:"";position:absolute;left:-1.1em;bottom:0;height:11px;width:3px;background:#FFF}.imagify-empty-folder{margin-top:-.5em}.imagify-empty-folder em{font-size:12px;font-weight:500;color:#A2AFBC}
assets/css/sweetalert-custom.css CHANGED
@@ -1,12 +1,91 @@
1
- /* Some Sweet alert styles */
2
- .imagify-sweet-alert button {
3
- margin-top: 17px !important;
4
- height: 45px !important;
5
- letter-spacing: 3px;
6
- text-transform: uppercase !important;
7
- border-radius: 3px !important;
8
- background-color: #40b1d0 !important;
9
- box-shadow: 0 3px 0 #338ea6 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  }
11
  .imagify-sweet-alert button.loading {
12
  border-radius: 100% !important;
@@ -15,15 +94,29 @@
15
  box-shadow: none!important;
16
  }
17
  .imagify-sweet-alert button.swal2-cancel {
18
- background: #777 !important;
19
- box-shadow: 0 3px 0 #444 !important;
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
  .imagify-sweet-alert-signup .sa-confirm-button-container {
22
  width: 40%;
23
  }
24
- .imagify-sweet-alert-signup button {
25
- width: 100%;
26
- padding: 0 !important;
 
 
27
  }
28
  .imagify-sweet-alert .sa-input-error:before,
29
  .imagify-sweet-alert .sa-input-error:after,
@@ -38,13 +131,39 @@
38
  border-right-width: 0 !important;
39
  }
40
 
41
- /* Make the page in background blured when sweet alert is open */
42
- #wpwrap {
43
- transition: all .4s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
- .stop-scrolling #wpwrap {
46
- filter: blur(2px);
47
  }
48
- .stop-scrolling #wpwrap #wpadminbar {
49
- top: -32px;
 
50
  }
 
 
 
 
 
1
+ /* Sub Layer */
2
+ body[class*="_imagify"] .swal2-container.swal2-shown {
3
+ background: rgb(31, 35 ,50);
4
+ background: rgba(31, 35 ,50, .9);
5
+ z-index: 100000;
6
+ }
7
+
8
+ /* White Container */
9
+ .imagify-sweet-alert .swal2-modal {
10
+ border-radius: 2px;
11
+ }
12
+
13
+ /* To get icon background dark */
14
+ .imagify-sweet-alert {
15
+ background: #1F2332!important;
16
+ }
17
+ .imagify-sweet-alert .swal2-icon {
18
+ margin-bottom: 5px;
19
+ }
20
+
21
+ /* header error color */
22
+ .imagify-swal-error-header {
23
+ background: #C51162!important;
24
+ }
25
+ .imagify-swal-error-header .swal2-icon {
26
+ border-color: #FFF;
27
+ color: #FFF;
28
+ }
29
+
30
+ /* Title and Subtitle */
31
+ .imagify-sweet-alert .swal2-title {
32
+ margin: 0;
33
+ padding: 28px 32px;
34
+ font-size: 24px;
35
+ text-align: center;
36
+ color: #FFF;
37
+ background: #1F2332;
38
+ }
39
+ .imagify-swal-has-subtitle .swal2-title {
40
+ text-align: left;
41
+ }
42
+ .imagify-swal-error-header .swal2-title {
43
+ background: #C51162;
44
+ text-align: center;
45
+ }
46
+ .imagify-sweet-alert .imagify-swal-subtitle {
47
+ padding: 0 32px 28px;
48
+ margin-top: -16px;
49
+ font-weight: 500;
50
+ font-size: 14px;
51
+ text-align: left;
52
+ color: #7A8996;
53
+ background: #1F2332;
54
+ }
55
+ .imagify-swal-error-header .imagify-swal-subtitle {
56
+ color: #FFF;
57
+ background: #C51162;
58
+ text-align: center;
59
+ }
60
+
61
+ /* Buttons */
62
+ .imagify-sweet-alert .swal2-buttonswrapper,
63
+ .imagify-swal-buttonswrapper {
64
+ margin-top: 0;
65
+ padding: 22px;
66
+ background: #F4F7F9;
67
+ }
68
+ .imagify-sweet-alert button.swal2-styled,
69
+ .imagify-swal-buttonswrapper a.button.imagify-button-primary {
70
+ height: auto;
71
+ padding: 12px 32px;
72
+ margin: 10px;
73
+ font-size: 14px;
74
+ letter-spacing: 1px;
75
+ text-transform: uppercase;
76
+ border-radius: 3px;
77
+ background-color: #40b1d0 !important;
78
+ text-shadow: none!important;
79
+ box-shadow: 0 3px 0 #338ea6;
80
+ }
81
+ .imagify-swal-buttonswrapper a.button.imagify-button-primary:focus,
82
+ .imagify-swal-buttonswrapper a.button.imagify-button-primary:hover {
83
+ text-shadow: none;
84
+ color: #FFF;
85
+ }
86
+ .imagify-swal-buttonswrapper a.button svg {
87
+ margin-right: 12px;
88
+ vertical-align: -2px;
89
  }
90
  .imagify-sweet-alert button.loading {
91
  border-radius: 100% !important;
94
  box-shadow: none!important;
95
  }
96
  .imagify-sweet-alert button.swal2-cancel {
97
+ color: #7A8996;
98
+ background: #E9EFF2 !important;
99
+ box-shadow: 0 3px 0 rgba(31, 35, 50, .2);
100
+ }
101
+ .imagify-sweet-alert-signup.imagify-sweet-alert {
102
+ background: #FFF!important;
103
+ }
104
+ .imagify-sweet-alert-signup .swal2-buttonswrapper {
105
+ padding: 12px 22px;
106
+ }
107
+ .swal2-success-circular-line-left,
108
+ .swal2-success-fix,
109
+ .swal2-success-circular-line-right {
110
+ background: #1F2332 !important
111
  }
112
  .imagify-sweet-alert-signup .sa-confirm-button-container {
113
  width: 40%;
114
  }
115
+ .imagify-sweet-alert-signup .swal2-input {
116
+ margin-top: 0;
117
+ margin-left: 40px;
118
+ margin-right: 40px;
119
+ width: calc( 100% - 80px);
120
  }
121
  .imagify-sweet-alert .sa-input-error:before,
122
  .imagify-sweet-alert .sa-input-error:after,
131
  border-right-width: 0 !important;
132
  }
133
 
134
+ /* Imagify swal contents */
135
+ .imagify-sweet-alert .swal2-content {
136
+ padding: 28px 32px;
137
+ background: #FFF;
138
+ }
139
+ .imagify-swal-has-subtitle .swal2-content {
140
+ padding: 0;
141
+ }
142
+ .imagify-swal-content {
143
+ font-size: 14px;
144
+ padding: 28px 32px;
145
+ }
146
+
147
+ /* Quota */
148
+ .imagify-swal-quota .imagify-space-left {
149
+ display: flex;
150
+ align-items: center;
151
+ justify-content: space-between;
152
+ padding: 4px 32px;
153
+ text-align: left;
154
+ font-weight: bold;
155
+ color: #FFF;
156
+ background: #343A49;
157
  }
158
+ .imagify-swal-quota .imagify-space-left p {
159
+ font-size: 14px;
160
  }
161
+ .imagify-swal-quota .imagify-space-left [class^="imagify-bar-"] {
162
+ width: auto;
163
+ flex-basis: 269px;
164
  }
165
+
166
+ /* Close button */
167
+ .imagify-sweet-alert .swal2-close {
168
+ color: rgba(255,255,255,.5);
169
+ }
assets/css/sweetalert-custom.min.css CHANGED
@@ -1 +1 @@
1
- .imagify-sweet-alert button{margin-top:17px!important;height:45px!important;letter-spacing:3px;text-transform:uppercase!important;border-radius:3px!important;background-color:#40b1d0!important;-webkit-box-shadow:0 3px 0 #338ea6!important;box-shadow:0 3px 0 #338ea6!important}.imagify-sweet-alert button.loading{border-radius:100%!important;height:40px!important;padding:0!important;-webkit-box-shadow:none!important;box-shadow:none!important}.imagify-sweet-alert button.swal2-cancel{background:#777!important;-webkit-box-shadow:0 3px 0 #444!important;box-shadow:0 3px 0 #444!important}.imagify-sweet-alert-signup .sa-confirm-button-container{width:40%}.imagify-sweet-alert-signup button{width:100%;padding:0!important}.imagify-sweet-alert .la-ball-fall,.imagify-sweet-alert .sa-input-error:after,.imagify-sweet-alert .sa-input-error:before{top:25%!important}.imagify-sweet-alert .swal2-buttonswrapper.swal2-loading .swal2-confirm.swal2-confirm{height:40px!important;border-radius:100%!important;border-left-width:0!important;border-right-width:0!important}#wpwrap{-webkit-transition:all .4s;-o-transition:all .4s;transition:all .4s}.stop-scrolling #wpwrap{-webkit-filter:blur(2px);filter:blur(2px)}.stop-scrolling #wpwrap #wpadminbar{top:-32px}
1
+ body[class*="_imagify"] .swal2-container.swal2-shown{background:rgb(31,35 ,50);background:rgba(31,35 ,50,.9);z-index:100000}.imagify-sweet-alert .swal2-modal{border-radius:2px}.imagify-sweet-alert{background:#1F2332!important}.imagify-sweet-alert .swal2-icon{margin-bottom:5px}.imagify-swal-error-header{background:#C51162!important}.imagify-swal-error-header .swal2-icon{border-color:#FFF;color:#FFF}.imagify-sweet-alert .swal2-title{margin:0;padding:28px 32px;font-size:24px;text-align:center;color:#FFF;background:#1F2332}.imagify-swal-has-subtitle .swal2-title{text-align:left}.imagify-swal-error-header .swal2-title{background:#C51162;text-align:center}.imagify-sweet-alert .imagify-swal-subtitle{padding:0 32px 28px;margin-top:-16px;font-weight:500;font-size:14px;text-align:left;color:#7A8996;background:#1F2332}.imagify-swal-error-header .imagify-swal-subtitle{color:#FFF;background:#C51162;text-align:center}.imagify-swal-buttonswrapper,.imagify-sweet-alert .swal2-buttonswrapper{margin-top:0;padding:22px;background:#F4F7F9}.imagify-swal-buttonswrapper a.button.imagify-button-primary,.imagify-sweet-alert button.swal2-styled{height:auto;padding:12px 32px;margin:10px;font-size:14px;letter-spacing:1px;text-transform:uppercase;border-radius:3px;background-color:#40b1d0!important;text-shadow:none!important;-webkit-box-shadow:0 3px 0 #338ea6;box-shadow:0 3px 0 #338ea6}.imagify-swal-buttonswrapper a.button.imagify-button-primary:focus,.imagify-swal-buttonswrapper a.button.imagify-button-primary:hover{text-shadow:none;color:#FFF}.imagify-swal-buttonswrapper a.button svg{margin-right:12px;vertical-align:-2px}.imagify-sweet-alert button.loading{border-radius:100%!important;height:40px!important;padding:0!important;-webkit-box-shadow:none!important;box-shadow:none!important}.imagify-sweet-alert button.swal2-cancel{color:#7A8996;background:#E9EFF2!important;-webkit-box-shadow:0 3px 0 rgba(31,35,50,.2);box-shadow:0 3px 0 rgba(31,35,50,.2)}.imagify-sweet-alert-signup.imagify-sweet-alert{background:#FFF!important}.imagify-sweet-alert-signup .swal2-buttonswrapper{padding:12px 22px}.swal2-success-circular-line-left,.swal2-success-circular-line-right,.swal2-success-fix{background:#1F2332!important}.imagify-sweet-alert-signup .sa-confirm-button-container{width:40%}.imagify-sweet-alert-signup .swal2-input{margin-top:0;margin-left:40px;margin-right:40px;width:calc(100% - 80px)}.imagify-sweet-alert .la-ball-fall,.imagify-sweet-alert .sa-input-error:after,.imagify-sweet-alert .sa-input-error:before{top:25%!important}.imagify-sweet-alert .swal2-buttonswrapper.swal2-loading .swal2-confirm.swal2-confirm{height:40px!important;border-radius:100%!important;border-left-width:0!important;border-right-width:0!important}.imagify-sweet-alert .swal2-content{padding:28px 32px;background:#FFF}.imagify-swal-has-subtitle .swal2-content{padding:0}.imagify-swal-content{font-size:14px;padding:28px 32px}.imagify-swal-quota .imagify-space-left{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:4px 32px;text-align:left;font-weight:700;color:#FFF;background:#343A49}.imagify-swal-quota .imagify-space-left p{font-size:14px}.imagify-swal-quota .imagify-space-left [class^=imagify-bar-]{width:auto;-ms-flex-preferred-size:269px;flex-basis:269px}.imagify-sweet-alert .swal2-close{color:rgba(255,255,255,.5)}
assets/css/twentytwenty.css CHANGED
@@ -305,12 +305,6 @@
305
  opacity: 1;
306
  }
307
 
308
- .imagify-settings .imagify-visual-comparison-text {
309
- margin-top: 1em;
310
- color: #40b1d0;
311
- font-weight: bold;
312
- }
313
-
314
  /* Specifics for too high modals */
315
  .modal-is-too-high .imagify-comparison-levels {
316
  position: absolute;
305
  opacity: 1;
306
  }
307
 
 
 
 
 
 
 
308
  /* Specifics for too high modals */
309
  .modal-is-too-high .imagify-comparison-levels {
310
  position: absolute;
assets/css/twentytwenty.min.css CHANGED
@@ -1 +1 @@
1
- .twentytwenty-handle{z-index:40;position:absolute;left:50%;top:50%;height:64px;width:64px;margin-left:-32px;margin-top:-32px;border-radius:50%;-webkit-box-shadow:0 3px 0 #338EA6;box-shadow:0 3px 0 #338EA6;background:#40B1D0;cursor:pointer}.twentytwenty-horizontal .twentytwenty-handle:after,.twentytwenty-horizontal .twentytwenty-handle:before{left:50%;width:2px;height:9999px;margin-left:-1px;content:"";position:absolute;z-index:30;display:block;background:#F2F5F7;-webkit-box-shadow:0 0 12px rgba(51,51,51,.5);box-shadow:0 0 12px rgba(51,51,51,.5)}.twentytwenty-horizontal .twentytwenty-handle:before{bottom:50%;margin-bottom:32px}.twentytwenty-horizontal .twentytwenty-handle:after{top:50%;margin-top:34px}.twentytwenty-labels,.twentytwenty-overlay{position:absolute;top:0;width:100%;height:100%;-webkit-transition-duration:.5s;-o-transition-duration:.5s;transition-duration:.5s}.twentytwenty-labels{opacity:1;-webkit-transition-property:opacity;-o-transition-property:opacity;transition-property:opacity}.twentytwenty-labels .twentytwenty-label-content{position:absolute;padding:0 12px;font-size:13px;letter-spacing:.1em;line-height:38px;color:#fff;background:#1F2332;border-radius:2px}.twentytwenty-horizontal .twentytwenty-labels .twentytwenty-label-content{bottom:15px}.twentytwenty-after-label .twentytwenty-label-content{background:#40B1D0}.twentytwenty-left-arrow,.twentytwenty-right-arrow{position:absolute;width:0;height:0;border:8px inset transparent;top:50%;margin-top:-8px}.twentytwenty-container{-webkit-box-sizing:content-box;box-sizing:content-box;position:relative;z-index:0;overflow:hidden;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.15);box-shadow:0 5px 10px rgba(0,0,0,.15);opacity:0;-webkit-transition:opacity .4s;-o-transition:opacity .4s;transition:opacity .4s;-webkit-user-select:none;-moz-user-select:none}.twentytwenty-container *{-webkit-box-sizing:content-box;box-sizing:content-box}.twentytwenty-container img{position:absolute;top:0;display:block;width:100%;height:auto}.loaded .twentytwenty-container{opacity:1}.twentytwenty-container.active .twentytwenty-overlay .twentytwenty-labels,.twentytwenty-container.active .twentytwenty-overlay:hover .twentytwenty-labels{opacity:0}.twentytwenty-horizontal .twentytwenty-before-label .twentytwenty-label-content{left:15px}.twentytwenty-horizontal .twentytwenty-after-label .twentytwenty-label-content{right:15px}.twentytwenty-overlay{z-index:25}.twentytwenty-before{z-index:20}.twentytwenty-after{z-index:10}.twentytwenty-duo-buttons{position:absolute;top:10px;z-index:30;overflow:hidden}.twentytwenty-duo-buttons button{float:left;padding:2px 6px;font-size:11px;text-transform:uppercase;letter-spacing:.125em;font-weight:700;border:0;background:#1f2332;color:#FFF;-webkit-transition:all .3s;-o-transition:all .3s;transition:all .3s;cursor:pointer}.twentytwenty-duo-buttons button:focus,.twentytwenty-duo-buttons button:hover{background:#444}.twentytwenty-duo-buttons button:first-child{border-radius:3px 0 0 3px}.twentytwenty-duo-buttons button:last-child{border-radius:0 3px 3px 0}.twentytwenty-duo-buttons button.selected{background:#8bc34a;text-shadow:0 0 1px rgba(0,0,0,.2);cursor:default}.twentytwenty-duo-left{left:10px}.twentytwenty-duo-right{right:10px}.twentytwenty-left-arrow{left:50%;margin-left:-22px;border-right:8px solid #fff}.twentytwenty-right-arrow{right:50%;margin-right:-22px;border-left:8px solid #fff}#imagify-visual-comparison .close-btn,.imagify-visual-comparison .close-btn{top:50px;right:5px;width:33px;height:33px;padding:1px 0 0 2px;border:1px solid #F2F2F2;color:#F2F2F2;line-height:19px;text-align:center;border-radius:50%}.imagify-modal .imagify-comparison-title{font-size:28px;margin-bottom:1em;color:#F2F2F2;text-align:left}.imagify-modal .imagify-comparison-title .twentytwenty-duo-buttons{position:static;margin:0 10px 0 15px}.imagify-comparison-title .twentytwenty-duo-buttons button{float:none;padding:6px 12px;font-size:16px;text-transform:none;border:1px solid #40B1D0;color:#889;letter-spacing:0}.imagify-comparison-title .twentytwenty-duo-buttons button:focus{outline:0;-webkit-box-shadow:none;box-shadow:none}.imagify-comparison-title .twentytwenty-duo-buttons .selected{border:1px solid #40B1D0;color:#FFF;background:#40B1D0}.imagify-comparison-levels{margin:15px 0;overflow:hidden}.imagify-comparison-levels .imagify-c-level{display:none;min-width:175px;font-size:11px}.imagify-c-level.go-left{float:left}.imagify-c-level.go-right{float:right}.imagify-c-level.go-left,.imagify-c-level.go-right{display:table}.imagify-c-level .imagify-c-level-row{display:table-row;margin:0;color:#FFF}.imagify-c-level-row>span{display:table-cell;padding:2px 0}.imagify-c-level-row .value{text-align:right;padding-left:5px}.imagify-c-level-row .value.level{color:#40b1d0}.imagify-c-level-row .value.size{color:#8bc34a;font-weight:700}.imagify-modal .loader{position:absolute;top:50%;left:50%;margin:-32px 0 0 -32px;opacity:0;visibility:hidden;-webkit-transition:opacity .4s;-o-transition:opacity .4s;transition:opacity .4s}.imagify-modal .loading .loader{visibility:visible;opacity:1}.imagify-settings .imagify-visual-comparison-text{margin-top:1em;color:#40b1d0;font-weight:700}.modal-is-too-high .imagify-comparison-levels{position:absolute;padding:15px 20px;background:rgba(31,35,50,.95);bottom:0;left:0;right:0;margin-bottom:0}
1
+ .twentytwenty-handle{z-index:40;position:absolute;left:50%;top:50%;height:64px;width:64px;margin-left:-32px;margin-top:-32px;border-radius:50%;-webkit-box-shadow:0 3px 0 #338EA6;box-shadow:0 3px 0 #338EA6;background:#40B1D0;cursor:pointer}.twentytwenty-horizontal .twentytwenty-handle:after,.twentytwenty-horizontal .twentytwenty-handle:before{left:50%;width:2px;height:9999px;margin-left:-1px;content:"";position:absolute;z-index:30;display:block;background:#F2F5F7;-webkit-box-shadow:0 0 12px rgba(51,51,51,.5);box-shadow:0 0 12px rgba(51,51,51,.5)}.twentytwenty-horizontal .twentytwenty-handle:before{bottom:50%;margin-bottom:32px}.twentytwenty-horizontal .twentytwenty-handle:after{top:50%;margin-top:34px}.twentytwenty-labels,.twentytwenty-overlay{position:absolute;top:0;width:100%;height:100%;-webkit-transition-duration:.5s;-o-transition-duration:.5s;transition-duration:.5s}.twentytwenty-labels{opacity:1;-webkit-transition-property:opacity;-o-transition-property:opacity;transition-property:opacity}.twentytwenty-labels .twentytwenty-label-content{position:absolute;padding:0 12px;font-size:13px;letter-spacing:.1em;line-height:38px;color:#fff;background:#1F2332;border-radius:2px}.twentytwenty-horizontal .twentytwenty-labels .twentytwenty-label-content{bottom:15px}.twentytwenty-after-label .twentytwenty-label-content{background:#40B1D0}.twentytwenty-left-arrow,.twentytwenty-right-arrow{position:absolute;width:0;height:0;border:8px inset transparent;top:50%;margin-top:-8px}.twentytwenty-container{-webkit-box-sizing:content-box;box-sizing:content-box;position:relative;z-index:0;overflow:hidden;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.15);box-shadow:0 5px 10px rgba(0,0,0,.15);opacity:0;-webkit-transition:opacity .4s;-o-transition:opacity .4s;transition:opacity .4s;-webkit-user-select:none;-moz-user-select:none}.twentytwenty-container *{-webkit-box-sizing:content-box;box-sizing:content-box}.twentytwenty-container img{position:absolute;top:0;display:block;width:100%;height:auto}.loaded .twentytwenty-container{opacity:1}.twentytwenty-container.active .twentytwenty-overlay .twentytwenty-labels,.twentytwenty-container.active .twentytwenty-overlay:hover .twentytwenty-labels{opacity:0}.twentytwenty-horizontal .twentytwenty-before-label .twentytwenty-label-content{left:15px}.twentytwenty-horizontal .twentytwenty-after-label .twentytwenty-label-content{right:15px}.twentytwenty-overlay{z-index:25}.twentytwenty-before{z-index:20}.twentytwenty-after{z-index:10}.twentytwenty-duo-buttons{position:absolute;top:10px;z-index:30;overflow:hidden}.twentytwenty-duo-buttons button{float:left;padding:2px 6px;font-size:11px;text-transform:uppercase;letter-spacing:.125em;font-weight:700;border:0;background:#1f2332;color:#FFF;-webkit-transition:all .3s;-o-transition:all .3s;transition:all .3s;cursor:pointer}.twentytwenty-duo-buttons button:focus,.twentytwenty-duo-buttons button:hover{background:#444}.twentytwenty-duo-buttons button:first-child{border-radius:3px 0 0 3px}.twentytwenty-duo-buttons button:last-child{border-radius:0 3px 3px 0}.twentytwenty-duo-buttons button.selected{background:#8bc34a;text-shadow:0 0 1px rgba(0,0,0,.2);cursor:default}.twentytwenty-duo-left{left:10px}.twentytwenty-duo-right{right:10px}.twentytwenty-left-arrow{left:50%;margin-left:-22px;border-right:8px solid #fff}.twentytwenty-right-arrow{right:50%;margin-right:-22px;border-left:8px solid #fff}#imagify-visual-comparison .close-btn,.imagify-visual-comparison .close-btn{top:50px;right:5px;width:33px;height:33px;padding:1px 0 0 2px;border:1px solid #F2F2F2;color:#F2F2F2;line-height:19px;text-align:center;border-radius:50%}.imagify-modal .imagify-comparison-title{font-size:28px;margin-bottom:1em;color:#F2F2F2;text-align:left}.imagify-modal .imagify-comparison-title .twentytwenty-duo-buttons{position:static;margin:0 10px 0 15px}.imagify-comparison-title .twentytwenty-duo-buttons button{float:none;padding:6px 12px;font-size:16px;text-transform:none;border:1px solid #40B1D0;color:#889;letter-spacing:0}.imagify-comparison-title .twentytwenty-duo-buttons button:focus{outline:0;-webkit-box-shadow:none;box-shadow:none}.imagify-comparison-title .twentytwenty-duo-buttons .selected{border:1px solid #40B1D0;color:#FFF;background:#40B1D0}.imagify-comparison-levels{margin:15px 0;overflow:hidden}.imagify-comparison-levels .imagify-c-level{display:none;min-width:175px;font-size:11px}.imagify-c-level.go-left{float:left}.imagify-c-level.go-right{float:right}.imagify-c-level.go-left,.imagify-c-level.go-right{display:table}.imagify-c-level .imagify-c-level-row{display:table-row;margin:0;color:#FFF}.imagify-c-level-row>span{display:table-cell;padding:2px 0}.imagify-c-level-row .value{text-align:right;padding-left:5px}.imagify-c-level-row .value.level{color:#40b1d0}.imagify-c-level-row .value.size{color:#8bc34a;font-weight:700}.imagify-modal .loader{position:absolute;top:50%;left:50%;margin:-32px 0 0 -32px;opacity:0;visibility:hidden;-webkit-transition:opacity .4s;-o-transition:opacity .4s;transition:opacity .4s}.imagify-modal .loading .loader{visibility:visible;opacity:1}.modal-is-too-high .imagify-comparison-levels{position:absolute;padding:15px 20px;background:rgba(31,35,50,.95);bottom:0;left:0;right:0;margin-bottom:0}
assets/images/big-blue-check.png CHANGED
Binary file
assets/images/icon-alert.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg width="36" height="33" viewBox="0 0 36 33" xmlns="http://www.w3.org/2000/svg"><g fill="#40b1d0" fill-rule="nonzero"><path d="m35.38 26.295c0-.002 0-.002 0 0l-13.501-23.787c-.8-1.41-2.247-2.252-3.873-2.252-1.625 0-3.071.843-3.872 2.252l-13.5 23.786c-.791 1.393-.78 3.051.027 4.437.807 1.386 2.246 2.214 3.846 2.214l27 0c1.6 0 3.038-.828 3.844-2.212.807-1.386.818-3.045.029-4.437m-1.325 3.681c-.534.919-1.488 1.469-2.548 1.469l-27 0c-1.062 0-2.01-.549-2.55-1.469-.534-.919-.541-2.02-.018-2.941l13.5-23.787c.529-.934 1.489-1.493 2.566-1.493 1.078 0 2.038.558 2.568 1.493l13.502 23.786c.523.924.516 2.025-.02 2.943"/><path d="m18.01 9.459c-.414 0-.75.336-.75.75l0 10.772c0 .414.336.75.75.75.414 0 .75-.336.75-.75l0-10.772c0-.414-.336-.75-.75-.75"/><path d="m18.01 24.933c-.414 0-.75.336-.75.75l0 .373c0 .414.336.75.75.75.414 0 .75-.336.75-.75l0-.373c0-.416-.336-.75-.75-.75"/></g></svg>
assets/images/icon-arrow-choice.png CHANGED
Binary file
assets/images/icon-doc-image.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg width="36" height="47" viewBox="0 0 36 47" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" transform="translate(-594-569)"><path d="m13.304 31.3l-7.826 0c-.432 0-.783.351-.783.783 0 .432.351.783.783.783l7.826 0c.432 0 .783-.351.783-.783 0-.432-.351-.783-.783-.783m0-4.696l-7.826 0c-.432 0-.783.351-.783.783 0 .432.351.783.783.783l7.826 0c.432 0 .783-.351.783-.783 0-.432-.351-.783-.783-.783m0 9.391l-7.826 0c-.432 0-.783.351-.783.783 0 .432.351.783.783.783l7.826 0c.432 0 .783-.351.783-.783 0-.432-.351-.783-.783-.783m0 4.696l-7.826 0c-.432 0-.783.351-.783.783 0 .432.351.783.783.783l7.826 0c.432 0 .783-.351.783-.783 0-.432-.351-.783-.783-.783m-8.609-22.696c0 .432.351.783.783.783l25.04 0c.432 0 .783-.351.783-.783 0-.432-.351-.783-.783-.783l-25.04 0c-.432 0-.783.351-.783.783m30.522-18l-21.913 0c-.105 0-.207.022-.302.061-.045.019-.08.053-.121.08-.044.03-.094.05-.131.088l-12.522 12.522c-.036.036-.056.085-.085.127-.028.042-.064.078-.083.125-.039.095-.061.197-.061.302l0 32.87c0 .432.351.783.783.783l34.435 0c.432 0 .783-.351.783-.783l0-45.39c0-.432-.351-.783-.783-.783m-22.696 2.672l0 9.85-9.85 0 9.85-9.85m21.913 42.719l-32.87 0 0-31.3 11.739 0c.432 0 .783-.351.783-.783l0-11.739 20.348 0 0 43.826m-3.913-23.478l-25.04 0c-.432 0-.783.351-.783.783 0 .432.351.783.783.783l25.04 0c.432 0 .783-.351.783-.783 0-.432-.351-.783-.783-.783m0 4.696l-14.09 0c-.432 0-.783.351-.783.783l0 14.09c0 .011.006.019.006.03.003.063.019.121.036.182.011.039.017.08.034.116.022.047.056.086.086.128.03.041.056.083.094.117.009.009.014.02.023.03.03.025.066.033.097.052.045.028.091.058.142.077.05.017.1.023.152.031.038.003.072.02.111.02l14.09 0c.161 0 .302-.059.426-.144.014-.009.03-.005.044-.014.014-.011.02-.025.033-.036.044-.038.08-.081.114-.128.028-.038.055-.072.075-.111.022-.044.034-.091.049-.141.014-.052.028-.102.031-.155.002-.019.011-.033.011-.052l0-14.09c0-.434-.351-.784-.783-.784m-12.417 14.09l2.446-2.936 2.576 1.288c.34.171.75.072.977-.23l1.722-2.295 3.13 4.173-10.852 0m11.634-1.567l-3.287-4.383c-.296-.394-.958-.394-1.252 0l-1.957 2.608-2.547-1.273c-.327-.164-.72-.08-.952.199l-2.528 3.033 0-11.14 12.522 0 0 10.955m-9.391-6.259c.862 0 1.565-.703 1.565-1.565 0-.862-.703-1.565-1.565-1.565-.862 0-1.565.703-1.565 1.565 0 .862.703 1.565 1.565 1.565" transform="translate(594 569)" fill="#40b1d0" fill-rule="nonzero"/></g></svg>
assets/images/icon-external.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="nonzero" transform="translate(1 1)" stroke="#fff"><polygon points="8.75 0 8.75 0.7 12.8065 0.7 5.0015 8.5015 5.4985 8.9985 13.3 1.1935 13.3 5.25 14 5.25 14 0"/><polygon points="11.9 13.3 0.7 13.3 0.7 2.1 6.3 2.1 6.3 1.4 0 1.4 0 14 12.6 14 12.6 7.7 11.9 7.7"/></g></svg>
assets/images/icon-level.svg ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
2
+ <g fill="#40b1d0" fill-rule="evenodd">
3
+ <polygon points="11.6054688 11.6054688 8.7890625 11.6054688 8.7890625 0.39453125 11.6054688 0.39453125"/>
4
+ <polygon points="7.39453125 11.6054688 4.60546875 11.6054688 4.60546875 3.89453125 7.39453125 3.89453125"/>
5
+ <polygon points="3.2109375 11.6054688 0.39453125 11.6054688 0.39453125 6 3.2109375 6"/>
6
+ </g>
7
+ </svg>
assets/images/icon-load.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg width="27" height="28" viewBox="0 0 27 28" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m13.3.254c-.773 0-1.4.627-1.4 1.4l0 4.2c0 .773.627 1.4 1.4 1.4.773 0 1.4-.627 1.4-1.4l0-4.2c0-.773-.627-1.4-1.4-1.4m-8.422 3.478c-.358 0-.711.142-.984.416-.547.547-.547 1.444 0 1.991l2.975 2.953c.547.547 1.422.547 1.969 0 .547-.547.547-1.422 0-1.969l-2.953-2.975c-.273-.273-.648-.416-1.01-.416m16.844 0c-.358 0-.733.142-1.01.416l-2.953 2.975c-.547.547-.547 1.422 0 1.969.547.547 1.422.547 1.969 0l2.975-2.953c.547-.547.547-1.444 0-1.991-.273-.273-.626-.416-.984-.416m-20.322 8.422c-.773 0-1.4.627-1.4 1.4 0 .773.627 1.4 1.4 1.4l4.2 0c.773 0 1.4-.627 1.4-1.4 0-.773-.627-1.4-1.4-1.4l-4.2 0m19.6 0c-.773 0-1.4.627-1.4 1.4 0 .773.627 1.4 1.4 1.4l4.2 0c.773 0 1.4-.627 1.4-1.4 0-.773-.627-1.4-1.4-1.4l-4.2 0m-13.147 5.447c-.358 0-.711.142-.984.416l-2.975 2.953c-.547.547-.547 1.444 0 1.991.547.547 1.444.547 1.991 0l2.953-2.975c.547-.547.547-1.422 0-1.969-.273-.273-.626-.416-.984-.416m10.894 0c-.358 0-.711.142-.984.416-.547.547-.547 1.422 0 1.969l2.953 2.975c.547.547 1.444.547 1.991 0 .547-.547.547-1.444 0-1.991l-2.975-2.953c-.273-.273-.626-.416-.984-.416m-5.447 2.253c-.773 0-1.4.627-1.4 1.4l0 4.2c0 .773.627 1.4 1.4 1.4.773 0 1.4-.627 1.4-1.4l0-4.2c0-.773-.627-1.4-1.4-1.4" fill="#40b1d0" fill-rule="nonzero"/></g></svg>
assets/images/icon-lock.png CHANGED
Binary file
assets/images/icon-pack.png CHANGED
Binary file
assets/images/icon-time.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg"><g fill="#40b1d0" fill-rule="nonzero"><path d="m18 36c-9.925 0-18-8.07-18-18 0-9.925 8.07-18 18-18 9.925 0 18 8.07 18 18 0 9.925-8.07 18-18 18m0-34.435c-9.06 0-16.435 7.372-16.435 16.435 0 9.06 7.372 16.435 16.435 16.435 9.06 0 16.435-7.372 16.435-16.435 0-9.06-7.372-16.435-16.435-16.435"/><path d="m27.391 18.783l-9.391 0c-.432 0-.783-.351-.783-.783l0-12.522c0-.432.351-.783.783-.783.432 0 .783.351.783.783l0 11.739 8.609 0c.432 0 .783.351.783.783 0 .432-.351.783-.783.783"/></g></svg>
assets/images/imagify-logo.png CHANGED
Binary file
assets/images/imagify-menu-bar.jpg CHANGED
Binary file
assets/images/lazyload.png ADDED
Binary file
assets/images/logo-wprocket.png CHANGED
Binary file
assets/images/logo-wprocket.svg ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="232px" height="63px" viewBox="0 0 232 63" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
+ <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
4
+ <title>Icon / WP Rocket / Light</title>
5
+ <desc>Created with Sketch.</desc>
6
+ <defs>
7
+ <linearGradient x1="50.1383841%" y1="100%" x2="50.1383841%" y2="32.5347067%" id="linearGradient-1">
8
+ <stop stop-color="#F56640" offset="0%"></stop>
9
+ <stop stop-color="#F57840" offset="24.3858064%"></stop>
10
+ <stop stop-color="#FFBE54" offset="100%"></stop>
11
+ </linearGradient>
12
+ <linearGradient x1="50.0029445%" y1="98.3205782%" x2="50.0029445%" y2="18.9773025%" id="linearGradient-2">
13
+ <stop stop-color="#F56640" offset="0%"></stop>
14
+ <stop stop-color="#F57840" offset="18.1093204%"></stop>
15
+ <stop stop-color="#FFBE54" offset="100%"></stop>
16
+ </linearGradient>
17
+ <linearGradient x1="50.000006%" y1="110.433207%" x2="50%" y2="-25.4443577%" id="linearGradient-3">
18
+ <stop stop-color="#F56640" offset="0%"></stop>
19
+ <stop stop-color="#F57840" offset="8.37881088%"></stop>
20
+ <stop stop-color="#FFBE54" offset="100%"></stop>
21
+ </linearGradient>
22
+ <linearGradient x1="50.000006%" y1="84.3779533%" x2="50.000006%" y2="0%" id="linearGradient-4">
23
+ <stop stop-color="#F56640" offset="0%"></stop>
24
+ <stop stop-color="#F57840" offset="39.1082657%"></stop>
25
+ <stop stop-color="#FFBE54" offset="100%"></stop>
26
+ </linearGradient>
27
+ </defs>
28
+ <g id="Imagify-Plugin-—-Settings" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(-1382.000000, -110.000000)">
29
+ <g id="Group-14" transform="translate(1382.000000, 68.000000)">
30
+ <g id="Icon-/-WP-Rocket-/-Light" transform="translate(0.000000, 42.000000)">
31
+ <path d="M26.2526316,32.5011628 C25.0804211,32.5011628 23.9814737,31.8174419 23.4442105,30.7430233 L22.5162105,28.6674419 L13.3094737,28.6674419 L12.3814737,30.7430233 C11.8442105,31.8174419 10.7452632,32.5011628 9.57305263,32.5011628 L9.08463158,32.5011628 L13.0164211,62.4627907 C13.2117895,63.0244186 13.5781053,63.0244186 13.7978947,62.4627907 L17.7052632,52.8906977 L21.0997895,62.4383721 C21.2951579,63 21.6614737,63 21.8812632,62.4383721 L26.4235789,32.5255814 L26.2526316,32.5011628 L26.2526316,32.5011628 Z" id="Shape" fill="url(#linearGradient-1)"></path>
32
+ <path d="M22.1498947,27.8372093 L13.7002105,27.8372093 L12.4058947,30.7430233 C11.9663158,31.622093 11.1115789,32.2569767 10.1835789,32.427907 L13.1629474,50.9127907 C13.3583158,51.2546512 13.7246316,51.2546512 13.9444211,50.9127907 L17.7541053,45.1255814 L21.2463158,50.8883721 C21.4661053,51.2302326 21.808,51.2302326 22.0277895,50.8883721 L25.3978947,32.3790698 C24.5675789,32.1348837 23.8349474,31.5488372 23.4442105,30.7430233 L22.1498947,27.8372093 Z" id="Shape" fill="url(#linearGradient-2)"></path>
33
+ <path d="M23.4442105,30.7430233 L17.9006316,18.3627907 L12.3814737,30.7430233 C12.1861053,31.1093023 11.9418947,31.4267442 11.6488421,31.6953488 L13.9444211,43.977907 C14.0665263,44.1488372 14.384,44.1976744 14.6037895,43.977907 L17.6808421,39.9 L20.7578947,43.9534884 C20.9532632,44.1976744 21.2951579,44.1488372 21.4172632,43.9534884 L23.9570526,31.5244186 C23.7616842,31.2802326 23.5907368,31.0360465 23.4442105,30.7430233" id="Shape" fill="url(#linearGradient-3)"></path>
34
+ <path d="M23.4442105,30.7430233 L17.9006316,18.3627907 L12.3814737,30.7430233 C12.1861053,31.1093023 11.9418947,31.4267442 11.6488421,31.6953488 L13.9444211,43.977907 C14.0665263,44.1488372 14.384,44.1976744 14.6037895,43.977907 L17.6808421,39.9 L20.7578947,43.9534884 C20.9532632,44.1976744 21.2951579,44.1488372 21.4172632,43.9534884 L23.9570526,31.5244186 C23.7616842,31.2802326 23.5907368,31.0360465 23.4442105,30.7430233" id="Shape" fill="url(#linearGradient-4)" opacity="0.5"></path>
35
+ <path d="M0.219789474,1.51395349 C0.0488421053,0.927906977 0.390736842,0.512790698 0.976842105,0.512790698 L5.83663158,0.512790698 C6.17852632,0.512790698 6.496,0.805813953 6.59368421,1.09883721 L10.1591579,15.1395349 L10.3301053,15.1395349 L16.7528421,0.561627907 C16.8749474,0.31744186 17.1191579,0.0976744186 17.4610526,0.0976744186 L18.2181053,0.0976744186 C18.5844211,0.0976744186 18.8286316,0.293023256 18.9263158,0.561627907 L25.3490526,15.1395349 L25.52,15.1395349 L29.0854737,1.09883721 C29.1587368,0.805813953 29.5006316,0.512790698 29.8425263,0.512790698 L34.7023158,0.512790698 C35.2884211,0.512790698 35.6058947,0.927906977 35.4593684,1.51395349 L27.6202105,29.3511628 C27.5469474,29.6930233 27.2050526,29.9372093 26.8631579,29.9372093 L26.2037895,29.9372093 C25.9107368,29.9372093 25.6176842,29.7418605 25.4955789,29.4732558 L17.9006316,12.5267442 L17.7785263,12.5267442 L10.2324211,29.4732558 C10.1103158,29.7174419 9.81726316,29.9372093 9.52421053,29.9372093 L8.86484211,29.9372093 C8.52294737,29.9372093 8.20547368,29.6930233 8.10778947,29.3511628 L0.219789474,1.51395349" id="Shape" fill="#FFFFFF"></path>
36
+ <path d="M39.7818947,1.29418605 C39.7818947,0.879069767 40.1237895,0.512790698 40.5633684,0.512790698 L50.5515789,0.512790698 C55.6555789,0.512790698 59.8315789,4.66395349 59.8315789,9.66976744 C59.8315789,14.7976744 55.6555789,18.9976744 50.6004211,18.9976744 L46.2534737,18.9976744 L46.2534737,28.7406977 C46.2534737,29.155814 45.8871579,29.522093 45.472,29.522093 L40.5877895,29.522093 C40.1237895,29.522093 39.8063158,29.155814 39.8063158,28.7406977 L39.8063158,1.29418605 L39.7818947,1.29418605 L39.7818947,1.29418605 Z M50.1852632,12.9418605 C51.968,12.9418605 53.4576842,11.5255814 53.4576842,9.66976744 C53.4576842,7.93604651 51.968,6.59302326 50.1852632,6.59302326 L46.2534737,6.59302326 L46.2534737,12.9418605 L50.1852632,12.9418605 L50.1852632,12.9418605 Z" id="Shape" fill="#FFFFFF"></path>
37
+ <path d="M73.3364211,1.29418605 C73.3364211,0.879069767 73.6783158,0.512790698 74.1178947,0.512790698 L86.4993684,0.512790698 C91.4812632,0.512790698 95.5351579,4.54186047 95.5351579,9.4744186 C95.5351579,13.2837209 93.0197895,16.3604651 89.4054737,17.8011628 L95.0711579,28.3255814 C95.3642105,28.8627907 95.0711579,29.522093 94.3629474,29.522093 L88.8437895,29.522093 C88.5018947,29.522093 88.2576842,29.3267442 88.1844211,29.155814 L82.6652632,18.1674419 L79.808,18.1674419 L79.808,28.7406977 C79.808,29.155814 79.4416842,29.522093 79.0265263,29.522093 L74.0934737,29.522093 C73.6294737,29.522093 73.312,29.155814 73.312,28.7406977 L73.3364211,1.29418605 L73.3364211,1.29418605 Z M85.9865263,12.8930233 C87.696,12.8930233 89.1368421,11.3302326 89.1368421,9.57209302 C89.1368421,7.83837209 87.696,6.42209302 85.9865263,6.42209302 L79.8568421,6.42209302 L79.8568421,12.8930233 L85.9865263,12.8930233 L85.9865263,12.8930233 Z" id="Shape" fill="#FFFFFF"></path>
38
+ <path d="M113.875368,0.0976744186 C122.154105,0.0976744186 128.796632,6.76395349 128.796632,15.0662791 C128.796632,23.3686047 122.178526,29.9372093 113.875368,29.9372093 C105.596632,29.9372093 99.0029474,23.344186 99.0029474,15.0662791 C99.0029474,6.78837209 105.596632,0.0976744186 113.875368,0.0976744186 M113.875368,23.2953488 C118.442105,23.2953488 122.154105,19.5593023 122.154105,15.0418605 C122.154105,10.4755814 118.417684,6.71511628 113.875368,6.71511628 C109.357474,6.71511628 105.621053,10.4755814 105.621053,15.0418605 C105.621053,19.5593023 109.357474,23.2953488 113.875368,23.2953488" id="Shape" fill="#FFFFFF"></path>
39
+ <path d="M146.648421,0.0976744186 C150.8,0.0976744186 153.852632,1.39186047 156.685474,3.95581395 C157.051789,4.29767442 157.051789,4.78604651 156.734316,5.10348837 L153.510737,8.44883721 C153.217684,8.74186047 152.753684,8.74186047 152.485053,8.44883721 C150.946526,7.08139535 148.919579,6.3244186 146.892632,6.3244186 C142.203789,6.3244186 138.760421,10.2313953 138.760421,14.8709302 C138.760421,19.4616279 142.252632,23.2953488 146.917053,23.2953488 C148.870737,23.2953488 150.970947,22.5872093 152.460632,21.3174419 C152.753684,21.0732558 153.290947,21.0732558 153.535158,21.3662791 L156.758737,24.8093023 C157.051789,25.1023256 157.002947,25.6395349 156.709895,25.9325581 C153.901474,28.6674419 150.336,29.9616279 146.648421,29.9616279 C138.369684,29.9616279 131.678316,23.3686047 131.678316,15.0906977 C131.678316,6.8127907 138.369684,0.0976744186 146.648421,0.0976744186" id="Shape" fill="#FFFFFF"></path>
40
+ <path d="M161.691789,1.51395349 C161.691789,0.976744186 162.106947,0.512790698 162.693053,0.512790698 L167.552842,0.512790698 C168.090105,0.512790698 168.554105,0.976744186 168.554105,1.51395349 L168.554105,12.4046512 L177.760842,0.927906977 C177.931789,0.73255814 178.298105,0.512790698 178.542316,0.512790698 L183.768421,0.512790698 C184.549895,0.512790698 184.965053,1.41627907 184.525474,2.05116279 L174.830316,14.1872093 L185.404632,27.9348837 C185.868632,28.5453488 185.404632,29.4732558 184.623158,29.4732558 L178.908632,29.4732558 C178.615579,29.4732558 178.249263,29.3023256 178.151579,29.1802326 L168.529684,16.1651163 L168.529684,28.5209302 C168.529684,29.0581395 168.065684,29.522093 167.528421,29.522093 L162.668632,29.522093 C162.082526,29.522093 161.667368,29.0581395 161.667368,28.5209302 L161.691789,1.51395349 L161.691789,1.51395349 Z" id="Shape" fill="#FFFFFF"></path>
41
+ <path d="M189.580632,1.29418605 C189.580632,0.879069767 189.922526,0.512790698 190.362105,0.512790698 L207.530105,0.512790698 C207.994105,0.512790698 208.311579,0.879069767 208.311579,1.29418605 L208.311579,5.81162791 C208.311579,6.22674419 207.969684,6.59302326 207.530105,6.59302326 L196.052211,6.59302326 L196.052211,11.7209302 L205.503158,11.7209302 C205.918316,11.7209302 206.284632,12.0872093 206.284632,12.5023256 L206.284632,17.0197674 C206.284632,17.4837209 205.918316,17.8011628 205.503158,17.8011628 L196.052211,17.8011628 L196.052211,23.4174419 L207.530105,23.4174419 C207.994105,23.4174419 208.311579,23.7837209 208.311579,24.1988372 L208.311579,28.7162791 C208.311579,29.1313953 207.969684,29.4976744 207.530105,29.4976744 L190.362105,29.4976744 C189.898105,29.4976744 189.580632,29.1313953 189.580632,28.7162791 L189.580632,1.29418605" id="Shape" fill="#FFFFFF"></path>
42
+ <path d="M217.591579,6.59302326 L211.657263,6.59302326 C211.193263,6.59302326 210.875789,6.22674419 210.875789,5.81162791 L210.875789,1.29418605 C210.875789,0.879069767 211.217684,0.512790698 211.657263,0.512790698 L230.046316,0.512790698 C230.510316,0.512790698 230.827789,0.879069767 230.827789,1.29418605 L230.827789,5.81162791 C230.827789,6.22674419 230.485895,6.59302326 230.046316,6.59302326 L224.112,6.59302326 L224.112,28.7162791 C224.112,29.1313953 223.745684,29.4976744 223.330526,29.4976744 L218.348632,29.4976744 C217.933474,29.4976744 217.567158,29.1313953 217.567158,28.7162791 L217.591579,6.59302326 L217.591579,6.59302326 Z" id="Shape" fill="#FFFFFF"></path>
43
+ <path d="M34.4739035,50.8163567 C36.2456718,50.8163567 37.4871149,49.876329 37.4871149,48.2975645 L37.4871149,48.2734612 C37.4871149,46.8754713 36.5710986,46.2367345 34.7872775,45.8028756 C33.1601433,45.4172232 32.7744523,45.1279839 32.7744523,44.4771955 L32.7744523,44.4530922 C32.7744523,43.8987169 33.2806718,43.4528063 34.1484767,43.4528063 C34.8113832,43.4528063 35.4381311,43.6697358 36.0889848,44.0915431 C36.2095132,44.1638529 36.3300417,44.2000078 36.4746758,44.2000078 C36.8603669,44.2000078 37.1737409,43.8987169 37.1737409,43.5130645 C37.1737409,43.2238252 37.0170539,43.0189474 36.848314,42.9225343 C36.0889848,42.4163655 35.2332328,42.1391778 34.1725824,42.1391778 C32.4972368,42.1391778 31.2919523,43.1394637 31.2919523,44.5856602 L31.2919523,44.6097635 C31.2919523,46.1644247 32.3043913,46.6946967 34.1002653,47.1285557 C35.6671352,47.4901048 36.0046149,47.815499 36.0046149,48.4180809 L36.0046149,48.4421841 C36.0046149,49.0688693 35.4260783,49.5027282 34.510062,49.5027282 C33.5940458,49.5027282 32.8467694,49.1893857 32.1477043,48.6350103 C32.0512816,48.5627005 31.9187003,48.5024423 31.7138019,48.5024423 C31.3281108,48.5024423 31.0147368,48.8037333 31.0147368,49.1893857 C31.0147368,49.4304184 31.1352653,49.6352962 31.3040051,49.7558126 C32.2441271,50.4548076 33.3288832,50.8163567 34.4739035,50.8163567 Z M44.7188221,44.9954159 C44.7188221,44.5856602 44.3813425,44.260266 43.9835986,44.260266 C43.5738018,44.260266 43.248375,44.5856602 43.248375,44.9954159 L43.248375,47.9239637 C43.248375,48.912198 42.6577856,49.4906766 41.8140864,49.4906766 C40.9462815,49.4906766 40.4521148,48.9363013 40.4521148,47.948067 L40.4521148,44.9954159 C40.4521148,44.5856602 40.126688,44.260266 39.7168913,44.260266 C39.3191474,44.260266 38.9937205,44.5856602 38.9937205,44.9954159 L38.9937205,48.381926 C38.9937205,49.8642773 39.8253669,50.8284083 41.2596555,50.8284083 C42.2479888,50.8284083 42.8385782,50.3101879 43.248375,49.7076061 L43.248375,50.0330003 C43.248375,50.4427559 43.5858547,50.7560985 43.9835986,50.7560985 C44.3933953,50.7560985 44.7188221,50.4307043 44.7188221,50.0330003 L44.7188221,44.9954159 Z M46.5388018,51.9612622 L46.5388018,44.9954159 C46.5388018,44.5856602 46.8521758,44.260266 47.2619725,44.260266 C47.6717693,44.260266 47.9971961,44.5856602 47.9971961,44.9954159 L47.9971961,45.3931199 C48.4672571,44.7302799 49.1301636,44.2000078 50.1667083,44.2000078 C51.6612611,44.2000078 53.1196554,45.3810683 53.1196554,47.5021564 L53.1196554,47.5262597 C53.1196554,49.6352962 51.673314,50.8284083 50.1667083,50.8284083 C49.1060579,50.8284083 48.4431514,50.2981363 47.9971961,49.7076061 L47.9971961,51.9612622 C47.9971961,52.3710179 47.6717693,52.6843605 47.2619725,52.6843605 C46.8642286,52.6843605 46.5388018,52.3710179 46.5388018,51.9612622 Z M49.8171758,49.5629864 C50.8175619,49.5629864 51.6371554,48.77963 51.6371554,47.5262597 L51.6371554,47.5021564 C51.6371554,46.2728894 50.8055091,45.4654297 49.8171758,45.4654297 C48.8288424,45.4654297 47.9610376,46.2849411 47.9610376,47.5021564 L47.9610376,47.5262597 C47.9610376,48.7434751 48.8288424,49.5629864 49.8171758,49.5629864 Z M55.72307,47.0682975 L58.9893912,47.0682975 C58.8929684,46.1282698 58.3385375,45.3931199 57.3743099,45.3931199 C56.4823993,45.3931199 55.8556513,46.0800632 55.72307,47.0682975 Z M59.8330903,49.9968454 C59.2545538,50.5150658 58.531383,50.84046 57.5430497,50.84046 C55.6989644,50.84046 54.2767286,49.5027282 54.2767286,47.5383113 L54.2767286,47.5142081 C54.2767286,45.6944108 55.5663831,44.2000078 57.3863627,44.2000078 C59.4112408,44.2000078 60.4116269,45.8631338 60.4116269,47.3334335 C60.4116269,47.7431892 60.0982529,48.0324285 59.7246147,48.0324285 L55.7351229,48.0324285 C55.8918099,49.0688693 56.6270335,49.6473479 57.5671554,49.6473479 C58.1818505,49.6473479 58.6639643,49.4304184 59.0737611,49.0929726 C59.1822367,49.0086111 59.2786595,48.9604045 59.4473993,48.9604045 C59.7728261,48.9604045 60.0259359,49.2134889 60.0259359,49.5509348 C60.0259359,49.7317093 59.941566,49.8883806 59.8330903,49.9968454 Z M61.9061798,50.0330003 C61.9061798,50.4427559 62.2316066,50.7560985 62.6293505,50.7560985 C63.0391472,50.7560985 63.3645741,50.4307043 63.3645741,50.0330003 L63.3645741,48.1529448 C63.3645741,46.6464902 64.0756919,45.8510821 65.1122367,45.6703076 C65.4376635,45.6100494 65.6907732,45.356965 65.6907732,44.9713126 C65.6907732,44.5495053 65.4256106,44.2482144 64.9676025,44.2482144 C64.3288017,44.2482144 63.7020537,44.8507962 63.3645741,45.6462043 L63.3645741,44.9954159 C63.3645741,44.5856602 63.0391472,44.260266 62.6293505,44.260266 C62.2195537,44.260266 61.9061798,44.5856602 61.9061798,44.9954159 L61.9061798,50.0330003 Z M66.8598993,42.6935532 C66.8598993,43.1274121 67.2214846,43.4166514 67.6915456,43.4166514 C68.1616066,43.4166514 68.5231919,43.1274121 68.5231919,42.6935532 L68.5231919,42.6212433 C68.5231919,42.1873844 68.1616066,41.9101967 67.6915456,41.9101967 C67.2214846,41.9101967 66.8598993,42.1873844 66.8598993,42.6212433 L66.8598993,42.6935532 Z M66.9683749,50.0330003 C66.9683749,50.4427559 67.2938017,50.7560985 67.6915456,50.7560985 C68.1013423,50.7560985 68.4267692,50.4427559 68.4267692,50.0330003 L68.4267692,44.9954159 C68.4267692,44.5856602 68.1013423,44.260266 67.6915456,44.260266 C67.2817488,44.260266 66.9683749,44.5856602 66.9683749,44.9954159 L66.9683749,50.0330003 Z M73.356383,50.84046 C71.4158748,50.84046 69.9815862,49.3581086 69.9815862,47.550363 L69.9815862,47.5262597 C69.9815862,45.7064625 71.4279277,44.2000078 73.3804886,44.2000078 C75.3330496,44.2000078 76.7673382,45.6823592 76.7673382,47.5021564 L76.7673382,47.5262597 C76.7673382,49.3340053 75.3209968,50.84046 73.356383,50.84046 Z M73.3804886,49.5629864 C74.5616675,49.5629864 75.3089439,48.647062 75.3089439,47.550363 L75.3089439,47.5262597 C75.3089439,46.4054574 74.5014033,45.4774814 73.356383,45.4774814 C72.1872569,45.4774814 71.4399805,46.3934058 71.4399805,47.5021564 L71.4399805,47.5262597 C71.4399805,48.6350103 72.2475212,49.5629864 73.3804886,49.5629864 Z M78.2498382,50.0330003 C78.2498382,50.4427559 78.575265,50.7560985 78.9730089,50.7560985 C79.3828057,50.7560985 79.7082325,50.4307043 79.7082325,50.0330003 L79.7082325,48.1529448 C79.7082325,46.6464902 80.4193504,45.8510821 81.4558951,45.6703076 C81.7813219,45.6100494 82.0344317,45.356965 82.0344317,44.9713126 C82.0344317,44.5495053 81.7692691,44.2482144 81.311261,44.2482144 C80.6724602,44.2482144 80.0457122,44.8507962 79.7082325,45.6462043 L79.7082325,44.9954159 C79.7082325,44.5856602 79.3828057,44.260266 78.9730089,44.260266 C78.5632122,44.260266 78.2498382,44.5856602 78.2498382,44.9954159 L78.2498382,50.0330003 Z M88.9889235,50.2017232 C89.1335577,50.5873756 89.3987203,50.8163567 89.7603056,50.8163567 L89.9049398,50.8163567 C90.2665251,50.8163567 90.5437406,50.5873756 90.6763219,50.2017232 L92.5565658,44.6941249 L94.4368097,50.2017232 C94.569391,50.5873756 94.8345536,50.8163567 95.1961389,50.8163567 L95.3407731,50.8163567 C95.7023584,50.8163567 95.9795739,50.5753239 96.1121552,50.2017232 L98.6070942,43.2117736 C98.6432527,43.1274121 98.6794113,43.0068957 98.6794113,42.9104826 C98.6794113,42.5127786 98.3419316,42.199436 97.9441877,42.199436 C97.5826023,42.199436 97.3294926,42.4284171 97.221017,42.7297081 L95.2805089,48.5385972 L93.3761593,42.765863 C93.2676837,42.4163655 93.0025211,42.1753328 92.6288829,42.1753328 L92.5204072,42.1753328 C92.146769,42.1753328 91.8936593,42.4163655 91.7731308,42.765863 L89.8687812,48.5385972 L87.9282731,42.7417597 C87.8318504,42.4284171 87.5546349,42.199436 87.1930495,42.199436 C86.7832528,42.199436 86.4337203,42.5127786 86.4337203,42.9225343 C86.4337203,43.0189474 86.457826,43.1274121 86.4939845,43.2117736 L88.9889235,50.2017232 Z M102.500163,50.84046 C100.559655,50.84046 99.1253666,49.3581086 99.1253666,47.550363 L99.1253666,47.5262597 C99.1253666,45.7064625 100.571708,44.2000078 102.524269,44.2000078 C104.47683,44.2000078 105.911119,45.6823592 105.911119,47.5021564 L105.911119,47.5262597 C105.911119,49.3340053 104.464777,50.84046 102.500163,50.84046 Z M102.524269,49.5629864 C103.705448,49.5629864 104.452724,48.647062 104.452724,47.550363 L104.452724,47.5262597 C104.452724,46.4054574 103.645184,45.4774814 102.500163,45.4774814 C101.331037,45.4774814 100.583761,46.3934058 100.583761,47.5021564 L100.583761,47.5262597 C100.583761,48.6350103 101.391302,49.5629864 102.524269,49.5629864 Z M107.393619,50.0330003 C107.393619,50.4427559 107.719045,50.7560985 108.116789,50.7560985 C108.526586,50.7560985 108.852013,50.4307043 108.852013,50.0330003 L108.852013,48.1529448 C108.852013,46.6464902 109.563131,45.8510821 110.599675,45.6703076 C110.925102,45.6100494 111.178212,45.356965 111.178212,44.9713126 C111.178212,44.5495053 110.913049,44.2482144 110.455041,44.2482144 C109.81624,44.2482144 109.189493,44.8507962 108.852013,45.6462043 L108.852013,44.9954159 C108.852013,44.5856602 108.526586,44.260266 108.116789,44.260266 C107.706993,44.260266 107.393619,44.5856602 107.393619,44.9954159 L107.393619,50.0330003 Z M118.446078,42.5609852 L118.446078,50.0330003 C118.446078,50.4427559 118.120651,50.7560985 117.722907,50.7560985 C117.31311,50.7560985 116.987684,50.4427559 116.987684,50.0330003 L116.987684,49.6352962 C116.517623,50.2981363 115.854716,50.8284083 114.818171,50.8284083 C113.323619,50.8284083 111.865224,49.6352962 111.865224,47.5262597 L111.865224,47.5021564 C111.865224,45.3931199 113.299513,44.2000078 114.818171,44.2000078 C115.866769,44.2000078 116.529675,44.7182282 116.987684,45.3208101 L116.987684,42.5609852 C116.987684,42.1512295 117.31311,41.8378869 117.710854,41.8378869 C118.120651,41.8378869 118.446078,42.1512295 118.446078,42.5609852 Z M115.167704,45.4654297 C114.167318,45.4654297 113.335671,46.2367345 113.335671,47.5021564 L113.335671,47.5262597 C113.335671,48.7555267 114.179371,49.5629864 115.167704,49.5629864 C116.143984,49.5629864 117.011789,48.7434751 117.011789,47.5262597 L117.011789,47.5021564 C117.011789,46.2728894 116.143984,45.4654297 115.167704,45.4654297 Z M120.44685,50.0209486 L120.44685,43.0068957 C120.44685,42.5850884 120.772277,42.2596942 121.194127,42.2596942 L123.773436,42.2596942 C125.750102,42.2596942 126.979492,43.3804965 126.979492,45.0797773 L126.979492,45.1038806 C126.979492,46.9959877 125.460834,47.9842219 123.604696,47.9842219 L121.92935,47.9842219 L121.92935,50.0209486 C121.92935,50.4307043 121.603923,50.7560985 121.194127,50.7560985 C120.772277,50.7560985 120.44685,50.4307043 120.44685,50.0209486 Z M121.92935,46.6464902 L123.652907,46.6464902 C124.773822,46.6464902 125.472887,46.019805 125.472887,45.1400355 L125.472887,45.1159323 C125.472887,44.127698 124.761769,43.6094776 123.652907,43.6094776 L121.92935,43.6094776 L121.92935,46.6464902 Z M128.329411,50.0330003 C128.329411,50.4427559 128.654838,50.7560985 129.052582,50.7560985 C129.462379,50.7560985 129.787805,50.4307043 129.787805,50.0330003 L129.787805,48.1529448 C129.787805,46.6464902 130.498923,45.8510821 131.535468,45.6703076 C131.860895,45.6100494 132.114005,45.356965 132.114005,44.9713126 C132.114005,44.5495053 131.848842,44.2482144 131.390834,44.2482144 C130.752033,44.2482144 130.125285,44.8507962 129.787805,45.6462043 L129.787805,44.9954159 C129.787805,44.5856602 129.462379,44.260266 129.052582,44.260266 C128.642785,44.260266 128.329411,44.5856602 128.329411,44.9954159 L128.329411,50.0330003 Z M134.187094,47.0682975 L137.453415,47.0682975 C137.356992,46.1282698 136.802561,45.3931199 135.838334,45.3931199 C134.946423,45.3931199 134.319675,46.0800632 134.187094,47.0682975 Z M138.297114,49.9968454 C137.718578,50.5150658 136.995407,50.84046 136.007074,50.84046 C134.162988,50.84046 132.740753,49.5027282 132.740753,47.5383113 L132.740753,47.5142081 C132.740753,45.6944108 134.030407,44.2000078 135.850387,44.2000078 C137.875265,44.2000078 138.875651,45.8631338 138.875651,47.3334335 C138.875651,47.7431892 138.562277,48.0324285 138.188639,48.0324285 L134.199147,48.0324285 C134.355834,49.0688693 135.091057,49.6473479 136.031179,49.6473479 C136.645874,49.6473479 137.127988,49.4304184 137.537785,49.0929726 C137.646261,49.0086111 137.742683,48.9604045 137.911423,48.9604045 C138.23685,48.9604045 138.48996,49.2134889 138.48996,49.5509348 C138.48996,49.7317093 138.40559,49.8883806 138.297114,49.9968454 Z M142.551769,50.8163567 C143.877582,50.8163567 144.914126,50.1535166 144.914126,48.8157849 L144.914126,48.7916816 C144.914126,47.6467761 143.865529,47.2249688 142.93746,46.9357295 C142.190183,46.6946967 141.503171,46.5018705 141.503171,46.0318567 L141.503171,46.0077534 C141.503171,45.6462043 141.828598,45.3810683 142.395082,45.3810683 C142.841037,45.3810683 143.371362,45.5497912 143.889635,45.8149272 C143.986057,45.8631338 144.058374,45.887237 144.178903,45.887237 C144.516383,45.887237 144.781545,45.6341527 144.781545,45.2967068 C144.781545,45.0436224 144.636911,44.8507962 144.432013,44.7423315 C143.817318,44.4169373 143.1062,44.2241111 142.43124,44.2241111 C141.165692,44.2241111 140.165305,44.959261 140.165305,46.1644247 L140.165305,46.188528 C140.165305,47.4057433 141.213903,47.7913957 142.154025,48.0565317 C142.901301,48.2855129 143.576261,48.4542358 143.576261,48.9483529 L143.576261,48.9724562 C143.576261,49.3942635 143.214675,49.6593995 142.587927,49.6593995 C142.009391,49.6593995 141.37059,49.4545217 140.755895,49.044766 C140.671525,48.9965595 140.563049,48.9604045 140.442521,48.9604045 C140.105041,48.9604045 139.851931,49.2255406 139.851931,49.5509348 C139.851931,49.7919675 139.97246,49.9727421 140.129147,50.0571035 C140.86437,50.5632723 141.744228,50.8163567 142.551769,50.8163567 Z M148.469716,50.8163567 C149.795529,50.8163567 150.832074,50.1535166 150.832074,48.8157849 L150.832074,48.7916816 C150.832074,47.6467761 149.783476,47.2249688 148.855407,46.9357295 C148.108131,46.6946967 147.421118,46.5018705 147.421118,46.0318567 L147.421118,46.0077534 C147.421118,45.6462043 147.746545,45.3810683 148.313029,45.3810683 C148.758984,45.3810683 149.289309,45.5497912 149.807582,45.8149272 C149.904004,45.8631338 149.976322,45.887237 150.09685,45.887237 C150.43433,45.887237 150.699492,45.6341527 150.699492,45.2967068 C150.699492,45.0436224 150.554858,44.8507962 150.34996,44.7423315 C149.735265,44.4169373 149.024147,44.2241111 148.349187,44.2241111 C147.083639,44.2241111 146.083252,44.959261 146.083252,46.1644247 L146.083252,46.188528 C146.083252,47.4057433 147.13185,47.7913957 148.071972,48.0565317 C148.819248,48.2855129 149.494208,48.4542358 149.494208,48.9483529 L149.494208,48.9724562 C149.494208,49.3942635 149.132622,49.6593995 148.505874,49.6593995 C147.927338,49.6593995 147.288537,49.4545217 146.673842,49.044766 C146.589472,48.9965595 146.480996,48.9604045 146.360468,48.9604045 C146.022988,48.9604045 145.769879,49.2255406 145.769879,49.5509348 C145.769879,49.7919675 145.890407,49.9727421 146.047094,50.0571035 C146.782318,50.5632723 147.662175,50.8163567 148.469716,50.8163567 Z M156.11122,50.0209486 L156.11122,43.0068957 C156.11122,42.5850884 156.436647,42.2596942 156.858496,42.2596942 L159.437805,42.2596942 C161.414472,42.2596942 162.643862,43.3804965 162.643862,45.0797773 L162.643862,45.1038806 C162.643862,46.9959877 161.125204,47.9842219 159.269065,47.9842219 L157.59372,47.9842219 L157.59372,50.0209486 C157.59372,50.4307043 157.268293,50.7560985 156.858496,50.7560985 C156.436647,50.7560985 156.11122,50.4307043 156.11122,50.0209486 Z M157.59372,46.6464902 L159.317277,46.6464902 C160.438191,46.6464902 161.137256,46.019805 161.137256,45.1400355 L161.137256,45.1159323 C161.137256,44.127698 160.426139,43.6094776 159.317277,43.6094776 L157.59372,43.6094776 L157.59372,46.6464902 Z M165.030326,47.0682975 L168.296647,47.0682975 C168.200224,46.1282698 167.645793,45.3931199 166.681565,45.3931199 C165.789655,45.3931199 165.162907,46.0800632 165.030326,47.0682975 Z M169.140346,49.9968454 C168.561809,50.5150658 167.838639,50.84046 166.850305,50.84046 C165.00622,50.84046 163.583984,49.5027282 163.583984,47.5383113 L163.583984,47.5142081 C163.583984,45.6944108 164.873639,44.2000078 166.693618,44.2000078 C168.718496,44.2000078 169.718882,45.8631338 169.718882,47.3334335 C169.718882,47.7431892 169.405508,48.0324285 169.03187,48.0324285 L165.042378,48.0324285 C165.199065,49.0688693 165.934289,49.6473479 166.874411,49.6473479 C167.489106,49.6473479 167.97122,49.4304184 168.381017,49.0929726 C168.489492,49.0086111 168.585915,48.9604045 168.754655,48.9604045 C169.080082,48.9604045 169.333191,49.2134889 169.333191,49.5509348 C169.333191,49.7317093 169.248821,49.8883806 169.140346,49.9968454 Z M171.213435,50.0330003 C171.213435,50.4427559 171.538862,50.7560985 171.936606,50.7560985 C172.346403,50.7560985 172.67183,50.4307043 172.67183,50.0330003 L172.67183,48.1529448 C172.67183,46.6464902 173.382947,45.8510821 174.419492,45.6703076 C174.744919,45.6100494 174.998029,45.356965 174.998029,44.9713126 C174.998029,44.5495053 174.732866,44.2482144 174.274858,44.2482144 C173.636057,44.2482144 173.009309,44.8507962 172.67183,45.6462043 L172.67183,44.9954159 C172.67183,44.5856602 172.346403,44.260266 171.936606,44.260266 C171.526809,44.260266 171.213435,44.5856602 171.213435,44.9954159 L171.213435,50.0330003 Z M176.516687,50.0330003 C176.516687,50.4307043 176.842114,50.7560985 177.239858,50.7560985 C177.649655,50.7560985 177.975082,50.4307043 177.975082,50.0330003 L177.975082,45.5859461 L179.083943,45.5859461 C179.421423,45.5859461 179.698638,45.3208101 179.698638,44.9833642 C179.698638,44.6459184 179.433476,44.3807824 179.083943,44.3807824 L177.950976,44.3807824 L177.950976,44.0312849 C177.950976,43.3684448 178.240244,43.0671539 178.794675,43.0671539 C178.891098,43.0671539 179.011626,43.0792056 179.095996,43.0912572 C179.433476,43.1033088 179.710691,42.8261212 179.710691,42.4886753 C179.710691,42.1632811 179.469634,41.9222484 179.204472,41.8860935 C178.999573,41.8499386 178.758517,41.8258353 178.469248,41.8258353 C177.8425,41.8258353 177.372439,41.9945582 177.03496,42.332004 C176.69748,42.6694499 176.516687,43.1997219 176.516687,43.8987169 L176.516687,44.3687307 L176.263578,44.3687307 C175.926098,44.3687307 175.648882,44.6459184 175.648882,44.9833642 C175.648882,45.3208101 175.926098,45.5859461 176.263578,45.5859461 L176.516687,45.5859461 L176.516687,50.0330003 Z M183.627866,50.84046 C181.687358,50.84046 180.253069,49.3581086 180.253069,47.550363 L180.253069,47.5262597 C180.253069,45.7064625 181.699411,44.2000078 183.651972,44.2000078 C185.604533,44.2000078 187.038821,45.6823592 187.038821,47.5021564 L187.038821,47.5262597 C187.038821,49.3340053 185.59248,50.84046 183.627866,50.84046 Z M183.651972,49.5629864 C184.833151,49.5629864 185.580427,48.647062 185.580427,47.550363 L185.580427,47.5262597 C185.580427,46.4054574 184.772886,45.4774814 183.627866,45.4774814 C182.45874,45.4774814 181.711464,46.3934058 181.711464,47.5021564 L181.711464,47.5262597 C181.711464,48.6350103 182.519004,49.5629864 183.651972,49.5629864 Z M188.521321,50.0330003 C188.521321,50.4427559 188.846748,50.7560985 189.244492,50.7560985 C189.654289,50.7560985 189.979716,50.4307043 189.979716,50.0330003 L189.979716,48.1529448 C189.979716,46.6464902 190.690834,45.8510821 191.727378,45.6703076 C192.052805,45.6100494 192.305915,45.356965 192.305915,44.9713126 C192.305915,44.5495053 192.040752,44.2482144 191.582744,44.2482144 C190.943943,44.2482144 190.317195,44.8507962 189.979716,45.6462043 L189.979716,44.9954159 C189.979716,44.5856602 189.654289,44.260266 189.244492,44.260266 C188.834695,44.260266 188.521321,44.5856602 188.521321,44.9954159 L188.521321,50.0330003 Z M193.499147,50.0330003 C193.499147,50.4427559 193.824573,50.7560985 194.222317,50.7560985 C194.632114,50.7560985 194.957541,50.4427559 194.957541,50.0330003 L194.957541,47.1044524 C194.957541,46.1282698 195.511972,45.5256879 196.319512,45.5256879 C197.127053,45.5256879 197.609167,46.0800632 197.609167,47.0682975 L197.609167,50.0330003 C197.609167,50.4427559 197.934594,50.7560985 198.34439,50.7560985 C198.742134,50.7560985 199.067561,50.4427559 199.067561,50.0330003 L199.067561,47.1044524 C199.067561,46.0800632 199.634045,45.5256879 200.429533,45.5256879 C201.249126,45.5256879 201.719187,46.0680116 201.719187,47.0803491 L201.719187,50.0330003 C201.719187,50.4427559 202.044614,50.7560985 202.454411,50.7560985 C202.864207,50.7560985 203.177581,50.4427559 203.177581,50.0330003 L203.177581,46.6344386 C203.177581,45.0797773 202.333882,44.2000078 200.947805,44.2000078 C199.983577,44.2000078 199.308618,44.6459184 198.802399,45.3208101 C198.464919,44.6459184 197.814065,44.2000078 196.898049,44.2000078 C195.921768,44.2000078 195.367338,44.7302799 194.957541,45.2967068 L194.957541,44.9954159 C194.957541,44.5856602 194.632114,44.260266 194.222317,44.260266 C193.812521,44.260266 193.499147,44.5856602 193.499147,44.9954159 L193.499147,50.0330003 Z M206.781382,50.8284083 C205.588151,50.8284083 204.5275,50.141465 204.5275,48.8639914 L204.5275,48.8398882 C204.5275,47.4660015 205.600203,46.7911098 207.15502,46.7911098 C207.866138,46.7911098 208.372358,46.8995746 208.866525,47.0562459 L208.866525,46.8995746 C208.866525,45.9957018 208.312094,45.5136363 207.287602,45.5136363 C206.733171,45.5136363 206.275163,45.6100494 205.877419,45.7667207 C205.793049,45.7908239 205.720732,45.8028756 205.648415,45.8028756 C205.310935,45.8028756 205.03372,45.5377396 205.03372,45.2002937 C205.03372,44.9351577 205.214512,44.7061766 205.431464,44.6218151 C206.034106,44.392834 206.648801,44.2482144 207.480447,44.2482144 C208.432622,44.2482144 209.14374,44.5012988 209.589695,44.959261 C210.059756,45.4172232 210.276707,46.0921149 210.276707,46.9236778 L210.276707,50.0571035 C210.276707,50.4548076 209.963333,50.7560985 209.56559,50.7560985 C209.14374,50.7560985 208.854472,50.4668592 208.854472,50.141465 L208.854472,49.9004323 C208.420569,50.4186527 207.757663,50.8284083 206.781382,50.8284083 Z M207.191179,49.7678642 C208.155407,49.7678642 208.89063,49.2134889 208.89063,48.4060292 L208.89063,47.9721703 C208.516992,47.8275506 208.022825,47.7190859 207.444289,47.7190859 C206.504167,47.7190859 205.949736,48.1167899 205.949736,48.77963 L205.949736,48.8037333 C205.949736,49.4183668 206.492114,49.7678642 207.191179,49.7678642 Z M212.02437,50.0330003 C212.02437,50.4427559 212.349797,50.7560985 212.747541,50.7560985 C213.157337,50.7560985 213.482764,50.4427559 213.482764,50.0330003 L213.482764,47.1044524 C213.482764,46.1162181 214.073354,45.5256879 214.917053,45.5256879 C215.784858,45.5256879 216.279024,46.0921149 216.279024,47.0803491 L216.279024,50.0330003 C216.279024,50.4427559 216.604451,50.7560985 217.014248,50.7560985 C217.424045,50.7560985 217.737419,50.4427559 217.737419,50.0330003 L217.737419,46.6344386 C217.737419,45.1641388 216.905772,44.2000078 215.471484,44.2000078 C214.48315,44.2000078 213.892561,44.7182282 213.482764,45.3087585 L213.482764,44.9954159 C213.482764,44.5856602 213.157337,44.260266 212.747541,44.260266 C212.337744,44.260266 212.02437,44.5856602 212.02437,44.9954159 L212.02437,50.0330003 Z M222.438028,50.84046 C223.498679,50.84046 224.185691,50.4789108 224.752175,49.9365872 C224.884756,49.8281224 224.957073,49.6714511 224.957073,49.4786249 C224.957073,49.1291275 224.679858,48.8398882 224.330325,48.8398882 C224.161585,48.8398882 224.016951,48.9242496 223.932581,48.9965595 C223.534837,49.3340053 223.088882,49.5629864 222.510346,49.5629864 C221.389431,49.5629864 220.605996,48.6591136 220.605996,47.5262597 L220.605996,47.5021564 C220.605996,46.3934058 221.389431,45.4774814 222.450081,45.4774814 C223.028618,45.4774814 223.438415,45.7064625 223.812053,46.019805 C223.908476,46.0921149 224.065163,46.1644247 224.245955,46.1644247 C224.619594,46.1644247 224.920915,45.8751854 224.920915,45.5015846 C224.920915,45.2605519 224.788333,45.0797773 224.691911,44.9954159 C224.149533,44.525402 223.46252,44.2000078 222.462134,44.2000078 C220.545732,44.2000078 219.147602,45.7185141 219.147602,47.5262597 L219.147602,47.550363 C219.147602,49.3581086 220.545732,50.84046 222.438028,50.84046 Z M227.295325,47.0682975 L230.561646,47.0682975 C230.465224,46.1282698 229.910793,45.3931199 228.946565,45.3931199 C228.054654,45.3931199 227.427907,46.0800632 227.295325,47.0682975 Z M231.405346,49.9968454 C230.826809,50.5150658 230.103638,50.84046 229.115305,50.84046 C227.27122,50.84046 225.848984,49.5027282 225.848984,47.5383113 L225.848984,47.5142081 C225.848984,45.6944108 227.138638,44.2000078 228.958618,44.2000078 C230.983496,44.2000078 231.983882,45.8631338 231.983882,47.3334335 C231.983882,47.7431892 231.670508,48.0324285 231.29687,48.0324285 L227.307378,48.0324285 C227.464065,49.0688693 228.199289,49.6473479 229.139411,49.6473479 C229.754106,49.6473479 230.23622,49.4304184 230.646016,49.0929726 C230.754492,49.0086111 230.850915,48.9604045 231.019654,48.9604045 C231.345081,48.9604045 231.598191,49.2134889 231.598191,49.5509348 C231.598191,49.7317093 231.513821,49.8883806 231.405346,49.9968454 Z" id="Superior-WordPress-P" fill="#FFFFFF"></path>
44
+ </g>
45
+ </g>
46
+ </g>
47
+ </svg>
assets/images/logo-wprocket2x.png DELETED
Binary file
assets/images/logo-wprocket@2x.png ADDED
Binary file
assets/images/pic-ericwaltr.jpg CHANGED
Binary file
assets/images/pic-srhdesign.jpg CHANGED
Binary file
assets/images/sidebar-wp-rocket.jpg DELETED
Binary file
assets/images/spinner.gif CHANGED
Binary file
assets/images/stormy.svg CHANGED
@@ -1 +1 @@
1
- <svg viewBox="0 0 39 36" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m1273.12 126.78c-.12 0-.24-.06-.36-.12-.24-.18-.3-.6-.12-.84l5.58-7.26h-3.84c-.48 0-.9-.24-1.08-.66-.18-.42-.18-.9.12-1.26l2.16-2.82c.18-.24.6-.3.84-.12.24.18.3.6.12.84l-2.16 2.82h3.84c.48 0 .9.24 1.08.66.18.42.18.9-.12 1.26l-5.58 7.26c-.12.18-.3.24-.48.24m11.76-8.34c6.12-1.32 10.56-6.96 10.56-13.38 0-7.56-6-13.68-13.32-13.68-6 0-11.52 4.32-13.14 10.26-.06.3.12.66.42.72.3.06.66-.12.72-.42 1.44-5.4 6.48-9.36 11.94-9.36 6.72 0 12.12 5.58 12.12 12.48 0 5.88-4.02 10.98-9.6 12.18-.3.06-.54.42-.48.72.06.3.3.48.6.48h.18m-18.66 0c0-.36-.24-.6-.54-.66-1.8-.12-3.54-.9-4.8-2.22-3-3-2.94-7.98.12-11.04 2.16-2.16 5.4-2.94 8.22-1.92.3.12.66-.06.78-.36.12-.3-.06-.66-.36-.78-3.24-1.08-6.96-.24-9.48 2.28-3.54 3.54-3.6 9.24-.12 12.72 1.5 1.5 3.48 2.4 5.58 2.52h.06c.3.06.54-.18.54-.54" transform="translate(-1257-91)" fill="#d0011b"/></g></svg>
1
+ <svg viewBox="0 0 39 36" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m1273.12 126.78c-.12 0-.24-.06-.36-.12-.24-.18-.3-.6-.12-.84l5.58-7.26h-3.84c-.48 0-.9-.24-1.08-.66-.18-.42-.18-.9.12-1.26l2.16-2.82c.18-.24.6-.3.84-.12.24.18.3.6.12.84l-2.16 2.82h3.84c.48 0 .9.24 1.08.66.18.42.18.9-.12 1.26l-5.58 7.26c-.12.18-.3.24-.48.24m11.76-8.34c6.12-1.32 10.56-6.96 10.56-13.38 0-7.56-6-13.68-13.32-13.68-6 0-11.52 4.32-13.14 10.26-.06.3.12.66.42.72.3.06.66-.12.72-.42 1.44-5.4 6.48-9.36 11.94-9.36 6.72 0 12.12 5.58 12.12 12.48 0 5.88-4.02 10.98-9.6 12.18-.3.06-.54.42-.48.72.06.3.3.48.6.48h.18m-18.66 0c0-.36-.24-.6-.54-.66-1.8-.12-3.54-.9-4.8-2.22-3-3-2.94-7.98.12-11.04 2.16-2.16 5.4-2.94 8.22-1.92.3.12.66-.06.78-.36.12-.3-.06-.66-.36-.78-3.24-1.08-6.96-.24-9.48 2.28-3.54 3.54-3.6 9.24-.12 12.72 1.5 1.5 3.48 2.4 5.58 2.52h.06c.3.06.54-.18.54-.54" transform="translate(-1257-91)" fill="#C51162"/></g></svg>
assets/images/sun.svg CHANGED
@@ -1 +1 @@
1
- <svg viewBox="0 0 37 38" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m1107.26 119.4c0 .555-.449 1-1 1-.554 0-1-.449-1-1 0-.554.45-1 1-1 .555 0 1 .45 1 1m3.716-13.671c-.554 0-1 .449-1 1 0 .555.45 1 1 1 .555 0 1-.449 1-1 0-.555-.449-1-1-1m-4.779-12.667c-.555 0-1 .45-1 1 0 .555.449 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m-12.38-5.067c-.555 0-1 .45-1 1 0 .555.449 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m-17.818 17.949c-.554 0-1 .449-1 1 0 .555.45 1 1 1 .555 0 1-.449 1-1 0-.555-.449-1-1-1m5.354-12.667c-.555 0-1 .45-1 1 0 .555.45 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m.287 24.7c-.554 0-1 .45-1 1 0 .555.45 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m11.949 5.485c-.554 0-1 .45-1 1 0 .555.45 1 1 1 .555 0 1-.449 1-1 0-.554-.449-1-1-1m13.829-16.436c0-7.496-6.098-13.593-13.593-13.593-7.495 0-13.593 6.098-13.593 13.593 0 7.495 6.098 13.593 13.593 13.593 7.495 0 13.593-6.098 13.593-13.593m-1.267 0c0 6.796-5.53 12.326-12.326 12.326-6.797 0-12.327-5.53-12.327-12.326 0-6.796 5.53-12.327 12.327-12.327 6.796 0 12.326 5.53 12.326 12.327" transform="translate(-1075-88)" fill="#f8e81c"/></g></svg>
1
+ <svg viewBox="0 0 37 38" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m1107.26 119.4c0 .555-.449 1-1 1-.554 0-1-.449-1-1 0-.554.45-1 1-1 .555 0 1 .45 1 1m3.716-13.671c-.554 0-1 .449-1 1 0 .555.45 1 1 1 .555 0 1-.449 1-1 0-.555-.449-1-1-1m-4.779-12.667c-.555 0-1 .45-1 1 0 .555.449 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m-12.38-5.067c-.555 0-1 .45-1 1 0 .555.449 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m-17.818 17.949c-.554 0-1 .449-1 1 0 .555.45 1 1 1 .555 0 1-.449 1-1 0-.555-.449-1-1-1m5.354-12.667c-.555 0-1 .45-1 1 0 .555.45 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m.287 24.7c-.554 0-1 .45-1 1 0 .555.45 1 1 1 .554 0 1-.449 1-1 0-.554-.45-1-1-1m11.949 5.485c-.554 0-1 .45-1 1 0 .555.45 1 1 1 .555 0 1-.449 1-1 0-.554-.449-1-1-1m13.829-16.436c0-7.496-6.098-13.593-13.593-13.593-7.495 0-13.593 6.098-13.593 13.593 0 7.495 6.098 13.593 13.593 13.593 7.495 0 13.593-6.098 13.593-13.593m-1.267 0c0 6.796-5.53 12.326-12.326 12.326-6.797 0-12.327-5.53-12.327-12.326 0-6.796 5.53-12.327 12.327-12.327 6.796 0 12.326 5.53 12.326 12.327" transform="translate(-1075-88)" fill="#F5A623"/></g></svg>
assets/images/upload-image.png CHANGED
Binary file
assets/js/admin.js CHANGED
@@ -21,6 +21,40 @@ jQuery.extend( window.imagify, {
21
  } ).focus().removeAttr( 'tabindex' ).addClass( 'modal-is-open' );
22
 
23
  jQuery( 'body' ).addClass( 'imagify-modal-is-open' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
  } );
26
 
21
  } ).focus().removeAttr( 'tabindex' ).addClass( 'modal-is-open' );
22
 
23
  jQuery( 'body' ).addClass( 'imagify-modal-is-open' );
24
+ },
25
+ template: function( id ) {
26
+ if ( undefined === _ ) {
27
+ // No need to load underscore everywhere if we don't use it.
28
+ return '';
29
+ }
30
+
31
+ return _.memoize( function( data ) {
32
+ var compiled,
33
+ options = {
34
+ evaluate: /<#([\s\S]+?)#>/g,
35
+ interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
36
+ escape: /\{\{([^}]+?)\}\}(?!\})/g,
37
+ variable: 'data'
38
+ };
39
+
40
+ return function() {
41
+ compiled = compiled || _.template( jQuery( '#tmpl-' + id ).html(), options );
42
+ data = data || {};
43
+ return compiled( data );
44
+ };
45
+ } );
46
+ },
47
+ humanSize: function( bytes ) {
48
+ var sizes = ['B', 'kB', 'MB'],
49
+ i;
50
+
51
+ if ( 0 === bytes ) {
52
+ return '0\xA0kB';
53
+ }
54
+
55
+ i = parseInt( Math.floor( Math.log( bytes ) / Math.log( 1024 ) ), 10 );
56
+
57
+ return ( bytes / Math.pow( 1024, i ) ).toFixed( 2 ) + '\xA0' + sizes[ i ];
58
  }
59
  } );
60
 
assets/js/admin.min.js CHANGED
@@ -1 +1 @@
1
- window.imagify=window.imagify||{},jQuery.extend(window.imagify,{concat:ajaxurl.indexOf("?")>0?"&":"?",log:function(a){void 0!==console&&console.log(a)},info:function(a){void 0!==console&&console.info(a)},openModal:function(a){var b=a.data("target")||a.attr("href");jQuery(b).css("display","flex").hide().fadeIn(400).attr({"aria-hidden":"false",tabindex:"0"}).focus().removeAttr("tabindex").addClass("modal-is-open"),jQuery("body").addClass("imagify-modal-is-open")}}),function(a,b,c,d){a(".imagify-modal").attr("aria-hidden","true"),a(b).on("click.imagify",".imagify-modal-trigger",function(b){b.preventDefault(),c.imagify.openModal(a(this))}).on("click.imagify",".imagify-modal .close-btn",function(){a(this).closest(".imagify-modal").fadeOut(400).attr("aria-hidden","true").removeClass("modal-is-open").trigger("modalClosed.imagify"),a("body").removeClass("imagify-modal-is-open")}).on("blur.imagify",".imagify-modal .close-btn",function(){var b=a(this).closest(".imagify-modal");"false"===b.attr("aria-hidden")&&b.attr("tabindex","0").focus().removeAttr("tabindex")}).on("click.imagify",".imagify-modal",function(b){a(b.target).filter(".modal-is-open").find(".close-btn").trigger("click.imagify")}).on("keydown.imagify",function(b){27===b.keyCode&&a(".imagify-modal.modal-is-open").length>0&&(b.preventDefault(),a(".imagify-modal.modal-is-open").find(".close-btn").trigger("click.imagify"))})}(jQuery,document,window);
1
+ window.imagify=window.imagify||{},jQuery.extend(window.imagify,{concat:ajaxurl.indexOf("?")>0?"&":"?",log:function(a){void 0!==console&&console.log(a)},info:function(a){void 0!==console&&console.info(a)},openModal:function(a){var b=a.data("target")||a.attr("href");jQuery(b).css("display","flex").hide().fadeIn(400).attr({"aria-hidden":"false",tabindex:"0"}).focus().removeAttr("tabindex").addClass("modal-is-open"),jQuery("body").addClass("imagify-modal-is-open")},template:function(a){return void 0===_?"":_.memoize(function(b){var c,d={evaluate:/<#([\s\S]+?)#>/g,interpolate:/\{\{\{([\s\S]+?)\}\}\}/g,escape:/\{\{([^}]+?)\}\}(?!\})/g,variable:"data"};return function(){return c=c||_.template(jQuery("#tmpl-"+a).html(),d),b=b||{},c(b)}})},humanSize:function(a){var b,c=["B","kB","MB"];return 0===a?"0 kB":(b=parseInt(Math.floor(Math.log(a)/Math.log(1024)),10),(a/Math.pow(1024,b)).toFixed(2)+" "+c[b])}}),function(a,b,c,d){a(".imagify-modal").attr("aria-hidden","true"),a(b).on("click.imagify",".imagify-modal-trigger",function(b){b.preventDefault(),c.imagify.openModal(a(this))}).on("click.imagify",".imagify-modal .close-btn",function(){a(this).closest(".imagify-modal").fadeOut(400).attr("aria-hidden","true").removeClass("modal-is-open").trigger("modalClosed.imagify"),a("body").removeClass("imagify-modal-is-open")}).on("blur.imagify",".imagify-modal .close-btn",function(){var b=a(this).closest(".imagify-modal");"false"===b.attr("aria-hidden")&&b.attr("tabindex","0").focus().removeAttr("tabindex")}).on("click.imagify",".imagify-modal",function(b){a(b.target).filter(".modal-is-open").find(".close-btn").trigger("click.imagify")}).on("keydown.imagify",function(b){27===b.keyCode&&a(".imagify-modal.modal-is-open").length>0&&(b.preventDefault(),a(".imagify-modal.modal-is-open").find(".close-btn").trigger("click.imagify"))})}(jQuery,document,window);
assets/js/bulk.js CHANGED
@@ -1,58 +1,225 @@
1
- (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
2
- var overviewCanvas = d.getElementById( 'imagify-overview-chart' ),
3
- overviewData = {
4
- labels: [
5
- imagifyBulk.labels.overviewChartLabels.unoptimized,
6
- imagifyBulk.labels.overviewChartLabels.optimized,
7
- imagifyBulk.labels.overviewChartLabels.error
8
- ],
9
- datasets: [{
10
- data: [
11
- imagifyBulk.totalUnoptimizedAttachments,
12
- imagifyBulk.totalOptimizedAttachments,
13
- imagifyBulk.totalErrorsAttachments
14
- ],
15
- backgroundColor: [ '#D9E4EB', '#46B1CE', '#2E3242' ],
16
- borderWidth: 0
17
- }]
18
- },
19
- overviewDoughnut, overviewLegend;
20
 
 
 
 
 
 
 
 
21
  /**
22
- * Mini chart.
23
- * You can use drawMeAChart() function with AJAX calls.
24
  *
25
- * @param {element} canvas
 
 
26
  */
27
- function drawMeAChart( canvas ) {
28
- var donuts;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- if ( ! this.donuts ) {
31
- this.donuts = {};
 
 
 
 
32
  }
33
 
34
- donuts = this.donuts;
 
 
 
 
35
 
36
- canvas.each( function() {
37
- var value = parseInt( $( this ).closest( '.imagify-chart' ).next( '.imagipercent' ).text(), 10 );
38
 
39
- if ( undefined !== donuts[ this.id ] ) {
40
- donuts[ this.id ].data.datasets[0].data[0] = value;
41
- donuts[ this.id ].data.datasets[0].data[1] = 100 - value;
42
- donuts[ this.id ].update();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  return;
44
  }
45
 
46
- donuts[ this.id ] = new w.imagify.Chart( this, {
47
- type: 'doughnut',
48
- data: {
49
- datasets: [{
50
- data: [ value, 100 - value ],
51
- backgroundColor: [ '#00B3D3', '#D8D8D8' ],
52
- borderColor: '#fff',
53
- borderWidth: 1
54
- }]
55
- },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  options: {
57
  legend: {
58
  display: false
@@ -62,36 +229,107 @@
62
  easing: 'easeOutBounce'
63
  },
64
  tooltips: {
65
- enabled: false
 
 
 
 
 
66
  },
67
- responsive: false
 
68
  }
69
  } );
70
- } );
71
 
72
- this.donuts = donuts;
73
- }
74
 
75
- /*
76
- * Complete Chart.
77
- * You can use drawMeCompleteChart() function with AJAX calls.
78
- *
79
- * @param {element} canvas
80
- */
81
- function drawMeCompleteChart( canvas ) {
82
- var donut = this.donut;
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
- canvas.each( function() {
85
- var value = parseInt( $( this ).closest( '.imagify-ac-chart' ).attr( 'data-percent' ), 10 );
 
 
 
 
 
86
 
87
- if ( undefined !== donut ) {
88
- donut.data.datasets[0].data[0] = value;
89
- donut.data.datasets[0].data[1] = 100 - value;
90
- donut.update();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  return;
92
  }
93
 
94
- donut = new w.imagify.Chart( this, {
 
95
  type: 'doughnut',
96
  data: {
97
  datasets: [{
@@ -115,336 +353,906 @@
115
  cutoutPercentage: 70
116
  }
117
  } );
118
- } );
119
-
120
- this.donut = donut;
121
- }
122
-
123
- if ( overviewCanvas ) {
124
- overviewDoughnut = new w.imagify.Chart( overviewCanvas, {
125
- type: 'doughnut',
126
- data: overviewData,
127
- options: {
128
- legend: {
129
- display: false/*,
130
- template: '<ul class="imagify-<%=name.toLowerCase()%>-legend"><% for (var i=0; i<segments.length; i++){%><li><span style="background-color:<%=segments[i].fillColor%>"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>',*/
131
- },
132
- events: [],
133
- animation: {
134
- easing: 'easeOutBounce'
135
- },
136
- tooltips: {
137
- displayColors: false,
138
- callbacks: {
139
- label: function( tooltipItem, data ) {
140
- return data.datasets[ tooltipItem.datasetIndex ].data[ tooltipItem.index ];
141
- }
142
- }
143
- },
144
- responsive: false,
145
- cutoutPercentage: 85
 
 
 
 
 
 
 
 
 
 
146
  }
147
- } );
148
 
149
- /**
150
- * Then you just need to generate the legend.
151
- * var overviewLegend = overviewDoughnut.generateLegend();
152
- * bugged `segments undefined`?
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
- // And append it to your page somewhere.
156
- overviewLegend = '<ul class="imagify-doughnut-legend">';
157
 
158
- $.each( overviewData.labels, function( i, label ) {
159
- overviewLegend += '<li><span style="background-color:' + overviewData.datasets[0].backgroundColor[ i ] + '"></span>' + label + '</li>';
160
- } );
161
 
162
- overviewLegend += '</ul>';
 
 
 
 
 
 
 
 
163
 
164
- d.getElementById( 'imagify-overview-chart-legend' ).innerHTML = overviewLegend;
165
- }
 
 
166
 
167
- // Heartbeat.
168
- $( d ).on( 'heartbeat-send', function( e, data ) {
169
- data.imagify_heartbeat = imagifyBulk.heartbeatId;
170
- } );
171
 
172
- // Listen for the custom event "heartbeat-tick" on $(document).
173
- $( d ).on( 'heartbeat-tick', function( e, data ) {
174
- var donutData;
175
 
176
- if ( ! data.imagify_bulk_data ) {
177
- return;
178
- }
 
 
 
 
 
179
 
180
- data = data.imagify_bulk_data;
181
- donutData = overviewDoughnut.data.datasets[0].data;
 
 
 
182
 
183
- if ( data.unoptimized_attachments === donutData[0] && data.optimized_attachments === donutData[1] && data.errors_attachments === donutData[2] ) {
184
- return;
185
- }
186
 
187
- // The overview chart percent.
188
- $( '#imagify-overview-chart-percent' ).html( data.optimized_attachments_percent + '<span>%</span>' );
189
- $( '.imagify-total-percent' ).html( data.optimized_attachments_percent + '%' );
 
 
 
 
190
 
191
- // The comsuption bar.
192
- $( '.imagify-unconsumed-percent' ).html( data.unconsumed_quota + '%' );
193
- $( '.imagify-unconsumed-bar' ).css( 'width', data.unconsumed_quota + '%' );
 
 
 
 
194
 
195
- // The total optimized images.
196
- $( '#imagify-total-optimized-attachments' ).html( data.already_optimized_attachments );
 
 
197
 
198
- // The original bar.
199
- $( '#imagify-original-bar' ).find( '.imagify-barnb' ).html( data.original_human );
 
 
 
200
 
201
- // The optimized bar.
202
- $( '#imagify-optimized-bar' ).css( 'width', ( 100 - data.optimized_percent ) + '%' ).find( '.imagify-barnb' ).html( data.optimized_human );
 
 
 
203
 
204
- // The Percent data.
205
- $( '#imagify-total-optimized-attachments-pct' ).html( data.optimized_percent + '%' );
206
 
207
- overviewDoughnut.data.datasets[0].data[0] = data.unoptimized_attachments;
208
- overviewDoughnut.data.datasets[0].data[1] = data.optimized_attachments;
209
- overviewDoughnut.data.datasets[0].data[2] = data.errors_attachments;
210
- overviewDoughnut.update();
211
- } );
 
 
 
 
 
 
 
 
 
212
 
213
- // Simulate a click on the "Imagif'em all" button
214
- $( '#imagify-simulate-bulk-action' ).on( 'click', function( e ) {
215
- e.preventDefault();
216
- $( '#imagify-bulk-action' ).trigger( 'click' );
217
- } );
218
 
219
- $( '#imagify-bulk-action' ).on( 'click', function( e ) {
220
- var $obj = $( this ),
221
- optimizationLevel = $( '[name="optimization_level"]:checked' ).val(),
222
- confirmMessage;
223
 
224
- e.preventDefault();
 
 
 
 
225
 
226
- if ( undefined === optimizationLevel ) {
227
- optimizationLevel = -1;
228
- }
229
 
230
- if ( $obj.attr( 'disabled' ) ) {
231
- return;
232
- }
233
 
234
- $obj.attr( 'disabled', 'disabled' );
235
- $obj.find( '.dashicons' ).addClass( 'rotate' );
 
236
 
237
- confirmMessage = function() {
238
- return imagifyBulk.labels.processing;
239
- };
240
-
241
- $( w ).on( 'beforeunload', confirmMessage );
242
-
243
- // Display an alert to wait.
244
- swal( {
245
- title: imagifyBulk.labels.waitTitle,
246
- html: imagifyBulk.labels.waitText,
247
- showConfirmButton: false,
248
- imageUrl: imagifyBulk.waitImageUrl,
249
- customClass: 'imagify-sweet-alert'
250
- } );
251
-
252
- $.get( ajaxurl + w.imagify.concat + 'action=' + imagifyBulk.ajaxAction + '&optimization_level=' + optimizationLevel + '&imagifybulkuploadnonce=' + $( '#imagifybulkuploadnonce' ).val() )
253
- .done( function( response ) {
254
- var swal_title = '',
255
- swal_text = '',
256
- Optimizer, table,
257
- files = 0,
258
- errors = 0,
259
- stopOptimization = 0,
260
- original_overall_size = 0,
261
- overall_saving = 0,
262
- incr = 0;
263
-
264
- if ( ! response.success ) {
265
- $obj.removeAttr( 'disabled' );
266
- $obj.find( '.dashicons' ).removeClass( 'rotate' );
267
-
268
- // Remove confirm dialog before quit the page.
269
- $( w ).off( 'beforeunload', confirmMessage );
270
-
271
- if ( 'invalid-api-key' === response.data.message ) {
272
- swal_title = imagifyBulk.labels.invalidAPIKeyTitle;
273
- } else if ( 'over-quota' === response.data.message ) {
274
- swal_title = imagifyBulk.labels.overQuotaTitle;
275
- swal_text = imagifyBulk.labels.overQuotaText;
276
- } else if ( 'no-images' === response.data.message ) {
277
- swal_title = imagifyBulk.labels.noAttachmentToOptimizeTitle;
278
- swal_text = imagifyBulk.labels.noAttachmentToOptimizeText;
279
- }
280
 
281
- // Display an alert to warn that all images has been optimized.
282
- swal( {
283
- title: swal_title,
284
- html: swal_text,
285
- type: 'info',
286
- customClass: 'imagify-sweet-alert'
287
- } );
288
 
289
- return;
290
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
- swal.close();
 
 
 
 
 
 
 
 
 
293
 
294
- $( '.imagify-row-progress' ).slideDown();
295
- $( '.imagify-no-uploaded-yet, .imagify-row-complete' ).hide( 200 );
 
 
 
 
 
 
 
 
 
296
 
297
- table = $( '.imagify-bulk-table table tbody' );
298
- Optimizer = new ImagifyGulp( {
299
- 'buffer_size': imagifyBulk.bufferSize,
300
- 'lib': ajaxurl + w.imagify.concat + 'action=imagify_bulk_upload&imagifybulkuploadnonce=' + $( '#imagifybulkuploadnonce' ).val(),
301
- 'images': response.data,
302
- 'context': imagifyBulk.ajaxContext
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  } );
 
 
304
 
305
- // Before the attachment optimization.
306
- Optimizer.before( function( data ) {
307
- table.find( '.imagify-row-progress' ).after( '<tr id="attachment-' + data.id + '"><td class="imagify-cell-filename"><span class="imagiuploaded"><img src="' + data.thumbnail + '" alt=""/></span><span class="imagifilename">' + data.filename + '</span></td><td class="imagify-cell-status"><span class="imagistatus status-compressing"><span class="dashicons dashicons-admin-generic rotate"></span>' + imagifyBulk.labels.optimizing + '<span></span></span></td><td class="imagify-cell-original"></td><td class="imagify-cell-optimized"></td><td class="imagify-cell-percentage"></td><td class="imagify-cell-thumbnails"></td><td class="imagify-cell-savings"></td></tr>' );
308
- } )
309
- // After the attachment optimization.
310
- .each( function( data ) {
311
- var $progress = $( '#imagify-progress-bar' ),
312
- errorClass = 'error',
313
- errorDashicon = 'dismiss',
314
- errorMessage = imagifyBulk.labels.error,
315
- $attachment = $( '#attachment-' + data.image );
316
-
317
- $progress.css( { 'width': data.progress + '%' } );
318
- $progress.find( '.percent' ).html( data.progress + '%' );
319
-
320
- if ( data.success ) {
321
- ++incr;
322
- $attachment.find( '.imagify-cell-status' ).html( '<span class="imagistatus status-complete"><span class="dashicons dashicons-yes"></span>' + imagifyBulk.labels.complete + '</span>' );
323
- $attachment.find( '.imagify-cell-original' ).html( data.original_size_human );
324
- $attachment.find( '.imagify-cell-optimized' ).html( data.new_size_human );
325
- $attachment.find( '.imagify-cell-percentage' ).html( '<span class="imagify-chart"><span class="imagify-chart-container"><canvas height="18" width="18" id="imagify-consumption-chart-' + data.image + '-' + incr + '"></canvas></span></span><span class="imagipercent">' + data.percent + '</span>%' );
326
- drawMeAChart( $attachment.find( '.imagify-cell-percentage canvas' ) );
327
- $attachment.find( '.imagify-cell-thumbnails' ).html( data.thumbnails );
328
- $attachment.find( '.imagify-cell-savings' ).html( Optimizer.humanSize( data.overall_saving, 1 ) );
329
-
330
- // The table footer total optimized files.
331
- files = files + data.thumbnails + 1;
332
- $( '.imagify-cell-nb-files' ).html( imagifyBulk.labels.nbrFiles.replace( '%s', files ) );
333
-
334
- // The table footer original size.
335
- original_overall_size = original_overall_size + data.original_overall_size;
336
- $( '.imagify-total-original' ).html( Optimizer.humanSize( original_overall_size, 1 ) );
337
-
338
- // The table footer overall saving.
339
- overall_saving = overall_saving + data.overall_saving;
340
- $( '.imagify-total-gain' ).html( Optimizer.humanSize( overall_saving, 1 ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  return;
343
  }
344
 
345
- if ( ! stopOptimization && data.error.indexOf( "You've consumed all your data" ) >= 0 ) {
346
- stopOptimization = 1;
347
- Optimizer.stopProcess();
348
-
349
- // Display an alert to warn that all data is consumed.
350
- swal( {
351
- title: imagifyBulk.labels.overQuotaTitle,
352
- html: imagifyBulk.labels.overQuotaText,
353
- type: 'error',
354
- customClass: 'imagify-sweet-alert',
355
- } ).then( function() {
356
- location.reload();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  } );
358
  }
 
 
 
 
359
 
360
- if ( data.error.indexOf( 'This image is already compressed' ) >= 0 ) {
361
- errorClass = 'warning';
362
- errorDashicon = 'warning';
363
- errorMessage = imagifyBulk.labels.notice;
364
  } else {
365
- errors++;
366
- $( '.imagify-cell-errors' ).html( imagifyBulk.labels.nbrErrors.replace( '%s', errors ) );
367
  }
 
 
368
 
369
- $attachment.after( '<tr><td colspan="7"><span class="status-' + errorClass + '">' + data.error + '</span></td></tr>' );
370
-
371
- $attachment.find( '.imagify-cell-status' ).html( '<span class="imagistatus status-' + errorClass + '"><span class="dashicons dashicons-' + errorDashicon + '"></span>' + errorMessage + '</span>' );
372
- } )
373
- // After all attachments optimization.
374
- .done( function( data ) {
375
- var text2share;
376
 
377
- $obj.removeAttr( 'disabled' ).find( '.dashicons' ).removeClass( 'rotate' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
 
379
- // Remove confirm dialog before quit the page.
380
- $( w ).off( 'beforeunload', confirmMessage );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
- // Hide the progress bar.
383
- $( '.imagify-row-progress' ).slideUp();
384
 
385
- if ( 'NaN' !== data.global_percent ) {
386
- // Display the complete section.
387
- $( '.imagify-row-complete' ).removeClass( 'hidden' ).addClass( 'done' ).attr( 'aria-hidden', 'false' );
388
- $( 'html, body' ).animate( {
389
- scrollTop: $( '.imagify-row-complete' ).offset().top
390
- }, 200 );
391
 
392
- $( '.imagify-ac-rt-total-gain' ).html( data.global_gain_human );
393
- $( '.imagify-ac-rt-total-original' ).html( data.global_original_size_human );
394
 
395
- text2share = imagifyBulk.labels.textToShare;
396
- text2share = text2share.replace( '%1$s', data.global_gain_human );
397
- text2share = text2share.replace( '%2$s', data.global_original_size_human );
398
- text2share = encodeURIComponent( text2share );
399
 
400
- $( '.imagify-sn-twitter' ).attr( 'href', imagifyBulk.labels.twitterShareURL + '&amp;text=' + text2share );
 
 
 
 
 
 
401
 
402
- $( '.imagify-ac-chart' ).attr( 'data-percent', data.global_percent );
403
- drawMeCompleteChart( $( '.imagify-ac-chart' ).find( 'canvas' ) );
404
- }
 
 
 
 
 
405
 
406
- stopOptimization = 0;
407
- } )
408
- .error( function( id ) {
409
- w.imagify.log( "Can't optimize image with id " + id + "." );
410
- } )
411
- .run();
412
- } )
413
- .fail( function() {
414
- // Display an error alert.
415
- swal( {
416
- title: imagifyBulk.labels.getUnoptimizedImagesErrorTitle,
417
- html: imagifyBulk.labels.getUnoptimizedImagesErrorText,
418
- type: 'error',
419
- customClass: 'imagify-sweet-alert'
420
- } ).then( function() {
421
- location.reload();
422
  } );
423
- } );
424
- } );
425
 
426
- } )(jQuery, document, window);
 
 
 
 
 
 
 
 
 
 
 
427
 
 
 
 
428
 
429
- (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
 
 
 
430
 
431
- var width = 700,
432
- height = 290,
433
- clientLeft, clientTop;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
 
435
- if ( w.innerWidth ) {
436
- clientLeft = ( w.innerWidth - width ) / 2;
437
- clientTop = ( w.innerHeight - height ) / 2;
438
- } else {
439
- clientLeft = ( d.body.clientWidth - width ) / 2;
440
- clientTop = ( d.body.clientHeight - height ) / 2;
441
- }
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
- [].forEach.call( d.querySelectorAll( '.imagify-share-networks a' ), function( el ) {
444
- el.addEventListener( 'click', function( evt ) {
445
  w.open( this.href, '', 'status=no, scrollbars=no, menubar=no, top=' + clientTop + ', left=' + clientLeft + ', width=' + width + ', height=' + height );
446
- evt.preventDefault();
447
- }, false );
448
- } );
 
449
 
450
  } )(jQuery, document, window);
1
+ (function( $, undefined ) { // eslint-disable-line no-shadow, no-shadow-restricted-names
2
+
3
+ var jqPropHookChecked = $.propHooks.checked;
4
+
5
+ // Force `.prop()` to trigger a `change` event.
6
+ $.propHooks.checked = {
7
+ set: function( elem, value, name ) {
8
+ var ret;
9
+
10
+ if ( undefined === jqPropHookChecked ) {
11
+ ret = ( elem[ name ] = value );
12
+ } else {
13
+ ret = jqPropHookChecked( elem, value, name );
14
+ }
 
 
 
 
 
15
 
16
+ $( elem ).trigger( 'change.imagify' );
17
+
18
+ return ret;
19
+ }
20
+ };
21
+
22
+ // Custom jQuery functions =====================================================================
23
  /**
24
+ * Hide element(s).
 
25
  *
26
+ * @param {int} duration A duration in ms.
27
+ * @param {function} callback A callback to execute once the element is hidden.
28
+ * @return {element} The jQuery element(s).
29
  */
30
+ $.fn.imagifyHide = function( duration, callback ) {
31
+ if ( duration && duration > 0 ) {
32
+ this.hide( duration, function() {
33
+ $( this ).addClass( 'hidden' ).css( 'display', '' );
34
+
35
+ if ( undefined !== callback ) {
36
+ callback();
37
+ }
38
+ } );
39
+ } else {
40
+ this.addClass( 'hidden' );
41
+
42
+ if ( undefined !== callback ) {
43
+ callback();
44
+ }
45
+ }
46
+
47
+ return this.attr( 'aria-hidden', 'true' );
48
+ };
49
+
50
+ /**
51
+ * Show element(s).
52
+ *
53
+ * @param {int} duration A duration in ms.
54
+ * @param {function} callback A callback to execute before starting to display the element.
55
+ * @return {element} The jQuery element(s).
56
+ */
57
+ $.fn.imagifyShow = function( duration, callback ) {
58
+ if ( undefined !== callback ) {
59
+ callback();
60
+ }
61
 
62
+ if ( duration && duration > 0 ) {
63
+ this.show( duration, function() {
64
+ $( this ).removeClass( 'hidden' ).css( 'display', '' );
65
+ } );
66
+ } else {
67
+ this.removeClass( 'hidden' );
68
  }
69
 
70
+ return this.attr( 'aria-hidden', 'false' );
71
+ };
72
+
73
+ }( jQuery ));
74
+
75
 
76
+ (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
 
77
 
78
+ w.imagify.bulk = {
79
+
80
+ // Properties ==============================================================================
81
+ charts: {
82
+ overview: {
83
+ canvas: false,
84
+ donut: false,
85
+ data: {
86
+ // Order: unoptimized, optimized, error.
87
+ labels: [
88
+ imagifyBulk.labels.overviewChartLabels.unoptimized,
89
+ imagifyBulk.labels.overviewChartLabels.optimized,
90
+ imagifyBulk.labels.overviewChartLabels.error
91
+ ],
92
+ datasets: [ {
93
+ data: [],
94
+ backgroundColor: [ '#10121A', '#46B1CE', '#C51162' ],
95
+ borderWidth: 0
96
+ } ]
97
+ }
98
+ },
99
+ files: {
100
+ donuts: {}
101
+ },
102
+ share: {
103
+ canvas: false,
104
+ donut: false
105
+ }
106
+ },
107
+ // Folder types in queue.
108
+ queue: [],
109
+ // Status of each folder type.
110
+ status: {},
111
+ // Tell if the message displayed when retrieving the image IDs has been shown once.
112
+ displayedWaitMessage: false,
113
+ // Tell how many rows are available.
114
+ hasMultipleRows: true,
115
+ // Set to true to stop the whole thing.
116
+ processIsStopped: false,
117
+ // Global stats.
118
+ globalGain: 0,
119
+ globalOriginalSize: 0,
120
+ globalOptimizedSize: 0,
121
+ // Heartbeat.
122
+ folderTypes: [],
123
+
124
+ // Methods =================================================================================
125
+
126
+ /*
127
+ * Init.
128
+ */
129
+ init: function () {
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.
141
+ $( '.imagify-level-selector-button' )
142
+ .on( 'click.imagify', this.openLevelSelectorFromButton );
143
+
144
+ $( '.imagify-level-selector-list input' )
145
+ .on( 'change.imagify init.imagify', this.syncLevelSelectorFromRadio )
146
+ .filter( ':checked' )
147
+ .trigger( 'init.imagify' );
148
+
149
+ $( d )
150
+ .on( 'keypress.imagify click.imagify', this.closeLevelSelectors );
151
+
152
+ // Other buttons/UI.
153
+ $( '.imagify-bulk-table [name="group[]"]' ).on( 'change.imagify init.imagify', this.toggleOptimizationButton ).trigger( 'init.imagify' );
154
+ $( '.imagify-show-table-details' ).on( 'click.imagify open.imagify close.imagify', this.toggleOptimizationDetails );
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
+ /**
175
+ * Overview chart.
176
+ * Used for the big overview chart.
177
+ */
178
+ drawOverviewChart: function ( data ) {
179
+ var initData, legend;
180
+
181
+ if ( ! this.charts.overview.canvas ) {
182
+ this.charts.overview.canvas = d.getElementById( 'imagify-overview-chart' );
183
+
184
+ if ( ! this.charts.overview.canvas ) {
185
+ return;
186
+ }
187
+ }
188
+
189
+ data = data && $.isArray( data ) ? data : [];
190
+
191
+ if ( this.charts.overview.donut ) {
192
+ // Update existing donut.
193
+ if ( data.length ) {
194
+ if ( data.reduce( function( a, b ) { return a + b; }, 0 ) === 0 ) {
195
+ data[0] = 1;
196
+ }
197
+
198
+ this.charts.overview.donut.data.datasets[0].data = data;
199
+ this.charts.overview.donut.update();
200
+ }
201
+ return;
202
+ }
203
+
204
+ // Create new donut.
205
+ this.charts.overview.data.datasets[0].data = [
206
+ parseInt( this.charts.overview.canvas.getAttribute( 'data-unoptimized' ), 10 ),
207
+ parseInt( this.charts.overview.canvas.getAttribute( 'data-optimized' ), 10 ),
208
+ parseInt( this.charts.overview.canvas.getAttribute( 'data-errors' ), 10 )
209
+ ];
210
+ initData = $.extend( {}, this.charts.overview.data );
211
+
212
+ if ( data.length ) {
213
+ initData.datasets[0].data = data;
214
+ }
215
+
216
+ if ( initData.datasets[0].data.reduce( function( a, b ) { return a + b; }, 0 ) === 0 ) {
217
+ initData.datasets[0].data[0] = 1;
218
+ }
219
+
220
+ this.charts.overview.donut = new w.imagify.Chart( this.charts.overview.canvas, {
221
+ type: 'doughnut',
222
+ data: initData,
223
  options: {
224
  legend: {
225
  display: false
229
  easing: 'easeOutBounce'
230
  },
231
  tooltips: {
232
+ displayColors: false,
233
+ callbacks: {
234
+ label: function( tooltipItem, localData ) {
235
+ return localData.datasets[ tooltipItem.datasetIndex ].data[ tooltipItem.index ];
236
+ }
237
+ }
238
  },
239
+ responsive: false,
240
+ cutoutPercentage: 85
241
  }
242
  } );
 
243
 
244
+ // Then generate the legend and insert it to your page somewhere.
245
+ legend = '<ul class="imagify-doughnut-legend">';
246
 
247
+ $.each( initData.labels, function( i, label ) {
248
+ legend += '<li><span style="background-color:' + initData.datasets[0].backgroundColor[ i ] + '"></span>' + label + '</li>';
249
+ } );
250
+
251
+ legend += '</ul>';
252
+
253
+ d.getElementById( 'imagify-overview-chart-legend' ).innerHTML = legend;
254
+ },
255
+
256
+ /**
257
+ * Mini chart.
258
+ * Used for the charts on each file row.
259
+ *
260
+ * @param {element} canvas A jQuery canvas element.
261
+ */
262
+ drawFileChart: function ( canvas ) {
263
+ var donuts = this.charts.files.donuts;
264
+
265
+ canvas.each( function() {
266
+ var value = parseInt( $( this ).closest( '.imagify-chart' ).next( '.imagipercent' ).text().replace( '%', '' ), 10 );
267
 
268
+ if ( undefined !== donuts[ this.id ] ) {
269
+ // Update existing donut.
270
+ donuts[ this.id ].data.datasets[0].data[0] = value;
271
+ donuts[ this.id ].data.datasets[0].data[1] = 100 - value;
272
+ donuts[ this.id ].update();
273
+ return;
274
+ }
275
 
276
+ // Create new donut.
277
+ donuts[ this.id ] = new w.imagify.Chart( this, {
278
+ type: 'doughnut',
279
+ data: {
280
+ datasets: [{
281
+ data: [ value, 100 - value ],
282
+ backgroundColor: [ '#00B3D3', '#D8D8D8' ],
283
+ borderColor: '#fff',
284
+ borderWidth: 1
285
+ }]
286
+ },
287
+ options: {
288
+ legend: {
289
+ display: false
290
+ },
291
+ events: [],
292
+ animation: {
293
+ easing: 'easeOutBounce'
294
+ },
295
+ tooltips: {
296
+ enabled: false
297
+ },
298
+ responsive: false
299
+ }
300
+ } );
301
+ } );
302
+
303
+ this.charts.files.donuts = donuts;
304
+ },
305
+
306
+ /*
307
+ * Share Chart.
308
+ * Used for the chart in the share box.
309
+ */
310
+ drawShareChart: function () {
311
+ var value;
312
+
313
+ if ( ! this.charts.share.canvas ) {
314
+ this.charts.share.canvas = d.getElementById( 'imagify-ac-chart' );
315
+
316
+ if ( ! this.charts.share.canvas ) {
317
+ return;
318
+ }
319
+ }
320
+
321
+ value = parseInt( $( this.charts.share.canvas ).closest( '.imagify-ac-chart' ).attr( 'data-percent' ), 10 );
322
+
323
+ if ( this.charts.share.donut ) {
324
+ // Update existing donut.
325
+ this.charts.share.donut.data.datasets[0].data[0] = value;
326
+ this.charts.share.donut.data.datasets[0].data[1] = 100 - value;
327
+ this.charts.share.donut.update();
328
  return;
329
  }
330
 
331
+ // Create new donut.
332
+ this.charts.share.donut = new w.imagify.Chart( this.charts.share.canvas, {
333
  type: 'doughnut',
334
  data: {
335
  datasets: [{
353
  cutoutPercentage: 70
354
  }
355
  } );
356
+ },
357
+
358
+ /*
359
+ * Get the URL used for ajax requests.
360
+ *
361
+ * @param {string} action An ajax action, or part of it.
362
+ * @param {object} item The current item.
363
+ * @return {string}
364
+ */
365
+ getAjaxUrl: function ( action, item ) {
366
+ var camelGroupID = item.groupId.replace( /[\s_-](\S)/g, function( c, l ) {
367
+ return l.toUpperCase();
368
+ } );
369
+
370
+ action = action.replace( '%GROUP_ID%', camelGroupID );
371
+ action = imagifyBulk.ajaxActions[ action ];
372
+
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
+ *
379
+ * @return {string}
380
+ */
381
+ getConfirmMessage: function () {
382
+ return imagifyBulk.labels.processing;
383
+ },
384
+
385
+ /*
386
+ * Close the given optimization level selector.
387
+ *
388
+ * @param {object} $lists A jQuery object.
389
+ * @param {int} timer Timer in ms to close the selector.
390
+ */
391
+ closeLevelSelector: function ( $lists, timer ) {
392
+ if ( ! $lists || ! $lists.length ) {
393
+ return;
394
  }
 
395
 
396
+ if ( undefined !== timer && timer > 0 ) {
397
+ w.setTimeout( function() {
398
+ w.imagify.bulk.closeLevelSelector( $lists );
399
+ }, timer );
400
+ return;
401
+ }
402
+
403
+ $lists.attr( 'aria-hidden', 'true' );
404
+ },
405
+
406
+ /*
407
+ * Stop everything and update the current item status as an error.
408
+ *
409
+ * @param {string} errorId An error ID.
410
+ * @param {object} item The current item.
411
+ */
412
+ stopProcess: function ( errorId, item ) {
413
+ this.processIsStopped = true;
414
+
415
+ w.imagify.bulk.status[ item.groupId ] = {
416
+ isError: true,
417
+ id: errorId
418
+ };
419
+
420
+ $( w ).trigger( 'queueEmpty.imagify' );
421
+ },
422
+
423
+ /*
424
+ * Display an error message in a modal.
425
+ *
426
+ * @param {string} title The modal title.
427
+ * @param {string} text The modal text.
428
+ * @param {object} args Other less common args.
429
  */
430
+ displayError: function ( title, text, args ) {
431
+ var def = {
432
+ title: '',
433
+ html: '',
434
+ type: 'error',
435
+ customClass: '',
436
+ width: 620,
437
+ padding: 0,
438
+ showCloseButton: true,
439
+ showConfirmButton: true
440
+ };
441
+
442
+ if ( $.isPlainObject( title ) ) {
443
+ args = $.extend( {}, def, title );
444
+ } else {
445
+ args = args || {};
446
+ args = $.extend( {}, def, {
447
+ title: title || '',
448
+ html: text || ''
449
+ }, args );
450
+ }
451
 
452
+ args.title = args.title || imagifyBulk.labels.error;
453
+ args.customClass += ' imagify-sweet-alert';
454
 
455
+ swal( args ).catch( swal.noop );
456
+ },
 
457
 
458
+ /*
459
+ * Display an error message in a file row.
460
+ *
461
+ * @param {function} $row The row template.
462
+ * @param {string} text The error text.
463
+ * @return {element} The row jQuery element.
464
+ */
465
+ displayErrorInRow: function ( $row, text ) {
466
+ var $toReplace, colspan;
467
 
468
+ $row = $( $row() );
469
+ $toReplace = $row.find( '.imagify-cell-status ~ td' );
470
+ colspan = $toReplace.length;
471
+ text = text || '';
472
 
473
+ $toReplace.remove();
474
+ $row.find( '.imagify-cell-status' ).after( '<td colspan="' + colspan + '">' + text + '</td>' );
 
 
475
 
476
+ return $row;
477
+ },
 
478
 
479
+ /*
480
+ * Display one of the 3 "folder" rows.
481
+ *
482
+ * @param {string} state One of the 3 states: 'resting' (it's the "normal" row), 'waiting' (waiting for other optimizations to finish), and 'working'.
483
+ * @param {element} $row jQuery element of the "normal" row.
484
+ */
485
+ displayFolderRow: function ( state, $row ) {
486
+ var $newRow, spinnerTemplate, spinnerColor, text;
487
 
488
+ if ( 'resting' === state ) {
489
+ $row.next( '.imagify-row-waiting, .imagify-row-working' ).remove();
490
+ $row.imagifyShow();
491
+ return;
492
+ }
493
 
494
+ // This part won't work to display multiple $newRow.
495
+ $newRow = $row.next( '.imagify-row-waiting, .imagify-row-working' );
 
496
 
497
+ if ( 'waiting' === state ) {
498
+ spinnerColor = '#d2d3d6';
499
+ text = imagifyBulk.labels.waitingOtimizationsText;
500
+ } else {
501
+ spinnerColor = '#40b1d0';
502
+ text = imagifyBulk.labels.imagesOptimizedText.replace( '%s', '<span>0</span>' );
503
+ }
504
 
505
+ if ( $newRow.length ) {
506
+ if ( ! $newRow.hasClass( 'imagify-row-' + state ) ) {
507
+ // Should happen when switching from 'waiting' to 'working'.
508
+ $newRow.attr( 'class', 'imagify-row-' + state );
509
+ $newRow.find( '.imagify-cell-checkbox svg' ).attr( 'fill', spinnerColor );
510
+ $newRow.children( '.imagify-cell-images-optimized' ).html( text );
511
+ }
512
 
513
+ $row.imagifyHide();
514
+ $newRow.imagifyShow();
515
+ return;
516
+ }
517
 
518
+ // Build the new row, based on a clone of the original one.
519
+ $newRow = $row.clone().attr( {
520
+ 'class': 'imagify-row-' + state,
521
+ 'aria-hidden': 'false'
522
+ } );
523
 
524
+ spinnerTemplate = w.imagify.template( 'imagify-spinner' );
525
+ $newRow.children( '.imagify-cell-checkbox' ).html( spinnerTemplate() ).find( 'svg' ).attr( 'fill', spinnerColor );
526
+ $newRow.children( '.imagify-cell-title' ).html( '<span class="imagify-cell-label">' + $newRow.children( '.imagify-cell-title' ).text() + '</span>' );
527
+ $newRow.children( '.imagify-cell-images-optimized' ).html( text );
528
+ $newRow.children( '.imagify-cell-errors, .imagify-cell-optimized, .imagify-cell-original, .imagify-cell-level' ).text( '' );
529
 
530
+ $row.imagifyHide().after( $newRow );
531
+ },
532
 
533
+ /*
534
+ * Display the share box.
535
+ */
536
+ displayShareBox: function () {
537
+ var text2share = imagifyBulk.labels.textToShare,
538
+ percent, gainHuman, originalSizeHuman,
539
+ $complete;
540
+
541
+ if ( ! this.globalGain || this.queue.length ) {
542
+ this.globalGain = 0;
543
+ this.globalOriginalSize = 0;
544
+ this.globalOptimizedSize = 0;
545
+ return;
546
+ }
547
 
548
+ percent = ( 100 - 100 * ( this.globalOptimizedSize / this.globalOriginalSize ) ).toFixed( 2 );
549
+ gainHuman = w.imagify.humanSize( this.globalGain, 1 );
550
+ originalSizeHuman = w.imagify.humanSize( this.globalOriginalSize, 1 );
 
 
551
 
552
+ text2share = text2share.replace( '%1$s', gainHuman );
553
+ text2share = text2share.replace( '%2$s', originalSizeHuman );
554
+ text2share = encodeURIComponent( text2share );
 
555
 
556
+ $complete = $( '.imagify-row-complete' );
557
+ $complete.find( '.imagify-ac-rt-total-gain' ).html( gainHuman );
558
+ $complete.find( '.imagify-ac-rt-total-original' ).html( originalSizeHuman );
559
+ $complete.find( '.imagify-ac-chart' ).attr( 'data-percent', percent );
560
+ $complete.find( '.imagify-sn-twitter' ).attr( 'href', imagifyBulk.labels.twitterShareURL + '&amp;text=' + text2share );
561
 
562
+ // Chart.
563
+ this.drawShareChart();
 
564
 
565
+ $complete.addClass( 'done' ).imagifyShow();
 
 
566
 
567
+ $( 'html, body' ).animate( {
568
+ scrollTop: $complete.offset().top
569
+ }, 200 );
570
 
571
+ // Reset the stats.
572
+ this.globalGain = 0;
573
+ this.globalOriginalSize = 0;
574
+ this.globalOptimizedSize = 0;
575
+ },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
 
577
+ // Event callbacks =========================================================================
 
 
 
 
 
 
578
 
579
+ /*
580
+ * Optimization level selector: on button click, open the dropdown and focus the current radio input.
581
+ * The dropdown must be open or the focus event won't be triggered.
582
+ *
583
+ * @param {object} e jQuery's Event object.
584
+ */
585
+ openLevelSelectorFromButton: function ( e ) {
586
+ var $list = $( '#' + $( this ).attr( 'aria-controls' ) );
587
+ // Stop click event from bubbling: this will allow to close the selector list if anything else id clicked.
588
+ e.stopPropagation();
589
+ // Close other lists.
590
+ $( '.imagify-level-selector-list' ).not( $list ).attr( 'aria-hidden', 'true' );
591
+ // Open the corresponding list and focus the radio.
592
+ $list.attr( 'aria-hidden', 'false' ).find( ':checked' ).trigger( 'focus.imagify' );
593
+ },
594
 
595
+ /*
596
+ * Optimization level selector: on radio change, make the row "current" and update the button text.
597
+ */
598
+ syncLevelSelectorFromRadio: function () {
599
+ var $row = $( this ).closest( '.imagify-level-choice' );
600
+ // Update rows attributes.
601
+ $row.addClass( 'imagify-current-level' ).attr( 'aria-current', 'true' ).siblings( '.imagify-level-choice' ).removeClass( 'imagify-current-level' ).attr( 'aria-current', 'false' );
602
+ // Change the button text.
603
+ $row.closest( '.imagify-level-selector' ).find( '.imagify-current-level-info' ).html( $row.find( 'label' ).html() );
604
+ },
605
 
606
+ /*
607
+ * Optimization level selector: on Escape or Enter kaystroke, close the dropdown.
608
+ *
609
+ * @param {object} e jQuery's Event object.
610
+ */
611
+ closeLevelSelectors: function ( e ) {
612
+ if ( 'keypress' === e.type && 27 !== e.keyCode && 13 !== e.keyCode ) {
613
+ return;
614
+ }
615
+ w.imagify.bulk.closeLevelSelector( $( '.imagify-level-selector-list[aria-hidden="false"]' ) );
616
+ },
617
 
618
+ /*
619
+ * Enable or disable the Optimization button depending on the checked checkboxes.
620
+ * Also, if there is only 1 checkbox in the page, don't allow it to be unchecked.
621
+ */
622
+ toggleOptimizationButton: function () {
623
+ // Prevent uncheck if there is only one checkbox.
624
+ if ( ! w.imagify.bulk.hasMultipleRows && ! this.checked ) {
625
+ $( this ).prop( 'checked', true );
626
+ return;
627
+ }
628
+
629
+ // Enable or disable the Optimization button.
630
+ if ( $( '.imagify-bulk-table [name="group[]"]:checked' ).length ) {
631
+ $( '#imagify-bulk-action' ).removeAttr( 'disabled' );
632
+ } else {
633
+ $( '#imagify-bulk-action' ).attr( 'disabled', 'disabled' );
634
+ }
635
+ },
636
+
637
+ /*
638
+ * Display/Hide optimization details.
639
+ *
640
+ * @param {object} e jQuery's Event object.
641
+ */
642
+ toggleOptimizationDetails: function ( e ) {
643
+ var $button = $( this ),
644
+ $details = $button.closest( '.imagify-bulk-table' ).find( '.imagify-bulk-table-details' ),
645
+ openDetails;
646
+
647
+ if ( 'open' === e.type ) {
648
+ openDetails = true;
649
+ } else if ( 'close' === e.type ) {
650
+ openDetails = false;
651
+ } else {
652
+ openDetails = $details.hasClass( 'hidden' );
653
+ }
654
+
655
+ if ( openDetails ) {
656
+ $button.html( $button.data( 'label-hide' ) + '<span class="dashicons dashicons-no-alt"></span>' );
657
+ $details.imagifyShow();
658
+ } else {
659
+ $button.html( $button.data( 'label-show' ) + '<span class="dashicons dashicons-menu"></span>' );
660
+ $details.imagifyHide();
661
+ }
662
+ },
663
+
664
+ /*
665
+ * Maybe display a modal, then launch all processes.
666
+ */
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
+
683
+ if ( ! $( '.imagify-bulk-table [name="group[]"]:checked' ).length ) {
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
+
703
+ $infosModal = $( '#tmpl-imagify-bulk-infos' );
704
+
705
+ if ( $infosModal.length ) {
706
+ // Swal Information before loading the optimize process.
707
+ swal( {
708
+ title: imagifyBulk.labels.bulkInfoTitle,
709
+ html: $infosModal.html(),
710
+ type: '',
711
+ customClass: 'imagify-sweet-alert imagify-swal-has-subtitle imagify-before-bulk-infos',
712
+ showCancelButton: true,
713
+ padding: 0,
714
+ width: 554,
715
+ confirmButtonText: imagifyBulk.labels.confirmBulk,
716
+ cancelButtonText: imagifySwal.labels.cancelButtonText,
717
+ reverseButtons: true
718
+ } ).then( function() {
719
+ var $row = $( '.imagify-bulk-table [name="group[]"]:checked' ).first().closest( '.imagify-row-folder-type' ),
720
+ groupId = $row.data( 'group-id' ),
721
+ context = $row.data( 'context' );
722
+
723
+ $.get( ajaxurl + w.imagify.concat + '_wpnonce=' + imagifyBulk.ajaxNonce + '&action=' + imagifyBulk.ajaxActions.bulkInfoSeen + '&folder_type=' + groupId + '&context=' + context );
724
+ $infosModal.remove();
725
+
726
+ w.imagify.bulk.launchAllProcesses();
727
+ } ).catch( swal.noop );
728
+ } else {
729
+ w.imagify.bulk.launchAllProcesses();
730
+ }
731
+ },
732
+
733
+ /*
734
+ * Build the queue and launch all processes.
735
+ */
736
+ launchAllProcesses: function () {
737
+ var $w = $( w ),
738
+ $button = $( '#imagify-bulk-action' ),
739
+ skip = true;
740
+
741
+ // Disable the button.
742
+ $button.attr( 'disabled', 'disabled' ).find( '.dashicons' ).addClass( 'rotate' );
743
+
744
+ // Add a message to be displayed when the user wants to quit the page.
745
+ $w.on( 'beforeunload', this.getConfirmMessage );
746
+
747
+ // Hide the "Complete" message.
748
+ $( '.imagify-row-complete' ).imagifyHide( 200, function() {
749
+ $( this ).removeClass( 'done' );
750
+ } );
751
 
752
+ // Close the optimization details.
753
+ $( '.imagify-show-table-details' ).trigger( 'close.imagify' );
754
+
755
+ // Make sure to reset properties.
756
+ this.queue = [];
757
+ this.status = {};
758
+ this.displayedWaitMessage = false;
759
+ this.processIsStopped = false;
760
+ this.globalGain = 0;
761
+ this.globalOriginalSize = 0;
762
+ this.globalOptimizedSize = 0;
763
+
764
+ $( '.imagify-bulk-table [name="group[]"]:checked' ).each( function() {
765
+ var $checkbox = $( this ),
766
+ $row = $checkbox.closest( '.imagify-row-folder-type' ),
767
+ groupId = $row.data( 'group-id' ),
768
+ context = $row.data( 'context' ),
769
+ level = $row.find( '.imagify-cell-level [name="level[' + groupId + ']"]:checked' ).val();
770
+
771
+ // Build the queue.
772
+ w.imagify.bulk.queue.push( {
773
+ groupId: groupId,
774
+ context: context,
775
+ level: undefined === level ? -1 : parseInt( level, 10 )
776
+ } );
777
+
778
+ // Set the status.
779
+ w.imagify.bulk.status[ groupId ] = {
780
+ isError: false,
781
+ id: 'waiting'
782
+ };
783
+
784
+ // Display a "waiting" message + spinner into the folder rows.
785
+ if ( skip ) {
786
+ // No need to do that for the first one, we'll display a "working" row instead.
787
+ skip = false;
788
+ return true;
789
+ }
790
+
791
+ // Display the "waiting" folder row and hide the "normal" one.
792
+ w.imagify.bulk.displayFolderRow( 'waiting', $row );
793
+ } );
794
+
795
+ // Process the queue.
796
+ $w.trigger( 'processQueue.imagify' );
797
+ },
798
+
799
+ /*
800
+ * Process the first item in the queue.
801
+ */
802
+ processQueue: function () {
803
+ var $row, item;
804
+
805
+ if ( w.imagify.bulk.processIsStopped ) {
806
+ return;
807
+ }
808
+
809
+ if ( ! w.imagify.bulk.queue.length ) {
810
+ $( w ).trigger( 'queueEmpty.imagify' );
811
+ return;
812
+ }
813
+
814
+ if ( ! w.imagify.bulk.displayedWaitMessage ) {
815
+ // Display an alert to wait.
816
+ swal( {
817
+ title: imagifyBulk.labels.waitTitle,
818
+ html: imagifyBulk.labels.waitText,
819
+ showConfirmButton: false,
820
+ padding: 0,
821
+ imageUrl: imagifyBulk.waitImageUrl,
822
+ customClass: 'imagify-sweet-alert'
823
+ } ).catch( swal.noop );
824
+ w.imagify.bulk.displayedWaitMessage = true;
825
+ }
826
+
827
+ /**
828
+ * Fetch files for the first folder type in the queue.
829
+ */
830
+ item = w.imagify.bulk.queue.shift();
831
+ $row = $( '#cb-select-' + item.groupId ).closest( '.imagify-row-folder-type' );
832
+
833
+ // Update status.
834
+ w.imagify.bulk.status[ item.groupId ].id = 'fetching';
835
+
836
+ // Display the "working" folder row and hide the "normal" one.
837
+ w.imagify.bulk.displayFolderRow( 'working', $row );
838
+
839
+ // Fetch image IDs.
840
+ $.get( w.imagify.bulk.getAjaxUrl( '%GROUP_ID%Fetch', item ) )
841
+ .done( function( response ) {
842
+ if ( w.imagify.bulk.processIsStopped ) {
843
+ return;
844
+ }
845
+
846
+ swal.close();
847
+
848
+ // Success.
849
+ if ( response.success && ( $.isArray( response.data ) || $.isPlainObject( response.data ) ) ) { // Array if empty, object otherwize.
850
+ if ( ! $.isEmptyObject( response.data ) ) {
851
+ // Optimize the files.
852
+ $( w ).trigger( 'optimizeFiles.imagify', [ item, response.data ] );
853
  return;
854
  }
855
 
856
+ // No images.
857
+ w.imagify.bulk.status[ item.groupId ].id = 'no-images';
858
+
859
+ if ( ! w.imagify.bulk.processIsStopped ) {
860
+ if ( w.imagify.bulk.hasMultipleRows ) {
861
+ $( '#cb-select-' + item.groupId ).prop( 'checked', false );
862
+ }
863
+
864
+ if ( ! w.imagify.bulk.queue.length ) {
865
+ $( w ).trigger( 'queueEmpty.imagify' );
866
+ return;
867
+ }
868
+
869
+ // Reset the folder row.
870
+ w.imagify.bulk.displayFolderRow( 'resting', $row );
871
+
872
+ $( w ).trigger( 'processQueue.imagify' );
873
+ }
874
+ return;
875
+ }
876
+
877
+ // Error.
878
+ w.imagify.bulk.stopProcess( response.data.message, item );
879
+ } )
880
+ .fail( function() {
881
+ // Error.
882
+ w.imagify.bulk.stopProcess( 'get-unoptimized-images', item );
883
+ } );
884
+ },
885
+
886
+ /*
887
+ * Optimize files.
888
+ *
889
+ * @param {object} e jQuery's Event object.
890
+ * @param {object} item Current item (from the queue).
891
+ * @param {object} files A list of file IDs (key) and URLs (values).
892
+ */
893
+ optimizeFiles: function ( e, item, files ) {
894
+ var $row = $( '#cb-select-' + item.groupId ).closest( '.imagify-row-folder-type' ),
895
+ $workingRow = $row.next( '.imagify-row-working' ),
896
+ $optimizedCount = $workingRow.find( '.imagify-cell-images-optimized span' ),
897
+ optimizedCount = parseInt( $optimizedCount.text(), 10 ),
898
+ $errorsCount = $workingRow.find( '.imagify-cell-errors span' ),
899
+ errorsCount = parseInt( $errorsCount.text(), 10 ),
900
+ $table = $row.closest( '.imagify-bulk-table' ),
901
+ $progressBar = $table.find( '.imagify-row-progress' ),
902
+ $progress = $progressBar.find( '.bar' ),
903
+ defaultsTemplate = {
904
+ groupId: item.groupId,
905
+ id: 0,
906
+ thumbnail: '', // Image src.
907
+ filename: '',
908
+ status: '',
909
+ icon: '',
910
+ label: '',
911
+ thumbnails: '',
912
+ original_size_human: '',
913
+ new_size_human: '',
914
+ chartSuffix: '',
915
+ percent_human: '',
916
+ overall_saving_human: ''
917
+ },
918
+ Optimizer, $resultsContainer;
919
+
920
+ if ( w.imagify.bulk.processIsStopped ) {
921
+ return;
922
+ }
923
+
924
+ // Update folder status.
925
+ w.imagify.bulk.status[ item.groupId ].id = 'optimizing';
926
+
927
+ // Fill in the result table header.
928
+ $table.find( '.imagify-bulk-table-details thead' ).html( $( '#tmpl-imagify-file-header-' + item.groupId ).html() );
929
+
930
+ // Empty the result table body.
931
+ $resultsContainer = $table.find( '.imagify-bulk-table-details tbody' ).text( '' );
932
+
933
+ // Reset and display the progress bar.
934
+ $progress.css( 'width', '0%' ).find( '.percent' ).text( '0%' );
935
+ $progressBar.slideDown().attr( 'aria-hidden', 'false' );
936
+
937
+ // Optimize the files.
938
+ Optimizer = new ImagifyGulp( {
939
+ 'buffer_size': imagifyBulk.bufferSizes[ item.context ] || 1,
940
+ 'lib': w.imagify.bulk.getAjaxUrl( '%GROUP_ID%Optimize', item ),
941
+ 'images': files,
942
+ 'context': item.context
943
+ } );
944
+
945
+ // Before the attachment optimization, add a file row displaying the optimization process.
946
+ Optimizer.before( function( data ) {
947
+ var template = w.imagify.template( 'imagify-file-row-' + item.groupId );
948
+
949
+ $resultsContainer.prepend( template( $.extend( {}, defaultsTemplate, {
950
+ status: 'compressing',
951
+ icon: 'admin-generic rotate',
952
+ label: imagifyBulk.labels.optimizing,
953
+ chartSuffix: data.image_id
954
+ }, data ) ) );
955
+ } );
956
+
957
+ // After the attachment optimization.
958
+ Optimizer.each( function( data ) {
959
+ var template = w.imagify.template( 'imagify-file-row-' + item.groupId ),
960
+ $fileRow = $( '#' + item.groupId + '-' + data.image );
961
+
962
+ // Update the progress bar.
963
+ $progress.css( 'width', data.progress + '%' ).find( '.percent' ).html( data.progress + '%' );
964
+
965
+ if ( data.success ) {
966
+ // Image successfully optimized.
967
+ $fileRow.replaceWith( template( $.extend( {}, defaultsTemplate, {
968
+ status: 'complete',
969
+ icon: 'yes',
970
+ label: imagifyBulk.labels.complete,
971
+ chartSuffix: data.image
972
+ }, data ) ) );
973
+
974
+ w.imagify.bulk.drawFileChart( $( '#' + item.groupId + '-' + data.image ).find( '.imagify-cell-percentage canvas' ) ); // Don't use $fileRow, its DOM is not refreshed with the new values.
975
+
976
+ // Update the optimized images counter.
977
+ optimizedCount += 1;
978
+ $optimizedCount.text( optimizedCount );
979
+ return;
980
+ }
981
+
982
+ if ( 'already-optimized' === data.error_code ) {
983
+ // The image was already optimized.
984
+ $fileRow.replaceWith( w.imagify.bulk.displayErrorInRow( template( $.extend( {}, defaultsTemplate, {
985
+ status: 'complete',
986
+ icon: 'yes',
987
+ label: imagifyBulk.labels.alreadyOptimized,
988
+ chartSuffix: data.image
989
+ }, data ) ), data.error ) );
990
+
991
+ // Update the optimized images counter.
992
+ optimizedCount += 1;
993
+ $optimizedCount.text( optimizedCount );
994
+ return;
995
+ }
996
+
997
+ // Display the error in the file row.
998
+ $fileRow.replaceWith( w.imagify.bulk.displayErrorInRow( template( $.extend( {}, defaultsTemplate, {
999
+ status: 'error',
1000
+ icon: 'dismiss',
1001
+ label: imagifyBulk.labels.error,
1002
+ chartSuffix: data.image
1003
+ }, data ) ), data.error || data ) );
1004
+
1005
+ // Update the "working" folder row.
1006
+ if ( ! $errorsCount.length ) {
1007
+ errorsCount = 1;
1008
+ $errorsCount = $workingRow.find( '.imagify-cell-errors' ).html( imagifyBulk.labels.imagesErrorText.replace( '%s', '<span>1</span>' ) ).find( 'span' );
1009
+ } else {
1010
+ errorsCount += 1;
1011
+ $errorsCount.text( errorsCount );
1012
+ }
1013
+
1014
+ if ( 'over-quota' === data.error_code ) {
1015
+ // No more data, stop everything.
1016
+ Optimizer.stopProcess();
1017
+ w.imagify.bulk.stopProcess( data.error_code, item );
1018
+ }
1019
+ } );
1020
+
1021
+ // After all image optimizations.
1022
+ Optimizer.done( function( data ) {
1023
+ // Uncheck the checkbox.
1024
+ if ( w.imagify.bulk.hasMultipleRows ) {
1025
+ $( '#cb-select-' + item.groupId ).prop( 'checked', false );
1026
+ }
1027
+
1028
+ if ( data.global_original_size ) {
1029
+ w.imagify.bulk.globalGain += parseInt( data.global_gain, 10 );
1030
+ w.imagify.bulk.globalOriginalSize += parseInt( data.global_original_size, 10 );
1031
+ w.imagify.bulk.globalOptimizedSize += parseInt( data.global_optimized_size, 10 );
1032
+ }
1033
+
1034
+ if ( w.imagify.bulk.processIsStopped ) {
1035
+ return;
1036
+ }
1037
+
1038
+ // Update folder type status.
1039
+ if ( ! w.imagify.bulk.status[ item.groupId ].isError ) {
1040
+ w.imagify.bulk.status[ item.groupId ].id = 'done';
1041
+ }
1042
+
1043
+ // Update the folder row.
1044
+ $row.addClass( 'updating' );
1045
+
1046
+ $.get( w.imagify.bulk.getAjaxUrl( 'getFolderData', item ) )
1047
+ .done( function( response ) {
1048
+ if ( response.success ) {
1049
+ $.each( response.data, function( dataName, dataHtml ) {
1050
+ $row.children( '.imagify-cell-' + dataName ).html( dataHtml );
1051
  } );
1052
  }
1053
+ w.imagify.bulk.displayFolderRow( 'resting', $row );
1054
+ } )
1055
+ .always( function() {
1056
+ $row.removeClass( 'updating' );
1057
 
1058
+ if ( ! w.imagify.bulk.queue.length ) {
1059
+ $( w ).trigger( 'queueEmpty.imagify' );
 
 
1060
  } else {
1061
+ $( w ).trigger( 'processQueue.imagify' );
 
1062
  }
1063
+ } );
1064
+ } );
1065
 
1066
+ // Run.
1067
+ Optimizer.run();
1068
+ },
 
 
 
 
1069
 
1070
+ /*
1071
+ * End.
1072
+ */
1073
+ queueEmpty: function () {
1074
+ var $tables = $( '.imagify-bulk-table' ),
1075
+ errorArgs = {},
1076
+ hasError = false,
1077
+ noImages = true;
1078
+
1079
+ // Display the share box.
1080
+ w.imagify.bulk.displayShareBox();
1081
+
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 ) {
1088
+ if ( typeStatus.isError ) {
1089
+ // One error is enough to display a message.
1090
+ hasError = typeStatus.id;
1091
+ noImages = false;
1092
+ return false;
1093
+ }
1094
+ if ( 'no-images' !== typeStatus.id ) {
1095
+ // All groups must have this ID.
1096
+ noImages = false;
1097
+ return false;
1098
+ }
1099
+ } );
1100
 
1101
+ if ( hasError ) {
1102
+ if ( 'invalid-api-key' === hasError ) {
1103
+ errorArgs = {
1104
+ title: imagifyBulk.labels.invalidAPIKeyTitle,
1105
+ type: 'info'
1106
+ };
1107
+ }
1108
+ else if ( 'over-quota' === hasError ) {
1109
+ errorArgs = {
1110
+ title: imagifyBulk.labels.overQuotaTitle,
1111
+ html: $( '#tmpl-imagify-overquota-alert' ).html(),
1112
+ type: 'info',
1113
+ customClass: 'imagify-swal-has-subtitle imagify-swal-error-header',
1114
+ showConfirmButton: false
1115
+ };
1116
+ }
1117
+ else if ( 'get-unoptimized-images' === hasError || 'consumed-all-data' === hasError ) {
1118
+ errorArgs = {
1119
+ title: imagifyBulk.labels.getUnoptimizedImagesErrorTitle,
1120
+ html: imagifyBulk.labels.getUnoptimizedImagesErrorText,
1121
+ type: 'info'
1122
+ };
1123
+ }
1124
+ w.imagify.bulk.displayError( errorArgs );
1125
+ }
1126
+ else if ( noImages ) {
1127
+ w.imagify.bulk.displayError( {
1128
+ title: imagifyBulk.labels.noAttachmentToOptimizeTitle,
1129
+ html: imagifyBulk.labels.noAttachmentToOptimizeText,
1130
+ type: 'info'
1131
+ } );
1132
+ }
1133
+ }
1134
 
1135
+ // Reset status.
1136
+ w.imagify.bulk.status = {};
1137
 
1138
+ // Unlink the message displayed when the user wants to quit the page.
1139
+ $( w ).off( 'beforeunload', w.imagify.bulk.getConfirmMessage );
 
 
 
 
1140
 
1141
+ // Display the "normal" folder rows (the values of the last one should being updated via ajax, don't display it for now).
1142
+ w.imagify.bulk.displayFolderRow( 'resting', $tables.find( '.imagify-row-folder-type' ).not( '.updating' ) );
1143
 
1144
+ // Reset the progress bars.
1145
+ $tables.find( '.imagify-row-progress' ).slideUp().attr( 'aria-hidden', 'true' ).find( '.bar' ).removeAttr( 'style' ).find( '.percent' ).text( '0%' );
 
 
1146
 
1147
+ // Enable (or not) the main button.
1148
+ if ( $( '.imagify-bulk-table [name="group[]"]:checked' ).length ) {
1149
+ $( '#imagify-bulk-action' ).removeAttr( 'disabled' ).find( '.dashicons' ).removeClass( 'rotate' );
1150
+ } else {
1151
+ $( '#imagify-bulk-action' ).find( '.dashicons' ).removeClass( 'rotate' );
1152
+ }
1153
+ },
1154
 
1155
+ /**
1156
+ * Add our Heartbeat ID on "heartbeat-send" event.
1157
+ *
1158
+ * @param {object} e Event object.
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
+ /**
1174
+ * Listen for the custom event "heartbeat-tick" on $(document).
1175
+ * It allows to update various data periodically.
1176
+ *
1177
+ * @param {object} e Event object.
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
+ /**
1233
+ * Open a popup window when the user clicks on a share link.
1234
+ *
1235
+ * @param {object} e jQuery Event object.
1236
+ */
1237
+ share: function ( e ) {
1238
+ var width = 700,
1239
+ height = 290,
1240
+ clientLeft, clientTop;
1241
+
1242
+ e.preventDefault();
1243
+
1244
+ if ( w.innerWidth ) {
1245
+ clientLeft = ( w.innerWidth - width ) / 2;
1246
+ clientTop = ( w.innerHeight - height ) / 2;
1247
+ } else {
1248
+ clientLeft = ( d.body.clientWidth - width ) / 2;
1249
+ clientTop = ( d.body.clientHeight - height ) / 2;
1250
+ }
1251
 
 
 
1252
  w.open( this.href, '', 'status=no, scrollbars=no, menubar=no, top=' + clientTop + ', left=' + clientLeft + ', width=' + width + ', height=' + height );
1253
+ }
1254
+ };
1255
+
1256
+ w.imagify.bulk.init();
1257
 
1258
  } )(jQuery, document, window);
assets/js/bulk.min.js CHANGED
@@ -1 +1 @@
1
- !function(a,b,c,d){function e(b){var e;this.donuts||(this.donuts={}),e=this.donuts,b.each(function(){var b=parseInt(a(this).closest(".imagify-chart").next(".imagipercent").text(),10);if(d!==e[this.id])return e[this.id].data.datasets[0].data[0]=b,e[this.id].data.datasets[0].data[1]=100-b,void e[this.id].update();e[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.donuts=e}function f(b){var e=this.donut;b.each(function(){var b=parseInt(a(this).closest(".imagify-ac-chart").attr("data-percent"),10);if(d!==e)return e.data.datasets[0].data[0]=b,e.data.datasets[0].data[1]=100-b,void e.update();e=new c.imagify.Chart(this,{type:"doughnut",data:{datasets:[{data:[b,100-b],backgroundColor:["#40B1D0","#FFFFFF"],borderWidth:0}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1,cutoutPercentage:70}})}),this.donut=e}var g,h,i=b.getElementById("imagify-overview-chart"),j={labels:[imagifyBulk.labels.overviewChartLabels.unoptimized,imagifyBulk.labels.overviewChartLabels.optimized,imagifyBulk.labels.overviewChartLabels.error],datasets:[{data:[imagifyBulk.totalUnoptimizedAttachments,imagifyBulk.totalOptimizedAttachments,imagifyBulk.totalErrorsAttachments],backgroundColor:["#D9E4EB","#46B1CE","#2E3242"],borderWidth:0}]};i&&(g=new c.imagify.Chart(i,{type:"doughnut",data:j,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}}),h='<ul class="imagify-doughnut-legend">',a.each(j.labels,function(a,b){h+='<li><span style="background-color:'+j.datasets[0].backgroundColor[a]+'"></span>'+b+"</li>"}),h+="</ul>",b.getElementById("imagify-overview-chart-legend").innerHTML=h),a(b).on("heartbeat-send",function(a,b){b.imagify_heartbeat=imagifyBulk.heartbeatId}),a(b).on("heartbeat-tick",function(b,c){var d;c.imagify_bulk_data&&(c=c.imagify_bulk_data,d=g.data.datasets[0].data,c.unoptimized_attachments===d[0]&&c.optimized_attachments===d[1]&&c.errors_attachments===d[2]||(a("#imagify-overview-chart-percent").html(c.optimized_attachments_percent+"<span>%</span>"),a(".imagify-total-percent").html(c.optimized_attachments_percent+"%"),a(".imagify-unconsumed-percent").html(c.unconsumed_quota+"%"),a(".imagify-unconsumed-bar").css("width",c.unconsumed_quota+"%"),a("#imagify-total-optimized-attachments").html(c.already_optimized_attachments),a("#imagify-original-bar").find(".imagify-barnb").html(c.original_human),a("#imagify-optimized-bar").css("width",100-c.optimized_percent+"%").find(".imagify-barnb").html(c.optimized_human),a("#imagify-total-optimized-attachments-pct").html(c.optimized_percent+"%"),g.data.datasets[0].data[0]=c.unoptimized_attachments,g.data.datasets[0].data[1]=c.optimized_attachments,g.data.datasets[0].data[2]=c.errors_attachments,g.update()))}),a("#imagify-simulate-bulk-action").on("click",function(b){b.preventDefault(),a("#imagify-bulk-action").trigger("click")}),a("#imagify-bulk-action").on("click",function(b){var g,h=a(this),i=a('[name="optimization_level"]:checked').val();b.preventDefault(),d===i&&(i=-1),h.attr("disabled")||(h.attr("disabled","disabled"),h.find(".dashicons").addClass("rotate"),g=function(){return imagifyBulk.labels.processing},a(c).on("beforeunload",g),swal({title:imagifyBulk.labels.waitTitle,html:imagifyBulk.labels.waitText,showConfirmButton:!1,imageUrl:imagifyBulk.waitImageUrl,customClass:"imagify-sweet-alert"}),a.get(ajaxurl+c.imagify.concat+"action="+imagifyBulk.ajaxAction+"&optimization_level="+i+"&imagifybulkuploadnonce="+a("#imagifybulkuploadnonce").val()).done(function(b){var d,i,j="",k="",l=0,m=0,n=0,o=0,p=0,q=0;if(!b.success)return h.removeAttr("disabled"),h.find(".dashicons").removeClass("rotate"),a(c).off("beforeunload",g),"invalid-api-key"===b.data.message?j=imagifyBulk.labels.invalidAPIKeyTitle:"over-quota"===b.data.message?(j=imagifyBulk.labels.overQuotaTitle,k=imagifyBulk.labels.overQuotaText):"no-images"===b.data.message&&(j=imagifyBulk.labels.noAttachmentToOptimizeTitle,k=imagifyBulk.labels.noAttachmentToOptimizeText),void swal({title:j,html:k,type:"info",customClass:"imagify-sweet-alert"});swal.close(),a(".imagify-row-progress").slideDown(),a(".imagify-no-uploaded-yet, .imagify-row-complete").hide(200),i=a(".imagify-bulk-table table tbody"),d=new ImagifyGulp({buffer_size:imagifyBulk.bufferSize,lib:ajaxurl+c.imagify.concat+"action=imagify_bulk_upload&imagifybulkuploadnonce="+a("#imagifybulkuploadnonce").val(),images:b.data,context:imagifyBulk.ajaxContext}),d.before(function(a){i.find(".imagify-row-progress").after('<tr id="attachment-'+a.id+'"><td class="imagify-cell-filename"><span class="imagiuploaded"><img src="'+a.thumbnail+'" alt=""/></span><span class="imagifilename">'+a.filename+'</span></td><td class="imagify-cell-status"><span class="imagistatus status-compressing"><span class="dashicons dashicons-admin-generic rotate"></span>'+imagifyBulk.labels.optimizing+'<span></span></span></td><td class="imagify-cell-original"></td><td class="imagify-cell-optimized"></td><td class="imagify-cell-percentage"></td><td class="imagify-cell-thumbnails"></td><td class="imagify-cell-savings"></td></tr>')}).each(function(b){var c=a("#imagify-progress-bar"),f="error",g="dismiss",h=imagifyBulk.labels.error,i=a("#attachment-"+b.image);if(c.css({width:b.progress+"%"}),c.find(".percent").html(b.progress+"%"),b.success)return++q,i.find(".imagify-cell-status").html('<span class="imagistatus status-complete"><span class="dashicons dashicons-yes"></span>'+imagifyBulk.labels.complete+"</span>"),i.find(".imagify-cell-original").html(b.original_size_human),i.find(".imagify-cell-optimized").html(b.new_size_human),i.find(".imagify-cell-percentage").html('<span class="imagify-chart"><span class="imagify-chart-container"><canvas height="18" width="18" id="imagify-consumption-chart-'+b.image+"-"+q+'"></canvas></span></span><span class="imagipercent">'+b.percent+"</span>%"),e(i.find(".imagify-cell-percentage canvas")),i.find(".imagify-cell-thumbnails").html(b.thumbnails),i.find(".imagify-cell-savings").html(d.humanSize(b.overall_saving,1)),l=l+b.thumbnails+1,a(".imagify-cell-nb-files").html(imagifyBulk.labels.nbrFiles.replace("%s",l)),o+=b.original_overall_size,a(".imagify-total-original").html(d.humanSize(o,1)),p+=b.overall_saving,void a(".imagify-total-gain").html(d.humanSize(p,1));!n&&b.error.indexOf("You've consumed all your data")>=0&&(n=1,d.stopProcess(),swal({title:imagifyBulk.labels.overQuotaTitle,html:imagifyBulk.labels.overQuotaText,type:"error",customClass:"imagify-sweet-alert"}).then(function(){location.reload()})),b.error.indexOf("This image is already compressed")>=0?(f="warning",g="warning",h=imagifyBulk.labels.notice):(m++,a(".imagify-cell-errors").html(imagifyBulk.labels.nbrErrors.replace("%s",m))),i.after('<tr><td colspan="7"><span class="status-'+f+'">'+b.error+"</span></td></tr>"),i.find(".imagify-cell-status").html('<span class="imagistatus status-'+f+'"><span class="dashicons dashicons-'+g+'"></span>'+h+"</span>")}).done(function(b){var d;h.removeAttr("disabled").find(".dashicons").removeClass("rotate"),a(c).off("beforeunload",g),a(".imagify-row-progress").slideUp(),"NaN"!==b.global_percent&&(a(".imagify-row-complete").removeClass("hidden").addClass("done").attr("aria-hidden","false"),a("html, body").animate({scrollTop:a(".imagify-row-complete").offset().top},200),a(".imagify-ac-rt-total-gain").html(b.global_gain_human),a(".imagify-ac-rt-total-original").html(b.global_original_size_human),d=imagifyBulk.labels.textToShare,d=d.replace("%1$s",b.global_gain_human),d=d.replace("%2$s",b.global_original_size_human),d=encodeURIComponent(d),a(".imagify-sn-twitter").attr("href",imagifyBulk.labels.twitterShareURL+"&amp;text="+d),a(".imagify-ac-chart").attr("data-percent",b.global_percent),f(a(".imagify-ac-chart").find("canvas"))),n=0}).error(function(a){c.imagify.log("Can't optimize image with id "+a+".")}).run()}).fail(function(){swal({title:imagifyBulk.labels.getUnoptimizedImagesErrorTitle,html:imagifyBulk.labels.getUnoptimizedImagesErrorText,type:"error",customClass:"imagify-sweet-alert"}).then(function(){location.reload()})}))})}(jQuery,document,window),function(a,b,c,d){var e,f;c.innerWidth?(e=(c.innerWidth-700)/2,f=(c.innerHeight-290)/2):(e=(b.body.clientWidth-700)/2,f=(b.body.clientHeight-290)/2),[].forEach.call(b.querySelectorAll(".imagify-share-networks a"),function(a){a.addEventListener("click",function(a){c.open(this.href,"","status=no, scrollbars=no, menubar=no, top="+f+", left="+e+", width=700, height=290"),a.preventDefault()},!1)})}(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(){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);
assets/js/files-list.js ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Mini chart.
3
+ *
4
+ * @param {element} canvas The canvas element.
5
+ */
6
+ window.imagify.drawMeAChart = function( canvas ) {
7
+ canvas.each( function() {
8
+ var value = parseInt( jQuery( this ).closest( '.imagify-chart' ).next( '.imagify-chart-value' ).text() );
9
+
10
+ new imagify.Chart( this, { // eslint-disable-line no-new
11
+ type: 'doughnut',
12
+ data: {
13
+ datasets: [{
14
+ data: [ value, 100 - value ],
15
+ backgroundColor: [ '#00B3D3', '#D8D8D8' ],
16
+ borderColor: '#fff',
17
+ borderWidth: 1
18
+ }]
19
+ },
20
+ options: {
21
+ legend: {
22
+ display: false
23
+ },
24
+ events: [],
25
+ animation: {
26
+ easing: 'easeOutBounce'
27
+ },
28
+ tooltips: {
29
+ enabled: false
30
+ },
31
+ responsive: false
32
+ }
33
+ } );
34
+ } );
35
+ };
36
+
37
+
38
+ (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
39
+
40
+ /**
41
+ * Update the charts.
42
+ */
43
+ $( w ).on( 'canvasprinted.imagify', function( e, selector ) {
44
+ var $canvas;
45
+
46
+ selector = selector || '.imagify-consumption-chart';
47
+ $canvas = $( selector );
48
+
49
+ w.imagify.drawMeAChart( $canvas );
50
+ } )
51
+ .trigger( 'canvasprinted.imagify' );
52
+
53
+ } )(jQuery, document, window);
54
+
55
+
56
+ (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
57
+
58
+ /**
59
+ * Handle bulk actions.
60
+ */
61
+ var bulkActions = '<option value="imagify-bulk-optimize">' + imagifyFiles.labels.bulkActionsOptimize + '</option>';
62
+
63
+ if ( imagifyFiles.backupOption || $( '.file-has-backup' ).length ) {
64
+ // If the backup option is enabled, or if we have items that can be restored.
65
+ bulkActions += '<option value="imagify-bulk-restore">' + imagifyFiles.labels.bulkActionsRestore + '</option>';
66
+ }
67
+
68
+ $( '.bulkactions select[name="action"] option:first-child, .bulkactions select[name="action2"] option:first-child' ).after( bulkActions );
69
+
70
+ /**
71
+ * Process one of these actions: bulk restore, bulk optimize, or bulk refresh-status.
72
+ */
73
+ $( '#doaction, #doaction2' )
74
+ .on( 'click.imagify', function( e ) {
75
+ var value = $( this ).prev( 'select' ).val(),
76
+ action, ids;
77
+
78
+ if ( 'imagify-bulk-optimize' !== value && 'imagify-bulk-restore' !== value && 'imagify-bulk-refresh-status' !== value ) {
79
+ return;
80
+ }
81
+
82
+ e.preventDefault();
83
+
84
+ action = value.replace( 'bulk-', '' );
85
+ ids = $( 'input[name="bulk_select[]"]:checked' ).map( function() {
86
+ return this.value;
87
+ } ).get();
88
+
89
+ ids.forEach( function( id, index ) {
90
+ var $button = $( '#' + action + '-' + id );
91
+
92
+ if ( ! $button.length ) {
93
+ $button.closest( 'tr' ).find( '#cb-select-' + id ).prop( 'checked', false );
94
+ return;
95
+ }
96
+
97
+ setTimeout( function() {
98
+ $button.trigger( 'click.imagify' );
99
+ }, index * 500 );
100
+ } );
101
+ } );
102
+
103
+ /**
104
+ * Process one of these actions: optimize, re-optimize, restore, or refresh-status.
105
+ */
106
+ $( d ).on( 'click.imagify', '.button-imagify-optimize, .button-imagify-reoptimize, .button-imagify-restore, .button-imagify-refresh-status', function( e ) {
107
+ var $button = $( this ),
108
+ $row = $button.closest( 'tr' ),
109
+ $parent, href;
110
+
111
+ e.preventDefault();
112
+
113
+ if ( $row.hasClass( 'working' ) ) {
114
+ return;
115
+ }
116
+
117
+ $row.addClass( 'working' );
118
+ $parent = $button.closest( '.column-actions, .column-status' );
119
+ href = $button.attr( 'href' );
120
+
121
+ $parent.html( '<div class="button"><span class="imagify-spinner"></span>' + $button.data( 'waiting-label' ) + '</div>' );
122
+
123
+ $.get( href.replace( 'admin-post.php', 'admin-ajax.php' ) )
124
+ .done( function( r ) {
125
+ if ( ! r.success ) {
126
+ if ( r.data.row ) {
127
+ $row.html( '<td class="colspanchange" colspan="' + $row.children().length + '">' + r.data.row + '</td>' );
128
+ } else {
129
+ $parent.html( r.data );
130
+ }
131
+ return;
132
+ }
133
+
134
+ $.each( r.data, function( k, v ) {
135
+ $row.children( '.column-' + k ).html( v );
136
+ } );
137
+ } )
138
+ .always( function() {
139
+ $row.removeClass( 'working' ).find( '.check-column [type="checkbox"]' ).prop( 'checked', false );
140
+ } );
141
+ } );
142
+
143
+ } )(jQuery, document, window);
144
+
145
+
146
+ (function(w) { // eslint-disable-line no-shadow, no-shadow-restricted-names
147
+
148
+ /**
149
+ * requestAnimationFrame polyfill by Erik Möller.
150
+ * Fixes from Paul Irish and Tino Zijdel.
151
+ * MIT license - http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating.
152
+ */
153
+ var lastTime = 0,
154
+ vendors = ['ms', 'moz', 'webkit', 'o'];
155
+
156
+ for ( var x = 0; x < vendors.length && ! w.requestAnimationFrame; ++x ) {
157
+ w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame'];
158
+ w.cancelAnimationFrame = w[vendors[x] + 'CancelAnimationFrame'] || w[vendors[x] + 'CancelRequestAnimationFrame'];
159
+ }
160
+
161
+ if ( ! w.requestAnimationFrame ) {
162
+ w.requestAnimationFrame = function( callback ) {
163
+ var currTime = new Date().getTime(),
164
+ timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ),
165
+ id = setTimeout( function() {
166
+ callback( currTime + timeToCall );
167
+ }, timeToCall );
168
+
169
+ lastTime = currTime + timeToCall;
170
+ return id;
171
+ };
172
+ }
173
+
174
+ if ( ! w.cancelAnimationFrame ) {
175
+ w.cancelAnimationFrame = function( id ) {
176
+ clearTimeout( id );
177
+ };
178
+ }
179
+
180
+ })(window);
181
+
182
+
183
+ (function($, d, w, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
184
+
185
+ /**
186
+ * LazyLoad images in the list.
187
+ */
188
+ var lazyImages = $( '#imagify-files-list-form' ).find( '[data-lazy-src]' ),
189
+ lazyTimer;
190
+
191
+ function lazyLoadThumbnails() {
192
+ w.cancelAnimationFrame( lazyTimer );
193
+ lazyTimer = w.requestAnimationFrame( lazyLoadThumbnailsCallback ); // eslint-disable-line no-use-before-define
194
+ }
195
+
196
+ function lazyLoadThumbnailsCallback() {
197
+ var $w = $( w ),
198
+ winScroll = $w.scrollTop(),
199
+ winHeight = $w.outerHeight();
200
+
201
+ $.each( lazyImages, function() {
202
+ var $image = $( this ),
203
+ imgTop = $image.offset().top,
204
+ imgBottom = imgTop + $image.outerHeight(),
205
+ screenTopThresholded = winScroll - 150,
206
+ screenBottomThresholded = winScroll + winHeight + 150,
207
+ src;
208
+
209
+ lazyImages = lazyImages.not( $image );
210
+
211
+ if ( ! lazyImages.length ) {
212
+ $w.off( 'scroll resize orientationchange', lazyLoadThumbnails );
213
+ }
214
+
215
+ /**
216
+ * Hidden images that are above the fold and below the top, are reported as:
217
+ * - offset: window scroll,
218
+ * - height: 0,
219
+ * (at least in Firefox).
220
+ * That's why I use <= and >=.
221
+ *
222
+ * 150 is the threshold.
223
+ */
224
+ if ( imgBottom >= screenTopThresholded && imgTop <= screenBottomThresholded ) {
225
+ src = $image.attr( 'data-lazy-src' );
226
+
227
+ if ( undefined !== src && src ) {
228
+ $image.attr( 'src', src ).removeAttr( 'data-lazy-src' );
229
+ }
230
+
231
+ $image.next( 'noscript' ).remove();
232
+ }
233
+ } );
234
+ }
235
+
236
+ if ( lazyImages.length ) {
237
+ $( w ).on( 'scroll resize orientationchange', lazyLoadThumbnails );
238
+ lazyLoadThumbnailsCallback();
239
+ }
240
+
241
+ } )(jQuery, document, window);
assets/js/files-list.min.js ADDED
@@ -0,0 +1 @@
 
1
+ window.imagify.drawMeAChart=function(a){a.each(function(){var a=parseInt(jQuery(this).closest(".imagify-chart").next(".imagify-chart-value").text());new imagify.Chart(this,{type:"doughnut",data:{datasets:[{data:[a,100-a],backgroundColor:["#00B3D3","#D8D8D8"],borderColor:"#fff",borderWidth:1}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1}})})},function(a,b,c,d){a(c).on("canvasprinted.imagify",function(b,d){var e;d=d||".imagify-consumption-chart",e=a(d),c.imagify.drawMeAChart(e)}).trigger("canvasprinted.imagify")}(jQuery,document,window),function(a,b,c,d){var e='<option value="imagify-bulk-optimize">'+imagifyFiles.labels.bulkActionsOptimize+"</option>";(imagifyFiles.backupOption||a(".file-has-backup").length)&&(e+='<option value="imagify-bulk-restore">'+imagifyFiles.labels.bulkActionsRestore+"</option>"),a('.bulkactions select[name="action"] option:first-child, .bulkactions select[name="action2"] option:first-child').after(e),a("#doaction, #doaction2").on("click.imagify",function(b){var c,d,e=a(this).prev("select").val();"imagify-bulk-optimize"!==e&&"imagify-bulk-restore"!==e&&"imagify-bulk-refresh-status"!==e||(b.preventDefault(),c=e.replace("bulk-",""),d=a('input[name="bulk_select[]"]:checked').map(function(){return this.value}).get(),d.forEach(function(b,d){var e=a("#"+c+"-"+b);if(!e.length)return void e.closest("tr").find("#cb-select-"+b).prop("checked",!1);setTimeout(function(){e.trigger("click.imagify")},500*d)}))}),a(b).on("click.imagify",".button-imagify-optimize, .button-imagify-reoptimize, .button-imagify-restore, .button-imagify-refresh-status",function(b){var c,d,e=a(this),f=e.closest("tr");b.preventDefault(),f.hasClass("working")||(f.addClass("working"),c=e.closest(".column-actions, .column-status"),d=e.attr("href"),c.html('<div class="button"><span class="imagify-spinner"></span>'+e.data("waiting-label")+"</div>"),a.get(d.replace("admin-post.php","admin-ajax.php")).done(function(b){if(!b.success)return void(b.data.row?f.html('<td class="colspanchange" colspan="'+f.children().length+'">'+b.data.row+"</td>"):c.html(b.data));a.each(b.data,function(a,b){f.children(".column-"+a).html(b)})}).always(function(){f.removeClass("working").find('.check-column [type="checkbox"]').prop("checked",!1)}))})}(jQuery,document,window),function(a){for(var b=0,c=["ms","moz","webkit","o"],d=0;d<c.length&&!a.requestAnimationFrame;++d)a.requestAnimationFrame=a[c[d]+"RequestAnimationFrame"],a.cancelAnimationFrame=a[c[d]+"CancelAnimationFrame"]||a[c[d]+"CancelRequestAnimationFrame"];a.requestAnimationFrame||(a.requestAnimationFrame=function(a){var c=(new Date).getTime(),d=Math.max(0,16-(c-b)),e=setTimeout(function(){a(c+d)},d);return b=c+d,e}),a.cancelAnimationFrame||(a.cancelAnimationFrame=function(a){clearTimeout(a)})}(window),function(a,b,c,d){function e(){c.cancelAnimationFrame(g),g=c.requestAnimationFrame(f)}function f(){var b=a(c),f=b.scrollTop(),g=b.outerHeight();a.each(h,function(){var c,i=a(this),j=i.offset().top,k=j+i.outerHeight(),l=f-150,m=f+g+150;h=h.not(i),h.length||b.off("scroll resize orientationchange",e),k>=l&&j<=m&&(c=i.attr("data-lazy-src"),d!==c&&c&&i.attr("src",c).removeAttr("data-lazy-src"),i.next("noscript").remove())})}var g,h=a("#imagify-files-list-form").find("[data-lazy-src]");h.length&&(a(c).on("scroll resize orientationchange",e),f())}(jQuery,document,window);
assets/js/imagify-gulp.js CHANGED
@@ -24,10 +24,6 @@ var ImagifyGulp = function () {
24
  this._each = new Function();
25
  this._done = new Function();
26
  this._error = new Function();
27
- this.total_original_size = 0;
28
- this.total_optimized_size = 0;
29
- this.total_gain = 0;
30
- this.total_percent = 0;
31
  this.global_original_size = 0;
32
  this.global_optimized_size = 0;
33
  this.global_gain = 0;
@@ -171,15 +167,17 @@ var ImagifyGulp = function () {
171
  key: 'send',
172
  value: function send(data) {
173
 
174
- var self = this,
175
- transport = new XMLHttpRequest(),
176
- err = false,
177
- json = {},
178
- response = {
179
- filename: data.filename,
180
- image: data.image_id,
181
- error: ''
182
- };
 
 
183
 
184
  transport.onreadystatechange = function () {
185
  if (4 === this.readyState) {
@@ -205,32 +203,20 @@ var ImagifyGulp = function () {
205
  response.success = json.success;
206
 
207
  if (true === json.success) {
208
-
209
- self.total_original_size += json_data.original_size;
210
- self.total_optimized_size += json_data.new_size;
211
- self.total_gain += json_data.original_size - json_data.new_size;
212
- self.total_percent = (self.total_optimized_size / self.total_original_size * 100).toFixed(2);
213
- self.global_original_size += json_data.original_overall_size;
214
  self.global_optimized_size += json_data.new_overall_size;
215
- self.global_gain += json_data.overall_saving;
216
- self.global_percent = (100 - self.global_optimized_size / self.global_optimized_size * 100).toFixed(2);
217
-
218
- response.original_size = json_data.original_size;
219
- response.original_size_human = self.humanSize(json_data.original_size);
220
-
221
- response.new_size = json_data.new_size;
222
- response.new_size_human = self.humanSize(json_data.new_size);
223
-
224
- response.percent = json_data.percent;
225
- response.thumbnails = json_data.thumbnails;
226
-
227
- response.overall_saving = json_data.overall_saving;
228
- response.overall_saving_human = self.humanSize(json_data.overall_saving);
229
-
230
- response.original_overall_size = json_data.original_overall_size;
231
- response.original_overall_size_human = self.humanSize(json_data.original_overall_size);
232
  } else {
233
- response.error = json_data.error;
 
234
  }
235
  }
236
 
@@ -240,19 +226,12 @@ var ImagifyGulp = function () {
240
  self.process(self.images_ids.shift());
241
  }
242
 
243
- if (self.processed_images === self.total_images) {
244
-
245
- var tmp_global_percent = 0;
246
-
247
- if (0 !== self.global_original_size) {
248
- tmp_global_percent = (100 - 100 * (self.global_optimized_size / self.global_original_size)).toFixed(2);
249
- }
250
-
251
- self._done({
252
- global_original_size_human: self.humanSize(self.global_original_size),
253
- global_gain_human: self.humanSize(self.global_gain),
254
- global_percent: tmp_global_percent
255
- });
256
  }
257
  }
258
  };
24
  this._each = new Function();
25
  this._done = new Function();
26
  this._error = new Function();
 
 
 
 
27
  this.global_original_size = 0;
28
  this.global_optimized_size = 0;
29
  this.global_gain = 0;
167
  key: 'send',
168
  value: function send(data) {
169
 
170
+ var self = this,
171
+ transport = new XMLHttpRequest(),
172
+ err = false,
173
+ json = {},
174
+ response = {
175
+ id: data.id,
176
+ image: data.image_id,
177
+ filename: data.filename,
178
+ thumbnail: data.thumbnail,
179
+ error: ''
180
+ };
181
 
182
  transport.onreadystatechange = function () {
183
  if (4 === this.readyState) {
203
  response.success = json.success;
204
 
205
  if (true === json.success) {
206
+ self.global_original_size += json_data.original_overall_size;
 
 
 
 
 
207
  self.global_optimized_size += json_data.new_overall_size;
208
+ self.global_gain += json_data.overall_saving;
209
+ self.global_percent = ( 100 - self.global_optimized_size / self.global_optimized_size * 100 ).toFixed( 2 );
210
+
211
+ response.original_size_human = json_data.original_size_human;
212
+ response.new_size_human = json_data.new_size_human;
213
+ response.overall_saving_human = json_data.overall_saving_human;
214
+ response.original_overall_size_human = json_data.original_overall_size_human;
215
+ response.percent_human = json_data.percent_human;
216
+ response.thumbnails = json_data.thumbnails;
 
 
 
 
 
 
 
 
217
  } else {
218
+ response.error_code = json_data.error_code;
219
+ response.error = json_data.error;
220
  }
221
  }
222
 
226
  self.process(self.images_ids.shift());
227
  }
228
 
229
+ if ( self.processed_images === self.total_images ) {
230
+ self._done( {
231
+ global_original_size: self.global_original_size,
232
+ global_optimized_size: self.global_optimized_size,
233
+ global_gain: self.global_gain
234
+ } );
 
 
 
 
 
 
 
235
  }
236
  }
237
  };
assets/js/imagify-gulp.min.js CHANGED
@@ -2,4 +2,246 @@
2
  * imagify-gulpjs - version 0.0.1 - 2017-07-28
3
  * WP Media <contact@wp-media.me>
4
  */
5
- "use strict";function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}(),ImagifyGulp=function(){function a(b){_classCallCheck(this,a),this.buffer_size=b.buffer_size||1,this.lib_url=b.lib,this.default_thumb="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAIAAACRuyQOAAACy0lEQVRIx+1XS1PTUBTuT/MB1NGF3bhyhys3Pv+Ai8pCF6Bb3bsWq8xAh8441I2TSlKwPIKQJsI40IbSV0pJ6qe3OeZ1b9PaYViQyaJzc+757j3nO985TVx5v3o+b+IS6XyQUgtrj/NqWtJmZf3lavnpl53bC2vjRLqRUV4pxqbZcnrBx3F6G2ZzTjFg879IMwWtanV7g56K1Z2RtBGRpj7IWb0a8HhqOz9O2rjHfrOD34GvWcPEruGQkhmlcFgnF7bjABVJmvA4uj4vY2VJr545/yClw/okBywaadF/m63j1gT/sNO5DbXW8t4sLtJzSQtnAme/ys/BrU9FpdIgY2R3MBLidtQ+ZRt2T9qZvQrtf/N9X5BwbEQKmSVIFGZjEGlWNojB93KbCBolDNl49nVPAIYwUs5QGAOQwCtmuuyGG5ExGhZbtM7s+5+3BGC5nyazhB8RElSAePRwRaX1u9nScadfVYjtncV1HtKDlW0KQMqvID4kCAydHST2fgKhu24B7dTaSY4oYBf2MjN44yK9+FZmRnrdCnuhr3jyB7Vr89G8L9f7oU77VSPhp4POjNarzUgv77Z/ERh+R9oUXbqDXFyktFtJ5ag74cU9cBsCwy3DNoht5Fcf0pO8ysuTt27IFzKH/Hm/Qopi5ekP99zzgkU8goF7VN21ThfM9BKHylHEPbzoQ8wUlSGoG1QVnR3Vhppj66hCtgg/Ayr3tdLXCFQ7al4ABr2gAEBHoCbQFDu+RkCvqPVBx5LCZvq2dEDsgELuDqV7rM/SfvmocfNjkYcEdV8Kdcu4Wh6QLzzoPQKtQ9DQvQL9ZYhOiCYteXougo/9UEIv9YHxKK+iZ9qenouE8QIunCMMMzxHYIIomU2ksBOaI5ZHmCO8OavEnI0K2hjmvTnevPe3bjBlTvKvMsoMi4kVAgO/qBXMGvidGu8Me/lf4yIg/QYbLcmjDg4bKwAAAABJRU5ErkJggg==",this.images=b.images,this.images_ids=Object.keys(b.images),this.total_images=this.images_ids.length,this.processed_images=0,this.inprocess_images=0,this._before=new Function,this._each=new Function,this._done=new Function,this._error=new Function,this.total_original_size=0,this.total_optimized_size=0,this.total_gain=0,this.total_percent=0,this.global_original_size=0,this.global_optimized_size=0,this.global_gain=0,this.global_percent=0,this.context=b.context||"wp"}return _createClass(a,[{key:"before",value:function(a){return this._before=a,this}},{key:"each",value:function(a){return this._each=a,this}},{key:"done",value:function(a){return this._done=a,this}},{key:"error",value:function(a){return this._error=a,this}},{key:"humanSize",value:function(a){if(0===a)return"0 kB";var b=["B","kB","MB"],c=parseInt(Math.floor(Math.log(a)/Math.log(1024)),10);return(a/Math.pow(1024,c)).toFixed(2)+" "+b[c]}},{key:"run",value:function(){for(var a=this.images_ids.length>this.buffer_size?this.buffer_size:this.images_ids.length,b=0;a>b;b++){var c=this.images_ids.shift();this.process(c)}return this}},{key:"stopProcess",value:function(){return this.total_images=this.total_images-this.images_ids.length,this.images_ids=[],this}},{key:"process",value:function(a){this.inprocess_images++;var b={id:a,image_id:parseInt(a.toString().substr(1),10),image_src:this.images[a],filename:this.images[a].split("/").pop(),thumbnail:this.default_thumb,error:""};this.createThumb(b)}},{key:"createThumb",value:function(a){var b=this,c=new Image;c.onerror=function(){var c=a;c.id=a.image_id,b._before(c),b.send(a)},c.onload=function(){var d=33,e=33,f=c.width,g=c.height,h=1,i=0,j=0,k=null,l=null;g>f?(h=d/f,j=d,i=g*h):(h=e/g,i=e,j=f*h),k=document.createElement("canvas"),k.width=j,k.height=i,c.width=j,c.height=i,l=k.getContext("2d"),l.drawImage(this,0,0,j,i);try{a.thumbnail=k.toDataURL("image/jpeg")}catch(m){a.thumbnail=b.default_thumb}var n=a;n.id=a.image_id,b._before(n),b.send(a),k=null},c.src=a.image_src}},{key:"send",value:function(a){var b=this,c=new XMLHttpRequest,d=!1,e={},f={filename:a.filename,image:a.image_id,error:""};c.onreadystatechange=function(){if(4===this.readyState){b.processed_images++;try{e=JSON.parse(this.responseText),d=!1}catch(a){f.success=!1,f.error="Unknown error occured",d=!0}if(f.progress=Math.floor(b.processed_images/b.total_images*100),!d){var c=e.data;f.success=e.success,!0===e.success?(b.total_original_size+=c.original_size,b.total_optimized_size+=c.new_size,b.total_gain+=c.original_size-c.new_size,b.total_percent=(b.total_optimized_size/b.total_original_size*100).toFixed(2),b.global_original_size+=c.original_overall_size,b.global_optimized_size+=c.new_overall_size,b.global_gain+=c.overall_saving,b.global_percent=(100-b.global_optimized_size/b.global_optimized_size*100).toFixed(2),f.original_size=c.original_size,f.original_size_human=b.humanSize(c.original_size),f.new_size=c.new_size,f.new_size_human=b.humanSize(c.new_size),f.percent=c.percent,f.thumbnails=c.thumbnails,f.overall_saving=c.overall_saving,f.overall_saving_human=b.humanSize(c.overall_saving),f.original_overall_size=c.original_overall_size,f.original_overall_size_human=b.humanSize(c.original_overall_size)):f.error=c.error}if(b._each(f),b.inprocess_images<b.total_images&&b.process(b.images_ids.shift()),b.processed_images===b.total_images){var g=0;0!==b.global_original_size&&(g=(100-100*(b.global_optimized_size/b.global_original_size)).toFixed(2)),b._done({global_original_size_human:b.humanSize(b.global_original_size),global_gain_human:b.humanSize(b.global_gain),global_percent:g})}}},c.open("POST",this.lib_url,!0),c.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),c.send("image="+a.image_id+"&context="+this.context)}}]),a}();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  * imagify-gulpjs - version 0.0.1 - 2017-07-28
3
  * WP Media <contact@wp-media.me>
4
  */
5
+ 'use strict';
6
+
7
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8
+
9
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
10
+
11
+ var ImagifyGulp = function () {
12
+ function ImagifyGulp(settings) {
13
+ _classCallCheck(this, ImagifyGulp);
14
+
15
+ this.buffer_size = settings.buffer_size || 1;
16
+ this.lib_url = settings.lib;
17
+ this.default_thumb = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAIAAACRuyQOAAACy0lEQVRIx+1XS1PTUBTuT/MB1NGF3bhyhys3Pv+Ai8pCF6Bb3bsWq8xAh8441I2TSlKwPIKQJsI40IbSV0pJ6qe3OeZ1b9PaYViQyaJzc+757j3nO985TVx5v3o+b+IS6XyQUgtrj/NqWtJmZf3lavnpl53bC2vjRLqRUV4pxqbZcnrBx3F6G2ZzTjFg879IMwWtanV7g56K1Z2RtBGRpj7IWb0a8HhqOz9O2rjHfrOD34GvWcPEruGQkhmlcFgnF7bjABVJmvA4uj4vY2VJr545/yClw/okBywaadF/m63j1gT/sNO5DbXW8t4sLtJzSQtnAme/ys/BrU9FpdIgY2R3MBLidtQ+ZRt2T9qZvQrtf/N9X5BwbEQKmSVIFGZjEGlWNojB93KbCBolDNl49nVPAIYwUs5QGAOQwCtmuuyGG5ExGhZbtM7s+5+3BGC5nyazhB8RElSAePRwRaX1u9nScadfVYjtncV1HtKDlW0KQMqvID4kCAydHST2fgKhu24B7dTaSY4oYBf2MjN44yK9+FZmRnrdCnuhr3jyB7Vr89G8L9f7oU77VSPhp4POjNarzUgv77Z/ERh+R9oUXbqDXFyktFtJ5ag74cU9cBsCwy3DNoht5Fcf0pO8ysuTt27IFzKH/Hm/Qopi5ekP99zzgkU8goF7VN21ThfM9BKHylHEPbzoQ8wUlSGoG1QVnR3Vhppj66hCtgg/Ayr3tdLXCFQ7al4ABr2gAEBHoCbQFDu+RkCvqPVBx5LCZvq2dEDsgELuDqV7rM/SfvmocfNjkYcEdV8Kdcu4Wh6QLzzoPQKtQ9DQvQL9ZYhOiCYteXougo/9UEIv9YHxKK+iZ9qenouE8QIunCMMMzxHYIIomU2ksBOaI5ZHmCO8OavEnI0K2hjmvTnevPe3bjBlTvKvMsoMi4kVAgO/qBXMGvidGu8Me/lf4yIg/QYbLcmjDg4bKwAAAABJRU5ErkJggg==";
18
+ this.images = settings.images;
19
+ this.images_ids = Object.keys(settings.images);
20
+ this.total_images = this.images_ids.length;
21
+ this.processed_images = 0;
22
+ this.inprocess_images = 0;
23
+ this._before = new Function();
24
+ this._each = new Function();
25
+ this._done = new Function();
26
+ this._error = new Function();
27
+ this.global_original_size = 0;
28
+ this.global_optimized_size = 0;
29
+ this.global_gain = 0;
30
+ this.global_percent = 0;
31
+ this.context = settings.context || 'wp';
32
+ }
33
+
34
+ _createClass(ImagifyGulp, [{
35
+ key: 'before',
36
+ value: function before(fnc) {
37
+ this._before = fnc;
38
+ return this;
39
+ }
40
+ }, {
41
+ key: 'each',
42
+ value: function each(fnc) {
43
+ this._each = fnc;
44
+ return this;
45
+ }
46
+ }, {
47
+ key: 'done',
48
+ value: function done(fnc) {
49
+ this._done = fnc;
50
+ return this;
51
+ }
52
+ }, {
53
+ key: 'error',
54
+ value: function error(fnc) {
55
+ this._error = fnc;
56
+ return this;
57
+ }
58
+ }, {
59
+ key: 'humanSize',
60
+ value: function humanSize(bytes) {
61
+ if (0 === bytes) return '0\xA0kB';
62
+
63
+ var sizes = ['B', 'kB', 'MB'],
64
+ i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
65
+
66
+ return (bytes / Math.pow(1024, i)).toFixed(2) + '\xA0' + sizes[i];
67
+ }
68
+ }, {
69
+ key: 'run',
70
+ value: function run() {
71
+ var cpt = this.images_ids.length > this.buffer_size ? this.buffer_size : this.images_ids.length;
72
+
73
+ for (var i = 0; i < cpt; i++) {
74
+ var id = this.images_ids.shift();
75
+ this.process(id);
76
+ }
77
+
78
+ return this;
79
+ }
80
+ }, {
81
+ key: 'stopProcess',
82
+ value: function stopProcess() {
83
+ this.total_images = this.total_images - this.images_ids.length;
84
+ this.images_ids = [];
85
+ return this;
86
+ }
87
+ }, {
88
+ key: 'process',
89
+ value: function process(id) {
90
+ this.inprocess_images++;
91
+
92
+ var data = {
93
+ id: id,
94
+ image_id: parseInt(id.toString().substr(1), 10),
95
+ image_src: this.images[id],
96
+ filename: this.images[id].split('/').pop(),
97
+ thumbnail: this.default_thumb,
98
+ error: ''
99
+ };
100
+
101
+ this.createThumb(data);
102
+ }
103
+ }, {
104
+ key: 'createThumb',
105
+ value: function createThumb(data) {
106
+ var self = this,
107
+ image = new Image();
108
+
109
+ image.onerror = function () {
110
+ var data_before = data;
111
+ data_before.id = data.image_id;
112
+
113
+ self._before(data_before);
114
+ self.send(data);
115
+ };
116
+
117
+ image.onload = function () {
118
+ var maxWidth = 33,
119
+ maxHeight = 33,
120
+ imageWidth = image.width,
121
+ imageHeight = image.height,
122
+ ratio = 1,
123
+ newHeight = 0,
124
+ newWidth = 0,
125
+ canvas = null,
126
+ ctx = null;
127
+
128
+ if (imageWidth < imageHeight) {
129
+ ratio = maxWidth / imageWidth;
130
+ newWidth = maxWidth;
131
+ newHeight = imageHeight * ratio;
132
+ } else {
133
+ ratio = maxHeight / imageHeight;
134
+ newHeight = maxHeight;
135
+ newWidth = imageWidth * ratio;
136
+ }
137
+
138
+ canvas = document.createElement('canvas');
139
+
140
+ canvas.width = newWidth;
141
+ canvas.height = newHeight;
142
+ image.width = newWidth;
143
+ image.height = newHeight;
144
+
145
+ ctx = canvas.getContext('2d');
146
+ ctx.drawImage(this, 0, 0, newWidth, newHeight);
147
+
148
+ try {
149
+ data.thumbnail = canvas.toDataURL('image/jpeg');
150
+ } catch (e) {
151
+ data.thumbnail = self.default_thumb;
152
+ }
153
+
154
+ var before_data = data;
155
+ before_data.id = data.image_id;
156
+
157
+ self._before(before_data);
158
+
159
+ self.send(data);
160
+
161
+ canvas = null;
162
+ };
163
+
164
+ image.src = data.image_src;
165
+ }
166
+ }, {
167
+ key: 'send',
168
+ value: function send(data) {
169
+
170
+ var self = this,
171
+ transport = new XMLHttpRequest(),
172
+ err = false,
173
+ json = {},
174
+ response = {
175
+ id: data.id,
176
+ image: data.image_id,
177
+ filename: data.filename,
178
+ thumbnail: data.thumbnail,
179
+ error: ''
180
+ };
181
+
182
+ transport.onreadystatechange = function () {
183
+ if (4 === this.readyState) {
184
+
185
+ self.processed_images++;
186
+
187
+ try {
188
+ json = JSON.parse(this.responseText);
189
+ err = false;
190
+ } catch (e) {
191
+
192
+ response.success = false;
193
+ response.error = 'Unknown error occured';
194
+
195
+ err = true;
196
+ }
197
+
198
+ response.progress = Math.floor(self.processed_images / self.total_images * 100);
199
+
200
+ if (!err) {
201
+ var json_data = json.data;
202
+
203
+ response.success = json.success;
204
+
205
+ if (true === json.success) {
206
+ self.global_original_size += json_data.original_overall_size;
207
+ self.global_optimized_size += json_data.new_overall_size;
208
+ self.global_gain += json_data.overall_saving;
209
+ self.global_percent = ( 100 - self.global_optimized_size / self.global_optimized_size * 100 ).toFixed( 2 );
210
+
211
+ response.original_size_human = json_data.original_size_human;
212
+ response.new_size_human = json_data.new_size_human;
213
+ response.overall_saving_human = json_data.overall_saving_human;
214
+ response.original_overall_size_human = json_data.original_overall_size_human;
215
+ response.percent_human = json_data.percent_human;
216
+ response.thumbnails = json_data.thumbnails;
217
+ } else {
218
+ response.error_code = json_data.error_code;
219
+ response.error = json_data.error;
220
+ }
221
+ }
222
+
223
+ self._each(response);
224
+
225
+ if (self.inprocess_images < self.total_images) {
226
+ self.process(self.images_ids.shift());
227
+ }
228
+
229
+ if ( self.processed_images === self.total_images ) {
230
+ self._done( {
231
+ global_original_size: self.global_original_size,
232
+ global_optimized_size: self.global_optimized_size,
233
+ global_gain: self.global_gain
234
+ } );
235
+ }
236
+ }
237
+ };
238
+
239
+ transport.open('POST', this.lib_url, true);
240
+ transport.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
241
+ transport.send('image=' + data.image_id + '&context=' + this.context);
242
+ }
243
+ }]);
244
+
245
+ return ImagifyGulp;
246
+ }();
247
+ //# sourceMappingURL=imagify-gulp.js.map
assets/js/jquery.twentytwenty.js CHANGED
@@ -66,7 +66,12 @@
66
  };
67
 
68
 
 
 
 
69
  $container.wrap( '<div class="twentytwenty-wrapper twentytwenty-' + sliderOrientation + '"></div>' );
 
 
70
  $container.append( '<div class="twentytwenty-overlay"></div>' );
71
  $container.append( '<div class="twentytwenty-handle"></div>' );
72
 
@@ -241,13 +246,13 @@
241
  w.imagify.openModal( $( this ) );
242
  }
243
 
244
- $modal.find( '.imagify-modal-content').css( {
245
  'width': ( $( w ).outerWidth() * 0.85 ) + 'px',
246
  'max-width': settings.width
247
  } );
248
 
249
  // Load before img.
250
- $modal.find( '.imagify-img-before').on( 'load', function() {
251
  imgsLoaded++;
252
  } ).attr( 'src', settings.originalUrl );
253
 
@@ -650,4 +655,54 @@
650
  }
651
  }
652
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
653
  } )(jQuery, document, window);
66
  };
67
 
68
 
69
+ if ( $container.parent( '.twentytwenty-wrapper' ).length ) {
70
+ $container.unwrap();
71
+ }
72
  $container.wrap( '<div class="twentytwenty-wrapper twentytwenty-' + sliderOrientation + '"></div>' );
73
+
74
+ $container.children( '.twentytwenty-overlay, .twentytwenty-handle' ).remove();
75
  $container.append( '<div class="twentytwenty-overlay"></div>' );
76
  $container.append( '<div class="twentytwenty-handle"></div>' );
77
 
246
  w.imagify.openModal( $( this ) );
247
  }
248
 
249
+ $modal.find( '.imagify-modal-content' ).css( {
250
  'width': ( $( w ).outerWidth() * 0.85 ) + 'px',
251
  'max-width': settings.width
252
  } );
253
 
254
  // Load before img.
255
+ $modal.find( '.imagify-img-before' ).on( 'load', function() {
256
  imgsLoaded++;
257
  } ).attr( 'src', settings.originalUrl );
258
 
655
  }
656
  }
657
 
658
+ /**
659
+ * Images comparison in custom folders list page.
660
+ */
661
+ if ( $( '#imagify-files-list-form' ).length > 0 ) {
662
+
663
+ var buildComparisonModal = function( $buttons ) {
664
+ $buttons.each( function() {
665
+ var $this = $( this ),
666
+ id = $this.data( 'id' ),
667
+ $datas = $this.closest( 'tr' ).find( '.column-optimization .imagify-data-item' );
668
+
669
+ $( '#imagify-comparison-' + id ).remove();
670
+
671
+ // Modal and trigger event creation.
672
+ imagifyTwentyModal( {
673
+ width: parseInt( $this.data( 'full-width' ), 10 ),
674
+ height: parseInt( $this.data( 'full-height' ), 10 ),
675
+ originalUrl: $this.data( 'backup-src' ),
676
+ optimizedUrl: $this.data( 'full-src' ),
677
+ originalSize: $datas.find( '.original' ).text(),
678
+ optimizedSize: $datas.find( '.optimized' ).text(),
679
+ saving: $datas.find( '.imagify-chart-value' ).text(),
680
+ modalAppendTo: $this.closest( '.column-primary' ),
681
+ trigger: $this,
682
+ modalId: 'imagify-comparison-' + id
683
+ } );
684
+ } );
685
+ };
686
+
687
+ /**
688
+ * Update the comparison tool window when a file row is updated via ajax, and the ones already printed.
689
+ */
690
+ $( w ).on( 'comparisonprinted.imagify', function( e, id ) {
691
+ var $buttons;
692
+
693
+ id = id || 0;
694
+
695
+ if ( id ) {
696
+ $buttons = $( '#imagify-files-list-form' ).find( '.imagify-compare-images[data-id="' + id + '"]' );
697
+ } else {
698
+ $buttons = $( '#imagify-files-list-form' ).find( '.imagify-compare-images' );
699
+ }
700
+
701
+ if ( $buttons.length ) {
702
+ buildComparisonModal( $buttons );
703
+ }
704
+ } )
705
+ .trigger( 'comparisonprinted.imagify' );
706
+ }
707
+
708
  } )(jQuery, document, window);
assets/js/jquery.twentytwenty.min.js CHANGED
@@ -1 +1 @@
1
- !function(a,b,c,d){a.fn.twentytwenty=function(b,d){return b=a.extend({handlePosition:.5,orientation:"horizontal",labelBefore:"Before",labelAfter:"After"},b),this.each(function(){var e,f,g=b.handlePosition,h=a(this),i=b.orientation,j="vertical"===i?"down":"left",k="vertical"===i?"up":"right",l=h.find("img:first"),m=h.find("img:last"),n=0,o=0,p=0,q=0,r=function(a){var b=parseInt(l.attr("width"),10),c=parseInt(l.attr("height"),10);return b&&c||(b=parseInt(l.width(),10),c=parseInt(l.height(),10)),{w:b+"px",h:c+"px",cw:a*b+"px",ch:a*c+"px"}},s=function(a){var b=h.find(".twentytwenty-before");"vertical"===i?b.css("clip","rect(0,"+a.w+","+a.ch+",0)"):b.css("clip","rect(0,"+a.cw+","+a.h+",0)"),h.css("height",a.h),"function"==typeof d&&d()},t=function(a){var b=r(a);"vertical"===i?e.css("top",b.ch):e.css("left",b.cw),s(b)};h.wrap('<div class="twentytwenty-wrapper twentytwenty-'+i+'"></div>'),h.append('<div class="twentytwenty-overlay"></div>'),h.append('<div class="twentytwenty-handle"></div>'),e=h.find(".twentytwenty-handle"),e.append('<span class="twentytwenty-'+j+'-arrow"></span>'),e.append('<span class="twentytwenty-'+k+'-arrow"></span>'),h.addClass("twentytwenty-container"),l.addClass("twentytwenty-before"),m.addClass("twentytwenty-after"),f=h.find(".twentytwenty-overlay"),f.append('<div class="twentytwenty-labels twentytwenty-before-label"><span class="twentytwenty-label-content">'+b.labelBefore+"</span></div>"),f.append('<div class="twentytwenty-labels twentytwenty-after-label"><span class="twentytwenty-label-content">'+b.labelAfter+"</span></div>"),a(c).on("resize.twentytwenty",function(){t(g)}),e.on("movestart",function(a){"vertical"!==i&&(a.distX>a.distY&&a.distX<-a.distY||a.distX<a.distY&&a.distX>-a.distY)?a.preventDefault():"vertical"===i&&(a.distX<a.distY&&a.distX<-a.distY||a.distX>a.distY&&a.distX>-a.distY)&&a.preventDefault(),h.addClass("active"),n=h.offset().left,o=h.offset().top,p=l.width(),q=l.height()}),e.on("moveend",function(){h.removeClass("active")}),e.on("move",function(a){h.hasClass("active")&&(g="vertical"===i?(a.pageY-o)/q:(a.pageX-n)/p,g<0?g=0:g>1&&(g=1),t(g))}),h.find("img").on("mousedown",function(a){a.preventDefault()}),a(c).trigger("resize.twentytwenty")})}}(jQuery,document,window),function(a,b,c,d){var e=function(b){b.each(function(){var b=parseInt(a(this).closest(".imagify-chart").next(".imagify-chart-value").text(),10);new c.imagify.Chart(this,{type:"doughnut",data:{datasets:[{data:[b,100-b],backgroundColor:["#00B3D3","#D8D8D8"],borderColor:"#2A2E3C",borderWidth:1}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1,cutoutPercentage:60}})})},f=function(b){var d,f={width:0,height:0,originalUrl:"",optimizedUrl:"",originalSize:0,optimizedSize:0,saving:0,modalAppendTo:a("body"),trigger:a('[data-target="imagify-visual-comparison"]'),modalId:"imagify-visual-comparison",openModal:!1},g=a.extend({},f,b);if(0===g.width||0===g.height||""===g.originalUrl||""===g.optimizedUrl||0===g.originalSize||0===g.optimizedSize||0===g.saving)return"error";d='<div id="'+g.modalId+'" class="imagify-modal imagify-visual-comparison" aria-hidden="true">',d+='<div class="imagify-modal-content loading">',d+='<div class="twentytwenty-container">',d+='<img class="imagify-img-before" alt="" width="'+g.width+'" height="'+g.height+'">',d+='<img class="imagify-img-after" alt="" width="'+g.width+'" height="'+g.height+'">',d+="</div>",d+='<div class="imagify-comparison-levels">',d+='<div class="imagify-c-level imagify-level-original go-left">',d+='<p class="imagify-c-level-row">',d+='<span class="label">'+imagifyTTT.labels.filesize+"</span>",d+='<span class="value level">'+g.originalSize+"</span>",d+="</p>",d+="</div>",d+='<div class="imagify-c-level imagify-level-optimized go-right">',d+='<p class="imagify-c-level-row">',d+='<span class="label">'+imagifyTTT.labels.filesize+"</span>",d+='<span class="value level">'+g.optimizedSize+"</span>",d+="</p>",d+='<p class="imagify-c-level-row">',d+='<span class="label">'+imagifyTTT.labels.saving+"</span>",d+='<span class="value"><span class="imagify-chart"><span class="imagify-chart-container"><canvas id="imagify-consumption-chart-normal" width="15" height="15"></canvas></span></span><span class="imagify-chart-value">'+g.saving+"</span>%</span>",d+="</p>",d+="</div>",d+="</div>",d+='<button class="close-btn absolute" type="button"><i aria-hidden="true" class="dashicons dashicons-no-alt"></i><span class="screen-reader-text">'+imagifyTTT.labels.close+"</span></button>",d+="</div>",d+="</div>",g.modalAppendTo.append(d),g.trigger.on("click.imagify",function(b){var d,f,h=a(a(this).data("target")),i=0;b.preventDefault(),g.openModal&&c.imagify.openModal(a(this)),h.find(".imagify-modal-content").css({width:.85*a(c).outerWidth()+"px","max-width":g.width}),h.find(".imagify-img-before").on("load",function(){i++}).attr("src",g.originalUrl),h.find(".imagify-img-after").on("load",function(){i++}).attr("src",g.optimizedUrl+(g.optimizedUrl.indexOf("?")>0?"&":"?")+"v="+Date.now()),d=h.find(".twentytwenty-container"),f=setInterval(function(){if(2===i)return d.twentytwenty({handlePosition:.3,orientation:"horizontal",labelBefore:imagifyTTT.labels.originalL,labelAfter:imagifyTTT.labels.optimizedL},function(){var b,f,g,i,j,k,l=a(c).height(),m=h.find(".twentytwenty-container").height(),n=h.find(".twentytwenty-wrapper").position().top;d.closest(".imagify-modal-content").hasClass("loaded")||(d.closest(".imagify-modal-content").removeClass("loading").addClass("loaded"),e(h.find(".imagify-level-optimized .imagify-chart canvas"))),l<m&&!h.hasClass("modal-is-too-high")&&(h.addClass("modal-is-too-high"),b=h.find(".twentytwenty-handle"),f=h.find(".twentytwenty-label-content"),g=h.find(".imagify-comparison-levels"),i=g.outerHeight(),j=(l-n-b.height())/2,k=l-3*n-i,b.css({top:j}),f.css({top:k,bottom:"auto"}),h.find(".twentytwenty-wrapper").css({paddingBottom:i}),h.find(".imagify-modal-content").on("scroll.imagify",function(){var c=a(this).scrollTop();b.css({top:j+c}),f.css({top:k+c}),g.css({bottom:-c})}))}),clearInterval(f),f=null,"done"},75)})};if(a(".imagify-visual-comparison-btn").on("click",function(){var b,d,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B;1!==a(".twentytwenty-wrapper").length&&(a(a(this).data("target")).find(".imagify-modal-content").css("width",.95*a(c).outerWidth()+"px"),a(".twentytwenty-container").length>0&&a(c).outerWidth()<=800||(b=a(".twentytwenty-container"),d=0,f=b.data("loader"),g=b.data("label-original"),h=b.data("label-normal"),i=b.data("label-aggressive"),j=b.data("label-ultra"),k=b.data("original-label").replace(/\*\*/,"<strong>").replace(/\*\*/,"</strong>"),l=b.data("original-alt"),m=b.data("original-img"),n=b.data("original-dim").split("x"),o=b.data("normal-alt"),p=b.data("normal-img"),q=b.data("normal-dim").split("x"),r=b.data("aggressive-alt"),s=b.data("aggressive-img"),t=b.data("aggressive-dim").split("x"),u=b.data("ultra-label").replace(/\*\*/,"<strong>").replace(/\*\*/,"</strong>"),v=b.data("ultra-alt"),w=b.data("ultra-img"),x=b.data("ultra-dim").split("x"),y='<span class="twentytwenty-duo-buttons twentytwenty-duo-left">',y+='<button type="button" class="imagify-comparison-original selected" data-img="original">'+g+"</button>",y+='<button type="button" class="imagify-comparison-normal" data-img="normal">'+h+"</button>",y+='<button type="button" class="imagify-comparison-aggressive" data-img="aggressive">'+i+"</button>",y+="</span>",z='<span class="twentytwenty-duo-buttons twentytwenty-duo-right">',z+='<button type="button" class="imagify-comparison-normal" data-img="normal">'+h+"</button>",z+='<button type="button" class="imagify-comparison-aggressive" data-img="aggressive">'+i+"</button>",z+='<button type="button" class="imagify-comparison-ultra selected" data-img="ultra">'+j+"</button>",z+="</span>",b.before('<img class="loader" src="'+f+'" alt="Loading…" width="64" height="64">'),a(".twentytwenty-left-buttons").append(y),a(".twentytwenty-right-buttons").append(z),A='<img class="img-original" alt="'+l+'" width="'+n[0]+'" height="'+n[1]+'">',A+='<img class="img-normal" alt="'+o+'" width="'+q[0]+'" height="'+q[1]+'">',A+='<img class="img-aggressive" alt="'+r+'" width="'+t[0]+'" height="'+t[1]+'">',A+='<img class="img-ultra" alt="'+v+'" width="'+x[0]+'" height="'+x[1]+'">',A+=a(".twentytwenty-left-buttons").lenght?y+z:"",b.closest(".imagify-modal-content").addClass("loading").find(".twentytwenty-container").append(A),a(".img-original").on("load",function(){d++}).attr("src",m),a(".img-normal").on("load",function(){d++}).attr("src",p),a(".img-aggressive").on("load",function(){d++}).attr("src",s),a(".img-ultra").on("load",function(){d++}).attr("src",w),B=setInterval(function(){4===d&&(b.twentytwenty({handlePosition:.6,orientation:"horizontal",labelBefore:k,labelAfter:u},function(){b.closest(".imagify-modal-content").hasClass("loaded")||(b.closest(".imagify-modal-content").removeClass("loading").addClass("loaded"),e(a(".imagify-level-ultra .imagify-chart canvas")))}),clearInterval(B),B=null)},75),a(".imagify-comparison-title").on("click",".twentytwenty-duo-buttons button:not(.selected)",function(b){var c,d=a(this),f=d.closest(".imagify-comparison-title").nextAll(".twentytwenty-wrapper").find(".twentytwenty-container"),g=d.closest(".twentytwenty-duo-buttons").hasClass("twentytwenty-duo-left")?"left":"right",h="left"===g?d.closest(".imagify-comparison-title").find(".twentytwenty-duo-right"):d.closest(".imagify-comparison-title").find(".twentytwenty-duo-left"),i=d.closest(".twentytwenty-duo-buttons").find("button"),j=f.find(".twentytwenty-before"),k=f.find(".twentytwenty-after"),l=d.data("img");b.stopPropagation(),b.preventDefault(),i.removeClass("selected"),d.addClass("selected"),h.find(".selected").data("img")===l&&h.find("button:not(.selected)").eq(0).trigger("click"),"left"===g&&(c=j.css("clip"),j.attr("style",""),j.removeClass("twentytwenty-before"),f.find(".img-"+l).addClass("twentytwenty-before").css("clip",c),a(".twentytwenty-before-label .twentytwenty-label-content").text(f.data(l+"-label")),a(".imagify-c-level.go-left").attr("aria-hidden","true").removeClass("go-left go-right"),a(".imagify-level-"+l).attr("aria-hidden","false").addClass("go-left")),"right"===g&&(k.removeClass("twentytwenty-after"),f.find(".img-"+l).addClass("twentytwenty-after"),a(".twentytwenty-after-label .twentytwenty-label-content").text(f.data(l+"-label")),a(".imagify-c-level.go-right").attr("aria-hidden","true").removeClass("go-left go-right"),a(".imagify-level-"+l).attr("aria-hidden","false").addClass("go-right")),e(a(".imagify-level-"+l+" .imagify-chart canvas"))})))}),imagifyTTT.imageWidth&&a(".post-php .wp_attachment_image .thumbnail").length>0){var g,h,i=a(".post-php .wp_attachment_image"),j={src:a("#imagify-full-original").val(),size:a("#imagify-full-original-size").val()},k=a("#misc-publishing-actions").find(".misc-pub-imagify .button-primary");imagifyTTT.widthLimit=parseInt(imagifyTTT.widthLimit,10),imagifyTTT.imageWidth>imagifyTTT.widthLimit&&j.src?(g=a(".misc-pub-filesize strong").text(),h=a(".imagify-data-item .imagify-chart-value").text(),a('[id^="imgedit-open-btn-"]').before('<button type="button" class="imagify-button-primary button-primary imagify-modal-trigger" data-target="#imagify-visual-comparison" id="imagify-start-comparison">'+imagifyTTT.labels.compare+"</button>"),f({width:parseInt(imagifyTTT.imageWidth,10),height:parseInt(imagifyTTT.imageHeight,10),originalUrl:j.src,optimizedUrl:imagifyTTT.imageSrc,originalSize:j.size,optimizedSize:g,saving:h,modalAppendTo:i,trigger:a("#imagify-start-comparison"),modalId:"imagify-visual-comparison"})):imagifyTTT.imageWidth<imagifyTTT.widthLimit&&j.src||a("#imagify-full-original").length>0&&""===j.src||1===a("#misc-publishing-actions").find(".misc-pub-imagify .button-primary").length&&(a('[id^="imgedit-open-btn-"]').before('<span class="spinner imagify-hidden"></span><a class="imagify-button-primary button-primary imagify-optimize-trigger" id="imagify-optimize-trigger" href="'+k.attr("href")+'">'+imagifyTTT.labels.optimize+"</a>"),a("#imagify-optimize-trigger").on("click",function(){a(this).prev(".spinner").removeClass("imagify-hidden").addClass("is-active")}))}if(a(".upload-php .imagify-compare-images").length>0&&a(".imagify-compare-images").each(function(){var b=a(this),c=b.data("id"),d=b.closest("#post-"+c).find(".column-imagify_optimized_file");f({width:parseInt(b.data("full-width"),10),height:parseInt(b.data("full-height"),10),originalUrl:b.data("backup-src"),optimizedUrl:b.data("full-src"),originalSize:d.find(".original").text(),optimizedSize:d.find(".imagify-data-item .big").text(),saving:d.find(".imagify-chart-value").text(),modalAppendTo:b.closest(".column-primary"),trigger:b,modalId:"imagify-comparison-"+c})}),a(".upload-php").length>0){var l=function(){var b=setInterval(function(){var c,d,e;a(".media-modal .imagify-datas-details").length&&(d=a("#imagify-original-src").val(),d&&(e=a(".media-frame-content .attachment-actions"),e.find("#imagify-media-frame-comparison-btn").remove(),e.prepend('<button type="button" class="imagify-button-primary button-primary imagify-modal-trigger" data-target="#imagify-comparison-modal" id="imagify-media-frame-comparison-btn">'+imagifyTTT.labels.compare+"</button>"),c=a(".media-frame-content .compat-field-imagify"),f({width:parseInt(a("#imagify-full-width").val(),10),height:parseInt(a("#imagify-full-height").val(),10),originalUrl:d,optimizedUrl:a("#imagify-full-src").val(),originalSize:a("#imagify-original-size").val(),optimizedSize:c.find(".imagify-data-item .big").text(),saving:c.find(".imagify-chart-value").text(),modalAppendTo:a(".media-frame-content .thumbnail-image"),trigger:a("#imagify-media-frame-comparison-btn"),modalId:"imagify-comparison-modal",openModal:!0})),clearInterval(b),b=null)},20)};a(".upload-php").on("click",".media-frame.mode-grid .attachment, .edit-media-header .left, .edit-media-header .right",function(){l()}),function(a){var b={};return c.location.href.replace(/[?&]+([^=&]+)=?([^&]*)?/gi,function(a,c,d){b[c]=void 0!==d?d:""}),a?b[a]?b[a]:null:b}("item")&&l()}}(jQuery,document,window);
1
+ !function(a,b,c,d){a.fn.twentytwenty=function(b,d){return b=a.extend({handlePosition:.5,orientation:"horizontal",labelBefore:"Before",labelAfter:"After"},b),this.each(function(){var e,f,g=b.handlePosition,h=a(this),i=b.orientation,j="vertical"===i?"down":"left",k="vertical"===i?"up":"right",l=h.find("img:first"),m=h.find("img:last"),n=0,o=0,p=0,q=0,r=function(a){var b=parseInt(l.attr("width"),10),c=parseInt(l.attr("height"),10);return b&&c||(b=parseInt(l.width(),10),c=parseInt(l.height(),10)),{w:b+"px",h:c+"px",cw:a*b+"px",ch:a*c+"px"}},s=function(a){var b=h.find(".twentytwenty-before");"vertical"===i?b.css("clip","rect(0,"+a.w+","+a.ch+",0)"):b.css("clip","rect(0,"+a.cw+","+a.h+",0)"),h.css("height",a.h),"function"==typeof d&&d()},t=function(a){var b=r(a);"vertical"===i?e.css("top",b.ch):e.css("left",b.cw),s(b)};h.parent(".twentytwenty-wrapper").length&&h.unwrap(),h.wrap('<div class="twentytwenty-wrapper twentytwenty-'+i+'"></div>'),h.children(".twentytwenty-overlay, .twentytwenty-handle").remove(),h.append('<div class="twentytwenty-overlay"></div>'),h.append('<div class="twentytwenty-handle"></div>'),e=h.find(".twentytwenty-handle"),e.append('<span class="twentytwenty-'+j+'-arrow"></span>'),e.append('<span class="twentytwenty-'+k+'-arrow"></span>'),h.addClass("twentytwenty-container"),l.addClass("twentytwenty-before"),m.addClass("twentytwenty-after"),f=h.find(".twentytwenty-overlay"),f.append('<div class="twentytwenty-labels twentytwenty-before-label"><span class="twentytwenty-label-content">'+b.labelBefore+"</span></div>"),f.append('<div class="twentytwenty-labels twentytwenty-after-label"><span class="twentytwenty-label-content">'+b.labelAfter+"</span></div>"),a(c).on("resize.twentytwenty",function(){t(g)}),e.on("movestart",function(a){"vertical"!==i&&(a.distX>a.distY&&a.distX<-a.distY||a.distX<a.distY&&a.distX>-a.distY)?a.preventDefault():"vertical"===i&&(a.distX<a.distY&&a.distX<-a.distY||a.distX>a.distY&&a.distX>-a.distY)&&a.preventDefault(),h.addClass("active"),n=h.offset().left,o=h.offset().top,p=l.width(),q=l.height()}),e.on("moveend",function(){h.removeClass("active")}),e.on("move",function(a){h.hasClass("active")&&(g="vertical"===i?(a.pageY-o)/q:(a.pageX-n)/p,g<0?g=0:g>1&&(g=1),t(g))}),h.find("img").on("mousedown",function(a){a.preventDefault()}),a(c).trigger("resize.twentytwenty")})}}(jQuery,document,window),function(a,b,c,d){var e=function(b){b.each(function(){var b=parseInt(a(this).closest(".imagify-chart").next(".imagify-chart-value").text(),10);new c.imagify.Chart(this,{type:"doughnut",data:{datasets:[{data:[b,100-b],backgroundColor:["#00B3D3","#D8D8D8"],borderColor:"#2A2E3C",borderWidth:1}]},options:{legend:{display:!1},events:[],animation:{easing:"easeOutBounce"},tooltips:{enabled:!1},responsive:!1,cutoutPercentage:60}})})},f=function(b){var d,f={width:0,height:0,originalUrl:"",optimizedUrl:"",originalSize:0,optimizedSize:0,saving:0,modalAppendTo:a("body"),trigger:a('[data-target="imagify-visual-comparison"]'),modalId:"imagify-visual-comparison",openModal:!1},g=a.extend({},f,b);if(0===g.width||0===g.height||""===g.originalUrl||""===g.optimizedUrl||0===g.originalSize||0===g.optimizedSize||0===g.saving)return"error";d='<div id="'+g.modalId+'" class="imagify-modal imagify-visual-comparison" aria-hidden="true">',d+='<div class="imagify-modal-content loading">',d+='<div class="twentytwenty-container">',d+='<img class="imagify-img-before" alt="" width="'+g.width+'" height="'+g.height+'">',d+='<img class="imagify-img-after" alt="" width="'+g.width+'" height="'+g.height+'">',d+="</div>",d+='<div class="imagify-comparison-levels">',d+='<div class="imagify-c-level imagify-level-original go-left">',d+='<p class="imagify-c-level-row">',d+='<span class="label">'+imagifyTTT.labels.filesize+"</span>",d+='<span class="value level">'+g.originalSize+"</span>",d+="</p>",d+="</div>",d+='<div class="imagify-c-level imagify-level-optimized go-right">',d+='<p class="imagify-c-level-row">',d+='<span class="label">'+imagifyTTT.labels.filesize+"</span>",d+='<span class="value level">'+g.optimizedSize+"</span>",d+="</p>",d+='<p class="imagify-c-level-row">',d+='<span class="label">'+imagifyTTT.labels.saving+"</span>",d+='<span class="value"><span class="imagify-chart"><span class="imagify-chart-container"><canvas id="imagify-consumption-chart-normal" width="15" height="15"></canvas></span></span><span class="imagify-chart-value">'+g.saving+"</span>%</span>",d+="</p>",d+="</div>",d+="</div>",d+='<button class="close-btn absolute" type="button"><i aria-hidden="true" class="dashicons dashicons-no-alt"></i><span class="screen-reader-text">'+imagifyTTT.labels.close+"</span></button>",d+="</div>",d+="</div>",g.modalAppendTo.append(d),g.trigger.on("click.imagify",function(b){var d,f,h=a(a(this).data("target")),i=0;b.preventDefault(),g.openModal&&c.imagify.openModal(a(this)),h.find(".imagify-modal-content").css({width:.85*a(c).outerWidth()+"px","max-width":g.width}),h.find(".imagify-img-before").on("load",function(){i++}).attr("src",g.originalUrl),h.find(".imagify-img-after").on("load",function(){i++}).attr("src",g.optimizedUrl+(g.optimizedUrl.indexOf("?")>0?"&":"?")+"v="+Date.now()),d=h.find(".twentytwenty-container"),f=setInterval(function(){if(2===i)return d.twentytwenty({handlePosition:.3,orientation:"horizontal",labelBefore:imagifyTTT.labels.originalL,labelAfter:imagifyTTT.labels.optimizedL},function(){var b,f,g,i,j,k,l=a(c).height(),m=h.find(".twentytwenty-container").height(),n=h.find(".twentytwenty-wrapper").position().top;d.closest(".imagify-modal-content").hasClass("loaded")||(d.closest(".imagify-modal-content").removeClass("loading").addClass("loaded"),e(h.find(".imagify-level-optimized .imagify-chart canvas"))),l<m&&!h.hasClass("modal-is-too-high")&&(h.addClass("modal-is-too-high"),b=h.find(".twentytwenty-handle"),f=h.find(".twentytwenty-label-content"),g=h.find(".imagify-comparison-levels"),i=g.outerHeight(),j=(l-n-b.height())/2,k=l-3*n-i,b.css({top:j}),f.css({top:k,bottom:"auto"}),h.find(".twentytwenty-wrapper").css({paddingBottom:i}),h.find(".imagify-modal-content").on("scroll.imagify",function(){var c=a(this).scrollTop();b.css({top:j+c}),f.css({top:k+c}),g.css({bottom:-c})}))}),clearInterval(f),f=null,"done"},75)})};if(a(".imagify-visual-comparison-btn").on("click",function(){var b,d,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B;1!==a(".twentytwenty-wrapper").length&&(a(a(this).data("target")).find(".imagify-modal-content").css("width",.95*a(c).outerWidth()+"px"),a(".twentytwenty-container").length>0&&a(c).outerWidth()<=800||(b=a(".twentytwenty-container"),d=0,f=b.data("loader"),g=b.data("label-original"),h=b.data("label-normal"),i=b.data("label-aggressive"),j=b.data("label-ultra"),k=b.data("original-label").replace(/\*\*/,"<strong>").replace(/\*\*/,"</strong>"),l=b.data("original-alt"),m=b.data("original-img"),n=b.data("original-dim").split("x"),o=b.data("normal-alt"),p=b.data("normal-img"),q=b.data("normal-dim").split("x"),r=b.data("aggressive-alt"),s=b.data("aggressive-img"),t=b.data("aggressive-dim").split("x"),u=b.data("ultra-label").replace(/\*\*/,"<strong>").replace(/\*\*/,"</strong>"),v=b.data("ultra-alt"),w=b.data("ultra-img"),x=b.data("ultra-dim").split("x"),y='<span class="twentytwenty-duo-buttons twentytwenty-duo-left">',y+='<button type="button" class="imagify-comparison-original selected" data-img="original">'+g+"</button>",y+='<button type="button" class="imagify-comparison-normal" data-img="normal">'+h+"</button>",y+='<button type="button" class="imagify-comparison-aggressive" data-img="aggressive">'+i+"</button>",y+="</span>",z='<span class="twentytwenty-duo-buttons twentytwenty-duo-right">',z+='<button type="button" class="imagify-comparison-normal" data-img="normal">'+h+"</button>",z+='<button type="button" class="imagify-comparison-aggressive" data-img="aggressive">'+i+"</button>",z+='<button type="button" class="imagify-comparison-ultra selected" data-img="ultra">'+j+"</button>",z+="</span>",b.before('<img class="loader" src="'+f+'" alt="Loading…" width="64" height="64">'),a(".twentytwenty-left-buttons").append(y),a(".twentytwenty-right-buttons").append(z),A='<img class="img-original" alt="'+l+'" width="'+n[0]+'" height="'+n[1]+'">',A+='<img class="img-normal" alt="'+o+'" width="'+q[0]+'" height="'+q[1]+'">',A+='<img class="img-aggressive" alt="'+r+'" width="'+t[0]+'" height="'+t[1]+'">',A+='<img class="img-ultra" alt="'+v+'" width="'+x[0]+'" height="'+x[1]+'">',A+=a(".twentytwenty-left-buttons").lenght?y+z:"",b.closest(".imagify-modal-content").addClass("loading").find(".twentytwenty-container").append(A),a(".img-original").on("load",function(){d++}).attr("src",m),a(".img-normal").on("load",function(){d++}).attr("src",p),a(".img-aggressive").on("load",function(){d++}).attr("src",s),a(".img-ultra").on("load",function(){d++}).attr("src",w),B=setInterval(function(){4===d&&(b.twentytwenty({handlePosition:.6,orientation:"horizontal",labelBefore:k,labelAfter:u},function(){b.closest(".imagify-modal-content").hasClass("loaded")||(b.closest(".imagify-modal-content").removeClass("loading").addClass("loaded"),e(a(".imagify-level-ultra .imagify-chart canvas")))}),clearInterval(B),B=null)},75),a(".imagify-comparison-title").on("click",".twentytwenty-duo-buttons button:not(.selected)",function(b){var c,d=a(this),f=d.closest(".imagify-comparison-title").nextAll(".twentytwenty-wrapper").find(".twentytwenty-container"),g=d.closest(".twentytwenty-duo-buttons").hasClass("twentytwenty-duo-left")?"left":"right",h="left"===g?d.closest(".imagify-comparison-title").find(".twentytwenty-duo-right"):d.closest(".imagify-comparison-title").find(".twentytwenty-duo-left"),i=d.closest(".twentytwenty-duo-buttons").find("button"),j=f.find(".twentytwenty-before"),k=f.find(".twentytwenty-after"),l=d.data("img");b.stopPropagation(),b.preventDefault(),i.removeClass("selected"),d.addClass("selected"),h.find(".selected").data("img")===l&&h.find("button:not(.selected)").eq(0).trigger("click"),"left"===g&&(c=j.css("clip"),j.attr("style",""),j.removeClass("twentytwenty-before"),f.find(".img-"+l).addClass("twentytwenty-before").css("clip",c),a(".twentytwenty-before-label .twentytwenty-label-content").text(f.data(l+"-label")),a(".imagify-c-level.go-left").attr("aria-hidden","true").removeClass("go-left go-right"),a(".imagify-level-"+l).attr("aria-hidden","false").addClass("go-left")),"right"===g&&(k.removeClass("twentytwenty-after"),f.find(".img-"+l).addClass("twentytwenty-after"),a(".twentytwenty-after-label .twentytwenty-label-content").text(f.data(l+"-label")),a(".imagify-c-level.go-right").attr("aria-hidden","true").removeClass("go-left go-right"),a(".imagify-level-"+l).attr("aria-hidden","false").addClass("go-right")),e(a(".imagify-level-"+l+" .imagify-chart canvas"))})))}),imagifyTTT.imageWidth&&a(".post-php .wp_attachment_image .thumbnail").length>0){var g,h,i=a(".post-php .wp_attachment_image"),j={src:a("#imagify-full-original").val(),size:a("#imagify-full-original-size").val()},k=a("#misc-publishing-actions").find(".misc-pub-imagify .button-primary");imagifyTTT.widthLimit=parseInt(imagifyTTT.widthLimit,10),imagifyTTT.imageWidth>imagifyTTT.widthLimit&&j.src?(g=a(".misc-pub-filesize strong").text(),h=a(".imagify-data-item .imagify-chart-value").text(),a('[id^="imgedit-open-btn-"]').before('<button type="button" class="imagify-button-primary button-primary imagify-modal-trigger" data-target="#imagify-visual-comparison" id="imagify-start-comparison">'+imagifyTTT.labels.compare+"</button>"),f({width:parseInt(imagifyTTT.imageWidth,10),height:parseInt(imagifyTTT.imageHeight,10),originalUrl:j.src,optimizedUrl:imagifyTTT.imageSrc,originalSize:j.size,optimizedSize:g,saving:h,modalAppendTo:i,trigger:a("#imagify-start-comparison"),modalId:"imagify-visual-comparison"})):imagifyTTT.imageWidth<imagifyTTT.widthLimit&&j.src||a("#imagify-full-original").length>0&&""===j.src||1===a("#misc-publishing-actions").find(".misc-pub-imagify .button-primary").length&&(a('[id^="imgedit-open-btn-"]').before('<span class="spinner imagify-hidden"></span><a class="imagify-button-primary button-primary imagify-optimize-trigger" id="imagify-optimize-trigger" href="'+k.attr("href")+'">'+imagifyTTT.labels.optimize+"</a>"),a("#imagify-optimize-trigger").on("click",function(){a(this).prev(".spinner").removeClass("imagify-hidden").addClass("is-active")}))}if(a(".upload-php .imagify-compare-images").length>0&&a(".imagify-compare-images").each(function(){var b=a(this),c=b.data("id"),d=b.closest("#post-"+c).find(".column-imagify_optimized_file");f({width:parseInt(b.data("full-width"),10),height:parseInt(b.data("full-height"),10),originalUrl:b.data("backup-src"),optimizedUrl:b.data("full-src"),originalSize:d.find(".original").text(),optimizedSize:d.find(".imagify-data-item .big").text(),saving:d.find(".imagify-chart-value").text(),modalAppendTo:b.closest(".column-primary"),trigger:b,modalId:"imagify-comparison-"+c})}),a(".upload-php").length>0){var l=function(){var b=setInterval(function(){var c,d,e;a(".media-modal .imagify-datas-details").length&&(d=a("#imagify-original-src").val(),d&&(e=a(".media-frame-content .attachment-actions"),e.find("#imagify-media-frame-comparison-btn").remove(),e.prepend('<button type="button" class="imagify-button-primary button-primary imagify-modal-trigger" data-target="#imagify-comparison-modal" id="imagify-media-frame-comparison-btn">'+imagifyTTT.labels.compare+"</button>"),c=a(".media-frame-content .compat-field-imagify"),f({width:parseInt(a("#imagify-full-width").val(),10),height:parseInt(a("#imagify-full-height").val(),10),originalUrl:d,optimizedUrl:a("#imagify-full-src").val(),originalSize:a("#imagify-original-size").val(),optimizedSize:c.find(".imagify-data-item .big").text(),saving:c.find(".imagify-chart-value").text(),modalAppendTo:a(".media-frame-content .thumbnail-image"),trigger:a("#imagify-media-frame-comparison-btn"),modalId:"imagify-comparison-modal",openModal:!0})),clearInterval(b),b=null)},20)};a(".upload-php").on("click",".media-frame.mode-grid .attachment, .edit-media-header .left, .edit-media-header .right",function(){l()}),function(a){var b={};return c.location.href.replace(/[?&]+([^=&]+)=?([^&]*)?/gi,function(a,c,d){b[c]=void 0!==d?d:""}),a?b[a]?b[a]:null:b}("item")&&l()}if(a("#imagify-files-list-form").length>0){var m=function(b){b.each(function(){var b=a(this),c=b.data("id"),d=b.closest("tr").find(".column-optimization .imagify-data-item");a("#imagify-comparison-"+c).remove(),f({width:parseInt(b.data("full-width"),10),height:parseInt(b.data("full-height"),10),originalUrl:b.data("backup-src"),optimizedUrl:b.data("full-src"),originalSize:d.find(".original").text(),optimizedSize:d.find(".optimized").text(),saving:d.find(".imagify-chart-value").text(),modalAppendTo:b.closest(".column-primary"),trigger:b,modalId:"imagify-comparison-"+c})})};a(c).on("comparisonprinted.imagify",function(b,c){var d;c=c||0,d=c?a("#imagify-files-list-form").find('.imagify-compare-images[data-id="'+c+'"]'):a("#imagify-files-list-form").find(".imagify-compare-images"),d.length&&m(d)}).trigger("comparisonprinted.imagify")}}(jQuery,document,window);
assets/js/notices.js CHANGED
@@ -40,6 +40,7 @@
40
  html: imagifyNotices.labels.signupText,
41
  confirmButtonText: imagifyNotices.labels.signupConfirmButtonText,
42
  input: 'email',
 
43
  showLoaderOnConfirm: true,
44
  customClass: 'imagify-sweet-alert imagify-sweet-alert-signup',
45
  inputValidator: function( inputValue ) {
@@ -70,6 +71,7 @@
70
  title: imagifyNotices.labels.signupSuccessTitle,
71
  html: imagifyNotices.labels.signupSuccessText,
72
  type: 'success',
 
73
  customClass: 'imagify-sweet-alert'
74
  } );
75
  } );
@@ -87,6 +89,7 @@
87
  html: imagifyNotices.labels.saveApiKeyText,
88
  confirmButtonText: imagifyNotices.labels.saveApiKeyConfirmButtonText,
89
  input: 'text',
 
90
  showLoaderOnConfirm: true,
91
  customClass: 'imagify-sweet-alert imagify-sweet-alert-signup',
92
  inputValidator: function( inputValue ) {
@@ -115,6 +118,7 @@
115
  title: imagifyNotices.labels.ApiKeyCheckSuccessTitle,
116
  html: imagifyNotices.labels.ApiKeyCheckSuccessText,
117
  type: 'success',
 
118
  customClass: 'imagify-sweet-alert'
119
  } );
120
  } );
40
  html: imagifyNotices.labels.signupText,
41
  confirmButtonText: imagifyNotices.labels.signupConfirmButtonText,
42
  input: 'email',
43
+ padding: 0,
44
  showLoaderOnConfirm: true,
45
  customClass: 'imagify-sweet-alert imagify-sweet-alert-signup',
46
  inputValidator: function( inputValue ) {
71
  title: imagifyNotices.labels.signupSuccessTitle,
72
  html: imagifyNotices.labels.signupSuccessText,
73
  type: 'success',
74
+ padding: 0,
75
  customClass: 'imagify-sweet-alert'
76
  } );
77
  } );
89
  html: imagifyNotices.labels.saveApiKeyText,
90
  confirmButtonText: imagifyNotices.labels.saveApiKeyConfirmButtonText,
91
  input: 'text',
92
+ padding: 0,
93
  showLoaderOnConfirm: true,
94
  customClass: 'imagify-sweet-alert imagify-sweet-alert-signup',
95
  inputValidator: function( inputValue ) {
118
  title: imagifyNotices.labels.ApiKeyCheckSuccessTitle,
119
  html: imagifyNotices.labels.ApiKeyCheckSuccessText,
120
  type: 'success',
121
+ padding: 0,
122
  customClass: 'imagify-sweet-alert'
123
  } );
124
  } );
assets/js/notices.min.js CHANGED
@@ -1 +1 @@
1
- !function(a,b,c,d){a(".imagify-notice-dismiss").on("click.imagify",function(b){var c=a(this),d=c.parents(".imagify-welcome, .imagify-notice, .imagify-rkt-notice"),e=c.attr("href");b.preventDefault(),d.fadeTo(100,0,function(){a(this).slideUp(100,function(){a(this).remove()})}),a.get(e.replace("admin-post.php","admin-ajax.php"))})}(jQuery,document,window),function(a,b,c,d){a("#imagify-signup").on("click.imagify",function(b){b.preventDefault(),swal({title:imagifyNotices.labels.signupTitle,html:imagifyNotices.labels.signupText,confirmButtonText:imagifyNotices.labels.signupConfirmButtonText,input:"email",showLoaderOnConfirm:!0,customClass:"imagify-sweet-alert imagify-sweet-alert-signup",inputValidator:function(b){return new Promise(function(c,d){""!==a.trim(b)&&b?c():d(imagifyNotices.labels.signupErrorEmptyEmail)})},preConfirm:function(b){return new Promise(function(d,e){setTimeout(function(){a.get(ajaxurl+c.imagify.concat+"action=imagify_signup&email="+b+"&imagifysignupnonce="+a("#imagifysignupnonce").val()).done(function(a){a.success?d():e(a.data)})},2e3)})}}).then(function(){swal({title:imagifyNotices.labels.signupSuccessTitle,html:imagifyNotices.labels.signupSuccessText,type:"success",customClass:"imagify-sweet-alert"})})}),a("#imagify-save-api-key").on("click.imagify",function(b){b.preventDefault(),swal({title:imagifyNotices.labels.saveApiKeyTitle,html:imagifyNotices.labels.saveApiKeyText,confirmButtonText:imagifyNotices.labels.saveApiKeyConfirmButtonText,input:"text",showLoaderOnConfirm:!0,customClass:"imagify-sweet-alert imagify-sweet-alert-signup",inputValidator:function(b){return new Promise(function(c,d){""!==a.trim(b)&&b?c():d(imagifyNotices.labels.ApiKeyErrorEmpty)})},preConfirm:function(b){return new Promise(function(d,e){a.get(ajaxurl+c.imagify.concat+"action=imagify_check_api_key_validity&api_key="+b+"&imagifycheckapikeynonce="+a("#imagifycheckapikeynonce").val()).done(function(a){a.success?d():e(a.data)})})}}).then(function(){swal({title:imagifyNotices.labels.ApiKeyCheckSuccessTitle,html:imagifyNotices.labels.ApiKeyCheckSuccessText,type:"success",customClass:"imagify-sweet-alert"})})})}(jQuery,document,window);
1
+ !function(a,b,c,d){a(".imagify-notice-dismiss").on("click.imagify",function(b){var c=a(this),d=c.parents(".imagify-welcome, .imagify-notice, .imagify-rkt-notice"),e=c.attr("href");b.preventDefault(),d.fadeTo(100,0,function(){a(this).slideUp(100,function(){a(this).remove()})}),a.get(e.replace("admin-post.php","admin-ajax.php"))})}(jQuery,document,window),function(a,b,c,d){a("#imagify-signup").on("click.imagify",function(b){b.preventDefault(),swal({title:imagifyNotices.labels.signupTitle,html:imagifyNotices.labels.signupText,confirmButtonText:imagifyNotices.labels.signupConfirmButtonText,input:"email",padding:0,showLoaderOnConfirm:!0,customClass:"imagify-sweet-alert imagify-sweet-alert-signup",inputValidator:function(b){return new Promise(function(c,d){""!==a.trim(b)&&b?c():d(imagifyNotices.labels.signupErrorEmptyEmail)})},preConfirm:function(b){return new Promise(function(d,e){setTimeout(function(){a.get(ajaxurl+c.imagify.concat+"action=imagify_signup&email="+b+"&imagifysignupnonce="+a("#imagifysignupnonce").val()).done(function(a){a.success?d():e(a.data)})},2e3)})}}).then(function(){swal({title:imagifyNotices.labels.signupSuccessTitle,html:imagifyNotices.labels.signupSuccessText,type:"success",padding:0,customClass:"imagify-sweet-alert"})})}),a("#imagify-save-api-key").on("click.imagify",function(b){b.preventDefault(),swal({title:imagifyNotices.labels.saveApiKeyTitle,html:imagifyNotices.labels.saveApiKeyText,confirmButtonText:imagifyNotices.labels.saveApiKeyConfirmButtonText,input:"text",padding:0,showLoaderOnConfirm:!0,customClass:"imagify-sweet-alert imagify-sweet-alert-signup",inputValidator:function(b){return new Promise(function(c,d){""!==a.trim(b)&&b?c():d(imagifyNotices.labels.ApiKeyErrorEmpty)})},preConfirm:function(b){return new Promise(function(d,e){a.get(ajaxurl+c.imagify.concat+"action=imagify_check_api_key_validity&api_key="+b+"&imagifycheckapikeynonce="+a("#imagifycheckapikeynonce").val()).done(function(a){a.success?d():e(a.data)})})}}).then(function(){swal({title:imagifyNotices.labels.ApiKeyCheckSuccessTitle,html:imagifyNotices.labels.ApiKeyCheckSuccessText,type:"success",padding:0,customClass:"imagify-sweet-alert"})})})}(jQuery,document,window);
assets/js/options.js CHANGED
@@ -38,6 +38,7 @@
38
  title: imagifyOptions.labels.ApiKeyCheckSuccessTitle,
39
  html: imagifyOptions.labels.ApiKeyCheckSuccessText,
40
  type: 'success',
 
41
  customClass: 'imagify-sweet-alert'
42
  } ).then( function() {
43
  location.reload();
@@ -81,7 +82,7 @@
81
  /**
82
  * Imagify Backup alert.
83
  */
84
- $( '.imagify-settings-section' ).find( '#backup' ).on( 'change', function() {
85
  var $_this = $( this ),
86
  $backupMessage = $_this.siblings( '#backup-dir-is-writable' ),
87
  params = {
@@ -111,6 +112,7 @@
111
  html: imagifyOptions.labels.noBackupText,
112
  type: 'warning',
113
  customClass: 'imagify-sweet-alert',
 
114
  showCancelButton: true,
115
  cancelButtonText: imagifySwal.labels.cancelButtonText,
116
  reverseButtons: true
@@ -129,6 +131,276 @@
129
  } )(jQuery, document, window);
130
 
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  // "Select all" checkboxes =========================================================================
133
  (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
134
 
@@ -152,21 +424,21 @@
152
  };
153
 
154
  // Check all checkboxes.
155
- $( '.imagify-check-group .imagify-row-check' ).on( 'click', function() {
156
- var $group = $( this ).closest( '.imagify-check-group' ),
157
- allChecked = 0 === $group.find( '.imagify-row-check' ).filter( ':visible:enabled' ).not( ':checked' ).length;
158
-
159
- // Toggle "check all" checkboxes.
160
- $group.find( '.imagify-toggle-check' ).prop( 'checked', allChecked );
161
- } ).first().trigger( 'change.imagify' );
 
 
 
162
 
163
- $( '.imagify-check-group .imagify-toggle-check' ).on( 'click.wp-toggle-checkboxes', function( e ) {
164
- var $this = $( this ),
165
- $wrap = $this.closest( '.imagify-check-group' ),
166
- controlChecked = $this.prop( 'checked' ),
167
- toggle = e.shiftKey || $this.data( 'wp-toggle' );
168
 
169
- $wrap.find( '.imagify-toggle-check' )
170
  .prop( 'checked', function() {
171
  var $this = $( this );
172
 
@@ -174,21 +446,34 @@
174
  return false;
175
  }
176
 
177
- if ( toggle ) {
178
- return ! $this.prop( 'checked' );
179
  }
180
 
181
- return controlChecked ? true : false;
182
  } );
183
 
184
- $wrap.find( '.imagify-row-check' )
185
- .prop( 'checked', function() {
186
- if ( toggle ) {
187
- return false;
188
- }
189
 
190
- return controlChecked ? true : false;
191
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  } );
193
 
194
  } )(window, document, jQuery);
38
  title: imagifyOptions.labels.ApiKeyCheckSuccessTitle,
39
  html: imagifyOptions.labels.ApiKeyCheckSuccessText,
40
  type: 'success',
41
+ padding: 0,
42
  customClass: 'imagify-sweet-alert'
43
  } ).then( function() {
44
  location.reload();
82
  /**
83
  * Imagify Backup alert.
84
  */
85
+ $( '.imagify-settings-section' ).find( '#imagify_backup' ).on( 'change', function() {
86
  var $_this = $( this ),
87
  $backupMessage = $_this.siblings( '#backup-dir-is-writable' ),
88
  params = {
112
  html: imagifyOptions.labels.noBackupText,
113
  type: 'warning',
114
  customClass: 'imagify-sweet-alert',
115
+ padding: 0,
116
  showCancelButton: true,
117
  cancelButtonText: imagifySwal.labels.cancelButtonText,
118
  reverseButtons: true
131
  } )(jQuery, document, window);
132
 
133
 
134
+ // Display Imagify User data =======================================================================
135
+ (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
136
+
137
+ if ( ! w.imagifyUser ) {
138
+ return;
139
+ }
140
+
141
+ $.getJSON( ajaxurl, w.imagifyUser )
142
+ .done( function( r ) {
143
+ if ( $.isPlainObject( r ) && r.success ) {
144
+ r.data.id = null;
145
+ r.data.plan_id = null;
146
+ r.data.is = [];
147
+
148
+ $.each( r.data, function( k, v ) {
149
+ var htmlClass = '.imagify-user-' + k.replace( /_/g, '-' );
150
+
151
+ if ( k.indexOf( 'is_' ) === 0 ) {
152
+ if ( v ) {
153
+ r.data.is.push( htmlClass );
154
+ }
155
+ } else if ( 'is' !== k ) {
156
+ $( htmlClass ).text( v );
157
+ }
158
+ } );
159
+
160
+ r.data.is.push( 'best-plan' );
161
+
162
+ $( r.data.is.join( ',' ) ).removeClass( 'hidden' );
163
+ }
164
+ } );
165
+
166
+ } )(window, document, jQuery);
167
+
168
+
169
+ // Files tree for "custom folders" =================================================================
170
+ (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
171
+
172
+ if ( ! imagifyOptions.getFilesTree ) {
173
+ return;
174
+ }
175
+
176
+ function imagifyInsertFolderRow( value ) {
177
+ var added = false,
178
+ prevPath = null,
179
+ valueTest, template, $wrap, $rows, $field;
180
+
181
+ if ( ! value ) {
182
+ return;
183
+ }
184
+
185
+ $wrap = $( '#imagify-custom-folders-selected' );
186
+ $rows = $wrap.find( '.imagify-custom-folder-line' );
187
+ $field = $rows.find( '[value="' + value + '"]' );
188
+
189
+ if ( $field.length ) {
190
+ // Shouldn't happen.
191
+ return;
192
+ }
193
+
194
+ // Path #///# Label.
195
+ value = value.split( '#///#' );
196
+ valueTest = value[1].replace( /\/+$/,'' ).toLowerCase();
197
+ template = w.imagify.template( 'imagify-custom-folder' );
198
+
199
+ $rows.each( function() {
200
+ var $this = $( this ),
201
+ thisValueTest = $this.data( 'path' ).replace( /\/+$/,'' ).toLowerCase();
202
+
203
+ if ( '' !== thisValueTest && valueTest.indexOf( thisValueTest ) === 0 ) {
204
+ // We try to add a sub-folder of an already selected folder. It shouldn't happen though, since it can't be selected.
205
+ added = true;
206
+ return false;
207
+ } else if ( valueTest < thisValueTest ) {
208
+ $this.before( template( {
209
+ value: value[0],
210
+ label: value[1]
211
+ } ) );
212
+ $rows = $wrap.find( '.imagify-custom-folder-line' );
213
+ added = true;
214
+ return false;
215
+ }
216
+ } );
217
+
218
+ if ( ! added ) {
219
+ $wrap.append( template( {
220
+ value: value[0],
221
+ label: value[1]
222
+ } ) );
223
+ $rows = $wrap.find( '.imagify-custom-folder-line' );
224
+ }
225
+
226
+ // Remove sub-paths: if 'a/b/' and 'a/b/c/' are in the array, we keep only the "parent" 'a/b/'.
227
+ if ( '' !== valueTest ) {
228
+ $rows.each( function() {
229
+ var $this = $( this ),
230
+ thisPath = $this.data( 'path' ).toLowerCase();
231
+
232
+ if ( null !== prevPath && thisPath.indexOf( prevPath ) === 0 ) {
233
+ $this.find( '.imagify-custom-folders-remove' ).trigger( 'click.imagify' );
234
+ } else {
235
+ prevPath = thisPath;
236
+ }
237
+ } );
238
+ }
239
+
240
+ // Display a message.
241
+ $wrap.next( '.hidden' ).removeClass( 'hidden' );
242
+ }
243
+
244
+ // Clicking the main button: fetch site's root folders and files, then display them in a modal.
245
+ $( '#imagify-add-custom-folder' ).on( 'click.imagify', function() {
246
+ var $button = $( this ),
247
+ selected = [],
248
+ $folders;
249
+
250
+ if ( $button.attr( 'disabled' ) ) {
251
+ return;
252
+ }
253
+
254
+ $button.attr( 'disabled', 'disabled' ).next( 'img' ).attr( 'aria-hidden', 'false' );
255
+
256
+ $folders = $( '#imagify-custom-folders-selected' );
257
+
258
+ $folders.find( 'input' ).each( function() {
259
+ selected.push( this.value );
260
+ } );
261
+
262
+ $.post( {
263
+ url: imagifyOptions.getFilesTree,
264
+ dataType: 'json',
265
+ data: {
266
+ folder: '/',
267
+ selected: selected
268
+ }
269
+ } ).done( function( response ) {
270
+ if ( ! response.success ) {
271
+ swal( {
272
+ title: imagifyOptions.labels.error,
273
+ html: response.data || '',
274
+ type: 'error',
275
+ padding: 0,
276
+ customClass: 'imagify-sweet-alert'
277
+ } );
278
+ return;
279
+ }
280
+
281
+ swal( {
282
+ title: imagifyOptions.labels.filesTreeTitle,
283
+ html: '<div class="imagify-swal-subtitle">' + imagifyOptions.labels.filesTreeSubTitle + '</div><div class="imagify-swal-content"><p class="imagify-folders-information"><i class="dashicons dashicons-info" aria-hidden="true"></i>' + imagifyOptions.labels.cleaningInfo + '</p><ul id="imagify-folders-tree" class="imagify-folders-tree">' + response.data + '</ul></div>',
284
+ type: '',
285
+ customClass: 'imagify-sweet-alert imagify-swal-has-subtitle imagify-folders-selection',
286
+ showCancelButton: true,
287
+ padding: 0,
288
+ confirmButtonText: imagifyOptions.labels.confirmFilesTreeBtn,
289
+ cancelButtonText: imagifySwal.labels.cancelButtonText,
290
+ reverseButtons: true
291
+ } ).then( function() {
292
+ var values = $( '#imagify-folders-tree input' ).serializeArray(); // Don't do `$( '#imagify-folders-tree' ).find( 'input' )`, it won't work.
293
+
294
+ if ( ! values.length ) {
295
+ return;
296
+ }
297
+
298
+ $.each( values, function( i, v ) {
299
+ imagifyInsertFolderRow( v.value );
300
+ } );
301
+ } ).catch( swal.noop );
302
+ } )
303
+ .fail( function() {
304
+ swal( {
305
+ title: imagifyOptions.labels.error,
306
+ type: 'error',
307
+ customClass: 'imagify-sweet-alert',
308
+ padding: 0
309
+ } );
310
+ } )
311
+ .always( function(){
312
+ $button.removeAttr( 'disabled' ).next( 'img' ).attr( 'aria-hidden', 'true' );
313
+ } );
314
+ } );
315
+
316
+ // Clicking a folder icon in the modal: fetch the folder's sub-folders and files, then display them.
317
+ $( d ).on( 'click.imagify', '#imagify-folders-tree [data-folder]', function() {
318
+ var $button = $( this ),
319
+ $tree = $button.nextAll( '.imagify-folders-sub-tree' ),
320
+ selected = [];
321
+
322
+ if ( $button.attr( 'disabled' ) || $button.siblings( ':checkbox' ).is( ':checked' ) ) {
323
+ return;
324
+ }
325
+
326
+ $button.attr( 'disabled', 'disabled' );
327
+
328
+ if ( $tree.length ) {
329
+ if ( $button.hasClass( 'imagify-is-open' ) ) {
330
+ $tree.addClass( 'hidden' );
331
+ $button.removeClass(' imagify-is-open' );
332
+ } else {
333
+ $tree.removeClass( 'hidden' );
334
+ $button.addClass(' imagify-is-open' );
335
+ }
336
+ $button.removeAttr( 'disabled' );
337
+ return;
338
+ }
339
+
340
+ $( '#imagify-custom-folders-selected' ).find( 'input' ).each( function() {
341
+ selected.push( this.value );
342
+ } );
343
+
344
+ $.post( {
345
+ url: imagifyOptions.getFilesTree,
346
+ dataType: 'json',
347
+ data: {
348
+ folder: $button.data( 'folder' ),
349
+ selected: selected
350
+ }
351
+ } )
352
+ .done( function( response ) {
353
+ if ( ! response.success ) {
354
+ swal( {
355
+ title: imagifyOptions.labels.error,
356
+ html: response.data || '',
357
+ type: 'error',
358
+ padding: 0,
359
+ customClass: 'imagify-sweet-alert'
360
+ } );
361
+ return;
362
+ }
363
+
364
+ $button.addClass( 'imagify-is-open' ).parent().append( '<ul class="imagify-folders-sub-tree">' + response.data + '</ul>' );
365
+ } )
366
+ .fail( function(){
367
+ swal( {
368
+ title: imagifyOptions.labels.error,
369
+ type: 'error',
370
+ padding: 0,
371
+ customClass: 'imagify-sweet-alert'
372
+ } );
373
+ } )
374
+ .always( function(){
375
+ $button.removeAttr( 'disabled' );
376
+ } );
377
+ } );
378
+
379
+ // Clicking a Remove folder button make it disappear.
380
+ $( '#imagify-custom-folders' ).on( 'click.imagify', '.imagify-custom-folders-remove', function() {
381
+ var $row = $( this ).closest( '.imagify-custom-folder-line' ).addClass( 'imagify-will-remove' );
382
+
383
+ w.setTimeout( function() {
384
+ $row.remove();
385
+ // Display a message.
386
+ $( '#imagify-custom-folders-selected' ).next( '.hidden' ).removeClass( 'hidden' );
387
+ }, 750 );
388
+ } );
389
+
390
+ // Clicking the "add themes to folders" button.
391
+ $( '#imagify-add-themes-to-custom-folder' ).on( 'click.imagify', function() {
392
+ var $this = $( this );
393
+
394
+ imagifyInsertFolderRow( $this.data( 'theme' ) );
395
+ imagifyInsertFolderRow( $this.data( 'theme-parent' ) );
396
+
397
+ // Remove clicked button.
398
+ $this.replaceWith( '<p>' + imagifyOptions.labels.themesAdded + '</p>' );
399
+ } );
400
+
401
+ } )(window, document, jQuery);
402
+
403
+
404
  // "Select all" checkboxes =========================================================================
405
  (function(w, d, $, undefined) { // eslint-disable-line no-unused-vars, no-shadow, no-shadow-restricted-names
406
 
424
  };
425
 
426
  // Check all checkboxes.
427
+ $( '.imagify-select-all' ).on( 'click.imagify', function() {
428
+ var $_this = $(this),
429
+ action = $_this.data( 'action' ),
430
+ $btns = $_this.closest( '.imagify-select-all-buttons' ),
431
+ $group = $btns.prev( '.imagify-check-group' ),
432
+ inactive = 'imagify-is-inactive';
433
+
434
+ if ( $_this.hasClass( inactive ) ) {
435
+ return false;
436
+ }
437
 
438
+ $btns.find( '.imagify-select-all' ).removeClass( inactive ).attr( 'aria-disabled', 'false' );
439
+ $_this.addClass( inactive ).attr( 'aria-disabled', 'true' );
 
 
 
440
 
441
+ $group.find( '.imagify-row-check' )
442
  .prop( 'checked', function() {
443
  var $this = $( this );
444
 
446
  return false;
447
  }
448
 
449
+ if ( action === 'select' ) {
450
+ return true;
451
  }
452
 
453
+ return false;
454
  } );
455
 
456
+ } );
 
 
 
 
457
 
458
+ // Change buttons status on checkboxes interation.
459
+ $( '.imagify-check-group .imagify-row-check' ).on( 'change.imagify', function() {
460
+ var $group = $( this ).closest( '.imagify-check-group' ),
461
+ $checks = $group.find( '.imagify-row-check' ),
462
+ could_be = $checks.filter( ':visible:enabled' ).length,
463
+ are_checked = $checks.filter( ':visible:enabled:checked' ).length,
464
+ $btns = $group.next( '.imagify-select-all-buttons' ),
465
+ inactive = 'imagify-is-inactive';
466
+
467
+ // Toggle status of "check all" buttons.
468
+ if ( are_checked === 0 ) {
469
+ $btns.find( '[data-action="unselect"]' ).addClass( inactive ).attr( 'aria-disabled', 'true' );
470
+ }
471
+ if ( are_checked === could_be ) {
472
+ $btns.find( '[data-action="select"]' ).addClass( inactive ).attr( 'aria-disabled', 'true' );
473
+ }
474
+ if ( are_checked !== could_be && are_checked > 0 ) {
475
+ $btns.find( '.imagify-select-all' ).removeClass( inactive ).attr( 'aria-disabled', 'false' );
476
+ }
477
  } );
478
 
479
  } )(window, document, jQuery);
assets/js/options.min.js CHANGED
@@ -1 +1 @@
1
- !function(a,b,c,d){var e=!1,f=!1;a("#imagify-settings #api_key").on("blur",function(){var b=a(this),d=b.val();return""!==a.trim(d)&&(a("#check_api_key").val()===d?(a("#imagify-check-api-container").html('<span class="dashicons dashicons-yes"></span> '+imagifyOptions.labels.ValidApiKeyText),!1):(!0===e?f.abort():(a("#imagify-check-api-container").remove(),b.after('<span id="imagify-check-api-container"><span class="imagify-spinner"></span>'+imagifyOptions.labels.waitApiKeyCheckText+"</span>")),e=!0,void(f=a.get(ajaxurl+c.imagify.concat+"action=imagify_check_api_key_validity&api_key="+b.val()+"&imagifycheckapikeynonce="+a("#imagifycheckapikeynonce").val()).done(function(b){b.success?(a("#imagify-check-api-container").remove(),swal({title:imagifyOptions.labels.ApiKeyCheckSuccessTitle,html:imagifyOptions.labels.ApiKeyCheckSuccessText,type:"success",customClass:"imagify-sweet-alert"}).then(function(){location.reload()})):a("#imagify-check-api-container").html('<span class="dashicons dashicons-no"></span> '+b.data),e=!1}))))}),a(".imagify-options-line").css("cursor","pointer").on("click",function(b){if("INPUT"!==b.target.nodeName)return a('input[aria-describedby="'+a(this).attr("id")+'"]').trigger("click"),!1}),a(".imagify-settings th span").on("click",function(){var b=a(this).parent().next("td").find("input:checkbox");1===b.length&&b.trigger("click")}),a(".imagify-options-line").find("input").on("change focus",function(){var b=a(this).closest(".imagify-options-line").prev("label").prev("input");b[0].checked||b.prop("checked",!0)}),a(".imagify-settings-section").find("#backup").on("change",function(){var b=a(this),c=b.siblings("#backup-dir-is-writable"),d={action:"imagify_check_backup_dir_is_writable",_wpnonce:c.data("nonce")};if(b.is(":checked"))return void a.getJSON(ajaxurl,d).done(function(b){a.isPlainObject(b)&&b.success&&(b.data.is_writable?c.addClass("hidden"):c.removeClass("hidden"))});swal({title:imagifyOptions.labels.noBackupTitle,html:imagifyOptions.labels.noBackupText,type:"warning",customClass:"imagify-sweet-alert",showCancelButton:!0,cancelButtonText:imagifySwal.labels.cancelButtonText,reverseButtons:!0}).then(function(){c.addClass("hidden")},function(){b.prop("checked",!0)})})}(jQuery,document,window),function(a,b,c,d){var e=c.propHooks.checked;c.propHooks.checked={set:function(a,b,d){var f;return f=void 0===e?a[d]=b:e(a,b,d),c(a).trigger("change.imagify"),f}},c(".imagify-check-group .imagify-row-check").on("click",function(){var a=c(this).closest(".imagify-check-group"),b=0===a.find(".imagify-row-check").filter(":visible:enabled").not(":checked").length;a.find(".imagify-toggle-check").prop("checked",b)}).first().trigger("change.imagify"),c(".imagify-check-group .imagify-toggle-check").on("click.wp-toggle-checkboxes",function(a){var b=c(this),d=b.closest(".imagify-check-group"),e=b.prop("checked"),f=a.shiftKey||b.data("wp-toggle");d.find(".imagify-toggle-check").prop("checked",function(){var a=c(this);return!a.is(":hidden,:disabled")&&(f?!a.prop("checked"):!!e)}),d.find(".imagify-row-check").prop("checked",function(){return!f&&!!e})})}(window,document,jQuery);
1
+ !function(a,b,c,d){var e=!1,f=!1;a("#imagify-settings #api_key").on("blur",function(){var b=a(this),d=b.val();return""!==a.trim(d)&&(a("#check_api_key").val()===d?(a("#imagify-check-api-container").html('<span class="dashicons dashicons-yes"></span> '+imagifyOptions.labels.ValidApiKeyText),!1):(!0===e?f.abort():(a("#imagify-check-api-container").remove(),b.after('<span id="imagify-check-api-container"><span class="imagify-spinner"></span>'+imagifyOptions.labels.waitApiKeyCheckText+"</span>")),e=!0,void(f=a.get(ajaxurl+c.imagify.concat+"action=imagify_check_api_key_validity&api_key="+b.val()+"&imagifycheckapikeynonce="+a("#imagifycheckapikeynonce").val()).done(function(b){b.success?(a("#imagify-check-api-container").remove(),swal({title:imagifyOptions.labels.ApiKeyCheckSuccessTitle,html:imagifyOptions.labels.ApiKeyCheckSuccessText,type:"success",padding:0,customClass:"imagify-sweet-alert"}).then(function(){location.reload()})):a("#imagify-check-api-container").html('<span class="dashicons dashicons-no"></span> '+b.data),e=!1}))))}),a(".imagify-options-line").css("cursor","pointer").on("click",function(b){if("INPUT"!==b.target.nodeName)return a('input[aria-describedby="'+a(this).attr("id")+'"]').trigger("click"),!1}),a(".imagify-settings th span").on("click",function(){var b=a(this).parent().next("td").find("input:checkbox");1===b.length&&b.trigger("click")}),a(".imagify-options-line").find("input").on("change focus",function(){var b=a(this).closest(".imagify-options-line").prev("label").prev("input");b[0].checked||b.prop("checked",!0)}),a(".imagify-settings-section").find("#imagify_backup").on("change",function(){var b=a(this),c=b.siblings("#backup-dir-is-writable"),d={action:"imagify_check_backup_dir_is_writable",_wpnonce:c.data("nonce")};if(b.is(":checked"))return void a.getJSON(ajaxurl,d).done(function(b){a.isPlainObject(b)&&b.success&&(b.data.is_writable?c.addClass("hidden"):c.removeClass("hidden"))});swal({title:imagifyOptions.labels.noBackupTitle,html:imagifyOptions.labels.noBackupText,type:"warning",customClass:"imagify-sweet-alert",padding:0,showCancelButton:!0,cancelButtonText:imagifySwal.labels.cancelButtonText,reverseButtons:!0}).then(function(){c.addClass("hidden")},function(){b.prop("checked",!0)})})}(jQuery,document,window),function(a,b,c,d){a.imagifyUser&&c.getJSON(ajaxurl,a.imagifyUser).done(function(a){c.isPlainObject(a)&&a.success&&(a.data.id=null,a.data.plan_id=null,a.data.is=[],c.each(a.data,function(b,d){var e=".imagify-user-"+b.replace(/_/g,"-");0===b.indexOf("is_")?d&&a.data.is.push(e):"is"!==b&&c(e).text(d)}),a.data.is.push("best-plan"),c(a.data.is.join(",")).removeClass("hidden"))})}(window,document,jQuery),function(a,b,c,d){function e(b){var d,e,f,g,h,i=!1,j=null;b&&(f=c("#imagify-custom-folders-selected"),g=f.find(".imagify-custom-folder-line"),h=g.find('[value="'+b+'"]'),h.length||(b=b.split("#///#"),d=b[1].replace(/\/+$/,"").toLowerCase(),e=a.imagify.template("imagify-custom-folder"),g.each(function(){var a=c(this),h=a.data("path").replace(/\/+$/,"").toLowerCase();return""!==h&&0===d.indexOf(h)?(i=!0,!1):d<h?(a.before(e({value:b[0],label:b[1]})),g=f.find(".imagify-custom-folder-line"),i=!0,!1):void 0}),i||(f.append(e({value:b[0],label:b[1]})),g=f.find(".imagify-custom-folder-line")),""!==d&&g.each(function(){var a=c(this),b=a.data("path").toLowerCase();null!==j&&0===b.indexOf(j)?a.find(".imagify-custom-folders-remove").trigger("click.imagify"):j=b}),f.next(".hidden").removeClass("hidden")))}imagifyOptions.getFilesTree&&(c("#imagify-add-custom-folder").on("click.imagify",function(){var a,b=c(this),d=[];b.attr("disabled")||(b.attr("disabled","disabled").next("img").attr("aria-hidden","false"),a=c("#imagify-custom-folders-selected"),a.find("input").each(function(){d.push(this.value)}),c.post({url:imagifyOptions.getFilesTree,dataType:"json",data:{folder:"/",selected:d}}).done(function(a){if(!a.success)return void swal({title:imagifyOptions.labels.error,html:a.data||"",type:"error",padding:0,customClass:"imagify-sweet-alert"});swal({title:imagifyOptions.labels.filesTreeTitle,html:'<div class="imagify-swal-subtitle">'+imagifyOptions.labels.filesTreeSubTitle+'</div><div class="imagify-swal-content"><p class="imagify-folders-information"><i class="dashicons dashicons-info" aria-hidden="true"></i>'+imagifyOptions.labels.cleaningInfo+'</p><ul id="imagify-folders-tree" class="imagify-folders-tree">'+a.data+"</ul></div>",type:"",customClass:"imagify-sweet-alert imagify-swal-has-subtitle imagify-folders-selection",showCancelButton:!0,padding:0,confirmButtonText:imagifyOptions.labels.confirmFilesTreeBtn,cancelButtonText:imagifySwal.labels.cancelButtonText,reverseButtons:!0}).then(function(){var a=c("#imagify-folders-tree input").serializeArray();a.length&&c.each(a,function(a,b){e(b.value)})}).catch(swal.noop)}).fail(function(){swal({title:imagifyOptions.labels.error,type:"error",customClass:"imagify-sweet-alert",padding:0})}).always(function(){b.removeAttr("disabled").next("img").attr("aria-hidden","true")}))}),c(b).on("click.imagify","#imagify-folders-tree [data-folder]",function(){var a=c(this),b=a.nextAll(".imagify-folders-sub-tree"),d=[];if(!a.attr("disabled")&&!a.siblings(":checkbox").is(":checked")){if(a.attr("disabled","disabled"),b.length)return a.hasClass("imagify-is-open")?(b.addClass("hidden"),a.removeClass(" imagify-is-open")):(b.removeClass("hidden"),a.addClass(" imagify-is-open")),void a.removeAttr("disabled");c("#imagify-custom-folders-selected").find("input").each(function(){d.push(this.value)}),c.post({url:imagifyOptions.getFilesTree,dataType:"json",data:{folder:a.data("folder"),selected:d}}).done(function(b){if(!b.success)return void swal({title:imagifyOptions.labels.error,html:b.data||"",type:"error",padding:0,customClass:"imagify-sweet-alert"});a.addClass("imagify-is-open").parent().append('<ul class="imagify-folders-sub-tree">'+b.data+"</ul>")}).fail(function(){swal({title:imagifyOptions.labels.error,type:"error",padding:0,customClass:"imagify-sweet-alert"})}).always(function(){a.removeAttr("disabled")})}}),c("#imagify-custom-folders").on("click.imagify",".imagify-custom-folders-remove",function(){var b=c(this).closest(".imagify-custom-folder-line").addClass("imagify-will-remove");a.setTimeout(function(){b.remove(),c("#imagify-custom-folders-selected").next(".hidden").removeClass("hidden")},750)}),c("#imagify-add-themes-to-custom-folder").on("click.imagify",function(){var a=c(this);e(a.data("theme")),e(a.data("theme-parent")),a.replaceWith("<p>"+imagifyOptions.labels.themesAdded+"</p>")}))}(window,document,jQuery),function(a,b,c,d){var e=c.propHooks.checked;c.propHooks.checked={set:function(a,b,d){var f;return f=void 0===e?a[d]=b:e(a,b,d),c(a).trigger("change.imagify"),f}},c(".imagify-select-all").on("click.imagify",function(){var a=c(this),b=a.data("action"),d=a.closest(".imagify-select-all-buttons"),e=d.prev(".imagify-check-group"),f="imagify-is-inactive";if(a.hasClass(f))return!1;d.find(".imagify-select-all").removeClass(f).attr("aria-disabled","false"),a.addClass(f).attr("aria-disabled","true"),e.find(".imagify-row-check").prop("checked",function(){return!c(this).is(":hidden,:disabled")&&"select"===b})}),c(".imagify-check-group .imagify-row-check").on("change.imagify",function(){var a=c(this).closest(".imagify-check-group"),b=a.find(".imagify-row-check"),d=b.filter(":visible:enabled").length,e=b.filter(":visible:enabled:checked").length,f=a.next(".imagify-select-all-buttons"),g="imagify-is-inactive";0===e&&f.find('[data-action="unselect"]').addClass(g).attr("aria-disabled","true"),e===d&&f.find('[data-action="select"]').addClass(g).attr("aria-disabled","true"),e!==d&&e>0&&f.find(".imagify-select-all").removeClass(g).attr("aria-disabled","false")})}(window,document,jQuery);
assets/js/pricing-modal.js CHANGED
@@ -942,7 +942,7 @@
942
  checkout_datas.monthly = JSON.parse( $monthly_offer.attr( 'data-offer' ) );
943
  }
944
 
945
- // If user choose a one time plan.
946
  if ( $onetime_offer.hasClass( 'imagify-offer-selected' ) ) {
947
  checkout_datas.onetime = JSON.parse( $onetime_offer.attr( 'data-offer' ) );
948
  }
942
  checkout_datas.monthly = JSON.parse( $monthly_offer.attr( 'data-offer' ) );
943
  }
944
 
945
+ // If user choose a One Time plan.
946
  if ( $onetime_offer.hasClass( 'imagify-offer-selected' ) ) {
947
  checkout_datas.onetime = JSON.parse( $onetime_offer.attr( 'data-offer' ) );
948
  }
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.6.14.2
7
  * Author: WP Media
8
  * Author URI: https://wp-media.me/
9
  * Licence: GPLv2
@@ -11,20 +11,18 @@
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.6.14.2' );
21
  define( 'IMAGIFY_SLUG' , 'imagify' );
22
- define( 'IMAGIFY_SETTINGS_SLUG' , IMAGIFY_SLUG . '_settings' );
23
  define( 'IMAGIFY_FILE' , __FILE__ );
24
  define( 'IMAGIFY_PATH' , realpath( plugin_dir_path( IMAGIFY_FILE ) ) . '/' );
25
  define( 'IMAGIFY_INC_PATH' , realpath( IMAGIFY_PATH . 'inc/' ) . '/' );
26
  define( 'IMAGIFY_ADMIN_PATH' , realpath( IMAGIFY_INC_PATH . 'admin' ) . '/' );
27
- define( 'IMAGIFY_ADMIN_UI_PATH' , realpath( IMAGIFY_ADMIN_PATH . 'ui' ) . '/' );
28
  define( 'IMAGIFY_COMMON_PATH' , realpath( IMAGIFY_INC_PATH . 'common' ) . '/' );
29
  define( 'IMAGIFY_FUNCTIONS_PATH', realpath( IMAGIFY_INC_PATH . 'functions' ) . '/' );
30
  define( 'IMAGIFY_CLASSES_PATH' , realpath( IMAGIFY_INC_PATH . 'classes' ) . '/' );
@@ -74,28 +72,32 @@ function _imagify_init() {
74
  require( IMAGIFY_FUNCTIONS_PATH . 'partners.php' );
75
  require( IMAGIFY_COMMON_PATH . 'attachments.php' );
76
  require( IMAGIFY_COMMON_PATH . 'admin-bar.php' );
77
- require( IMAGIFY_COMMON_PATH . 'cron.php' );
78
  require( IMAGIFY_COMMON_PATH . 'partners.php' );
79
  require( IMAGIFY_3RD_PARTY_PATH . '3rd-party.php' );
80
 
 
 
 
 
 
 
 
 
81
  if ( is_admin() ) {
82
  require( IMAGIFY_ADMIN_PATH . 'upgrader.php' );
83
  require( IMAGIFY_ADMIN_PATH . 'heartbeat.php' );
84
- require( IMAGIFY_ADMIN_PATH . 'options.php' );
85
- require( IMAGIFY_ADMIN_PATH . 'menu.php' );
86
- require( IMAGIFY_ADMIN_PATH . 'plugins.php' );
87
  require( IMAGIFY_ADMIN_PATH . 'upload.php' );
88
  require( IMAGIFY_ADMIN_PATH . 'media.php' );
89
  require( IMAGIFY_ADMIN_PATH . 'meta-boxes.php' );
90
- require( IMAGIFY_ADMIN_UI_PATH . 'options.php' );
91
- require( IMAGIFY_ADMIN_UI_PATH . 'bulk.php' );
92
- require( IMAGIFY_CLASSES_PATH . 'class-imagify-admin-ajax-post.php' );
93
 
94
  Imagify_Notices::get_instance()->init();
95
  Imagify_Admin_Ajax_Post::get_instance()->init();
 
 
96
  }
97
 
98
- if ( ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
99
  Imagify_Assets::get_instance()->init();
100
  }
101
 
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
  * 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 ) ) . '/' );
24
  define( 'IMAGIFY_INC_PATH' , realpath( IMAGIFY_PATH . 'inc/' ) . '/' );
25
  define( 'IMAGIFY_ADMIN_PATH' , realpath( IMAGIFY_INC_PATH . 'admin' ) . '/' );
 
26
  define( 'IMAGIFY_COMMON_PATH' , realpath( IMAGIFY_INC_PATH . 'common' ) . '/' );
27
  define( 'IMAGIFY_FUNCTIONS_PATH', realpath( IMAGIFY_INC_PATH . 'functions' ) . '/' );
28
  define( 'IMAGIFY_CLASSES_PATH' , realpath( IMAGIFY_INC_PATH . 'classes' ) . '/' );
72
  require( IMAGIFY_FUNCTIONS_PATH . 'partners.php' );
73
  require( IMAGIFY_COMMON_PATH . 'attachments.php' );
74
  require( IMAGIFY_COMMON_PATH . 'admin-bar.php' );
 
75
  require( IMAGIFY_COMMON_PATH . 'partners.php' );
76
  require( IMAGIFY_3RD_PARTY_PATH . '3rd-party.php' );
77
 
78
+ Imagify_Options::get_instance()->init();
79
+ Imagify_Data::get_instance()->init();
80
+ Imagify_Folders_DB::get_instance()->init();
81
+ Imagify_Files_DB::get_instance()->init();
82
+ Imagify_Cron_Library_Size::get_instance()->init();
83
+ Imagify_Cron_Rating::get_instance()->init();
84
+ Imagify_Cron_Sync_Files::get_instance()->init();
85
+
86
  if ( is_admin() ) {
87
  require( IMAGIFY_ADMIN_PATH . 'upgrader.php' );
88
  require( IMAGIFY_ADMIN_PATH . 'heartbeat.php' );
 
 
 
89
  require( IMAGIFY_ADMIN_PATH . 'upload.php' );
90
  require( IMAGIFY_ADMIN_PATH . 'media.php' );
91
  require( IMAGIFY_ADMIN_PATH . 'meta-boxes.php' );
92
+ require( IMAGIFY_ADMIN_PATH . 'custom-folders.php' );
 
 
93
 
94
  Imagify_Notices::get_instance()->init();
95
  Imagify_Admin_Ajax_Post::get_instance()->init();
96
+ Imagify_Settings::get_instance()->init();
97
+ Imagify_Views::get_instance()->init();
98
  }
99
 
100
+ if ( ! wp_doing_ajax() ) {
101
  Imagify_Assets::get_instance()->init();
102
  }
103
 
inc/3rd-party/amazon-s3-and-cloudfront/amazon-s3-and-cloudfront.php CHANGED
@@ -1,7 +1,51 @@
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
- if ( is_admin() && ( function_exists( 'as3cf_init' ) || function_exists( 'as3cf_pro_init' ) ) ) :
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  add_action( 'imagify_loaded', array( Imagify_AS3CF::get_instance(), 'init' ), 1 );
7
 
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
+ /**
5
+ * Tell if WP Offload S3 compatibility is loaded.
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ *
10
+ * @return bool
11
+ */
12
+ function imagify_load_as3cf_compat() {
13
+ if ( function_exists( 'as3cf_init' ) ) {
14
+ // WP Offload S3 Lite.
15
+ $version = ! empty( $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] ) ? $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] : false;
16
+
17
+ if ( ! $version ) {
18
+ return false;
19
+ }
20
+
21
+ if ( ! function_exists( 'amazon_web_services_init' ) && version_compare( $version, '1.3' ) < 0 ) {
22
+ // Old version, plugin Amazon Web Services is required.
23
+ return false;
24
+ }
25
+
26
+ return true;
27
+ }
28
+
29
+ if ( function_exists( 'as3cf_pro_init' ) ) {
30
+ // WP Offload S3 Pro.
31
+ $version = ! empty( $GLOBALS['aws_meta']['amazon-s3-and-cloudfront-pro']['version'] ) ? $GLOBALS['aws_meta']['amazon-s3-and-cloudfront-pro']['version'] : false;
32
+
33
+ if ( ! $version ) {
34
+ return false;
35
+ }
36
+
37
+ if ( ! function_exists( 'amazon_web_services_init' ) && version_compare( $version, '1.6' ) < 0 ) {
38
+ // Old version, plugin Amazon Web Services is required.
39
+ return false;
40
+ }
41
+
42
+ return true;
43
+ }
44
+
45
+ return false;
46
+ }
47
+
48
+ if ( is_admin() && imagify_load_as3cf_compat() ) :
49
 
50
  add_action( 'imagify_loaded', array( Imagify_AS3CF::get_instance(), 'init' ), 1 );
51
 
inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf-attachment.php CHANGED
@@ -78,8 +78,8 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
78
  * @return string|bool Path to the file if it exists or has been successfully retrieved from S3. False on failure.
79
  */
80
  public function get_thumbnail_path( $size_file = false ) {
81
- if ( ! $this->is_mime_type_supported() ) {
82
- return false;
83
  }
84
 
85
  $file_path = get_attached_file( $this->id, true );
@@ -114,7 +114,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
114
  * @return string|bool The file URL. False on failure.
115
  */
116
  public function get_thumbnail_url( $size_file = false ) {
117
- if ( ! $this->is_mime_type_supported() ) {
118
  return false;
119
  }
120
 
@@ -151,7 +151,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
151
  */
152
 
153
  // Check if the attachment extension is allowed.
154
- if ( ! $this->is_mime_type_supported() ) {
155
  return false;
156
  }
157
 
@@ -160,7 +160,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
160
  $this->delete_imagify_data();
161
  }
162
 
163
- $optimization_level = isset( $optimization_level ) ? (int) $optimization_level : (int) get_imagify_option( 'optimization_level', 1 );
164
 
165
  // Check if the full size is already optimized with this level.
166
  if ( $this->is_optimized() && $this->get_optimization_level() === $optimization_level ) {
@@ -219,7 +219,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
219
  'original_size' => $this->get_original_size( false ),
220
  ) );
221
 
222
- $data = $this->fill_data( null, $response, $this->get_original_url() );
223
 
224
  if ( $this->delete_files ) {
225
  $to_delete[] = $attachment_path;
@@ -247,7 +247,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
247
 
248
  // Optimize all thumbnails.
249
  if ( ! empty( $metadata['sizes'] ) ) {
250
- $disallowed_sizes = (array) get_imagify_option( 'disallowed-sizes', array() );
251
  $is_active_for_network = imagify_is_active_for_network();
252
 
253
  foreach ( $metadata['sizes'] as $size_key => $size_data ) {
@@ -301,7 +301,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
301
  'context' => 'wp',
302
  ) );
303
 
304
- $data = $this->fill_data( $data, $response, $thumbnail_url, $size_key );
305
 
306
  /** This filter is documented in /inc/classes/class-imagify-attachment.php. */
307
  $data = apply_filters( 'imagify_fill_thumbnail_data', $data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level );
@@ -346,7 +346,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
346
  */
347
  public function optimize_missing_thumbnails( $optimization_level = null ) {
348
  // Check if the attachment extension is allowed.
349
- if ( ! $this->is_mime_type_supported() ) {
350
  return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
351
  }
352
 
@@ -516,7 +516,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
516
  */
517
  public function restore() {
518
  // Check if the attachment extension is allowed.
519
- if ( ! $this->is_mime_type_supported() ) {
520
  return false;
521
  }
522
 
@@ -712,28 +712,6 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
712
  return $filesystem->exists( $attachment_path );
713
  }
714
 
715
- /**
716
- * Maybe backup a file.
717
- *
718
- * @since 1.6.6
719
- * @since 1.6.8 Deprecated.
720
- * @author Grégory Viguier
721
- *
722
- * @param string $attachment_path The file path.
723
- * @return bool|null True on success. False on failure. Null if backup is not needed.
724
- */
725
- protected function maybe_backup( $attachment_path ) {
726
- _deprecated_function( get_class( $this ) . '::' . __FUNCTION__ . '()', '1.6.8', 'imagify_backup_file()' );
727
-
728
- $result = imagify_backup_file( $attachment_path );
729
-
730
- if ( false === $result ) {
731
- return null;
732
- }
733
-
734
- return ! is_wp_error( $result );
735
- }
736
-
737
  /**
738
  * Tell if the files must be deleted after being optimized or restored.
739
  * It sets the 2 properties $this->use_s3_settings and $this->delete_files.
@@ -753,7 +731,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
753
  * This means we'll follow AS3CF settings to know if the local files must be sent to S3 and/or deleted.
754
  */
755
  $this->use_s3_settings = true;
756
- $this->delete_files = $as3cf->get_setting( 'remove-local-file' ) && $this->can_send_to_s3();
757
 
758
  return $metadata;
759
  }
@@ -789,7 +767,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
789
  static $is;
790
 
791
  if ( ! isset( $is ) ) {
792
- $is = $as3cf->is_plugin_setup();
793
  }
794
 
795
  return $is;
@@ -805,7 +783,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
805
  */
806
  public function get_s3_info() {
807
  global $as3cf;
808
- return $as3cf->get_attachment_s3_info( $this->id );
809
  }
810
 
811
  /**
@@ -820,7 +798,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
820
  protected function get_file_from_s3( $file_path ) {
821
  global $as3cf;
822
 
823
- if ( ! $this->is_mime_type_supported() ) {
824
  return false;
825
  }
826
 
@@ -900,7 +878,7 @@ class Imagify_AS3CF_Attachment extends Imagify_Attachment {
900
  }
901
 
902
  if ( ! isset( $copy_to_s3 ) ) {
903
- $copy_to_s3 = (bool) $as3cf->get_setting( 'copy-to-s3' );
904
  }
905
 
906
  $is_s3_setup = $this->is_s3_setup();
78
  * @return string|bool Path to the file if it exists or has been successfully retrieved from S3. False on failure.
79
  */
80
  public function get_thumbnail_path( $size_file = false ) {
81
+ if ( ! $this->is_valid() ) {
82
+ return '';
83
  }
84
 
85
  $file_path = get_attached_file( $this->id, true );
114
  * @return string|bool The file URL. False on failure.
115
  */
116
  public function get_thumbnail_url( $size_file = false ) {
117
+ if ( ! $this->is_extension_supported() ) {
118
  return false;
119
  }
120
 
151
  */
152
 
153
  // Check if the attachment extension is allowed.
154
+ if ( ! $this->is_extension_supported() ) {
155
  return false;
156
  }
157
 
160
  $this->delete_imagify_data();
161
  }
162
 
163
+ $optimization_level = isset( $optimization_level ) ? (int) $optimization_level : get_imagify_option( 'optimization_level' );
164
 
165
  // Check if the full size is already optimized with this level.
166
  if ( $this->is_optimized() && $this->get_optimization_level() === $optimization_level ) {
219
  'original_size' => $this->get_original_size( false ),
220
  ) );
221
 
222
+ $data = $this->fill_data( null, $response );
223
 
224
  if ( $this->delete_files ) {
225
  $to_delete[] = $attachment_path;
247
 
248
  // Optimize all thumbnails.
249
  if ( ! empty( $metadata['sizes'] ) ) {
250
+ $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
251
  $is_active_for_network = imagify_is_active_for_network();
252
 
253
  foreach ( $metadata['sizes'] as $size_key => $size_data ) {
301
  'context' => 'wp',
302
  ) );
303
 
304
+ $data = $this->fill_data( $data, $response, $size_key );
305
 
306
  /** This filter is documented in /inc/classes/class-imagify-attachment.php. */
307
  $data = apply_filters( 'imagify_fill_thumbnail_data', $data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level );
346
  */
347
  public function optimize_missing_thumbnails( $optimization_level = null ) {
348
  // Check if the attachment extension is allowed.
349
+ if ( ! $this->is_extension_supported() ) {
350
  return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
351
  }
352
 
516
  */
517
  public function restore() {
518
  // Check if the attachment extension is allowed.
519
+ if ( ! $this->is_extension_supported() ) {
520
  return false;
521
  }
522
 
712
  return $filesystem->exists( $attachment_path );
713
  }
714
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
  /**
716
  * Tell if the files must be deleted after being optimized or restored.
717
  * It sets the 2 properties $this->use_s3_settings and $this->delete_files.
731
  * This means we'll follow AS3CF settings to know if the local files must be sent to S3 and/or deleted.
732
  */
733
  $this->use_s3_settings = true;
734
+ $this->delete_files = $as3cf && $as3cf->get_setting( 'remove-local-file' ) && $this->can_send_to_s3();
735
 
736
  return $metadata;
737
  }
767
  static $is;
768
 
769
  if ( ! isset( $is ) ) {
770
+ $is = $as3cf && $as3cf->is_plugin_setup();
771
  }
772
 
773
  return $is;
783
  */
784
  public function get_s3_info() {
785
  global $as3cf;
786
+ return $as3cf ? $as3cf->get_attachment_s3_info( $this->id ) : false;
787
  }
788
 
789
  /**
798
  protected function get_file_from_s3( $file_path ) {
799
  global $as3cf;
800
 
801
+ if ( ! $this->is_extension_supported() ) {
802
  return false;
803
  }
804
 
878
  }
879
 
880
  if ( ! isset( $copy_to_s3 ) ) {
881
+ $copy_to_s3 = $as3cf && $as3cf->get_setting( 'copy-to-s3' );
882
  }
883
 
884
  $is_s3_setup = $this->is_s3_setup();
inc/3rd-party/amazon-s3-and-cloudfront/inc/classes/class-imagify-as3cf.php CHANGED
@@ -7,7 +7,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
7
  * @since 1.6.6
8
  * @author Grégory Viguier
9
  */
10
- class Imagify_AS3CF {
11
 
12
  /**
13
  * Class version.
@@ -165,7 +165,7 @@ class Imagify_AS3CF {
165
  public function maybe_copy_files_from_s3( $ids, $results, $optimization_level ) {
166
  global $wpdb, $as3cf;
167
 
168
- if ( ! $as3cf->is_plugin_setup() ) {
169
  return;
170
  }
171
 
@@ -431,26 +431,4 @@ class Imagify_AS3CF {
431
  // Optimize it.
432
  $attachment->optimize( $optimization_level, $_POST['metadata'] );
433
  }
434
-
435
-
436
- /** ----------------------------------------------------------------------------------------- */
437
- /** TOOLS =================================================================================== */
438
- /** ----------------------------------------------------------------------------------------- */
439
-
440
- /**
441
- * Tell if the attachment has a supported mime type.
442
- *
443
- * @since 1.6.6
444
- * @since 1.6.8 Deprecated.
445
- * @see imagify_is_attachment_mime_type_supported()
446
- * @author Grégory Viguier
447
- *
448
- * @param int $post_id The attachment ID.
449
- * @return bool
450
- */
451
- public function is_mime_type_supported( $post_id ) {
452
- _deprecated_function( 'Imagify_AS3CF::is_mime_type_supported()', '1.6.8', 'imagify_is_attachment_mime_type_supported()' );
453
-
454
- return imagify_is_attachment_mime_type_supported( $post_id );
455
- }
456
  }
7
  * @since 1.6.6
8
  * @author Grégory Viguier
9
  */
10
+ class Imagify_AS3CF extends Imagify_AS3CF_Deprecated {
11
 
12
  /**
13
  * Class version.
165
  public function maybe_copy_files_from_s3( $ids, $results, $optimization_level ) {
166
  global $wpdb, $as3cf;
167
 
168
+ if ( ! $as3cf || ! $as3cf->is_plugin_setup() ) {
169
  return;
170
  }
171
 
431
  // Optimize it.
432
  $attachment->optimize( $optimization_level, $_POST['metadata'] );
433
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  }
inc/3rd-party/hosting/siteground.php CHANGED
@@ -18,7 +18,13 @@ 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 ) || false === strpos( $r['user-agent'], $_SERVER['HTTP_HOST'] ) ) {
 
 
 
 
 
 
22
  return $r;
23
  }
24
 
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
 
inc/3rd-party/nextgen-gallery/inc/admin/ajax.php CHANGED
@@ -7,6 +7,8 @@ add_action( 'wp_ajax_imagify_manual_override_upload', '_do_admin_post_imagify
7
  add_action( 'admin_post_imagify_manual_override_upload', '_do_admin_post_imagify_ngg_user_capacity', 5 );
8
  add_action( 'wp_ajax_imagify_restore_upload', '_do_admin_post_imagify_ngg_user_capacity', 5 );
9
  add_action( 'admin_post_imagify_restore_upload', '_do_admin_post_imagify_ngg_user_capacity', 5 );
 
 
10
  /**
11
  * On manual optimization, manual re-optimization, and manual restoration, filter the user capacity to operate Imagify within NGG.
12
  *
@@ -24,11 +26,12 @@ add_filter( 'imagify_current_user_can', 'imagify_ngg_current_user_can', 10, 4 );
24
  * Filter the current user capability to operate Imagify.
25
  *
26
  * @since 1.6.11
 
27
  * @author Grégory Viguier
28
  *
29
  * @param bool $user_can Tell if the current user has the required capacity to operate Imagify.
30
  * @param string $capacity The user capacity.
31
- * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', and 'auto-optimize'.
32
  * @param int $post_id A post ID (a gallery ID for NGG).
33
  * @return bool
34
  */
@@ -69,19 +72,15 @@ add_action( 'wp_ajax_imagify_ngg_get_unoptimized_attachment_ids', '_do_wp_ajax_i
69
  function _do_wp_ajax_imagify_ngg_get_unoptimized_attachment_ids() {
70
  global $wpdb;
71
 
72
- imagify_check_nonce( 'imagify-bulk-upload', 'imagifybulkuploadnonce' );
73
- imagify_check_user_capacity( 'bulk-optimize' );
74
-
75
- $user = new Imagify_User();
76
 
77
- if ( $user->is_over_quota() ) {
78
- wp_send_json_error( array( 'message' => 'over-quota' ) );
79
- }
80
 
81
  @set_time_limit( 0 );
82
 
83
- $optimization_level = (int) $_GET['optimization_level'];
84
- $optimization_level = -1 !== $optimization_level ? $optimization_level : (int) get_imagify_option( 'optimization_level', 1 );
85
 
86
  $storage = C_Gallery_Storage::get_instance();
87
  $ngg_table = $wpdb->prefix . 'ngg_pictures';
@@ -99,8 +98,11 @@ function _do_wp_ajax_imagify_ngg_get_unoptimized_attachment_ids() {
99
  imagify_get_unoptimized_attachment_limit()
100
  ), ARRAY_A );
101
 
102
- // Save the optimization level in a transient to retrieve it later during the process.
103
- set_transient( 'imagify_bulk_optimization_level', $optimization_level );
 
 
 
104
 
105
  foreach ( $images as $image ) {
106
  $id = absint( $image['id'] );
@@ -109,7 +111,7 @@ function _do_wp_ajax_imagify_ngg_get_unoptimized_attachment_ids() {
109
  /** This filter is documented in inc/functions/process.php. */
110
  $file_path = apply_filters( 'imagify_file_path', $file_path );
111
 
112
- if ( ! $file_path || ! file_exists( $file_path ) ) {
113
  continue;
114
  }
115
 
@@ -131,7 +133,7 @@ function _do_wp_ajax_imagify_ngg_get_unoptimized_attachment_ids() {
131
  }
132
 
133
  // Don't try to re-optimize if there is no backup file.
134
- if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! file_exists( $attachment_backup_path ) ) {
135
  continue;
136
  }
137
 
@@ -148,9 +150,9 @@ function _do_wp_ajax_imagify_ngg_get_unoptimized_attachment_ids() {
148
  $data[ '_' . $id ] = $storage->get_image_url( $id );
149
  } // End foreach().
150
 
151
- if ( $data ) {
152
- wp_send_json_success( $data );
153
  }
154
 
155
- wp_send_json_error( array( 'message' => 'no-images' ) );
156
  }
7
  add_action( 'admin_post_imagify_manual_override_upload', '_do_admin_post_imagify_ngg_user_capacity', 5 );
8
  add_action( 'wp_ajax_imagify_restore_upload', '_do_admin_post_imagify_ngg_user_capacity', 5 );
9
  add_action( 'admin_post_imagify_restore_upload', '_do_admin_post_imagify_ngg_user_capacity', 5 );
10
+ add_action( 'wp_ajax_imagify_get_folder_type_data', '_do_admin_post_imagify_ngg_user_capacity', 5 );
11
+ add_action( 'wp_ajax_bulk_info_seen_callback', '_do_admin_post_imagify_ngg_user_capacity', 5 );
12
  /**
13
  * On manual optimization, manual re-optimization, and manual restoration, filter the user capacity to operate Imagify within NGG.
14
  *
26
  * Filter the current user capability to operate Imagify.
27
  *
28
  * @since 1.6.11
29
+ * @see imagify_get_capacity()
30
  * @author Grégory Viguier
31
  *
32
  * @param bool $user_can Tell if the current user has the required capacity to operate Imagify.
33
  * @param string $capacity The user capacity.
34
+ * @param string $describer Capacity describer. See imagify_get_capacity() for possible values. Can also be a "real" user capacity.
35
  * @param int $post_id A post ID (a gallery ID for NGG).
36
  * @return bool
37
  */
72
  function _do_wp_ajax_imagify_ngg_get_unoptimized_attachment_ids() {
73
  global $wpdb;
74
 
75
+ $ajax_post = Imagify_Admin_Ajax_Post::get_instance();
 
 
 
76
 
77
+ imagify_check_nonce( 'imagify-bulk-upload' );
78
+ imagify_check_user_capacity( 'bulk-optimize' );
79
+ $ajax_post->check_can_optimize();
80
 
81
  @set_time_limit( 0 );
82
 
83
+ $optimization_level = $ajax_post->get_optimization_level();
 
84
 
85
  $storage = C_Gallery_Storage::get_instance();
86
  $ngg_table = $wpdb->prefix . 'ngg_pictures';
98
  imagify_get_unoptimized_attachment_limit()
99
  ), ARRAY_A );
100
 
101
+ if ( ! $images ) {
102
+ wp_send_json_success( array() );
103
+ }
104
+
105
+ $filesystem = imagify_get_filesystem();
106
 
107
  foreach ( $images as $image ) {
108
  $id = absint( $image['id'] );
111
  /** This filter is documented in inc/functions/process.php. */
112
  $file_path = apply_filters( 'imagify_file_path', $file_path );
113
 
114
+ if ( ! $file_path || ! $filesystem->exists( $file_path ) ) {
115
  continue;
116
  }
117
 
133
  }
134
 
135
  // Don't try to re-optimize if there is no backup file.
136
+ if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! $filesystem->exists( $attachment_backup_path ) ) {
137
  continue;
138
  }
139
 
150
  $data[ '_' . $id ] = $storage->get_image_url( $id );
151
  } // End foreach().
152
 
153
+ if ( ! $data ) {
154
+ wp_send_json_success( array() );
155
  }
156
 
157
+ wp_send_json_success( $data );
158
  }
inc/3rd-party/nextgen-gallery/inc/admin/bulk.php CHANGED
@@ -1,22 +1,81 @@
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
- add_action( 'admin_init', '_imagify_ngg_update_bulk_stats' );
5
  /**
6
- * Update all Imagify stats for NGG Bulk Optimization.
7
  *
8
- * @since 1.5
9
- * @author Jonathan Buttigieg
 
 
 
10
  */
11
- function _imagify_ngg_update_bulk_stats() {
12
- if ( empty( $_GET['page'] ) || 'imagify-ngg-bulk-optimization' !== $_GET['page'] ) { // WPCS: CSRF ok.
13
- return;
14
  }
15
 
 
16
  add_filter( 'imagify_count_attachments' , 'imagify_ngg_count_attachments' );
17
  add_filter( 'imagify_count_optimized_attachments' , 'imagify_ngg_count_optimized_attachments' );
18
  add_filter( 'imagify_count_error_attachments' , 'imagify_ngg_count_error_attachments' );
19
  add_filter( 'imagify_count_unoptimized_attachments' , 'imagify_ngg_count_unoptimized_attachments' );
20
  add_filter( 'imagify_percent_optimized_attachments' , 'imagify_ngg_percent_optimized_attachments' );
21
  add_filter( 'imagify_count_saving_data' , 'imagify_ngg_count_saving_data', 8 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
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
 
19
+ add_filter( 'imagify_get_folder_type_data' , 'imagify_ngg_get_folder_type_data', 10, 2 );
20
  add_filter( 'imagify_count_attachments' , 'imagify_ngg_count_attachments' );
21
  add_filter( 'imagify_count_optimized_attachments' , 'imagify_ngg_count_optimized_attachments' );
22
  add_filter( 'imagify_count_error_attachments' , 'imagify_ngg_count_error_attachments' );
23
  add_filter( 'imagify_count_unoptimized_attachments' , 'imagify_ngg_count_unoptimized_attachments' );
24
  add_filter( 'imagify_percent_optimized_attachments' , 'imagify_ngg_percent_optimized_attachments' );
25
  add_filter( 'imagify_count_saving_data' , 'imagify_ngg_count_saving_data', 8 );
26
+
27
+ $total_saving_data = imagify_count_saving_data();
28
+
29
+ // Global chart.
30
+ $data['total_attachments'] += imagify_ngg_count_attachments();
31
+ $data['unoptimized_attachments'] += imagify_ngg_count_unoptimized_attachments();
32
+ $data['optimized_attachments'] += imagify_ngg_count_optimized_attachments();
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.
41
+ $data['groups']['NGG'] = array(
42
+ /**
43
+ * The group_id corresponds to the file names like 'part-bulk-optimization-results-row-{$group_id}'.
44
+ * It is also used in get_imagify_localize_script_translations() and imagify_get_folder_type_data().
45
+ */
46
+ 'group_id' => 'library',
47
+ 'context' => 'NGG',
48
+ 'title' => __( 'NextGen Galleries', 'imagify' ),
49
+ /* translators: 1 is the opening of a link, 2 is the closing of this link. */
50
+ 'footer' => sprintf( __( 'You can also re-optimize your images more finely directly in each %1$sgallery%2$s.', 'imagify' ), '<a href="' . esc_url( admin_url( 'admin.php?page=nggallery-manage-gallery' ) ) . '">', '</a>' ),
51
+ );
52
+
53
+ return $data;
54
+ }
55
+
56
+ /**
57
+ * Provide custom folder type data.
58
+ *
59
+ * @since 1.7
60
+ * @author Grégory Viguier
61
+ *
62
+ * @param array $data An array with keys corresponding to cell classes, and values formatted with HTML.
63
+ * @param string $folder_type A folder type.
64
+ * @return array
65
+ */
66
+ function imagify_ngg_get_folder_type_data( $data, $folder_type ) {
67
+ if ( 'NGG' !== $folder_type ) {
68
+ return $data;
69
+ }
70
+
71
+ // Already filtered in imagify_ngg_bulk_page_data().
72
+ $total_saving_data = imagify_count_saving_data();
73
+
74
+ return array(
75
+ 'images-optimized' => imagify_ngg_count_optimized_attachments(),
76
+ 'errors' => imagify_ngg_count_error_attachments(),
77
+ 'optimized' => $total_saving_data['optimized_size'],
78
+ 'original' => $total_saving_data['original_size'],
79
+ 'errors_url' => admin_url( 'admin.php?page=nggallery-manage-gallery' ),
80
+ );
81
  }
inc/3rd-party/nextgen-gallery/inc/admin/db.php DELETED
@@ -1,17 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- add_action( 'admin_init' , '_imagify_create_ngg_table' );
5
- /**
6
- * Create the Imagify table needed for NGG compatibility.
7
- *
8
- * @since 1.5
9
- * @author Jonathan Buttigieg
10
- */
11
- function _imagify_create_ngg_table() {
12
- global $wpdb;
13
-
14
- if ( ! get_option( $wpdb->prefix . 'ngg_imagify_data_db_version' ) ) {
15
- Imagify_NGG_DB::get_instance()->create_table();
16
- }
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/3rd-party/nextgen-gallery/inc/admin/enqueue.php CHANGED
@@ -34,12 +34,16 @@ function _imagify_ngg_admin_print_styles() {
34
 
35
  $l10n = $assets->get_localization_data( 'bulk', array(
36
  'heartbeatId' => 'update_ngg_bulk_data',
37
- 'ajaxAction' => 'imagify_ngg_get_unoptimized_attachment_ids',
38
- 'ajaxContext' => 'NGG',
39
- /** This filter is documented in inc/classes/class-imagify-assets.php */
40
- 'bufferSize' => apply_filters( 'imagify_bulk_buffer_size', 4 ),
41
  ) );
42
 
 
 
 
 
 
43
  $assets->enqueue_assets( array( 'pricing-modal', 'bulk' ) )->localize( 'imagifyBulk', $l10n );
44
 
45
  // Intercom.
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
+ ),
 
40
  ) );
41
 
42
+ $l10n['ajaxActions']['libraryFetch'] = 'imagify_ngg_get_unoptimized_attachment_ids';
43
+
44
+ /** This filter is documented in inc/classes/class-imagify-assets.php */
45
+ $l10n['bufferSizes'] = apply_filters( 'imagify_bulk_buffer_sizes', $l10n['bufferSizes'] );
46
+
47
  $assets->enqueue_assets( array( 'pricing-modal', 'bulk' ) )->localize( 'imagifyBulk', $l10n );
48
 
49
  // Intercom.
inc/3rd-party/nextgen-gallery/inc/admin/heartbeat.php CHANGED
@@ -23,15 +23,18 @@ function _imagify_ngg_heartbeat_received( $response, $data ) {
23
  $user = new Imagify_User();
24
 
25
  $response['imagify_bulk_data'] = array(
26
- 'already_optimized_attachments' => number_format_i18n( $saving_data['count'] ),
27
- 'optimized_attachments' => imagify_ngg_count_optimized_attachments(),
 
 
28
  'unoptimized_attachments' => imagify_ngg_count_unoptimized_attachments(),
 
29
  'errors_attachments' => imagify_ngg_count_error_attachments(),
30
- 'optimized_attachments_percent' => imagify_ngg_percent_optimized_attachments(),
 
 
 
31
  'optimized_percent' => $saving_data['percent'],
32
- 'original_human' => size_format( $saving_data['original_size'], 1 ),
33
- 'optimized_human' => size_format( $saving_data['optimized_size'], 1 ),
34
- 'unconsumed_quota' => $user->get_percent_unconsumed_quota(),
35
  );
36
 
37
  return $response;
@@ -42,6 +45,6 @@ function _imagify_ngg_heartbeat_received( $response, $data ) {
42
  *
43
  * @since 1.5
44
  */
45
- if ( 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'imagify-ngg-bulk-optimization' === $_GET['page'] ) { // WPCS: CSRF ok.
46
  add_filter( 'heartbeat_settings', '_imagify_heartbeat_settings', IMAGIFY_INT_MAX );
47
  }
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;
45
  *
46
  * @since 1.5
47
  */
48
+ if ( 'admin.php' === $pagenow && isset( $_GET['page'] ) && imagify_get_ngg_bulk_screen_slug() === $_GET['page'] ) { // WPCS: CSRF ok.
49
  add_filter( 'heartbeat_settings', '_imagify_heartbeat_settings', IMAGIFY_INT_MAX );
50
  }
inc/3rd-party/nextgen-gallery/inc/admin/menu.php CHANGED
@@ -13,5 +13,5 @@ function _imagify_ngg_bulk_optimization_menu() {
13
  return;
14
  }
15
 
16
- add_submenu_page( NGGFOLDER, __( 'Bulk Optimization', 'imagify' ), __( 'Bulk Optimization', 'imagify' ), imagify_get_capacity( 'bulk-optimize' ), IMAGIFY_SLUG . '-ngg-bulk-optimization', '_imagify_display_bulk_page' );
17
  }
13
  return;
14
  }
15
 
16
+ add_submenu_page( NGGFOLDER, __( 'Bulk Optimization', 'imagify' ), __( 'Bulk Optimization', 'imagify' ), imagify_get_capacity( 'bulk-optimize' ), imagify_get_ngg_bulk_screen_slug(), '_imagify_display_bulk_page' );
17
  }
inc/3rd-party/nextgen-gallery/inc/classes/class-imagify-ngg-attachment.php CHANGED
@@ -14,36 +14,35 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.1.3';
18
 
19
  /**
20
- * The image object.
21
- *
22
- * @since 1.5
23
  *
24
- * @var object A nggImage object.
25
- * @access public
 
26
  */
27
- public $image;
28
 
29
  /**
30
- * The attachment SQL data row.
31
- *
32
- * @since 1.5
33
  *
34
- * @var array
 
 
35
  * @access public
36
  */
37
- public $row;
38
 
39
  /**
40
  * Tell if the file mime type can be optimized by Imagify.
41
  *
42
- * @since 1.6.9
43
- *
44
- * @var bool
45
  * @access protected
46
- * see $this->is_mime_type_supported()
47
  */
48
  protected $is_mime_type_supported;
49
 
@@ -54,19 +53,17 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
54
  * @author Jonathan Buttigieg
55
  *
56
  * @param int|object $id An image attachment ID or a NGG object.
57
- * @return void
58
  */
59
  public function __construct( $id ) {
60
  if ( is_object( $id ) ) {
61
  $this->image = $id;
62
- $this->id = $id->pid;
63
  } else {
64
  $this->image = nggdb::find_image( absint( $id ) );
65
- $this->id = $this->image->pid;
66
  }
67
 
68
- $this->id = absint( $this->id );
69
- $this->row = $this->get_row();
70
 
71
  // Load nggAdmin class.
72
  $ngg_admin_functions_path = WP_PLUGIN_DIR . '/' . NGGFOLDER . '/products/photocrati_nextgen/modules/ngglegacy/admin/functions.php';
@@ -76,6 +73,40 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
76
  }
77
  }
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  /**
80
  * Get the attachment backup file path, even if the file doesn't exist.
81
  *
@@ -86,6 +117,10 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
86
  * @return string|bool The file path. False on failure.
87
  */
88
  public function get_raw_backup_path() {
 
 
 
 
89
  return get_imagify_ngg_attachment_backup_path( $this->get_original_path() );
90
  }
91
 
@@ -98,20 +133,11 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
98
  * @return string|false
99
  */
100
  public function get_backup_url() {
101
- return site_url( '/' ) . imagify_make_file_path_relative( $this->get_raw_backup_path() );
102
- }
 
103
 
104
- /**
105
- * Get the attachment SQL data row.
106
- *
107
- * @since 1.5
108
- * @author Jonathan Buttigieg
109
- *
110
- * @access public
111
- * @return array
112
- */
113
- public function get_row() {
114
- return Imagify_NGG_DB::get_instance()->get( $this->id );
115
  }
116
 
117
  /**
@@ -121,11 +147,11 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
121
  * @author Jonathan Buttigieg
122
  *
123
  * @access public
124
- * @return array|bool
125
  */
126
  public function get_data() {
127
- $row = $this->row ? $this->row : $this->get_row();
128
- return isset( $row['data'] ) ? maybe_unserialize( $row['data'] ) : false;
129
  }
130
 
131
  /**
@@ -138,7 +164,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
138
  * @return int|bool
139
  */
140
  public function get_optimization_level() {
141
- $row = $this->row ? $this->row : $this->get_row();
142
  return isset( $row['optimization_level'] ) ? (int) $row['optimization_level'] : false;
143
  }
144
 
@@ -152,34 +178,39 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
152
  * @return string|bool
153
  */
154
  public function get_status() {
155
- $row = $this->row ? $this->row : $this->get_row();
156
  return isset( $row['status'] ) ? $row['status'] : false;
157
  }
158
 
159
  /**
160
- * Get the original attachment path.
161
- *
162
- * @since 1.5
163
- * @author Jonathan Buttigieg
164
  *
 
165
  * @access public
166
- * @return string
 
 
167
  */
168
- public function get_original_path() {
169
- return $this->image->imagePath;
 
 
 
170
  }
171
 
172
  /**
173
- * Get the original attachment URL.
174
- *
175
- * @since 1.5
176
- * @author Jonathan Buttigieg
177
  *
 
178
  * @access public
179
- * @return string
180
  */
181
- public function get_original_url() {
182
- return $this->image->imageURL;
 
 
 
 
183
  }
184
 
185
  /**
@@ -202,7 +233,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
202
  return $this->is_mime_type_supported;
203
  }
204
 
205
- $mime_types = get_imagify_mime_type();
206
  $mime_types = array_flip( $mime_types );
207
 
208
  $this->is_mime_type_supported = isset( $mime_types[ $mime_type ] );
@@ -239,13 +270,12 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
239
  $size = @getimagesize( $this->get_original_path() );
240
 
241
  if ( isset( $size[0], $size[1] ) ) {
242
- $metadata = $this->image->meta_data;
243
- $metadata['width'] = $size[0];
244
- $metadata['height'] = $size[1];
245
- $metadata['full']['width'] = $size[0];
246
- $metadata['full']['height'] = $size[1];
247
 
248
- nggdb::update_image_meta( $this->id , $metadata );
249
  }
250
  }
251
 
@@ -255,16 +285,16 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
255
  * @since 1.5
256
  * @since 1.6.5 Not static anymore.
257
  * @since 1.6.6 Removed the attachment ID parameter.
 
258
  * @author Jonathan Buttigieg
259
  * @access public
260
  *
261
  * @param array $data The statistics data.
262
  * @param object $response The API response.
263
- * @param int $url The attachment URL.
264
  * @param string $size The attachment size key.
265
  * @return bool|array False if the original size has an error or an array contains the data for other result.
266
  */
267
- public function fill_data( $data, $response, $url, $size = 'full' ) {
268
  $data = is_array( $data ) ? $data : array();
269
  $data['sizes'] = ! empty( $data['sizes'] ) && is_array( $data['sizes'] ) ? $data['sizes'] : array();
270
 
@@ -277,6 +307,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
277
  }
278
 
279
  if ( is_wp_error( $response ) ) {
 
280
  $error = $response->get_error_message();
281
  $error_status = 'error';
282
 
@@ -291,34 +322,59 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
291
  $error_status = 'already_optimized';
292
  }
293
 
294
- Imagify_NGG_DB::get_instance()->update( $this->id, array(
 
295
  'pid' => $this->id,
296
  'status' => $error_status,
297
- 'data' => serialize( $data ),
298
  ) );
299
- $this->row = null;
300
 
301
  return false;
302
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  } else {
304
- $response = (object) array_merge( array(
305
- 'original_size' => 0,
306
- 'new_size' => 0,
307
- 'percent' => 0,
308
- ), (array) $response );
309
 
310
- $data['sizes'][ $size ] = array(
311
- 'success' => true,
312
- 'file_url' => $url,
313
- 'original_size' => $response->original_size,
314
- 'optimized_size' => $response->new_size,
315
- 'percent' => $response->percent,
316
- );
317
 
318
- $data['stats']['original_size'] += $response->original_size;
319
- $data['stats']['optimized_size'] += $response->new_size;
320
- $data['stats']['percent'] = round( ( ( $data['stats']['original_size'] - $data['stats']['optimized_size'] ) / $data['stats']['original_size'] ) * 100, 2 );
321
- } // End if().
 
 
 
 
 
 
322
 
323
  return $data;
324
  }
@@ -336,11 +392,11 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
336
  */
337
  public function optimize( $optimization_level = null, $metadata = array() ) {
338
  // Check if the attachment extension is allowed.
339
- if ( ! $this->is_mime_type_supported() ) {
340
  return;
341
  }
342
 
343
- $optimization_level = is_null( $optimization_level ) ? (int) get_imagify_option( 'optimization_level', 1 ) : (int) $optimization_level;
344
 
345
  // To avoid issue with "original_size" at 0 in "_imagify_data".
346
  if ( 0 === (int) $this->get_stats_data( 'original_size' ) ) {
@@ -352,9 +408,8 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
352
  return;
353
  }
354
 
355
- // Get file path & URL for original image.
356
  $attachment_path = $this->get_original_path();
357
- $attachment_url = $this->get_original_url();
358
 
359
  /**
360
  * Fires before optimizing an attachment.
@@ -376,16 +431,17 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
376
  'backup_path' => $this->get_raw_backup_path(),
377
  ) );
378
 
379
- $data = $this->fill_data( null, $response, $attachment_url );
380
 
381
  // Save the optimization level.
382
- Imagify_NGG_DB::get_instance()->update( $this->id, array(
 
383
  'pid' => $this->id,
384
  'optimization_level' => $optimization_level,
385
  ) );
386
- $this->row = null;
387
 
388
  if ( ! $data ) {
 
389
  delete_transient( 'imagify-ngg-async-in-progress-' . $this->id );
390
  return;
391
  }
@@ -394,11 +450,9 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
394
  $data = $this->optimize_thumbnails( $optimization_level, $data );
395
 
396
  // Save the status to success.
397
- Imagify_NGG_DB::get_instance()->update( $this->id, array(
398
- 'pid' => $this->id,
399
  'status' => 'success',
400
  ) );
401
- $this->row = null;
402
 
403
  /**
404
  * Fires after optimizing an attachment.
@@ -469,7 +523,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
469
  return $data;
470
  }
471
 
472
- $optimization_level = is_null( $optimization_level ) ? (int) get_imagify_option( 'optimization_level', 1 ) : (int) $optimization_level;
473
 
474
  /**
475
  * Fires before optimizing all thumbnails.
@@ -497,7 +551,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
497
  'backup' => false,
498
  ) );
499
 
500
- $data = $this->fill_data( $data, $response, $thumbnail_url, $size_key );
501
 
502
  /**
503
  * Filter the optimization data of a specific thumbnail.
@@ -516,11 +570,9 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
516
  $data = apply_filters( 'imagify_fill_ngg_thumbnail_data', $data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level );
517
  }
518
 
519
- Imagify_NGG_DB::get_instance()->update( $this->id, array(
520
- 'pid' => $this->id,
521
- 'data' => serialize( $data ),
522
  ) );
523
- $this->row = null;
524
  } // End if().
525
 
526
  /**
@@ -549,7 +601,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
549
  */
550
  public function restore() {
551
  // Check if the attachment extension is allowed.
552
- if ( ! $this->is_mime_type_supported() ) {
553
  return new WP_Error( 'mime_not_type_supported', __( 'Mime type not supported.', 'imagify' ) );
554
  }
555
 
@@ -596,8 +648,7 @@ class Imagify_NGG_Attachment extends Imagify_Attachment {
596
  /**
597
  * Remove Imagify data.
598
  */
599
- Imagify_NGG_DB::get_instance()->delete( $image->pid );
600
- $this->row = null;
601
 
602
  /**
603
  * Fill in the NGG meta data.
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.3';
18
 
19
  /**
20
+ * The attachment SQL DB class.
 
 
21
  *
22
+ * @var string
23
+ * @since 1.7
24
+ * @access protected
25
  */
26
+ protected $db_class_name = 'Imagify_NGG_DB';
27
 
28
  /**
29
+ * The image object.
 
 
30
  *
31
+ * @var object A nggImage object.
32
+ * @since 1.5
33
+ * @since 1.7 Not public anymore.
34
  * @access public
35
  */
36
+ protected $image;
37
 
38
  /**
39
  * Tell if the file mime type can be optimized by Imagify.
40
  *
41
+ * @var bool
42
+ * @since 1.6.9
43
+ * @since 1.7 Not public anymore.
44
  * @access protected
45
+ * @see $this->is_mime_type_supported()
46
  */
47
  protected $is_mime_type_supported;
48
 
53
  * @author Jonathan Buttigieg
54
  *
55
  * @param int|object $id An image attachment ID or a NGG object.
 
56
  */
57
  public function __construct( $id ) {
58
  if ( is_object( $id ) ) {
59
  $this->image = $id;
60
+ $this->id = (int) $id->pid;
61
  } else {
62
  $this->image = nggdb::find_image( absint( $id ) );
63
+ $this->id = ! empty( $this->image->pid ) ? (int) $this->image->pid : 0;
64
  }
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';
73
  }
74
  }
75
 
76
+ /**
77
+ * Get the original attachment path.
78
+ *
79
+ * @since 1.5
80
+ * @author Jonathan Buttigieg
81
+ *
82
+ * @access public
83
+ * @return string
84
+ */
85
+ public function get_original_path() {
86
+ if ( ! $this->is_valid() ) {
87
+ return '';
88
+ }
89
+
90
+ return $this->image->imagePath;
91
+ }
92
+
93
+ /**
94
+ * Get the original attachment URL.
95
+ *
96
+ * @since 1.5
97
+ * @author Jonathan Buttigieg
98
+ *
99
+ * @access public
100
+ * @return string
101
+ */
102
+ public function get_original_url() {
103
+ if ( ! $this->is_valid() ) {
104
+ return '';
105
+ }
106
+
107
+ return $this->image->imageURL;
108
+ }
109
+
110
  /**
111
  * Get the attachment backup file path, even if the file doesn't exist.
112
  *
117
  * @return string|bool The file path. False on failure.
118
  */
119
  public function get_raw_backup_path() {
120
+ if ( ! $this->is_valid() ) {
121
+ return false;
122
+ }
123
+
124
  return get_imagify_ngg_attachment_backup_path( $this->get_original_path() );
125
  }
126
 
133
  * @return string|false
134
  */
135
  public function get_backup_url() {
136
+ if ( ! $this->is_valid() ) {
137
+ return false;
138
+ }
139
 
140
+ return site_url( '/' ) . imagify_make_file_path_relative( $this->get_raw_backup_path() );
 
 
 
 
 
 
 
 
 
 
141
  }
142
 
143
  /**
147
  * @author Jonathan Buttigieg
148
  *
149
  * @access public
150
+ * @return array
151
  */
152
  public function get_data() {
153
+ $row = $this->get_row();
154
+ return isset( $row['data'] ) ? $row['data'] : array();
155
  }
156
 
157
  /**
164
  * @return int|bool
165
  */
166
  public function get_optimization_level() {
167
+ $row = $this->get_row();
168
  return isset( $row['optimization_level'] ) ? (int) $row['optimization_level'] : false;
169
  }
170
 
178
  * @return string|bool
179
  */
180
  public function get_status() {
181
+ $row = $this->get_row();
182
  return isset( $row['status'] ) ? $row['status'] : false;
183
  }
184
 
185
  /**
186
+ * Get width and height of the original image.
 
 
 
187
  *
188
+ * @since 1.7
189
  * @access public
190
+ * @author Grégory Viguier
191
+ *
192
+ * @return array
193
  */
194
+ public function get_dimensions() {
195
+ return array(
196
+ 'width' => ! empty( $this->image->meta_data['width'] ) ? (int) $this->image->meta_data['width'] : 0,
197
+ 'height' => ! empty( $this->image->meta_data['height'] ) ? (int) $this->image->meta_data['height'] : 0,
198
+ );
199
  }
200
 
201
  /**
202
+ * Delete the data related to optimization.
 
 
 
203
  *
204
+ * @since 1.7
205
  * @access public
206
+ * @author Grégory Viguier
207
  */
208
+ public function delete_imagify_data() {
209
+ if ( ! $this->get_row() ) {
210
+ return;
211
+ }
212
+
213
+ $this->delete_row();
214
  }
215
 
216
  /**
233
  return $this->is_mime_type_supported;
234
  }
235
 
236
+ $mime_types = imagify_get_mime_types();
237
  $mime_types = array_flip( $mime_types );
238
 
239
  $this->is_mime_type_supported = isset( $mime_types[ $mime_type ] );
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
  }
280
  }
281
 
285
  * @since 1.5
286
  * @since 1.6.5 Not static anymore.
287
  * @since 1.6.6 Removed the attachment ID parameter.
288
+ * @since 1.7 Removed the image URL parameter.
289
  * @author Jonathan Buttigieg
290
  * @access public
291
  *
292
  * @param array $data The statistics data.
293
  * @param object $response The API response.
 
294
  * @param string $size The attachment size key.
295
  * @return bool|array False if the original size has an error or an array contains the data for other result.
296
  */
297
+ public function fill_data( $data, $response, $size = 'full' ) {
298
  $data = is_array( $data ) ? $data : array();
299
  $data['sizes'] = ! empty( $data['sizes'] ) && is_array( $data['sizes'] ) ? $data['sizes'] : array();
300
 
307
  }
308
 
309
  if ( is_wp_error( $response ) ) {
310
+ // Error or already optimized.
311
  $error = $response->get_error_message();
312
  $error_status = 'error';
313
 
322
  $error_status = 'already_optimized';
323
  }
324
 
325
+ $this->update_row( array(
326
+ // The pid column is needed in case the row doesn't exist yet.
327
  'pid' => $this->id,
328
  'status' => $error_status,
329
+ 'data' => $data,
330
  ) );
 
331
 
332
  return false;
333
  }
334
+
335
+ return $data;
336
+ }
337
+
338
+ // Success.
339
+ $old_data = $this->get_data();
340
+ $original_size = ! empty( $old_data['sizes'][ $size ]['original_size'] ) ? (int) $old_data['sizes'][ $size ]['original_size'] : 0;
341
+
342
+ $response = (object) array_merge( array(
343
+ 'original_size' => 0,
344
+ 'new_size' => 0,
345
+ 'percent' => 0,
346
+ ), (array) $response );
347
+
348
+ if ( ! empty( $response->original_size ) && ! $original_size ) {
349
+ $original_size = (int) $response->original_size;
350
+ }
351
+
352
+ if ( ! empty( $response->new_size ) ) {
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 ) {
361
+ $percent = round( ( $original_size - $optimized_size ) / $original_size * 100, 2 );
362
+ } elseif ( ! empty( $response->percent ) ) {
363
+ $percent = round( $response->percent, 2 );
364
+ } else {
365
+ $percent = 0;
366
+ }
367
 
368
+ $data['sizes'][ $size ] = array(
369
+ 'success' => true,
370
+ 'original_size' => $original_size,
371
+ 'optimized_size' => $optimized_size,
372
+ 'percent' => $percent,
373
+ );
374
+
375
+ $data['stats']['original_size'] += $original_size;
376
+ $data['stats']['optimized_size'] += $optimized_size;
377
+ $data['stats']['percent'] = round( ( ( $data['stats']['original_size'] - $data['stats']['optimized_size'] ) / $data['stats']['original_size'] ) * 100, 2 );
378
 
379
  return $data;
380
  }
392
  */
393
  public function optimize( $optimization_level = null, $metadata = array() ) {
394
  // Check if the attachment extension is allowed.
395
+ if ( ! $this->is_extension_supported() ) {
396
  return;
397
  }
398
 
399
+ $optimization_level = isset( $optimization_level ) ? (int) $optimization_level : get_imagify_option( 'optimization_level' );
400
 
401
  // To avoid issue with "original_size" at 0 in "_imagify_data".
402
  if ( 0 === (int) $this->get_stats_data( 'original_size' ) ) {
408
  return;
409
  }
410
 
411
+ // Get file path for original image.
412
  $attachment_path = $this->get_original_path();
 
413
 
414
  /**
415
  * Fires before optimizing an attachment.
431
  'backup_path' => $this->get_raw_backup_path(),
432
  ) );
433
 
434
+ $data = $this->fill_data( null, $response );
435
 
436
  // Save the optimization level.
437
+ $this->update_row( array(
438
+ // The pid column is needed in case the row doesn't exist yet.
439
  'pid' => $this->id,
440
  'optimization_level' => $optimization_level,
441
  ) );
 
442
 
443
  if ( ! $data ) {
444
+ // Already optimized.
445
  delete_transient( 'imagify-ngg-async-in-progress-' . $this->id );
446
  return;
447
  }
450
  $data = $this->optimize_thumbnails( $optimization_level, $data );
451
 
452
  // Save the status to success.
453
+ $this->update_row( array(
 
454
  'status' => 'success',
455
  ) );
 
456
 
457
  /**
458
  * Fires after optimizing an attachment.
523
  return $data;
524
  }
525
 
526
+ $optimization_level = isset( $optimization_level ) ? (int) $optimization_level : get_imagify_option( 'optimization_level' );
527
 
528
  /**
529
  * Fires before optimizing all thumbnails.
551
  'backup' => false,
552
  ) );
553
 
554
+ $data = $this->fill_data( $data, $response, $size_key );
555
 
556
  /**
557
  * Filter the optimization data of a specific thumbnail.
570
  $data = apply_filters( 'imagify_fill_ngg_thumbnail_data', $data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level );
571
  }
572
 
573
+ $this->update_row( array(
574
+ 'data' => $data,
 
575
  ) );
 
576
  } // End if().
577
 
578
  /**
601
  */
602
  public function restore() {
603
  // Check if the attachment extension is allowed.
604
+ if ( ! $this->is_extension_supported() ) {
605
  return new WP_Error( 'mime_not_type_supported', __( 'Mime type not supported.', 'imagify' ) );
606
  }
607
 
648
  /**
649
  * Remove Imagify data.
650
  */
651
+ $this->delete_row();
 
652
 
653
  /**
654
  * Fill in the NGG meta data.
inc/3rd-party/nextgen-gallery/inc/classes/class-imagify-ngg-db.php CHANGED
@@ -14,38 +14,54 @@ class Imagify_NGG_DB extends Imagify_Abstract_DB {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.0.2';
18
 
19
  /**
20
  * The single instance of the class.
21
  *
 
22
  * @since 1.5
23
  * @access protected
24
- *
25
- * @var object
26
  */
27
  protected static $_instance;
28
 
29
  /**
30
- * Get things started.
31
  *
32
- * @since 1.5
 
33
  * @access protected
34
- * @author Jonathan Buttigieg
35
  */
36
- protected function __construct() {
37
- global $wpdb;
38
 
39
- $this->table_name = $wpdb->prefix . 'ngg_imagify_data';
40
- $this->primary_key = 'pid'; // Instead of data_id.
41
- $this->version = '1.0';
 
 
 
 
 
 
42
 
43
- // Database declaration.
44
- $wpdb->ngg_imagify_data = $this->table_name;
 
 
 
 
 
 
45
 
46
- // Add table to the index of WordPress tables.
47
- $wpdb->tables[] = 'ngg_imagify_data';
48
- }
 
 
 
 
 
 
49
 
50
  /**
51
  * Get the main Instance.
@@ -94,54 +110,31 @@ class Imagify_NGG_DB extends Imagify_Abstract_DB {
94
  */
95
  public function get_column_defaults() {
96
  return array(
 
97
  'pid' => 0,
98
  'optimization_level' => '',
99
  'status' => '',
100
- 'data' => '',
101
  );
102
  }
103
 
104
  /**
105
- * Create the table.
106
  *
107
- * @since 1.5
108
- * @access public
109
- * @author Jonathan Buttigieg
110
- */
111
- public function create_table() {
112
- global $wpdb;
113
-
114
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
115
-
116
- $charset_collate = $wpdb->get_charset_collate();
117
-
118
- $sql = "CREATE TABLE {$this->table_name} (
119
- data_id int(11) NOT NULL AUTO_INCREMENT,
120
- pid int(11) NOT NULL,
121
- optimization_level varchar(1) NOT NULL,
122
- status varchar(30) NOT NULL,
123
- data longtext NOT NULL,
124
- PRIMARY KEY (data_id)
125
- ) $charset_collate;";
126
-
127
- maybe_create_table( $this->table_name, $sql );
128
-
129
- update_option( $this->table_name . '_db_version', $this->version );
130
- }
131
-
132
- /**
133
- * Main Instance.
134
- * Ensures only one instance of class is loaded or can be loaded.
135
- * Well, actually it ensures nothing since it's not a full singleton pattern.
136
- *
137
- * @since 1.5
138
- * @access public
139
- * @author Jonathan Buttigieg
140
  *
141
- * @return object Main instance.
142
  */
143
- public static function instance() {
144
- _deprecated_function( get_class( $this ) . '::' . __FUNCTION__ . '()', '1.6.5', 'Imagify_NGG_DB::get_instance()' );
145
- return self::get_instance();
 
 
 
 
 
 
146
  }
147
  }
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.1';
18
 
19
  /**
20
  * The single instance of the class.
21
  *
22
+ * @var object
23
  * @since 1.5
24
  * @access protected
 
 
25
  */
26
  protected static $_instance;
27
 
28
  /**
29
+ * The suffix used in the name of the database table (so, without the wpdb prefix).
30
  *
31
+ * @var string
32
+ * @since 1.7
33
  * @access protected
 
34
  */
35
+ protected $table = 'ngg_imagify_data';
 
36
 
37
+ /**
38
+ * The version of our database table.
39
+ *
40
+ * @var int
41
+ * @since 1.5
42
+ * @since 1.7 Not public anymore, now an integer.
43
+ * @access protected
44
+ */
45
+ protected $table_version = 100;
46
 
47
+ /**
48
+ * Tell if the table is the same for each site of a Multisite.
49
+ *
50
+ * @var bool
51
+ * @since 1.7
52
+ * @access protected
53
+ */
54
+ protected $table_is_global = false;
55
 
56
+ /**
57
+ * The name of the primary column.
58
+ *
59
+ * @var string
60
+ * @since 1.5
61
+ * @since 1.7 Not public anymore.
62
+ * @access protected
63
+ */
64
+ protected $primary_key = 'pid';
65
 
66
  /**
67
  * Get the main Instance.
110
  */
111
  public function get_column_defaults() {
112
  return array(
113
+ 'data_id' => 0,
114
  'pid' => 0,
115
  'optimization_level' => '',
116
  'status' => '',
117
+ 'data' => array(),
118
  );
119
  }
120
 
121
  /**
122
+ * Get the query to create the table fields.
123
  *
124
+ * @since 1.7
125
+ * @access protected
126
+ * @author Grégory Viguier
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  *
128
+ * @return string
129
  */
130
+ protected function get_table_schema() {
131
+ return "
132
+ data_id int(11) unsigned NOT NULL AUTO_INCREMENT,
133
+ pid int(11) unsigned NOT NULL default 0,
134
+ optimization_level varchar(1) NOT NULL default '',
135
+ status varchar(30) NOT NULL default '',
136
+ data longtext NOT NULL default '',
137
+ PRIMARY KEY (data_id),
138
+ KEY pid (pid)";
139
  }
140
  }
inc/3rd-party/nextgen-gallery/inc/common/attachments.php CHANGED
@@ -78,7 +78,7 @@ function _imagify_ngg_media_library_imported_image_data( $image, $attachment ) {
78
  'pid' => $image->pid,
79
  'optimization_level' => $attachment->get_optimization_level(),
80
  'status' => $attachment->get_status(),
81
- 'data' => maybe_serialize( array(
82
  'sizes' => array(
83
  'full' => $full_size,
84
  ),
@@ -87,7 +87,7 @@ function _imagify_ngg_media_library_imported_image_data( $image, $attachment ) {
87
  'optimized_size' => $full_size['optimized_size'],
88
  'percent' => $full_size['percent'],
89
  ),
90
- ) ),
91
  ) );
92
 
93
  $imagify_image = new Imagify_NGG_Attachment( $image->pid );
78
  'pid' => $image->pid,
79
  'optimization_level' => $attachment->get_optimization_level(),
80
  'status' => $attachment->get_status(),
81
+ 'data' => array(
82
  'sizes' => array(
83
  'full' => $full_size,
84
  ),
87
  'optimized_size' => $full_size['optimized_size'],
88
  'percent' => $full_size['percent'],
89
  ),
90
+ ),
91
  ) );
92
 
93
  $imagify_image = new Imagify_NGG_Attachment( $image->pid );
inc/3rd-party/nextgen-gallery/inc/functions/admin-stats.php CHANGED
@@ -38,7 +38,9 @@ function imagify_ngg_count_error_attachments() {
38
  return $count;
39
  }
40
 
41
- $count = (int) Imagify_NGG_DB::get_instance()->get_column_by( 'COUNT(*)', 'status', 'error' );
 
 
42
 
43
  return $count;
44
  }
@@ -58,7 +60,9 @@ function imagify_ngg_count_optimized_attachments() {
58
  return $count;
59
  }
60
 
61
- $count = (int) Imagify_NGG_DB::get_instance()->get_column_in( 'COUNT(*)', 'status', array( 'success', 'already_optimized' ) );
 
 
62
 
63
  return $count;
64
  }
38
  return $count;
39
  }
40
 
41
+ $ngg_db = Imagify_NGG_DB::get_instance();
42
+ $key = $ngg_db->get_primary_key();
43
+ $count = (int) $ngg_db->get_var_by( "COUNT($key)", 'status', 'error' );
44
 
45
  return $count;
46
  }
60
  return $count;
61
  }
62
 
63
+ $ngg_db = Imagify_NGG_DB::get_instance();
64
+ $key = $ngg_db->get_primary_key();
65
+ $count = (int) $ngg_db->get_var_in( "COUNT($key)", 'status', array( 'success', 'already_optimized' ) );
66
 
67
  return $count;
68
  }
inc/3rd-party/nextgen-gallery/inc/functions/common.php CHANGED
@@ -6,10 +6,11 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
6
  * It is meant to be used to filter 'imagify_capacity'.
7
  *
8
  * @since 1.6.11
 
9
  * @author Grégory Viguier
10
  *
11
  * @param string $capacity The user capacity.
12
- * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', and 'auto-optimize'.
13
  * @return string
14
  */
15
  function imagify_get_ngg_capacity( $capacity = 'edit_post', $describer = 'manual-optimize' ) {
@@ -35,5 +36,17 @@ function imagify_get_ngg_bulk_screen_id() {
35
  $ngg_menu_slug = defined( 'NGGFOLDER' ) ? plugin_basename( NGGFOLDER ) : 'nextgen-gallery';
36
  $ngg_menu_slug = isset( $admin_page_hooks[ $ngg_menu_slug ] ) ? $admin_page_hooks[ $ngg_menu_slug ] : 'gallery';
37
 
38
- return $ngg_menu_slug . '_page_' . IMAGIFY_SLUG . '-ngg-bulk-optimization';
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
6
  * It is meant to be used to filter 'imagify_capacity'.
7
  *
8
  * @since 1.6.11
9
+ * @see imagify_get_capacity()
10
  * @author Grégory Viguier
11
  *
12
  * @param string $capacity The user capacity.
13
+ * @param string $describer Capacity describer. See imagify_get_capacity() for possible values. Can also be a "real" user capacity.
14
  * @return string
15
  */
16
  function imagify_get_ngg_capacity( $capacity = 'edit_post', $describer = 'manual-optimize' ) {
36
  $ngg_menu_slug = defined( 'NGGFOLDER' ) ? plugin_basename( NGGFOLDER ) : 'nextgen-gallery';
37
  $ngg_menu_slug = isset( $admin_page_hooks[ $ngg_menu_slug ] ) ? $admin_page_hooks[ $ngg_menu_slug ] : 'gallery';
38
 
39
+ return $ngg_menu_slug . '_page_' . imagify_get_ngg_bulk_screen_slug();
40
+ }
41
+
42
+ /**
43
+ * Get NGG Bulk Optimization screen slug.
44
+ *
45
+ * @since 1.7
46
+ * @author Grégory Viguier
47
+ *
48
+ * @return string
49
+ */
50
+ function imagify_get_ngg_bulk_screen_slug() {
51
+ return IMAGIFY_SLUG . '-ngg-bulk-optimization';
52
  }
inc/3rd-party/nextgen-gallery/nextgen-gallery.php CHANGED
@@ -18,12 +18,11 @@ require( IMAGIFY_NGG_FUNCTIONS_PATH . 'common.php' );
18
  require( IMAGIFY_NGG_COMMON_PATH . 'attachments.php' );
19
 
20
  Imagify_NGG::get_instance()->init();
21
- Imagify_NGG_DB::get_instance(); // Required to set $wpdb->ngg_imagify_data.
22
 
23
  if ( is_admin() ) {
24
  require( IMAGIFY_NGG_ADMIN_PATH . 'enqueue.php' );
25
  require( IMAGIFY_NGG_ADMIN_PATH . 'ajax.php' );
26
- require( IMAGIFY_NGG_ADMIN_PATH . 'db.php' );
27
  require( IMAGIFY_NGG_ADMIN_PATH . 'menu.php' );
28
  require( IMAGIFY_NGG_ADMIN_PATH . 'gallery.php' );
29
  require( IMAGIFY_NGG_ADMIN_PATH . 'bulk.php' );
18
  require( IMAGIFY_NGG_COMMON_PATH . 'attachments.php' );
19
 
20
  Imagify_NGG::get_instance()->init();
21
+ Imagify_NGG_DB::get_instance()->init();
22
 
23
  if ( is_admin() ) {
24
  require( IMAGIFY_NGG_ADMIN_PATH . 'enqueue.php' );
25
  require( IMAGIFY_NGG_ADMIN_PATH . 'ajax.php' );
 
26
  require( IMAGIFY_NGG_ADMIN_PATH . 'menu.php' );
27
  require( IMAGIFY_NGG_ADMIN_PATH . 'gallery.php' );
28
  require( IMAGIFY_NGG_ADMIN_PATH . 'bulk.php' );
inc/3rd-party/wp-retina-2x.php CHANGED
@@ -81,7 +81,7 @@ if ( function_exists( 'wr2x_get_retina' ) ) :
81
  ) );
82
  $attachment = get_imagify_attachment( 'wp', $id, 'imagify_fill_thumbnail_data' );
83
 
84
- return $attachment->fill_data( $data, $response, $url, $size_key . '@2x' );
85
  }
86
 
87
  endif;
81
  ) );
82
  $attachment = get_imagify_attachment( 'wp', $id, 'imagify_fill_thumbnail_data' );
83
 
84
+ return $attachment->fill_data( $data, $response, $size_key . '@2x' );
85
  }
86
 
87
  endif;
inc/3rd-party/yoast-seo.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
- if ( defined( 'WPSEO_VERSION' ) && is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) :
5
 
6
  add_action( 'wp_print_scripts', '_imagify_dequeue_yoastseo_script' );
7
  /**
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
+ if ( defined( 'WPSEO_VERSION' ) && is_admin() && ! wp_doing_ajax() ) :
5
 
6
  add_action( 'wp_print_scripts', '_imagify_dequeue_yoastseo_script' );
7
  /**
inc/admin/custom-folders.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ add_filter( 'upgrader_post_install', 'imagify_sync_theme_plugin_files_on_update', IMAGIFY_INT_MAX, 3 );
5
+ /**
6
+ * Sync files after a theme or plugin has been updated.
7
+ *
8
+ * @since 1.7
9
+ * @author Grégory Viguier
10
+ *
11
+ * @param bool $response Installation response.
12
+ * @param array $hook_extra Extra arguments passed to hooked filters.
13
+ * @param array $result Installation result data.
14
+ */
15
+ function imagify_sync_theme_plugin_files_on_update( $response, $hook_extra, $result ) {
16
+ global $wpdb;
17
+
18
+ if ( ( empty( $hook_extra['plugin'] ) && empty( $hook_extra['theme'] ) ) || empty( $result['destination'] ) ) {
19
+ return $response;
20
+ }
21
+
22
+ $folders_db = Imagify_Folders_DB::get_instance();
23
+ $files_db = Imagify_Files_DB::get_instance();
24
+
25
+ if ( ! $folders_db->can_operate() || ! $files_db->can_operate() ) {
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
+
39
+ $folder_path = trailingslashit( $result['destination'] );
40
+
41
+ if ( Imagify_Files_Scan::is_path_forbidden( $folder_path ) ) {
42
+ // This theme or plugin must not be optimized.
43
+ return $response;
44
+ }
45
+
46
+ // Get the related folder.
47
+ $placeholder = Imagify_Files_Scan::add_placeholder( $folder_path );
48
+ $folder = Imagify_Folders_DB::get_instance()->get_in( 'path', $placeholder );
49
+
50
+ if ( ! $folder ) {
51
+ // This theme or plugin is not in the database.
52
+ return $response;
53
+ }
54
+
55
+ // Sync the folder files.
56
+ Imagify_Custom_Folders::synchronize_files_from_folders( array(
57
+ $folder['folder_id'] => array(
58
+ 'folder_id' => $folder['folder_id'],
59
+ 'path' => $placeholder,
60
+ 'active' => $folder['active'],
61
+ 'folder_path' => $folder_path,
62
+ ),
63
+ ) );
64
+
65
+ return $response;
66
+ }
inc/admin/heartbeat.php CHANGED
@@ -18,26 +18,83 @@ function _imagify_heartbeat_received( $response, $data ) {
18
  return $response;
19
  }
20
 
21
- $saving_data = imagify_count_saving_data();
22
- $user = new Imagify_User();
23
-
24
- $response['imagify_bulk_data'] = array(
25
- 'already_optimized_attachments' => number_format_i18n( $saving_data['count'] ),
26
- 'optimized_attachments' => imagify_count_optimized_attachments(),
27
- 'unoptimized_attachments' => imagify_count_unoptimized_attachments(),
28
- 'errors_attachments' => imagify_count_error_attachments(),
29
- 'optimized_attachments_percent' => imagify_percent_optimized_attachments(),
30
- 'optimized_percent' => $saving_data['percent'],
31
- 'original_human' => size_format( $saving_data['original_size'], 1 ),
32
- 'optimized_human' => size_format( $saving_data['optimized_size'], 1 ),
33
- 'unconsumed_quota' => $user->get_percent_unconsumed_quota(),
 
34
  );
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  return $response;
37
  }
38
 
39
 
40
- if ( 'upload.php' === $pagenow && isset( $_GET['page'] ) && 'imagify-bulk-optimization' === $_GET['page'] ) { // WPCS: CSRF ok.
41
  add_filter( 'heartbeat_settings', '_imagify_heartbeat_settings', IMAGIFY_INT_MAX );
42
  }
43
  /**
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
  }
95
 
96
 
97
+ if ( 'upload.php' === $pagenow && ! empty( $_GET['page'] ) && Imagify_Views::get_instance()->get_bulk_page_slug() === $_GET['page'] ) { // WPCS: CSRF ok.
98
  add_filter( 'heartbeat_settings', '_imagify_heartbeat_settings', IMAGIFY_INT_MAX );
99
  }
100
  /**
inc/admin/media.php CHANGED
@@ -55,7 +55,7 @@ function _imagify_add_actions_to_media_list_row( $actions, $post ) {
55
  $attachment = get_imagify_attachment( 'wp', $post->ID, 'media_row_actions' );
56
 
57
  // If this attachment is not an image, do nothing.
58
- if ( ! $attachment->is_mime_type_supported() ) {
59
  return $actions;
60
  }
61
 
55
  $attachment = get_imagify_attachment( 'wp', $post->ID, 'media_row_actions' );
56
 
57
  // If this attachment is not an image, do nothing.
58
+ if ( ! $attachment->is_extension_supported() ) {
59
  return $actions;
60
  }
61
 
inc/admin/menu.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- add_action( ( imagify_is_active_for_network() ? 'network_' : '' ) . 'admin_menu', '_imagify_settings_menu' );
5
- /**
6
- * Add submenu in menu "Settings".
7
- *
8
- * @since 1.0
9
- */
10
- function _imagify_settings_menu() {
11
- $page = imagify_is_active_for_network() ? 'settings.php' : 'options-general.php';
12
-
13
- add_submenu_page( $page, 'Imagify', 'Imagify', imagify_get_capacity(), IMAGIFY_SLUG, '_imagify_display_options_page' );
14
- }
15
-
16
- add_action( 'admin_menu', '_imagify_bulk_optimization_menu' );
17
- /**
18
- * Add submenu in menu "Media".
19
- *
20
- * @since 1.0
21
- */
22
- function _imagify_bulk_optimization_menu() {
23
- add_media_page( __( 'Bulk Optimization', 'imagify' ), __( 'Bulk Optimization', 'imagify' ), imagify_get_capacity( 'bulk-optimize' ), IMAGIFY_SLUG . '-bulk-optimization', '_imagify_display_bulk_page' );
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/admin/meta-boxes.php CHANGED
@@ -16,7 +16,7 @@ function _imagify_attachment_submitbox_misc_actions() {
16
 
17
  $attachment = get_imagify_attachment( 'wp', $post->ID, 'attachment_submitbox_misc_actions' );
18
 
19
- if ( ! $attachment->is_mime_type_supported() ) {
20
  return;
21
  }
22
 
@@ -30,7 +30,7 @@ function _imagify_attachment_submitbox_misc_actions() {
30
  echo '<div class="misc-pub-section misc-pub-imagify">';
31
  echo __( 'Invalid API key', 'imagify' );
32
  echo '<br/>';
33
- echo '<a href="' . esc_url( get_imagify_admin_url( 'options-general' ) ) . '">' . __( 'Check your Settings', 'imagify' ) . '</a>';
34
  echo '</div>';
35
 
36
  } elseif ( $attachment->is_optimized() || $attachment->has_error() ) {
16
 
17
  $attachment = get_imagify_attachment( 'wp', $post->ID, 'attachment_submitbox_misc_actions' );
18
 
19
+ if ( ! $attachment->is_extension_supported() ) {
20
  return;
21
  }
22
 
30
  echo '<div class="misc-pub-section misc-pub-imagify">';
31
  echo __( 'Invalid API key', 'imagify' );
32
  echo '<br/>';
33
+ echo '<a href="' . esc_url( get_imagify_admin_url() ) . '">' . __( 'Check your Settings', 'imagify' ) . '</a>';
34
  echo '</div>';
35
 
36
  } elseif ( $attachment->is_optimized() || $attachment->has_error() ) {
inc/admin/options.php DELETED
@@ -1,207 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- add_filter( 'option_page_capability_' . IMAGIFY_SLUG, '_imagify_correct_capability_for_options_page' );
5
- /**
6
- * Fix the capability for our capacity filter hook
7
- *
8
- * @since 1.0
9
- * @author Jonathan
10
- */
11
- function _imagify_correct_capability_for_options_page() {
12
- return imagify_get_capacity();
13
- }
14
-
15
- add_action( 'admin_init', '_imagify_register_setting' );
16
- /**
17
- * Tell to WordPress to be confident with our setting, we are clean!
18
- *
19
- * @since 1.0
20
- * @author Jonathan
21
- */
22
- function _imagify_register_setting() {
23
- register_setting( IMAGIFY_SLUG, IMAGIFY_SETTINGS_SLUG );
24
- }
25
-
26
- add_filter( 'pre_update_option_' . IMAGIFY_SETTINGS_SLUG, '_imagify_pre_update_option', 10, 2 );
27
- /**
28
- * Filter specific options before its value is (maybe) serialized and updated.
29
- *
30
- * @since 1.0
31
- * @author Jonathan
32
- *
33
- * @param mixed $value The new option value.
34
- * @param mixed $old_value The old option value.
35
- * @return array The new option value.
36
- */
37
- function _imagify_pre_update_option( $value, $old_value ) {
38
- $value = is_array( $value ) ? $value : array();
39
-
40
- // Store all sizes even if one of them isn't checked.
41
- if ( ! empty( $value['sizes'] ) && is_array( $value['sizes'] ) ) {
42
- $value['disallowed-sizes'] = array();
43
-
44
- foreach ( $value['sizes'] as $size_key => $size_value ) {
45
- if ( strpos( $size_key , '-hidden' ) ) {
46
- $key = str_replace( '-hidden', '', $size_key );
47
-
48
- if ( ! isset( $value['sizes'][ $key ] ) ) {
49
- $value['disallowed-sizes'][ $key ] = '1';
50
- }
51
- }
52
- }
53
- }
54
-
55
- unset( $value['sizes'] );
56
-
57
- // The max width for the "Resize larger images" option can't be 0.
58
- if ( empty( $value['resize_larger_w'] ) ) {
59
- $value['resize_larger_w'] = '';
60
- $value['resize_larger'] = 0;
61
- }
62
-
63
- // The max width for the "Resize larger images" option can't be less than the largest thumbnail width.
64
- $max_sizes = get_imagify_max_intermediate_image_size();
65
-
66
- if ( ! empty( $value['resize_larger_w'] ) && $value['resize_larger_w'] < $max_sizes['width'] ) {
67
- $value['resize_larger_w'] = $max_sizes['width'];
68
- }
69
-
70
- if ( ! isset( $value['total_size_images_library'] ) && isset( $old_value['total_size_images_library'] ) ) {
71
- $value['total_size_images_library'] = $old_value['total_size_images_library'];
72
- }
73
-
74
- if ( ! isset( $value['average_size_images_per_month'] ) && isset( $old_value['average_size_images_per_month'] ) ) {
75
- $value['average_size_images_per_month'] = $old_value['average_size_images_per_month'];
76
- }
77
-
78
- return $value;
79
- }
80
-
81
- if ( imagify_is_active_for_network() ) {
82
- add_filter( 'pre_update_site_option_' . IMAGIFY_SETTINGS_SLUG, '_imagify_maybe_set_redirection_before_save_options', 10, 2 );
83
- } else {
84
- add_filter( 'pre_update_option_' . IMAGIFY_SETTINGS_SLUG, '_imagify_maybe_set_redirection_before_save_options', 10, 2 );
85
- }
86
- /**
87
- * If the user clicked the "Save & Go to Bulk Optimizer" button, set a redirection to the bulk optimizer.
88
- * We use this hook because it can be triggered even if the option value hasn't changed.
89
- *
90
- * @since 1.6.8
91
- * @author Grégory Viguier
92
- *
93
- * @param mixed $value The new, unserialized option value.
94
- * @param mixed $old_value The old option value.
95
- * @return mixed The option value.
96
- */
97
- function _imagify_maybe_set_redirection_before_save_options( $value, $old_value ) {
98
-
99
- if ( ! is_admin() || ! isset( $_POST['submit-goto-bulk'] ) ) { // WPCS: CSRF ok.
100
- return $value;
101
- }
102
-
103
- $_REQUEST['_wp_http_referer'] = esc_url_raw( get_admin_url( get_current_blog_id(), 'upload.php?page=imagify-bulk-optimization' ) );
104
-
105
- return $value;
106
- }
107
-
108
- if ( imagify_is_active_for_network() ) {
109
- add_action( 'update_site_option_' . IMAGIFY_SETTINGS_SLUG, '_imagify_after_save_network_options', 10, 3 );
110
- }
111
- /**
112
- * Used to launch some actions after saving the network options.
113
- *
114
- * @since 1.6.5
115
- * @author Grégory Viguier
116
- *
117
- * @param string $option Name of the network option.
118
- * @param mixed $value Current value of the network option.
119
- * @param mixed $old_value Old value of the network option.
120
- */
121
- function _imagify_after_save_network_options( $option, $value, $old_value ) {
122
- _imagify_after_save_options( $old_value, $value );
123
- }
124
-
125
- if ( ! imagify_is_active_for_network() ) {
126
- add_action( 'update_option_' . IMAGIFY_SETTINGS_SLUG, '_imagify_after_save_options', 10, 2 );
127
- }
128
- /**
129
- * Used to launch some actions after saving the options.
130
- *
131
- * @since 1.0
132
- * @since 1.5 Used to redirect user to Bulk Optimizer (if requested).
133
- * @since 1.6.8 Not used to redirect user to Bulk Optimizer anymore: see _imagify_maybe_set_redirection_before_save_options().
134
- * @author Jonathan
135
- *
136
- * @param mixed $old_value The old option value.
137
- * @param mixed $value The new option value.
138
- */
139
- function _imagify_after_save_options( $old_value, $value ) {
140
-
141
- if ( ! $old_value || ! $value || isset( $old_value['api_key'], $value['api_key'] ) && $old_value['api_key'] === $value['api_key'] ) {
142
- return;
143
- }
144
-
145
- if ( is_wp_error( get_imagify_user() ) ) {
146
- Imagify_Notices::renew_notice( 'wrong-api-key' );
147
- delete_site_transient( 'imagify_check_licence_1' );
148
- } else {
149
- Imagify_Notices::dismiss_notice( 'wrong-api-key' );
150
- }
151
- }
152
-
153
- if ( imagify_is_active_for_network() ) :
154
-
155
- add_action( 'admin_post_update', '_imagify_update_site_option_on_network' );
156
- /**
157
- * `options.php` do not handle site options. Let's use `admin-post.php` for multisite installations.
158
- *
159
- * @since 1.0
160
- */
161
- function _imagify_update_site_option_on_network() {
162
- $option_group = IMAGIFY_SLUG;
163
-
164
- if ( ! isset( $_POST['option_page'] ) || $_POST['option_page'] !== $option_group ) { // WPCS: CSRF ok.
165
- return;
166
- }
167
-
168
- $capability = apply_filters( "option_page_capability_{$option_group}", 'manage_network_options' );
169
-
170
- if ( ! current_user_can( $capability ) ) {
171
- imagify_die();
172
- }
173
-
174
- imagify_check_nonce( $option_group . '-options' );
175
-
176
- $whitelist_options = apply_filters( 'whitelist_options', array() );
177
-
178
- if ( ! isset( $whitelist_options[ $option_group ] ) ) {
179
- wp_die( __( '<strong>ERROR</strong>: options page not found.' ) );
180
- }
181
-
182
- $options = $whitelist_options[ $option_group ];
183
-
184
- if ( $options ) {
185
- foreach ( $options as $option ) {
186
- $option = trim( $option );
187
- $value = null;
188
-
189
- if ( isset( $_POST[ $option ] ) ) {
190
- $value = $_POST[ $option ];
191
- if ( ! is_array( $value ) ) {
192
- $value = trim( $value );
193
- }
194
- $value = wp_unslash( $value );
195
- }
196
-
197
- update_site_option( $option, $value );
198
- }
199
- }
200
-
201
- /**
202
- * Redirect back to the settings page that was submitted.
203
- */
204
- imagify_maybe_redirect( false, array( 'settings-updated' => 'true' ) );
205
- }
206
-
207
- endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/admin/plugins.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- add_filter( 'plugin_action_links_' . plugin_basename( IMAGIFY_FILE ), '_imagify_plugin_action_links' );
5
- add_filter( 'network_admin_plugin_action_links_' . plugin_basename( IMAGIFY_FILE ), '_imagify_plugin_action_links' );
6
- /**
7
- * Add link to the plugin configuration pages.
8
- *
9
- * @since 1.0
10
- *
11
- * @param array $actions An array of action links.
12
- * @return array
13
- */
14
- function _imagify_plugin_action_links( $actions ) {
15
- array_unshift( $actions, sprintf( '<a href="%s" target="_blank">%s</a>', esc_url( imagify_get_external_url( 'documentation' ) ), __( 'Documentation', 'imagify' ) ) );
16
- array_unshift( $actions, sprintf( '<a href="%s">%s</a>', esc_url( get_imagify_admin_url( 'bulk-optimization' ) ), __( 'Bulk Optimization', 'imagify' ) ) );
17
- array_unshift( $actions, sprintf( '<a href="%s">%s</a>', esc_url( get_imagify_admin_url() ), __( 'Settings' ) ) );
18
- return $actions;
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/admin/ui/bulk.php DELETED
@@ -1,393 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- /**
5
- * The main settings page construtor using the required functions from WP.
6
- *
7
- * @since 1.0
8
- */
9
- function _imagify_display_bulk_page() {
10
- $user = new Imagify_User();
11
- ?>
12
- <div class="wrap imagify-settings imagify-bulk">
13
- <div class="imagify-title">
14
-
15
- <?php if ( ! defined( 'IMAGIFY_HIDDEN_ACCOUNT' ) || false === IMAGIFY_HIDDEN_ACCOUNT ) { ?>
16
-
17
- <div class="imagify-title-right">
18
- <div class="imagify-account">
19
- <p class="imagify-meteo-title"><?php _e( 'Account status', 'imagify' ); ?></p>
20
- <p class="imagify-meteo-subs"><?php _e( 'Your subscription:', 'imagify' ); ?>&nbsp;<strong class="imagify-user-plan"><?php echo $user->plan_label; ?></strong></p>
21
- </div>
22
- <div class="imagify-account-link">
23
- <a href="<?php echo esc_url( imagify_get_external_url( 'subscription' ) ); ?>" class="button button-ghost" target="_blank">
24
- <span class="dashicons dashicons-admin-users"></span>
25
- <span class="button-text"><?php _e( 'View My Subscription', 'imagify' ); ?></span>
26
- </a>
27
- </div>
28
-
29
- <?php if ( 1 === $user->plan_id ) { ?>
30
-
31
- <div class="imagify-sep-v"></div>
32
- <div class="imagify-credit-left">
33
- <?php
34
- $unconsumed_quota = $user->get_percent_unconsumed_quota();
35
- $meteo_icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'sun.svg" width="37" height="38" alt="" />';
36
- $bar_class = 'positive';
37
- $is_display_bubble = false;
38
-
39
- if ( $unconsumed_quota >= 21 && $unconsumed_quota <= 50 ) {
40
- $bar_class = 'neutral';
41
- $meteo_icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'cloudy-sun.svg" width="37" height="38" alt="" />';
42
- } elseif ( $unconsumed_quota <= 20 ) {
43
- $bar_class = 'negative';
44
- $is_display_bubble = true;
45
- $meteo_icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'stormy.svg" width="38" height="36" alt="" />';
46
- }
47
- ?>
48
- <span class="imagify-meteo-icon"><?php echo $meteo_icon; ?></span>
49
- <div class="imagify-space-left">
50
-
51
- <p>
52
- <?php
53
- printf(
54
- /* translators: %s is a data quota. */
55
- __( 'You have %s space credit left' , 'imagify' ),
56
- '<span class="imagify-unconsumed-percent">' . $unconsumed_quota . '%</span>'
57
- );
58
- ?>
59
- </p>
60
-
61
- <div class="imagify-bar-<?php echo $bar_class; ?>">
62
- <div class="imagify-unconsumed-bar imagify-progress" style="width: <?php echo $unconsumed_quota . '%'; ?>;"></div>
63
- </div>
64
- </div>
65
- <div class="imagify-space-tooltips imagify-tooltips <?php echo ( ! $is_display_bubble ) ? 'hidden' : ''; ?>">
66
- <div class="tooltip-content tooltip-table">
67
- <div class="cell-icon">
68
- <span aria-hidden="true" class="icon icon-round">i</span>
69
- </div>
70
- <div class="cell-text">
71
- <?php _e( 'Upgrade your account to continue optimizing your images', 'imagify' ); ?>
72
- </div>
73
- <div class="cell-sep"></div>
74
- <div class="cell-cta">
75
- <a href="<?php echo esc_url( imagify_get_external_url( 'subscription' ) ); ?>" target="_blank"><?php _e( 'More info', 'imagify' ); ?></a>
76
- </div>
77
- </div>
78
- </div>
79
- </div>
80
-
81
- <?php } // End if(). ?>
82
-
83
- </div>
84
-
85
- <?php } // End if(). ?>
86
-
87
- <img width="225" height="26" alt="Imagify" src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>imagify-logo.png" class="imagify-logo" />
88
- </div>
89
-
90
- <?php echo get_imagify_new_to_imagify(); ?>
91
-
92
- <div class="imagify-sub-title">
93
- <svg class="icon icon-bulk" viewBox="0 0 38 36" width="38" height="36" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m233.09 177.21l-5.52 10.248c-.08.145-.018.272-.023.388-.074.193-.033.4-.033.619v21.615c0 .952.601 1.429 1.552 1.429h33.897c.952 0 1.962-.478 1.962-1.429v-21.615c0-.487-.323-.925-.649-1.24l-5.623-9.976c-.405-.726-1.202-1.179-2.034-1.182l-21.486-.068c-.849 0-1.64.464-2.043 1.211m30.424 32.869c0 .173-.378.018-.551.018h-33.897c-.172 0-.14.155-.14-.018v-21.576l33.961-.281c.066.008.186.09.263.128.054.027.205.049.258.073.002.014.106.027.106.041v21.615m-6.153-32.11l4.91 8.835h-14.992v-9.354l9.306.045c.322.001.619.192.776.474m-11.494-.523v9.358h-16.306l4.773-8.892c.155-.289.456-.484.787-.484l10.746.018m7.06 17.12c0 .39-.316.706-.706.706h-12.706c-.39 0-.706-.316-.706-.706 0-.39.316-.706.706-.706h12.706c.39 0 .706.316.706.706" transform="translate(-227-176)" fill="#7a8996"/></g></svg>
94
- <span class="title-text">
95
- <?php _e( 'Bulk Optimization', 'imagify' ); ?>
96
- <small><sup><?php echo IMAGIFY_VERSION; ?></sup></small>
97
- </span>
98
- </div>
99
-
100
- <div class="imagify-settings-section">
101
-
102
- <div class="imagify-columns">
103
-
104
- <div class="col-1-3 col-overview">
105
- <h3><?php _e( 'Overview', 'imagify' ); ?></h3>
106
-
107
- <div class="imagify-chart-container imagify-overview-chart-container">
108
- <canvas id="imagify-overview-chart" width="180" height="180"></canvas>
109
- <div id="imagify-overview-chart-percent" class="imagify-chart-percent"><?php echo imagify_percent_optimized_attachments(); ?><span>%</span></div>
110
- </div>
111
- <div id="imagify-overview-chart-legend"></div>
112
-
113
- <p class="imagify-global-optim-phrase imagify-clear">
114
- <?php
115
- printf(
116
- /* translators: %s is a percentage. */
117
- esc_html__( 'You optimized %s of your website\'s images', 'imagify' ),
118
- '<span class="imagify-total-percent">' . imagify_percent_optimized_attachments() . '%</span>'
119
- );
120
- ?>
121
- </p>
122
- </div>
123
-
124
- <div class="col-1-3 col-statistics">
125
- <h3><?php _e( 'Statistics', 'imagify' ); ?></h3>
126
-
127
- <?php
128
- $total_saving_data = imagify_count_saving_data();
129
- $optimized_percent = $total_saving_data['percent'];
130
- $optimized_nb = $total_saving_data['optimized_size'];
131
- $original_nb = $total_saving_data['original_size'];
132
- ?>
133
-
134
- <div class="imagify-number-you-optimized">
135
- <p>
136
- <span id="imagify-total-optimized-attachments" class="number"><?php echo number_format_i18n( $total_saving_data['count'] ); ?></span>
137
- <span class="text">
138
- <?php
139
- printf(
140
- /* translators: you can use %s to include a line break. */
141
- __( 'that\'s the number of original images you optimized with Imagify', 'imagify' ),
142
- '<br>'
143
- );
144
- ?>
145
- </span>
146
- </p>
147
- </div>
148
-
149
- <div class="imagify-bars">
150
- <p><?php _e( 'Original size', 'imagify' ); ?></p>
151
- <div class="imagify-bar-negative base-transparent right-outside-number">
152
- <div id="imagify-original-bar" class="imagify-progress" style="width: 100%"><span class="imagify-barnb"><?php echo size_format( $original_nb, 1 ); ?></span></div>
153
- </div>
154
-
155
- <p><?php _e( 'Optimized size', 'imagify' ); ?></p>
156
- <div class="imagify-bar-positive base-transparent right-outside-number">
157
- <div id="imagify-optimized-bar" class="imagify-progress" style="width: <?php echo ( 100 - $optimized_percent ); ?>%"><span class="imagify-barnb"><?php echo size_format( $optimized_nb, 1 ); ?></span></div>
158
- </div>
159
-
160
- </div>
161
-
162
- <div class="imagify-number-you-optimized">
163
- <p>
164
- <span id="imagify-total-optimized-attachments-pct" class="number"><?php echo number_format_i18n( $optimized_percent ); ?>%</span>
165
- <span class="text">
166
- <?php
167
- printf(
168
- /* translators: %s is a line break. */
169
- __( 'that\'s the size you saved %sby using Imagify', 'imagify' ),
170
- '<br>'
171
- );
172
- ?>
173
- </span>
174
- </p>
175
- </div>
176
- </div>
177
-
178
- <div class="col-1-3 col-informations">
179
- <h3><?php _e( 'Information', 'imagify' ); ?></h3>
180
- <ul class="imagify-list-infos">
181
- <li>
182
- <?php
183
- esc_html_e( 'Please be aware that optimizing a large number of images can take a while depending on your server and network speed.', 'imagify' );
184
-
185
- if ( get_transient( 'imagify_large_library' ) ) {
186
- printf(
187
- /* translators: %s is a formatted number. Don't use %d. */
188
- __( 'If you have more than %s images, you will need to launch the bulk optimization several times.' , 'imagify' ),
189
- number_format_i18n( imagify_get_unoptimized_attachment_limit() )
190
- );
191
- }
192
- ?>
193
- </li>
194
- <li>
195
- <?php esc_html_e( 'You must keep this page open while the bulk optimization is processing. If you leave you can come back to continue where it left off.', 'imagify' ); ?>
196
- </li>
197
- <li class="imagify-documentation-link-box">
198
- <span class="imagify-documentation-icon"><svg viewBox="0 0 15 20" xmlns="http://www.w3.org/2000/svg"><g fill="#40b1d0" fill-rule="nonzero"><g><path d="m14.583 20h-14.167c-.23 0-.417-.187-.417-.417v-14.167c0-.111.044-.217.122-.295l5-5c.078-.078.184-.122.295-.122h9.167c.23 0 .417.187.417.417v19.17c0 .23-.187.417-.417.417m-13.75-.833h13.333v-18.333h-8.578l-4.756 4.756v13.578"/><path d="m5.417 5.833h-5c-.23 0-.417-.187-.417-.417 0-.23.187-.417.417-.417h4.583v-4.583c0-.23.187-.417.417-.417.23 0 .417.187.417.417v5c0 .23-.187.417-.417.417"/></g><path d="m12.583 7h-9.167c-.23 0-.417-.224-.417-.5 0-.276.187-.5.417-.5h9.167c.23 0 .417.224.417.5 0 .276-.187.5-.417.5"/><path d="m12.583 5h-4.167c-.23 0-.417-.224-.417-.5 0-.276.187-.5.417-.5h4.167c.23 0 .417.224.417.5 0 .276-.187.5-.417.5"/><path d="m12.583 10h-9.167c-.23 0-.417-.224-.417-.5 0-.276.187-.5.417-.5h9.167c.23 0 .417.224.417.5 0 .276-.187.5-.417.5"/><path d="m12.583 13h-9.167c-.23 0-.417-.224-.417-.5 0-.276.187-.5.417-.5h9.167c.23 0 .417.224.417.5 0 .276-.187.5-.417.5"/><path d="m12.583 15h-9.167c-.23 0-.417-.224-.417-.5 0-.276.187-.5.417-.5h9.167c.23 0 .417.224.417.5 0 .276-.187.5-.417.5"/><path d="m12.583 18h-9.167c-.23 0-.417-.224-.417-.5 0-.276.187-.5.417-.5h9.167c.23 0 .417.224.417.5 0 .276-.187.5-.417.5"/></g></svg></span>
199
- <span>
200
- <?php _e( 'Need help or have questions?', 'imagify' ); ?>
201
- <a class="imagify-documentation-link" href="<?php echo esc_url( imagify_get_external_url( 'documentation' ) ); ?>" target="_blank"><?php _e( 'Check our documentation.', 'imagify' ); ?></a>
202
- <span>
203
- </li>
204
- </ul>
205
- </div><!-- .col-1-2 -->
206
- </div><!-- .imagify-columns -->
207
- </div><!-- .imagify-settings-section -->
208
-
209
- <div class="imagify-section imagify-section-gray">
210
- <div class="imagify-bulk-submit imagify-columns imagify-count">
211
- <div class="col-1-2">
212
-
213
- <?php if ( (int) get_imagify_option( 'backup', 0 ) === 1 ) { ?>
214
-
215
- <p class="imagify-count-title"><?php esc_html_e( 'Select Your Compression Level', 'imagify' ); ?>
216
- <?php
217
- $default_set = esc_html__( 'Ultra', 'imagify' );
218
-
219
- switch ( (int) get_imagify_option( 'optimization_level' ) ) {
220
- case 1:
221
- $default_set = esc_html__( 'Aggressive', 'imagify' );
222
- break;
223
- case 0:
224
- $default_set = esc_html__( 'Normal', 'imagify' );
225
- }
226
-
227
- /* translators: %s is an optimization level. */
228
- echo '<em class="imagify-default-settings">(' . sprintf( esc_html__( 'Your default setting: %s', 'imagify' ), '&nbsp;<strong class="imagify-primary">' . $default_set . '</strong>' ) . ')</em>';
229
- ?>
230
- </p>
231
- <p class="imagify-inline-options">
232
- <input type="radio" id="imagify-optimization_level_normal" name="optimization_level" value="0" <?php checked( get_imagify_option( 'optimization_level' ), 0 ); ?>>
233
- <label for="imagify-optimization_level_normal">
234
- <?php esc_html_e( 'Normal', 'imagify' ); ?>
235
- </label>
236
-
237
- <input type="radio" id="imagify-optimization_level_aggro" name="optimization_level" value="1" <?php checked( get_imagify_option( 'optimization_level' ), 1 ); ?>>
238
- <label for="imagify-optimization_level_aggro">
239
- <?php esc_html_e( 'Aggressive', 'imagify' ); ?>
240
- </label>
241
-
242
- <input type="radio" id="imagify-optimization_level_ultra" name="optimization_level" value="2" <?php checked( get_imagify_option( 'optimization_level' ), 2 ); ?>>
243
- <label for="imagify-optimization_level_ultra">
244
- <?php esc_html_e( 'Ultra', 'imagify' ); ?>
245
- </label>
246
- </p>
247
-
248
- <?php } else { ?>
249
-
250
- <p>
251
- <strong>
252
- <?php
253
- printf(
254
- /* translators: 1 is the opening of a link, 2 is the closing of this link. */
255
- __( 'Don\'t forget to check %1$syour settings%2$s before bulk optimization.', 'imagify' ),
256
- '<a href="' . esc_url( get_imagify_admin_url() ) . '">',
257
- '</a>'
258
- );
259
- ?>
260
- </strong>
261
- </p>
262
-
263
- <?php } // End if(). ?>
264
-
265
- </div>
266
- <div class="col-1-2">
267
- <p class="imagify-count-title"><?php esc_html_e( 'Let\'s go!', 'imagify' ); ?></p>
268
- <div class="imagify-table">
269
- <div class="imagify-cell imagify-pl0">
270
- <p>
271
- <?php wp_nonce_field( 'imagify-bulk-upload', 'imagifybulkuploadnonce' ); ?>
272
- <button id="imagify-bulk-action" type="button" class="button button-primary">
273
- <span class="dashicons dashicons-admin-generic"></span>
274
- <span class="button-text"><?php _e( 'Imagif\'em all', 'imagify' ); ?></span>
275
- </button>
276
- </p>
277
- </div>
278
- <?php if ( ! is_wp_error( get_imagify_max_image_size() ) ) { ?>
279
- <div class="imagify-cell imagify-pl0">
280
- <p class="imagify-info-block">
281
- <?php
282
- printf(
283
- /* translators: %s is a file size. */
284
- __( 'All images greater than %s will be optimized when using a paid plan.', 'imagify' ),
285
- size_format( get_imagify_max_image_size() )
286
- );
287
- ?>
288
- </p>
289
- </div>
290
- <?php } ?>
291
- </div>
292
- </div>
293
- </div><!-- .imagify-bulk-submit -->
294
- </div>
295
-
296
- <!-- The Success/Complete bar -->
297
- <div class="imagify-row-complete hidden" aria-hidden="true">
298
- <div class="imagify-all-complete">
299
- <div class="imagify-ac-report">
300
- <div class="imagify-ac-chart" data-percent="0">
301
- <span class="imagify-chart">
302
- <span class="imagify-chart-container">
303
- <canvas width="46" height="46"></canvas>
304
- </span>
305
- </span>
306
- </div>
307
- <div class="imagify-ac-report-text">
308
- <p class="imagify-ac-rt-big"><?php _e( 'Well done!', 'imagify' ); ?></p>
309
- <p>
310
- <?php
311
- printf(
312
- /* translators: 1 and 2 are data sizes. */
313
- __( 'you saved %1$s out of %2$s', 'imagify' ),
314
- '<strong class="imagify-ac-rt-total-gain"></strong>',
315
- '<strong class="imagify-ac-rt-total-original"></strong>'
316
- );
317
- ?>
318
- </p>
319
- </div>
320
- </div>
321
- <div class="imagify-ac-share">
322
- <div class="imagify-ac-share-content">
323
- <p><?php _e( 'Share your awesome result', 'imagify' ); ?></p>
324
- <ul class="imagify-share-networks">
325
- <li>
326
- <a target="_blank" class="imagify-sn-twitter" href="<?php echo esc_url( imagify_get_external_url( 'share-twitter' ) ); ?>"><svg viewBox="0 0 23 18" width="23" height="18" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><title>Twitter</title><path d="m244.15 12.13c-.815.361-1.691.606-2.61.716.939-.563 1.659-1.453 1.998-2.514-.878.521-1.851.898-2.886 1.103-.829-.883-2.01-1.435-3.317-1.435-2.51 0-4.544 2.034-4.544 4.544 0 .356.04.703.118 1.035-3.777-.19-7.125-1.999-9.367-4.748-.391.671-.615 1.452-.615 2.285 0 1.576.802 2.967 2.02 3.782-.745-.024-1.445-.228-2.058-.568-.001.019-.001.038-.001.057 0 2.202 1.566 4.04 3.646 4.456-.381.104-.783.159-1.197.159-.293 0-.577-.028-.855-.081.578 1.805 2.256 3.119 4.245 3.156-1.555 1.219-3.515 1.945-5.644 1.945-.367 0-.728-.021-1.084-.063 2.01 1.289 4.399 2.041 6.966 2.041 8.359 0 12.929-6.925 12.929-12.929 0-.197-.004-.393-.013-.588.888-.64 1.658-1.44 2.268-2.352" transform="translate(-222-10)" fill="#fff"/></g></svg></a>
327
- </li>
328
- <li>
329
- <a target="_blank" class="imagify-sn-facebook" href="<?php echo esc_url( imagify_get_external_url( 'share-facebook' ) ); ?>"><svg viewBox="0 0 18 18" width="18" height="18" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><title>Facebook</title><path d="m203.25 10h-16.5c-.415 0-.75.336-.75.75v16.5c0 .414.336.75.75.75h8.812v-6.75h-2.25v-2.813h2.25v-2.25c0-2.325 1.472-3.469 3.546-3.469.993 0 1.847.074 2.096.107v2.43h-1.438c-1.128 0-1.391.536-1.391 1.322v1.859h2.813l-.563 2.813h-2.25l.045 6.75h4.83c.414 0 .75-.336.75-.75v-16.5c0-.414-.336-.75-.75-.75" transform="translate(-186-10)" fill="#fff"/></g></svg></a>
330
- </li>
331
- </ul>
332
- </div>
333
- </div>
334
- </div>
335
- </div>
336
-
337
- <div class="imagify-bulk-table">
338
- <table summary="<?php _e( 'Compression process results', 'imagify' ); ?>">
339
- <thead>
340
- <tr>
341
- <th class="imagify-cell-filename"><?php _e( 'Filename', 'imagify' ); ?></th>
342
- <th class="imagify-cell-status"><?php _e( 'Status', 'imagify' ); ?></th>
343
- <th class="imagify-cell-original"><?php _e( 'Original', 'imagify' ); ?></th>
344
- <th class="imagify-cell-optimized"><?php _e( 'Optimized', 'imagify' ); ?></th>
345
- <th class="imagify-cell-percentage"><?php _e( 'Percentage', 'imagify' ); ?></th>
346
- <th class="imagify-cell-thumbnails"><?php _e( 'Thumbnails optimized', 'imagify' ); ?></th>
347
- <th class="imagify-cell-savings"><?php _e( 'Overall saving', 'imagify' ); ?></th>
348
- </tr>
349
- </thead>
350
- <tfoot>
351
- <tr>
352
- <td class="imagify-cell-nb-files">
353
- <?php
354
- /* translators: %s is a number. Don't use %d. */
355
- printf( _n( '%s file', '%s files', 0, 'imagify' ), '<span class="imagify-nb-files">0</span>' );
356
- ?>
357
- </td>
358
- <td class="imagify-cell-errors">
359
- <?php
360
- /* translators: %s is a number. Don't use %d. */
361
- printf( _n( '%s error', '%s errors', 0, 'imagify' ), '<span class="imagify-nb-errors">0</span>' );
362
- ?>
363
- </td>
364
- <td class="imagify-cell-totaloriginal" colspan="4"><?php _e( 'Total:', 'imagify' ); ?><strong> <span class="imagify-total-original">0&nbsp;kB</span></strong></td>
365
- <td class="imagify-cell-totalgain"><?php _e( 'Gain:', 'imagify' ); ?><strong> <span class="imagify-total-gain">0&nbsp;kB</span></strong></td>
366
- </tr>
367
- </tfoot>
368
- <tbody>
369
- <!-- The progress bar -->
370
- <tr aria-hidden="true" class="imagify-row-progress hidden">
371
- <td colspan="7">
372
- <div class="media-item">
373
- <div class="progress">
374
- <div id="imagify-progress-bar" class="bar"><div class="percent">0%</div></div>
375
- </div>
376
- </div>
377
- </td>
378
- </tr>
379
- <!-- No image uploaded yet -->
380
- <tr class="imagify-no-uploaded-yet">
381
- <td colspan="7">
382
- <p><a id="imagify-simulate-bulk-action" href="#"><?php _e( 'Start the bulk optimization', 'imagify' ); ?></a></p>
383
- </td>
384
- </tr>
385
- </tbody>
386
- </table>
387
- </div>
388
-
389
- <?php imagify_payment_modal(); ?>
390
-
391
- </div>
392
- <?php
393
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/admin/ui/notice-free-over-quota.php DELETED
@@ -1,30 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- $this->render_view( 'header', array(
5
- 'title' => __( 'Oops, It\'s Over!', 'imagify' ),
6
- ) );
7
- ?>
8
- <p>
9
- <?php
10
- printf(
11
- /* translators: 1 is a "bold" tag start, 2 is a formatted data quota, 3 is a date, 4 is the "bold" tag end. */
12
- __( 'You have consumed all your credit for this month. You will have %1$s%2$s back on %3$s%4$s.', 'imagify' ),
13
- '<strong>',
14
- size_format( $data->quota * 1048576 ),
15
- date_i18n( get_option( 'date_format' ), strtotime( $data->next_date_update ) ),
16
- '</strong>'
17
- );
18
- echo '<br/><br/>';
19
- printf(
20
- /* translators: 1 is a link tag start, 2 is the link tag end. */
21
- __( 'To continue to optimize your images, log in to your Imagify account to %1$sbuy a pack or subscribe to a plan%2$s.', 'imagify' ),
22
- '<a target="_blank" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '">',
23
- '</a>'
24
- );
25
- ?>
26
- </p>
27
- <?php
28
- $this->render_view( 'footer', array(
29
- 'dismissible' => 'free-over-quota',
30
- ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/admin/ui/options.php DELETED
@@ -1,598 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- /**
5
- * The main settings page construtor using the required functions from WP
6
- *
7
- * @since 1.0
8
- */
9
- function _imagify_display_options_page() {
10
- global $wp_version;
11
-
12
- if ( isset( $_POST['submit-goto-bulk'] ) ) { // WPCS: CSRF ok.
13
- wp_safe_redirect( get_admin_url( get_current_blog_id(), 'upload.php?page=imagify-bulk-optimization' ) );
14
- }
15
- ?>
16
- <div class="wrap imagify-settings <?php echo defined( 'WP_ROCKET_VERSION' ) ? 'imagify-have-rocket' : 'imagify-dont-have-rocket'; ?>">
17
-
18
- <?php if ( ! defined( 'WP_ROCKET_VERSION' ) ) { ?>
19
-
20
- <div class="imagify-col imagify-sidebar">
21
- <div class="imagify-sidebar-section">
22
- <span class="imagify-sidebar-title">
23
- <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>phone.svg" width="19" height="16" alt=""> <?php _e( 'Is your website too slow?', 'imagify' ); ?>
24
- </span>
25
- <ul class="wp-media-products">
26
- <li>
27
- <div class="links wprocket-link">
28
- <p><strong>
29
- <?php _e( 'Discover the best caching plugin to speed up your website.', 'imagify' ); ?>
30
- </strong></p>
31
-
32
- <p class="imagify-big-text">
33
- <?php
34
- $discount_percent = '20%';
35
- $discount_code = 'IMAGIFY20';
36
-
37
- printf(
38
- /* translators: 1 is the start of a styled wrapper, 2 is a "bold" tag start, 3 is a percentage, 4 is the "bold" tag end, 5 is the styled wrapper end, 6 is a discount code. */
39
- __( '%1$sGet %2$s%3$s off%4$s%5$s with this coupon code: %6$s', 'imagify' ),
40
- '<span class="imagify-mark-styled"><span>',
41
- '<strong>',
42
- $discount_percent,
43
- '</strong>',
44
- '</span></span>',
45
- '<span class="imagify-discount-code">' . $discount_code . '</span>'
46
- );
47
- ?>
48
- </p>
49
-
50
- <p>
51
- <a class="btn btn-rocket" href="<?php echo esc_url( imagify_get_wp_rocket_url() ); ?>" target="_blank"><?php _e( 'Get WP Rocket now', 'imagify' ); ?></a>
52
- </p>
53
- </div>
54
- </li>
55
- </ul>
56
- </div>
57
- </div>
58
-
59
- <?php } // End if(). ?>
60
-
61
- <div class="imagify-col imagify-main">
62
- <div class="imagify-title">
63
- <div class="imagify-logo-block">
64
- <img width="225" height="26" alt="Imagify" src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>imagify-logo.png" class="imagify-logo" />
65
- <small>
66
- <sup><?php echo IMAGIFY_VERSION; ?></sup>
67
- </small>
68
- </div>
69
-
70
- <p class="imagify-rate-us">
71
- <?php
72
- printf(
73
- /* translators: 1 is a "bold" tag start, 2 is the "bold" tag end + a line break tag, 3 is a link tag start, 4 is the link tag end. */
74
- __( '%1$sDo you like this plugin?%2$s Please take a few seconds to %3$srate it on WordPress.org%4$s!', 'imagify' ),
75
- '<strong>',
76
- '</strong><br />',
77
- '<a href="' . esc_url( imagify_get_external_url( 'rate' ) ) . '" target="_blank">',
78
- '</a>'
79
- );
80
- ?>
81
- <br>
82
- <a class="stars" href="<?php echo esc_url( imagify_get_external_url( 'rate' ) ); ?>" target="_blank"><?php echo str_repeat( '<span class="dashicons dashicons-star-filled"></span>', 5 ); ?></a>
83
- </p>
84
-
85
- <p class="imagify-documentation-link-box">
86
- <span class="imagify-documentation-link-icon" aria-hidden="true"><svg viewBox="0 0 23 31" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="nonzero"><g fill="#40b1d0"><path d="m22.361 31h-21.722c-.353 0-.639-.289-.639-.646v-21.958c0-.172.068-.336.187-.457l7.667-7.75c.12-.12.282-.189.452-.189h14.06c.353 0 .639.289.639.646v29.708c0 .357-.286.646-.639.646m-21.08-1.292h20.444v-28.417h-13.152l-7.292 7.372v21.05"/><path d="m8.306 9.04h-7.667c-.353 0-.639-.289-.639-.646 0-.357.286-.646.639-.646h7.03v-7.104c0-.357.286-.646.639-.646.353 0 .639.289.639.646v7.75c0 .357-.286.646-.639.646"/></g><g fill="#f2f5f7"><path d="m18.375 11h-13.75c-.345 0-.625-.224-.625-.5 0-.276.28-.5.625-.5h13.75c.345 0 .625.224.625.5 0 .276-.28.5-.625.5"/><path d="m19.333 7h-6.667c-.368 0-.667-.224-.667-.5 0-.276.299-.5.667-.5h6.667c.368 0 .667.224.667.5 0 .276-.299.5-.667.5"/><path d="m18.375 15h-13.75c-.345 0-.625-.224-.625-.5 0-.276.28-.5.625-.5h13.75c.345 0 .625.224.625.5 0 .276-.28.5-.625.5"/><path d="m18.375 19h-13.75c-.345 0-.625-.224-.625-.5 0-.276.28-.5.625-.5h13.75c.345 0 .625.224.625.5 0 .276-.28.5-.625.5"/><path d="m18.375 23h-13.75c-.345 0-.625-.224-.625-.5 0-.276.28-.5.625-.5h13.75c.345 0 .625.224.625.5 0 .276-.28.5-.625.5"/><path d="m18.375 27h-13.75c-.345 0-.625-.224-.625-.5 0-.276.28-.5.625-.5h13.75c.345 0 .625.224.625.5 0 .276-.28.5-.625.5"/></g></g></svg></span>
87
- <span><?php _e( 'Need help or have questions?', 'imagify' ); ?><br/>
88
- <a class="imagify-documentation-link" href="<?php echo esc_url( imagify_get_external_url( 'documentation' ) ); ?>" target="_blank"><?php _e( 'Check our documentation.', 'imagify' ); ?></a></span>
89
- </p>
90
- </div>
91
- <?php $form_action = ( imagify_is_active_for_network() ) ? admin_url( 'admin-post.php' ) : admin_url( 'options.php' ); ?>
92
- <form action="<?php echo $form_action; ?>" id="imagify-settings" method="post">
93
-
94
- <?php settings_fields( IMAGIFY_SLUG ); ?>
95
- <?php wp_nonce_field( 'imagify-signup', 'imagifysignupnonce', false ); ?>
96
- <?php wp_nonce_field( 'imagify-check-api-key', 'imagifycheckapikeynonce', false ); ?>
97
-
98
- <input id="check_api_key" type="hidden" value="<?php echo esc_attr( get_imagify_option( 'api_key' ) ); ?>" name="check_api_key">
99
- <input id="version" type="hidden" value="<?php echo esc_attr( get_imagify_option( 'version' ) ); ?>" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[version]">
100
-
101
- <h3 class="screen-reader-text"><?php _e( 'Settings' ); ?></h3>
102
-
103
- <?php echo get_imagify_new_to_imagify(); ?>
104
-
105
- <?php if ( ! defined( 'IMAGIFY_API_KEY' ) || ! IMAGIFY_API_KEY ) { ?>
106
-
107
- <div class="imagify-sub-header">
108
- <table class="form-table">
109
- <tbody>
110
- <tr>
111
- <th scope="row"><label for="api_key"><?php _e( 'API Key', 'imagify' ); ?></label></th>
112
- <td>
113
- <input type="text" size="35" value="<?php echo esc_attr( get_imagify_option( 'api_key' ) ); ?>" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[api_key]" id="api_key">
114
- <?php
115
- if ( imagify_valid_key() ) {
116
- ?>
117
-
118
- <span id="imagify-check-api-container" class="imagify-valid">
119
- <span class="dashicons dashicons-yes"></span> <?php _e( 'Your API key is valid.', 'imagify' ); ?>
120
- </span>
121
-
122
- <?php
123
- } elseif ( ! imagify_valid_key() && get_imagify_option( 'api_key', false ) ) {
124
- ?>
125
-
126
- <span id="imagify-check-api-container">
127
- <span class="dashicons dashicons-no"></span> <?php _e( 'Your API key isn\'t valid!', 'imagify' ); ?>
128
- </span>
129
-
130
- <?php
131
- }
132
-
133
- if ( ! get_imagify_option( 'api_key', false ) ) {
134
- echo '<p class="description desc api_key">';
135
- printf(
136
- /* translators: 1 is a link tag start, 2 is the link tag end. */
137
- __( 'Don\'t have an API Key yet? %1$sCreate one, it\'s FREE%2$s.', 'imagify' ),
138
- '<a id="imagify-signup" href="' . esc_url( imagify_get_external_url( 'register' ) ) . '" target="_blank">',
139
- '</a>'
140
- );
141
- echo '</p>';
142
- }
143
- ?>
144
- </td>
145
- </tr>
146
- </tbody>
147
- </table>
148
- </div>
149
-
150
- <?php } // End if(). ?>
151
-
152
- <div class="imagify-settings-section <?php echo ( ! imagify_valid_key() ) ? 'hidden' : ''; ?>">
153
-
154
- <table class="form-table">
155
- <tbody>
156
- <tr class="imagify-middle">
157
- <th scope="row"><?php _e( 'Optimization Level', 'imagify' ); ?></th>
158
- <td>
159
- <p class="imagify-inline-options">
160
- <input type="radio" id="imagify-optimization_level_normal" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[optimization_level]" value="0" <?php checked( get_imagify_option( 'optimization_level' ), 0 ); ?>>
161
- <label for="imagify-optimization_level_normal">
162
- <?php _e( 'Normal', 'imagify' ); ?>
163
- </label>
164
-
165
- <input type="radio" id="imagify-optimization_level_aggro" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[optimization_level]" value="1" <?php checked( get_imagify_option( 'optimization_level' ), 1 ); ?>>
166
- <label for="imagify-optimization_level_aggro">
167
- <?php _e( 'Aggressive', 'imagify' ); ?>
168
- </label>
169
-
170
- <input type="radio" id="imagify-optimization_level_ultra" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[optimization_level]" value="2" <?php checked( get_imagify_option( 'optimization_level' ), 2 ); ?>>
171
- <label for="imagify-optimization_level_ultra">
172
- <?php _e( 'Ultra', 'imagify' ); ?>
173
- </label>
174
-
175
- <span class="imagify-info">
176
- <span class="dashicons dashicons-info"></span>
177
- <a href="#imagify-more-info" class="imagify-modal-trigger"><?php _e( 'More info?', 'imagify' ); ?></a>
178
- </span>
179
- </p>
180
-
181
- <p class="imagify-visual-comparison-text">
182
- <?php
183
- printf(
184
- /* translators: 1 is a button tag start, 2 is the button tag end. */
185
- __( 'Need help to choose? %1$sTry the Visual Comparison%2$s', 'imagify' ),
186
- '<button type="button" class="button button-primary button-mini-flat imagify-visual-comparison-btn imagify-modal-trigger" data-target="#imagify-visual-comparison">',
187
- '</button>'
188
- );
189
- ?>
190
- </p>
191
- </td>
192
- </tr>
193
- <tr>
194
- <th scope="row"><span><?php _e( 'Auto-Optimize images on upload', 'imagify' ); ?></span></th>
195
- <td>
196
- <input type="checkbox" value="1" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[auto_optimize]" id="auto_optimize" <?php checked( get_imagify_option( 'auto_optimize', 0 ), 1 ); ?> aria-describedby="describe-auto-optimize" />
197
- <!-- Empty onclick attribute to make clickable labels on iTruc & Mac -->
198
- <label for="auto_optimize" onclick=""><span class="screen-reader-text"><?php _e( 'Auto-Optimize images on upload', 'imagify' ); ?></span></label>
199
-
200
- <span id="describe-auto-optimize" class="imagify-info">
201
- <span class="dashicons dashicons-info"></span>
202
- <?php _e( 'Automatically optimize every image you upload to WordPress.', 'imagify' ); ?>
203
- </span>
204
- </td>
205
- </tr>
206
- <tr>
207
- <th scope="row"><span><?php _e( 'Backup original images', 'imagify' ); ?></span></th>
208
- <td>
209
- <?php
210
- $backup_enabled = (int) get_imagify_option( 'backup', 0 );
211
- $backup_error_class = ' hidden';
212
-
213
- if ( $backup_enabled && ! imagify_backup_dir_is_writable() ) {
214
- $backup_error_class = '';
215
- }
216
- ?>
217
- <input type="checkbox" value="1" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[backup]" id="backup" <?php checked( $backup_enabled, 1 ); ?> aria-describedby="describe-backup" />
218
- <label for="backup" onclick=""><span class="screen-reader-text"><?php _e( 'Backup original images', 'imagify' ); ?></span></label>
219
-
220
- <span id="describe-backup" class="imagify-info">
221
- <span class="dashicons dashicons-info"></span>
222
- <?php _e( 'Keep your original images in a separate folder before optimization process.', 'imagify' ); ?>
223
- </span>
224
-
225
- <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' ) ); ?>">
226
- <?php
227
- $backup_path = imagify_make_file_path_relative( get_imagify_backup_dir_path( true ) );
228
- /* translators: %s is a file path. */
229
- 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>" );
230
- ?>
231
- </strong>
232
- </td>
233
- </tr>
234
- <tr>
235
- <th scope="row"><span><?php _e( 'Resize larger images', 'imagify' ); ?></span></th>
236
- <td>
237
- <input type="checkbox" value="1" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[resize_larger]" id="resize_larger" <?php checked( get_imagify_option( 'resize_larger', 0 ), 1 ); ?> aria-describedby="describe-resize-larger" />
238
- <label for="resize_larger" onclick=""><span class="screen-reader-text"><?php _e( 'Resize larger images', 'imagify' ); ?></span></label>
239
-
240
- <p id="describe-resize-larger" class="imagify-options-line">
241
- <?php
242
- $max_sizes = get_imagify_max_intermediate_image_size();
243
- printf(
244
- /* translators: 1 is a text input for a number of pixels (don't use %d). */
245
- __( 'to maximum %s pixels width', 'imagify' ),
246
- '<input type="number" min="' . $max_sizes['width'] . '" name="' . IMAGIFY_SETTINGS_SLUG . '[resize_larger_w]" value="' . get_imagify_option( 'resize_larger_w', false ) . '" size="5">'
247
- );
248
- ?>
249
- </p>
250
-
251
- <p class="imagify-checkbox-marged">
252
- <span class="imagify-info">
253
- <span class="dashicons dashicons-info"></span>
254
- <?php
255
- printf(
256
- /* translators: 1 is a number of pixels. */
257
- __( 'This option is recommended to reduce larger images. You can save up to 80%% after resizing. The new width should not be less than your largest thumbnail width, which is actually %dpx.', 'imagify' ),
258
- $max_sizes['width']
259
- );
260
- ?>
261
- </span>
262
- </p>
263
- </td>
264
- </tr>
265
- <tr>
266
- <th scope="row"><span><?php _e( 'EXIF Data', 'imagify' ); ?></span></th>
267
- <td>
268
- <input type="checkbox" value="1" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[exif]" id="exif" <?php checked( get_imagify_option( 'exif', 0 ), 1 ); ?> aria-describedby="describe-exif" />
269
- <label for="exif" onclick=""><span class="screen-reader-text"><?php _e( 'EXIF Data', 'imagify' ); ?></span></label>
270
-
271
- <span id="describe-exif" class="imagify-info">
272
- <span class="dashicons dashicons-info"></span>
273
- <?php _e( 'Keep all EXIF data from your images. EXIF are informations stored in your pictures like shutter speed, exposure compensation, ISO, etc...', 'imagify' ); ?>
274
- <a href="<?php echo esc_url( imagify_get_external_url( 'exif' ) ); ?>" target="_blank"><?php _e( 'Learn more', 'imagify' ); ?></a>
275
- <br/><br/>
276
- <?php _e( 'If you are a photographer, you may be interested in this option if you are displaying on your pages some info like the model of your camera.', 'imagify' ); ?>
277
-
278
- </span>
279
- </td>
280
- </tr>
281
-
282
- <?php if ( ! imagify_is_active_for_network() ) { ?>
283
-
284
- <tr>
285
- <th scope="row"><?php _e( 'Files optimization', 'imagify' ); ?></th>
286
- <td>
287
- <p>
288
- <?php _e( 'You can choose to compress different image sizes created by WordPress here.', 'imagify' ); ?>
289
- <br/>
290
- <?php
291
- printf(
292
- /* translators: 1 is a "bold" tag start, 2 is the "bold" tag end. */
293
- __( 'The %1$soriginal size%2$s is %1$sautomatically optimized%2$s by Imagify.', 'imagify' ),
294
- '<strong>', '</strong>'
295
- );
296
- ?>
297
- <br>
298
- <span class="imagify-important">
299
- <?php _e( 'Remember each additional image size will affect your Imagify monthly usage!', 'imagify' ); ?>
300
- </span>
301
- </p>
302
-
303
- <br>
304
-
305
- <fieldset class="imagify-check-group">
306
- <legend class="screen-reader-text"><?php _e( 'Choose the sizes to optimize', 'imagify' ); ?></legend>
307
- <?php
308
- $sizes = get_imagify_thumbnail_sizes();
309
- $select_all = count( $sizes ) > 3;
310
- $disallowed = (array) get_imagify_option( 'disallowed-sizes', array() );
311
-
312
- if ( $select_all ) {
313
- $has_disallowed = array_intersect_key( $disallowed, $sizes );
314
- $has_disallowed = ! empty( $has_disallowed );
315
- ?>
316
- <em class="hide-if-no-js">
317
- <input id="imagify-toggle-check-thumbnail-sizes-1" type="checkbox" class="mini imagify-toggle-check" <?php checked( ! $has_disallowed ); ?>>
318
- <label for="imagify-toggle-check-thumbnail-sizes-1" onclick=""><?php _e( '(Un)Select All', 'imagify' ); ?></label>
319
- </em>
320
- <br class="imagify-br">
321
- <?php
322
- }
323
-
324
- foreach ( $sizes as $size_key => $size_data ) {
325
- $label = esc_html( stripslashes( $size_data['name'] ) );
326
- $label = sprintf( '%s - %d &times; %d', $label, $size_data['width'], $size_data['height'] );
327
- $checked = ! isset( $disallowed[ $size_key ] );
328
- ?>
329
- <input type="hidden" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[sizes][<?php echo $size_key; ?>-hidden]" value="1" />
330
- <input type="checkbox" id="imagify_sizes_<?php echo $size_key; ?>" class="mini imagify-row-check" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[sizes][<?php echo $size_key; ?>]" value="1" <?php checked( $checked ); ?>/>
331
- <label for="imagify_sizes_<?php echo $size_key; ?>" onclick=""><?php echo $label; ?></label>
332
- <br class="imagify-br">
333
- <?php
334
- }
335
-
336
- if ( $select_all ) {
337
- ?>
338
- <em class="hide-if-no-js">
339
- <input id="imagify-toggle-check-thumbnail-sizes-2" type="checkbox" class="mini imagify-toggle-check" <?php checked( ! $has_disallowed ); ?>>
340
- <label for="imagify-toggle-check-thumbnail-sizes-2" onclick=""><?php _e( '(Un)Select All', 'imagify' ); ?></label>
341
- </em>
342
- <?php
343
- }
344
- ?>
345
- </fieldset>
346
- </td>
347
- </tr>
348
-
349
- <?php } // End if(). ?>
350
-
351
- </tbody>
352
- </table>
353
-
354
- <h3 class="imagify-options-title"><?php _e( 'Display options', 'imagify' ); ?></h3>
355
-
356
- <table class="form-table">
357
- <tbody>
358
- <tr>
359
- <th scope="row"><span><?php _e( 'Show Admin Bar menu', 'imagify' ); ?></span></th>
360
- <td>
361
- <input type="checkbox" value="1" name="<?php echo IMAGIFY_SETTINGS_SLUG; ?>[admin_bar_menu]" id="admin_bar_menu" <?php checked( get_imagify_option( 'admin_bar_menu', 0 ), 1 ); ?> aria-describedby="describe-admin-bar-menu" />
362
- <!-- Empty onclick attribute to make clickable labels on iTruc & Mac -->
363
- <label for="admin_bar_menu" onclick="">
364
- <span class="screen-reader-text"><?php _e( 'Show Admin Bar menu', 'imagify' ); ?></span>
365
- <span class="imagify-visual-label"><?php _e( 'I want this awesome quick access menu on my admin bar.', 'imagify' ); ?></span>
366
- </label>
367
-
368
- <p>
369
- <img class="imagify-menu-bar-img" src="<?php echo IMAGIFY_ASSETS_IMG_URL . 'imagify-menu-bar.jpg'; ?>" width="300" height="225" alt="">
370
- </p>
371
- </td>
372
- </tr>
373
- </tbody>
374
- </table>
375
-
376
- </div>
377
- <div class="submit">
378
- <?php
379
- // Classical submit.
380
- submit_button();
381
-
382
- // Submit and go to bulk page.
383
- submit_button(
384
- esc_html__( 'Save &amp; Go to Bulk Optimizer', 'imagify' ),
385
- 'secondary imagify-button-secondary', // Type/classes.
386
- 'submit-goto-bulk', // Name (id).
387
- true, // Wrap.
388
- array() // Other attributes.
389
- );
390
- ?>
391
-
392
- <div class="imagify-bulk-info">
393
- <p>
394
- <?php
395
- printf(
396
- /* translators: 1 is a link tag start, 2 is the link tag end. */
397
- __( 'Once your settings saved, optimize all your images by using the %1$sImagify Bulk Optimization%2$s feature.', 'imagify' ),
398
- '<a href="' . esc_url( get_admin_url() ) . 'upload.php?page=' . IMAGIFY_SLUG . '-bulk-optimization">',
399
- '</a>'
400
- );
401
- ?>
402
- </p>
403
- </div>
404
- </div>
405
- </form>
406
- </div>
407
-
408
- <div id="imagify-more-info" class="imagify-modal">
409
- <div class="imagify-modal-content">
410
- <p class="h2"><?php _e( 'You can choose three levels of compression', 'imagify' ); ?></p>
411
- <div class="imagify-columns">
412
- <div class="col-1-3">
413
- <p class="h3"><?php _e( 'Normal', 'imagify' ); ?></p>
414
- <p>
415
- <?php _e( 'This mode provides lossless optimization, your images will be optimized without any visible change.', 'imagify' ); ?>
416
- </p>
417
- <p>
418
- <?php _e( 'If you want the perfect quality for your images, we recommend you that mode.', 'imagify' ); ?>
419
- </p>
420
- <p>
421
- <em><?php _e( 'Note: the file size reduction will be less, compared to aggressive mode.', 'imagify' ); ?></em>
422
- </p>
423
- </div>
424
-
425
- <div class="col-1-3">
426
- <p class="h3"><?php _e( 'Aggressive', 'imagify' ); ?></p>
427
- <p>
428
- <?php _e( 'This mode provides perfect optimization of your images without any significant quality loss.', 'imagify' ); ?>
429
- </p>
430
- <p>
431
- <?php _e( 'This will provide a drastic savings on the initial weight, with a small reduction in image quality. Most of the time it\'s not even noticeable.', 'imagify' ); ?>
432
- </p>
433
- <p>
434
- <?php _e( 'If you want the maximum weight reduction, we recommend using this mode.' , 'imagify' ); ?>
435
- </p>
436
- </div>
437
-
438
- <div class="col-1-3">
439
- <p class="h3"><?php _e( 'Ultra', 'imagify' ); ?></p>
440
- <p>
441
- <?php _e( 'This mode will apply all available optimizations for maximum image compression.', 'imagify' ); ?>
442
- </p>
443
- <p>
444
- <?php _e( 'This will provide a huge savings on the initial weight. Sometimes the image quality could be degraded a little.', 'imagify' ); ?>
445
- </p>
446
- <p>
447
- <?php _e( 'If you want the maximum weight reduction, and you agree to lose some quality on the images we recommend using this mode.' , 'imagify' ); ?>
448
- </p>
449
- </div>
450
- </div>
451
-
452
- <button type="button" class="close-btn">
453
- <i class="dashicons dashicons-no-alt" aria-hidden="true"></i>
454
- <span class="screen-reader-text"><?php _e( 'Close' ); ?></span>
455
- </button>
456
- </div>
457
- </div>
458
-
459
-
460
- <div class="imagify-modal" id="imagify-visual-comparison">
461
- <div class="imagify-modal-content">
462
-
463
- <p class="imagify-comparison-title">
464
- <?php
465
- printf(
466
- /* translators: 1 and 2 are optimization levels: "Original", "Normal", "Aggressive", or "Ultra". */
467
- __( 'I want to compare %1$s and %2$s', 'imagify' ),
468
- '<span class="twentytwenty-left-buttons"></span>',
469
- '<span class="twentytwenty-right-buttons"></span>'
470
- );
471
- ?>
472
- </p>
473
-
474
- <div class="twentytwenty-container"
475
- data-loader="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>loader-balls.svg"
476
- data-label-original="<?php esc_attr_e( 'Original', 'imagify' ); ?>"
477
- data-label-normal="<?php esc_attr_e( 'Normal', 'imagify' ); ?>"
478
- data-label-aggressive="<?php esc_attr_e( 'Aggressive', 'imagify' ); ?>"
479
- data-label-ultra="<?php esc_attr_e( 'Ultra', 'imagify' ); ?>"
480
-
481
- data-original-label="<?php esc_attr_e( 'Original', 'imagify' ); ?>"
482
- data-original-img="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>mushrooms-original.jpg"
483
- data-original-dim="1220x350"
484
- data-original-alt="<?php
485
- /* translators: %s is a formatted file size. */
486
- printf( esc_attr__( 'Original photography about %s', 'imagify' ), size_format( 343040 ) );
487
- ?>"
488
-
489
- data-normal-label="<?php esc_attr_e( 'Normal', 'imagify' ); ?>"
490
- data-normal-img="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>mushrooms-normal.jpg"
491
- data-normal-dim="1220x350"
492
- data-normal-alt="<?php
493
- /* translators: %s is a formatted file size. */
494
- printf( esc_attr__( 'Optimized photography about %s', 'imagify' ), size_format( 301056 ) );
495
- ?>"
496
-
497
- data-aggressive-label="<?php esc_attr_e( 'Aggressive', 'imagify' ); ?>"
498
- data-aggressive-img="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>mushrooms-aggressive.jpg"
499
- data-aggressive-dim="1220x350"
500
- data-aggressive-alt="<?php
501
- /* translators: %s is a formatted file size. */
502
- printf( esc_attr__( 'Optimized photography about %s', 'imagify' ), size_format( 108544 ) );
503
- ?>"
504
-
505
- data-ultra-label="<?php esc_attr_e( 'Ultra', 'imagify' ); ?>"
506
- data-ultra-img="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>mushrooms-ultra.jpg"
507
- data-ultra-dim="1220x350"
508
- data-ultra-alt="<?php
509
- /* translators: %s is a formatted file size. */
510
- printf( esc_attr__( 'Optimized photography about %s', 'imagify' ), size_format( 46080 ) );
511
- ?>"></div>
512
-
513
- <div class="imagify-comparison-levels">
514
- <div class="imagify-c-level imagify-level-original go-left">
515
- <p class="imagify-c-level-row">
516
- <span class="label"><?php _e( 'Level:', 'imagify' ); ?></span>
517
- <span class="value level"><?php _e( 'Original', 'imagify' ); ?></span>
518
- </p>
519
- <p class="imagify-c-level-row">
520
- <span class="label"><?php _e( 'File Size:', 'imagify' ); ?></span>
521
- <span class="value"><?php echo size_format( 343040 ); ?></span>
522
- </p>
523
- </div>
524
- <div class="imagify-c-level imagify-level-optimized imagify-level-normal" aria-hidden="true">
525
- <p class="imagify-c-level-row">
526
- <span class="label"><?php _e( 'Level:', 'imagify' ); ?></span>
527
- <span class="value level"><?php _e( 'Normal', 'imagify' ); ?></span>
528
- </p>
529
- <p class="imagify-c-level-row">
530
- <span class="label"><?php _e( 'File Size:', 'imagify' ); ?></span>
531
- <span class="value size"><?php echo size_format( 301056 ); ?></span>
532
- </p>
533
- <p class="imagify-c-level-row">
534
- <span class="label"><?php _e( 'Original Saving:', 'imagify' ); ?></span>
535
- <span class="value">
536
- <span class="imagify-chart">
537
- <span class="imagify-chart-container">
538
- <canvas id="imagify-consumption-chart-normal" width="15" height="15"></canvas>
539
- </span>
540
- </span><span class="imagify-chart-value">12.24</span>%
541
- </span>
542
- </p>
543
- </div>
544
- <div class="imagify-c-level imagify-level-aggressive">
545
- <p class="imagify-c-level-row">
546
- <span class="label"><?php _e( 'Level:', 'imagify' ); ?></span>
547
- <span class="value level"><?php _e( 'Aggressive', 'imagify' ); ?></span>
548
- </p>
549
- <p class="imagify-c-level-row">
550
- <span class="label"><?php _e( 'File Size:', 'imagify' ); ?></span>
551
- <span class="value size"><?php echo size_format( 108544 ); ?></span>
552
- </p>
553
- <p class="imagify-c-level-row">
554
- <span class="label"><?php _e( 'Original Saving:', 'imagify' ); ?></span>
555
- <span class="value">
556
- <span class="imagify-chart">
557
- <span class="imagify-chart-container">
558
- <canvas id="imagify-consumption-chart-aggressive" width="15" height="15"></canvas>
559
- </span>
560
- </span><span class="imagify-chart-value">68.36</span>%
561
- </span>
562
- </p>
563
- </div>
564
-
565
- <div class="imagify-c-level imagify-level-ultra go-right">
566
- <p class="imagify-c-level-row">
567
- <span class="label"><?php _e( 'Level:', 'imagify' ); ?></span>
568
- <span class="value level"><?php _e( 'Ultra', 'imagify' ); ?></span>
569
- </p>
570
- <p class="imagify-c-level-row">
571
- <span class="label"><?php _e( 'File Size:', 'imagify' ); ?></span>
572
- <span class="value size"><?php echo size_format( 46080 ); ?></span>
573
- </p>
574
- <p class="imagify-c-level-row">
575
- <span class="label"><?php _e( 'Original Saving:', 'imagify' ); ?></span>
576
- <span class="value">
577
- <span class="imagify-chart">
578
- <span class="imagify-chart-container">
579
- <canvas id="imagify-consumption-chart-ultra" width="15" height="15"></canvas>
580
- </span>
581
- </span><span class="imagify-chart-value">86.57</span>%
582
- </span>
583
- </p>
584
- </div>
585
- </div>
586
-
587
- <button type="button" class="close-btn">
588
- <i class="dashicons dashicons-no-alt" aria-hidden="true"></i>
589
- <span class="screen-reader-text"><?php _e( 'Close' ); ?></span>
590
- </button>
591
- </div>
592
- </div>
593
-
594
- <?php imagify_payment_modal(); ?>
595
-
596
- </div>
597
- <?php
598
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/admin/upgrader.php CHANGED
@@ -8,28 +8,157 @@ add_action( 'admin_init', '_imagify_upgrader' );
8
  * @since 1.0
9
  */
10
  function _imagify_upgrader() {
11
- $current_version = get_imagify_option( 'version' );
 
12
 
13
- // You can hook the upgrader to trigger any action when Imagify is upgraded.
14
- // First install.
15
- if ( ! $current_version ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  do_action( 'imagify_first_install' );
17
  }
18
- // Already installed but got updated.
19
- elseif ( IMAGIFY_VERSION !== $current_version ) {
20
- do_action( 'imagify_upgrade', IMAGIFY_VERSION, $current_version );
 
 
 
 
 
 
 
 
 
21
  }
22
 
23
  // If any upgrade has been done, we flush and update version.
24
  if ( did_action( 'imagify_first_install' ) || did_action( 'imagify_upgrade' ) ) {
25
- $options = get_site_option( IMAGIFY_SETTINGS_SLUG ); // Do not use get_imagify_option() here.
26
- $options['version'] = IMAGIFY_VERSION;
 
27
 
28
- update_site_option( IMAGIFY_SETTINGS_SLUG, $options );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
 
32
- add_action( 'imagify_first_install', '_imagify_first_install' );
33
  /**
34
  * Keeps this function up to date at each version.
35
  *
@@ -38,19 +167,6 @@ add_action( 'imagify_first_install', '_imagify_first_install' );
38
  function _imagify_first_install() {
39
  // Set a transient to know when we will have to display a notice to ask the user to rate the plugin.
40
  set_site_transient( 'imagify_seen_rating_notice', true, DAY_IN_SECONDS * 3 );
41
-
42
- // Create Options.
43
- add_site_option( IMAGIFY_SETTINGS_SLUG, array(
44
- 'api_key' => '',
45
- 'optimization_level' => 1,
46
- 'auto_optimize' => 1,
47
- 'backup' => 1,
48
- 'resize_larger' => '',
49
- 'resize_larger_w' => '',
50
- 'exif' => 0,
51
- 'disallowed-sizes' => array(),
52
- 'admin_bar_menu' => 1,
53
- ) );
54
  }
55
 
56
  add_action( 'imagify_upgrade', '_imagify_new_upgrade', 10, 2 );
@@ -58,21 +174,24 @@ add_action( 'imagify_upgrade', '_imagify_new_upgrade', 10, 2 );
58
  * What to do when Imagify is updated, depending on versions.
59
  *
60
  * @since 1.0
 
61
  *
62
- * @param string $imagify_version New Imagify version.
63
- * @param string $current_version Old Imagify version.
64
  */
65
- function _imagify_new_upgrade( $imagify_version, $current_version ) {
66
  global $wpdb;
67
 
 
 
68
  // 1.2
69
- if ( version_compare( $current_version, '1.2' ) < 0 ) {
70
  // Update all already optimized images status from 'error' to 'already_optimized'.
71
  $query = new WP_Query( array(
72
  'is_imagify' => true,
73
  'post_type' => 'attachment',
74
- 'post_status' => 'inherit',
75
- 'post_mime_type' => get_imagify_mime_type(),
76
  'meta_key' => '_imagify_status',
77
  'meta_value' => 'error',
78
  'posts_per_page' => -1,
@@ -92,21 +211,17 @@ function _imagify_new_upgrade( $imagify_version, $current_version ) {
92
  }
93
 
94
  // Auto-activate the Admin Bar option.
95
- $options = get_site_option( IMAGIFY_SETTINGS_SLUG );
96
- $options = is_array( $options ) ? $options : array();
97
- $options['admin_bar_menu'] = 1;
98
-
99
- update_site_option( IMAGIFY_SETTINGS_SLUG, $options );
100
  }
101
 
102
  // 1.3.2
103
- if ( version_compare( $current_version, '1.3.2' ) < 0 ) {
104
  // Update all already optimized images status from 'error' to 'already_optimized'.
105
  $query = new WP_Query( array(
106
  'is_imagify' => true,
107
  'post_type' => 'attachment',
108
- 'post_status' => 'inherit',
109
- 'post_mime_type' => get_imagify_mime_type(),
110
  'meta_query' => array(
111
  'relation' => 'AND',
112
  array(
@@ -136,8 +251,22 @@ function _imagify_new_upgrade( $imagify_version, $current_version ) {
136
  }
137
 
138
  // 1.4.5
139
- if ( version_compare( $current_version, '1.4.5' ) < 0 ) {
140
  // Delete all transients used for async optimization.
141
  $wpdb->query( 'DELETE from ' . $wpdb->options . ' WHERE option_name LIKE "_transient_imagify-async-in-progress-%"' );
142
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  }
8
  * @since 1.0
9
  */
10
  function _imagify_upgrader() {
11
+ // Back-compat' with previous version of the upgrader.
12
+ imagify_upgrader_upgrade();
13
 
14
+ // Version stored on the network.
15
+ $network_version = Imagify_Options::get_instance()->get( 'version' );
16
+ // Version stored at the site level.
17
+ $site_version = Imagify_Data::get_instance()->get( 'version' );
18
+
19
+ // First install (network).
20
+ if ( ! $network_version ) {
21
+ /**
22
+ * Triggered on Imagify first install (network).
23
+ *
24
+ * @since 1.7
25
+ * @author Grégory Viguier
26
+ */
27
+ do_action( 'imagify_first_network_install' );
28
+ }
29
+ // Already installed but got updated (network).
30
+ elseif ( IMAGIFY_VERSION !== $network_version ) {
31
+ /**
32
+ * Triggered on Imagify upgrade (network).
33
+ *
34
+ * @since 1.7
35
+ * @author Grégory Viguier
36
+ *
37
+ * @param string $network_version Previous version stored on the network.
38
+ * @param string $site_version Previous version stored on site level.
39
+ */
40
+ do_action( 'imagify_network_upgrade', $network_version, $site_version );
41
+ }
42
+
43
+ // If any upgrade has been done, we flush and update version.
44
+ if ( did_action( 'imagify_first_network_install' ) || did_action( 'imagify_network_upgrade' ) ) {
45
+ Imagify_Options::get_instance()->set( 'version', IMAGIFY_VERSION );
46
+ }
47
+
48
+ // First install (site level).
49
+ if ( ! $site_version ) {
50
+ /**
51
+ * Triggered on Imagify first install (site level).
52
+ *
53
+ * @since 1.0
54
+ */
55
  do_action( 'imagify_first_install' );
56
  }
57
+ // Already installed but got updated (site level).
58
+ elseif ( IMAGIFY_VERSION !== $site_version ) {
59
+ /**
60
+ * Triggered on Imagify upgrade (site level).
61
+ *
62
+ * @since 1.0
63
+ * @since 1.7 $network_version replaces the "new version" (which can easily be grabbed with the constant).
64
+ *
65
+ * @param string $network_version Previous version stored on the network.
66
+ * @param string $site_version Previous version stored on site level.
67
+ */
68
+ do_action( 'imagify_upgrade', $network_version, $site_version );
69
  }
70
 
71
  // If any upgrade has been done, we flush and update version.
72
  if ( did_action( 'imagify_first_install' ) || did_action( 'imagify_upgrade' ) ) {
73
+ Imagify_Data::get_instance()->set( 'version', IMAGIFY_VERSION );
74
+ }
75
+ }
76
 
77
+ /**
78
+ * Upgrade the upgrader:
79
+ * Imagify 1.7 splits "network version" and "site version". Since the "site version" didn't exist before 1.7, we need to provide a version based on the "network version".
80
+ *
81
+ * @since 1.7
82
+ * @author Grégory Viguier
83
+ */
84
+ function imagify_upgrader_upgrade() {
85
+ global $wpdb;
86
+
87
+ // Version stored on the network.
88
+ $network_version = Imagify_Options::get_instance()->get( 'version' );
89
+
90
+ if ( ! $network_version ) {
91
+ // Really first install.
92
+ return;
93
+ }
94
+
95
+ // Version stored at the site level.
96
+ $site_version = Imagify_Data::get_instance()->get( 'version' );
97
+
98
+ if ( $site_version ) {
99
+ // This site's upgrader is already upgraded.
100
+ return;
101
  }
102
+
103
+ if ( ! is_multisite() ) {
104
+ // Not a multisite, so both versions must have the same value.
105
+ Imagify_Data::get_instance()->set( 'version', $network_version );
106
+ return;
107
+ }
108
+
109
+ $sites = get_site_option( 'imagify_old_version' );
110
+
111
+ if ( IMAGIFY_VERSION !== $network_version && ! $sites ) {
112
+ // The network is not up-to-date yet: store the site IDs that must be updated.
113
+ $network_id = function_exists( 'get_current_network_id' ) ? get_current_network_id() : $wpdb->siteid;
114
+ $sites = $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d AND archived = 0 AND deleted = 0", $network_id ) );
115
+ $sites = array_map( 'absint', $sites );
116
+ $sites = array_filter( $sites );
117
+
118
+ if ( ! $sites ) {
119
+ // Uh?
120
+ return;
121
+ }
122
+
123
+ // We store the old network version and the site Ids: those sites will need to be upgraded from this version.
124
+ $sites['version'] = $network_version;
125
+
126
+ add_site_option( 'imagify_old_version', $sites );
127
+ }
128
+
129
+ if ( empty( $sites['version'] ) ) {
130
+ // WTF.
131
+ delete_site_option( 'imagify_old_version' );
132
+ return;
133
+ }
134
+
135
+ $network_version = $sites['version'];
136
+ unset( $sites['version'] );
137
+
138
+ $sites = array_flip( $sites );
139
+ $site_id = get_current_blog_id();
140
+
141
+ if ( ! isset( $sites[ $site_id ] ) ) {
142
+ // This site is already upgraded.
143
+ return;
144
+ }
145
+
146
+ unset( $sites[ $site_id ] );
147
+
148
+ if ( ! $sites ) {
149
+ // We're done, all the sites have been upgraded.
150
+ delete_site_option( 'imagify_old_version' );
151
+ } else {
152
+ // Some sites still need to be upgraded.
153
+ $sites = array_flip( $sites );
154
+ $sites['version'] = $network_version;
155
+ update_site_option( 'imagify_old_version', $sites );
156
+ }
157
+
158
+ Imagify_Data::get_instance()->set( 'version', $network_version );
159
  }
160
 
161
+ add_action( 'imagify_first_network_install', '_imagify_first_install' );
162
  /**
163
  * Keeps this function up to date at each version.
164
  *
167
  function _imagify_first_install() {
168
  // Set a transient to know when we will have to display a notice to ask the user to rate the plugin.
169
  set_site_transient( 'imagify_seen_rating_notice', true, DAY_IN_SECONDS * 3 );
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  }
171
 
172
  add_action( 'imagify_upgrade', '_imagify_new_upgrade', 10, 2 );
174
  * What to do when Imagify is updated, depending on versions.
175
  *
176
  * @since 1.0
177
+ * @since 1.7 $network_version replaces the "new version" (which can easily be grabbed with the constant).
178
  *
179
+ * @param string $network_version Previous version stored on the network.
180
+ * @param string $site_version Previous version stored on site level.
181
  */
182
+ function _imagify_new_upgrade( $network_version, $site_version ) {
183
  global $wpdb;
184
 
185
+ $options = Imagify_Options::get_instance();
186
+
187
  // 1.2
188
+ if ( version_compare( $site_version, '1.2' ) < 0 ) {
189
  // Update all already optimized images status from 'error' to 'already_optimized'.
190
  $query = new WP_Query( array(
191
  'is_imagify' => true,
192
  'post_type' => 'attachment',
193
+ 'post_status' => imagify_get_post_statuses(),
194
+ 'post_mime_type' => imagify_get_mime_types(),
195
  'meta_key' => '_imagify_status',
196
  'meta_value' => 'error',
197
  'posts_per_page' => -1,
211
  }
212
 
213
  // Auto-activate the Admin Bar option.
214
+ $options->set( 'admin_bar_menu', 1 );
 
 
 
 
215
  }
216
 
217
  // 1.3.2
218
+ if ( version_compare( $site_version, '1.3.2' ) < 0 ) {
219
  // Update all already optimized images status from 'error' to 'already_optimized'.
220
  $query = new WP_Query( array(
221
  'is_imagify' => true,
222
  'post_type' => 'attachment',
223
+ 'post_status' => imagify_get_post_statuses(),
224
+ 'post_mime_type' => imagify_get_mime_types(),
225
  'meta_query' => array(
226
  'relation' => 'AND',
227
  array(
251
  }
252
 
253
  // 1.4.5
254
+ if ( version_compare( $site_version, '1.4.5' ) < 0 ) {
255
  // Delete all transients used for async optimization.
256
  $wpdb->query( 'DELETE from ' . $wpdb->options . ' WHERE option_name LIKE "_transient_imagify-async-in-progress-%"' );
257
  }
258
+
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.
266
+ $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->options} SET `autoload` = 'yes' WHERE `autoload` != 'yes' AND option_name = %s", $options->get_option_name() ) );
267
+ }
268
+
269
+ // Rename the option that stores the NGG table version. Since the table is also updated in 1.7, let's simply delete the option.
270
+ delete_option( $wpdb->prefix . 'ngg_imagify_data_db_version' );
271
+ }
272
  }
inc/admin/upload.php CHANGED
@@ -123,7 +123,7 @@ function _imagify_sort_attachments_by_status( $vars ) {
123
  ),
124
  ) );
125
 
126
- $vars['post_mime_type'] = get_imagify_mime_type();
127
 
128
  return $vars;
129
  }
123
  ),
124
  ) );
125
 
126
+ $vars['post_mime_type'] = imagify_get_mime_types();
127
 
128
  return $vars;
129
  }
inc/classes/class-imagify-abstract-attachment.php CHANGED
@@ -6,14 +6,14 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
6
  *
7
  * @since 1.0
8
  */
9
- abstract class Imagify_Abstract_Attachment {
10
 
11
  /**
12
  * Class version.
13
  *
14
  * @var string
15
  */
16
- const VERSION = '1.2';
17
 
18
  /**
19
  * The attachment ID.
@@ -24,14 +24,43 @@ abstract class Imagify_Abstract_Attachment {
24
  */
25
  public $id = 0;
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  /**
28
  * The constructor.
29
  *
30
  * @since 1.0
31
  * @access public
32
  *
33
- * @param int $id The attachment ID.
34
- * @return void
35
  */
36
  public function __construct( $id = 0 ) {
37
  global $post;
@@ -46,9 +75,55 @@ abstract class Imagify_Abstract_Attachment {
46
  $this->id = $post->ID;
47
  }
48
 
49
- $this->id = absint( $this->id );
50
  }
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  /**
53
  * Get the attachment backup file path, even if the file doesn't exist.
54
  *
@@ -69,9 +144,13 @@ abstract class Imagify_Abstract_Attachment {
69
  * @return string|false The file path. False if it doesn't exist.
70
  */
71
  public function get_backup_path() {
 
 
 
 
72
  $backup_path = $this->get_raw_backup_path();
73
 
74
- if ( $backup_path && file_exists( $backup_path ) ) {
75
  return $backup_path;
76
  }
77
 
@@ -87,6 +166,10 @@ abstract class Imagify_Abstract_Attachment {
87
  * @return string|false
88
  */
89
  public function get_backup_url() {
 
 
 
 
90
  return get_imagify_attachment_url( $this->get_raw_backup_path() );
91
  }
92
 
@@ -101,43 +184,24 @@ abstract class Imagify_Abstract_Attachment {
101
  abstract public function get_data();
102
 
103
  /**
104
- * Get the attachment extension.
105
  *
106
  * @since 1.0
107
  * @access public
108
  *
109
- * @return string
110
- */
111
- public function get_extension() {
112
- $fullsize_path = $this->get_original_path();
113
- return pathinfo( $fullsize_path, PATHINFO_EXTENSION );
114
- }
115
-
116
- /**
117
- * Tell if the current file mime type is supported.
118
- *
119
- * @since 1.6.9
120
- * @access public
121
- * @author Grégory Viguier
122
- *
123
- * @return bool
124
  */
125
- public function is_mime_type_supported() {
126
- return imagify_is_attachment_mime_type_supported( $this->id );
127
- }
128
 
129
  /**
130
- * Tell if the current attachment has the required WP metadata.
131
  *
132
- * @since 1.6.12
133
  * @access public
134
- * @author Grégory Viguier
135
  *
136
- * @return bool
137
  */
138
- public function has_required_metadata() {
139
- return imagify_attachment_has_required_metadata( $this->id );
140
- }
141
 
142
  /**
143
  * Get the attachment error if there is one.
@@ -154,18 +218,36 @@ abstract class Imagify_Abstract_Attachment {
154
  return trim( $error );
155
  }
156
 
157
- return false;
158
  }
159
 
160
  /**
161
- * Get the attachment optimization level.
162
  *
163
  * @since 1.0
164
  * @access public
165
  *
166
  * @return int
167
  */
168
- abstract public function get_optimization_level();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
  /**
171
  * Delete the 3 metas used by Imagify.
@@ -175,80 +257,121 @@ abstract class Imagify_Abstract_Attachment {
175
  * @author Grégory Viguier
176
  */
177
  public function delete_imagify_data() {
 
 
 
 
178
  delete_post_meta( $this->id, '_imagify_data' );
179
  delete_post_meta( $this->id, '_imagify_status' );
180
  delete_post_meta( $this->id, '_imagify_optimization_level' );
181
  }
182
 
183
  /**
184
- * Get the attachment optimization level label.
185
  *
186
- * @since 1.2
 
187
  * @access public
188
  *
189
- * @return string
190
  */
191
- public function get_optimization_level_label() {
192
- $label = '';
193
- $level = $this->get_optimization_level();
 
 
 
194
 
195
- switch ( $level ) {
196
- case 2:
197
- $label = __( 'Ultra', 'imagify' );
198
- break;
199
- case 1:
200
- $label = __( 'Aggressive', 'imagify' );
201
- break;
202
- case 0:
203
- $label = __( 'Normal', 'imagify' );
 
 
204
  }
205
 
206
- return $label;
 
207
  }
208
 
209
  /**
210
- * Count number of optimized sizes.
211
  *
212
- * @since 1.0
213
  * @access public
 
214
  *
215
- * @return int
216
  */
217
- public function get_optimized_sizes_count() {
218
- $data = $this->get_data();
219
- $sizes = (array) $data['sizes'];
220
- $count = 0;
221
 
222
- unset( $sizes['full'] );
 
 
 
223
 
224
- foreach ( $sizes as $size ) {
225
- if ( $size['success'] ) {
226
- $count++;
227
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  }
229
 
230
- return $count;
231
  }
232
 
233
  /**
234
- * Get the attachment optimization status (success or error).
235
  *
236
- * @since 1.0
237
  * @access public
 
238
  *
239
- * @return string
240
  */
241
- abstract public function get_status();
 
 
 
 
 
 
242
 
243
  /**
244
- * Get the original attachment path.
245
  *
246
- * @since 1.0
 
247
  * @access public
248
  *
 
249
  * @return string
250
  */
251
- abstract public function get_original_path();
 
 
252
 
253
  /**
254
  * Get the original attachment size.
@@ -258,29 +381,104 @@ abstract class Imagify_Abstract_Attachment {
258
  *
259
  * @param bool $human_format True to display the image human format size (1Mb).
260
  * @param int $decimals Precision of number of decimal places.
261
- * @return string
262
  */
263
  public function get_original_size( $human_format = true, $decimals = 2 ) {
264
- $filesystem = imagify_get_filesystem();
265
- $original_size = $this->get_size_data( 'full', 'original_size' );
266
- $original_size = empty( $original_size ) ? $filesystem->size( $this->get_original_path() ) : (int) $original_size;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
 
268
- if ( true === $human_format ) {
269
- $original_size = @size_format( $original_size, $decimals );
270
  }
271
 
272
- return $original_size;
273
  }
274
 
275
  /**
276
- * Get the original attachment URL.
277
  *
278
- * @since 1.0
279
  * @access public
 
280
  *
281
- * @return string
 
 
282
  */
283
- abstract public function get_original_url();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
  /**
286
  * Get the statistics of a specific size.
@@ -367,8 +565,8 @@ abstract class Imagify_Abstract_Attachment {
367
  $filepath = $this->get_original_path();
368
  $size = 0;
369
 
370
- if ( file_exists( $filepath ) ) {
371
- $size = filesize( $filepath );
372
  }
373
 
374
  return $size > IMAGIFY_MAX_BYTES;
@@ -395,8 +593,7 @@ abstract class Imagify_Abstract_Attachment {
395
  * @return bool True if the attachment has an error.
396
  */
397
  public function has_error() {
398
- $has_error = $this->get_size_data( 'full', 'error' );
399
- return is_string( $has_error );
400
  }
401
 
402
  /**
@@ -420,7 +617,7 @@ abstract class Imagify_Abstract_Attachment {
420
  public function delete_backup() {
421
  $backup_path = $this->get_backup_path();
422
 
423
- if ( ! empty( $backup_path ) ) {
424
  imagify_get_filesystem()->delete( $backup_path );
425
  }
426
  }
@@ -456,7 +653,7 @@ abstract class Imagify_Abstract_Attachment {
456
  */
457
  public function get_unoptimized_sizes() {
458
  // The attachment must have been optimized once and have a backup.
459
- if ( ! $this->is_optimized() || ! $this->has_backup() ) {
460
  return array();
461
  }
462
 
@@ -484,7 +681,7 @@ abstract class Imagify_Abstract_Attachment {
484
  $orig_f = $orig_f['filename'] . '-{%suffix%}.' . $orig_f['extension'];
485
 
486
  // Test if the missing sizes are needed.
487
- $disallowed_sizes = get_imagify_option( 'disallowed-sizes', array() );
488
  $is_active_for_network = imagify_is_active_for_network();
489
 
490
  foreach ( $missing_sizes as $size_name => $size_data ) {
@@ -524,15 +721,15 @@ abstract class Imagify_Abstract_Attachment {
524
  * @since 1.0
525
  * @since 1.6.5 Not static anymore.
526
  * @since 1.6.6 Removed the attachment ID parameter.
 
527
  * @access public
528
  *
529
  * @param array $data The statistics data.
530
  * @param object $response The API response.
531
- * @param int $url The attachment URL.
532
  * @param string $size The attachment size key.
533
  * @return bool|array False if the original size has an error or an array contains the data for other result.
534
  */
535
- abstract public function fill_data( $data, $response, $url, $size = 'full' );
536
 
537
  /**
538
  * Optimize all sizes with Imagify.
@@ -581,6 +778,10 @@ abstract class Imagify_Abstract_Attachment {
581
  * @return string Path the the resized image or the original image if the resize failed.
582
  */
583
  public function resize( $attachment_path, $attachment_sizes, $max_width ) {
 
 
 
 
584
  // Prevent removal of the exif/meta data when resizing (only works with Imagick).
585
  add_filter( 'image_strip_meta', '__return_false' );
586
 
@@ -628,4 +829,103 @@ abstract class Imagify_Abstract_Attachment {
628
 
629
  return $resized_image_path;
630
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
631
  }
6
  *
7
  * @since 1.0
8
  */
9
+ abstract class Imagify_Abstract_Attachment extends Imagify_Abstract_Attachment_Deprecated {
10
 
11
  /**
12
  * Class version.
13
  *
14
  * @var string
15
  */
16
+ const VERSION = '1.3';
17
 
18
  /**
19
  * The attachment ID.
24
  */
25
  public $id = 0;
26
 
27
+ /**
28
+ * The attachment SQL DB class.
29
+ *
30
+ * @var string
31
+ * @since 1.7
32
+ * @access protected
33
+ */
34
+ protected $db_class_name = '';
35
+
36
+ /**
37
+ * The attachment SQL data row.
38
+ *
39
+ * @var array
40
+ * @since 1.7
41
+ * @access protected
42
+ */
43
+ protected $row = null;
44
+
45
+ /**
46
+ * Tell if the file extension can be optimized by Imagify.
47
+ * This is used to cache the result of $this->is_extension_supported().
48
+ *
49
+ * @var bool
50
+ * @since 1.7
51
+ * @access protected
52
+ * @see $this->is_extension_supported()
53
+ */
54
+ protected $is_extension_supported;
55
+
56
  /**
57
  * The constructor.
58
  *
59
  * @since 1.0
60
  * @access public
61
  *
62
+ * @param int|object $id The attachment ID or the attachment itself.
63
+ * If an integer, make sure the attachment exists.
64
  */
65
  public function __construct( $id = 0 ) {
66
  global $post;
75
  $this->id = $post->ID;
76
  }
77
 
78
+ $this->id = (int) $this->id;
79
  }
80
 
81
+ /**
82
+ * Tell if the current attachment is valid.
83
+ *
84
+ * @since 1.7
85
+ * @author Grégory Viguier
86
+ * @access public
87
+ *
88
+ * @return bool
89
+ */
90
+ public function is_valid() {
91
+ return $this->id > 0;
92
+ }
93
+
94
+ /**
95
+ * Get the attachment ID.
96
+ *
97
+ * @since 1.7
98
+ * @author Grégory Viguier
99
+ * @access public
100
+ *
101
+ * @return int
102
+ */
103
+ public function get_id() {
104
+ return $this->id;
105
+ }
106
+
107
+ /**
108
+ * Get the original attachment path.
109
+ *
110
+ * @since 1.0
111
+ * @access public
112
+ *
113
+ * @return string
114
+ */
115
+ abstract public function get_original_path();
116
+
117
+ /**
118
+ * Get the original attachment URL.
119
+ *
120
+ * @since 1.0
121
+ * @access public
122
+ *
123
+ * @return string
124
+ */
125
+ abstract public function get_original_url();
126
+
127
  /**
128
  * Get the attachment backup file path, even if the file doesn't exist.
129
  *
144
  * @return string|false The file path. False if it doesn't exist.
145
  */
146
  public function get_backup_path() {
147
+ if ( ! $this->is_valid() ) {
148
+ return false;
149
+ }
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
 
166
  * @return string|false
167
  */
168
  public function get_backup_url() {
169
+ if ( ! $this->is_valid() ) {
170
+ return false;
171
+ }
172
+
173
  return get_imagify_attachment_url( $this->get_raw_backup_path() );
174
  }
175
 
184
  abstract public function get_data();
185
 
186
  /**
187
+ * Get the attachment optimization level.
188
  *
189
  * @since 1.0
190
  * @access public
191
  *
192
+ * @return int
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  */
194
+ abstract public function get_optimization_level();
 
 
195
 
196
  /**
197
+ * Get the attachment optimization status (success or error).
198
  *
199
+ * @since 1.0
200
  * @access public
 
201
  *
202
+ * @return string
203
  */
204
+ abstract public function get_status();
 
 
205
 
206
  /**
207
  * Get the attachment error if there is one.
218
  return trim( $error );
219
  }
220
 
221
+ return '';
222
  }
223
 
224
  /**
225
+ * Count number of optimized sizes.
226
  *
227
  * @since 1.0
228
  * @access public
229
  *
230
  * @return int
231
  */
232
+ public function get_optimized_sizes_count() {
233
+ $data = $this->get_data();
234
+ $sizes = ! empty( $data['sizes'] ) && is_array( $data['sizes'] ) ? $data['sizes'] : array();
235
+ $count = 0;
236
+
237
+ unset( $sizes['full'] );
238
+
239
+ if ( ! $sizes ) {
240
+ return 0;
241
+ }
242
+
243
+ foreach ( $sizes as $size ) {
244
+ if ( ! empty( $size['success'] ) ) {
245
+ $count++;
246
+ }
247
+ }
248
+
249
+ return $count;
250
+ }
251
 
252
  /**
253
  * Delete the 3 metas used by Imagify.
257
  * @author Grégory Viguier
258
  */
259
  public function delete_imagify_data() {
260
+ if ( ! $this->is_valid() ) {
261
+ return;
262
+ }
263
+
264
  delete_post_meta( $this->id, '_imagify_data' );
265
  delete_post_meta( $this->id, '_imagify_status' );
266
  delete_post_meta( $this->id, '_imagify_optimization_level' );
267
  }
268
 
269
  /**
270
+ * Get width and height of the original image.
271
  *
272
+ * @since 1.7
273
+ * @author Grégory Viguier
274
  * @access public
275
  *
276
+ * @return array
277
  */
278
+ public function get_dimensions() {
279
+ return array(
280
+ 'width' => 0,
281
+ 'height' => 0,
282
+ );
283
+ }
284
 
285
+ /**
286
+ * Get the attachment extension.
287
+ *
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
  /**
303
+ * Tell if the current file extension is supported.
304
  *
305
+ * @since 1.7
306
  * @access public
307
+ * @author Grégory Viguier
308
  *
309
+ * @return bool
310
  */
311
+ public function is_extension_supported() {
312
+ if ( isset( $this->is_extension_supported ) ) {
313
+ return $this->is_extension_supported;
314
+ }
315
 
316
+ if ( ! $this->is_valid() ) {
317
+ $this->is_extension_supported = false;
318
+ return $this->is_extension_supported;
319
+ }
320
 
321
+ $file_type = wp_check_filetype( $this->get_original_path(), imagify_get_mime_types() );
322
+
323
+ $this->is_extension_supported = (bool) $file_type['ext'];
324
+
325
+ return $this->is_extension_supported;
326
+ }
327
+
328
+ /**
329
+ * Tell if the current file mime type is supported.
330
+ *
331
+ * @since 1.6.9
332
+ * @access public
333
+ * @author Grégory Viguier
334
+ *
335
+ * @return bool
336
+ */
337
+ public function is_mime_type_supported() {
338
+ if ( ! $this->is_valid() ) {
339
+ return false;
340
  }
341
 
342
+ return imagify_is_attachment_mime_type_supported( $this->id );
343
  }
344
 
345
  /**
346
+ * Tell if the current attachment has the required WP metadata.
347
  *
348
+ * @since 1.6.12
349
  * @access public
350
+ * @author Grégory Viguier
351
  *
352
+ * @return bool
353
  */
354
+ public function has_required_metadata() {
355
+ if ( ! $this->is_valid() ) {
356
+ return false;
357
+ }
358
+
359
+ return imagify_attachment_has_required_metadata( $this->id );
360
+ }
361
 
362
  /**
363
+ * Get the attachment optimization level label.
364
  *
365
+ * @since 1.2
366
+ * @since 1.7 Added $format parameter.
367
  * @access public
368
  *
369
+ * @param string $format Format to display the label. Use %ICON% for the icon and %s for the label.
370
  * @return string
371
  */
372
+ public function get_optimization_level_label( $format = '%s' ) {
373
+ return imagify_get_optimization_level_label( $this->get_optimization_level(), $format );
374
+ }
375
 
376
  /**
377
  * Get the original attachment size.
381
  *
382
  * @param bool $human_format True to display the image human format size (1Mb).
383
  * @param int $decimals Precision of number of decimal places.
384
+ * @return string|int
385
  */
386
  public function get_original_size( $human_format = true, $decimals = 2 ) {
387
+ if ( ! $this->is_valid() ) {
388
+ return $human_format ? imagify_size_format( 0, $decimals ) : 0;
389
+ }
390
+
391
+ $size = $this->get_size_data( 'full', 'original_size' );
392
+
393
+ if ( ! $size ) {
394
+ // Check for the backup file first.
395
+ $filepath = $this->get_backup_path();
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 ) {
406
+ return imagify_size_format( (int) $size, $decimals );
407
  }
408
 
409
+ return (int) $size;
410
  }
411
 
412
  /**
413
+ * Get the optimized attachment size.
414
  *
415
+ * @since 1.7
416
  * @access public
417
+ * @author Grégory Viguier
418
  *
419
+ * @param bool $human_format True to display the image human format size (1Mb).
420
+ * @param int $decimals Precision of number of decimal places.
421
+ * @return string|int
422
  */
423
+ public function get_optimized_size( $human_format = true, $decimals = 2 ) {
424
+ if ( ! $this->is_valid() ) {
425
+ return $human_format ? imagify_size_format( 0, $decimals ) : 0;
426
+ }
427
+
428
+ $size = $this->get_size_data( 'full', 'optimized_size' );
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 ) {
437
+ return imagify_size_format( (int) $size, $decimals );
438
+ }
439
+
440
+ return (int) $size;
441
+ }
442
+
443
+ /**
444
+ * Get the optimized attachment size.
445
+ *
446
+ * @since 1.7
447
+ * @access public
448
+ * @author Grégory Viguier
449
+ *
450
+ * @return float A 2-decimals float.
451
+ */
452
+ public function get_saving_percent() {
453
+ if ( ! $this->is_valid() ) {
454
+ return round( (float) 0, 2 );
455
+ }
456
+
457
+ $percent = $this->get_size_data( 'full', 'percent' );
458
+ $percent = $percent ? $percent : (float) 0;
459
+
460
+ return round( $percent, 2 );
461
+ }
462
+
463
+ /**
464
+ * Get the overall optimized size (all thumbnails).
465
+ *
466
+ * @since 1.7
467
+ * @access public
468
+ * @author Grégory Viguier
469
+ *
470
+ * @return float A 2-decimals float.
471
+ */
472
+ public function get_overall_saving_percent() {
473
+ if ( ! $this->is_valid() ) {
474
+ return round( (float) 0, 2 );
475
+ }
476
+
477
+ $percent = $this->get_data();
478
+ $percent = ! empty( $percent['stats']['percent'] ) ? $percent['stats']['percent'] : (float) 0;
479
+
480
+ return round( $percent, 2 );
481
+ }
482
 
483
  /**
484
  * Get the statistics of a specific size.
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;
593
  * @return bool True if the attachment has an error.
594
  */
595
  public function has_error() {
596
+ return 'error' === $this->get_status();
 
597
  }
598
 
599
  /**
617
  public function delete_backup() {
618
  $backup_path = $this->get_backup_path();
619
 
620
+ if ( $backup_path ) {
621
  imagify_get_filesystem()->delete( $backup_path );
622
  }
623
  }
653
  */
654
  public function get_unoptimized_sizes() {
655
  // The attachment must have been optimized once and have a backup.
656
+ if ( ! $this->is_valid() || ! $this->is_optimized() || ! $this->has_backup() ) {
657
  return array();
658
  }
659
 
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' );
685
  $is_active_for_network = imagify_is_active_for_network();
686
 
687
  foreach ( $missing_sizes as $size_name => $size_data ) {
721
  * @since 1.0
722
  * @since 1.6.5 Not static anymore.
723
  * @since 1.6.6 Removed the attachment ID parameter.
724
+ * @since 1.7 Removed the image URL parameter.
725
  * @access public
726
  *
727
  * @param array $data The statistics data.
728
  * @param object $response The API response.
 
729
  * @param string $size The attachment size key.
730
  * @return bool|array False if the original size has an error or an array contains the data for other result.
731
  */
732
+ abstract public function fill_data( $data, $response, $size = 'full' );
733
 
734
  /**
735
  * Optimize all sizes with Imagify.
778
  * @return string Path the the resized image or the original image if the resize failed.
779
  */
780
  public function resize( $attachment_path, $attachment_sizes, $max_width ) {
781
+ if ( ! $this->is_valid() ) {
782
+ return '';
783
+ }
784
+
785
  // Prevent removal of the exif/meta data when resizing (only works with Imagick).
786
  add_filter( 'image_strip_meta', '__return_false' );
787
 
829
 
830
  return $resized_image_path;
831
  }
832
+
833
+
834
+ /** ----------------------------------------------------------------------------------------- */
835
+ /** DB ROW ================================================================================== */
836
+ /** ----------------------------------------------------------------------------------------- */
837
+
838
+ /**
839
+ * Get the data row.
840
+ *
841
+ * @since 1.7
842
+ * @author Grégory Viguier
843
+ * @access public
844
+ *
845
+ * @return array
846
+ */
847
+ public function get_row() {
848
+ if ( isset( $this->row ) ) {
849
+ return $this->row;
850
+ }
851
+
852
+ if ( ! $this->db_class_name || ! $this->is_valid() ) {
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();
861
+ }
862
+
863
+ return $this->row;
864
+ }
865
+
866
+ /**
867
+ * Update the data row.
868
+ *
869
+ * @since 1.7
870
+ * @author Grégory Viguier
871
+ * @access public
872
+ *
873
+ * @param array $data The data to update.
874
+ */
875
+ public function update_row( $data ) {
876
+ if ( ! $this->db_class_name || ! $this->is_valid() ) {
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
+ }
885
+
886
+ /**
887
+ * Delete the data row.
888
+ *
889
+ * @since 1.7
890
+ * @author Grégory Viguier
891
+ * @access public
892
+ */
893
+ public function delete_row() {
894
+ if ( ! $this->db_class_name || ! $this->is_valid() ) {
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
+ *
907
+ * @since 1.7
908
+ * @author Grégory Viguier
909
+ * @access public
910
+ *
911
+ * @return array The row
912
+ */
913
+ public function invalidate_row() {
914
+ $this->row = array();
915
+ return $this->row;
916
+ }
917
+
918
+ /**
919
+ * Reset the row cache.
920
+ *
921
+ * @since 1.7
922
+ * @author Grégory Viguier
923
+ * @access public
924
+ *
925
+ * @return null The row.
926
+ */
927
+ public function reset_row_cache() {
928
+ $this->row = null;
929
+ return $this->row;
930
+ }
931
  }
inc/classes/class-imagify-abstract-cron.php ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Basis class that handles events.
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ abstract class Imagify_Abstract_Cron {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7
17
+ */
18
+ const VERSION = '1.0';
19
+
20
+ /**
21
+ * Cron name.
22
+ *
23
+ * @var string
24
+ * @since 1.7
25
+ * @access protected
26
+ */
27
+ protected $event_name = '';
28
+
29
+ /**
30
+ * Cron recurrence.
31
+ *
32
+ * @var string
33
+ * @since 1.7
34
+ * @access protected
35
+ */
36
+ protected $event_recurrence = '';
37
+
38
+ /**
39
+ * Cron time.
40
+ *
41
+ * @var string
42
+ * @since 1.7
43
+ * @access protected
44
+ */
45
+ protected $event_time = '';
46
+
47
+ /**
48
+ * The single instance of the class.
49
+ *
50
+ * @var object
51
+ * @since 1.7
52
+ * @access protected
53
+ */
54
+ protected static $_instance;
55
+
56
+ /**
57
+ * Get the main Instance.
58
+ *
59
+ * @since 1.7
60
+ * @access public
61
+ * @author Grégory Viguier
62
+ *
63
+ * @return object Main instance.
64
+ */
65
+ public static function get_instance() {
66
+ if ( ! isset( self::$_instance ) ) {
67
+ self::$_instance = new self();
68
+ }
69
+
70
+ return self::$_instance;
71
+ }
72
+
73
+
74
+ /** ----------------------------------------------------------------------------------------- */
75
+ /** INIT ==================================================================================== */
76
+ /** ----------------------------------------------------------------------------------------- */
77
+
78
+ /**
79
+ * Launch hooks.
80
+ *
81
+ * @since 1.7
82
+ * @access public
83
+ * @author Grégory Viguier
84
+ */
85
+ public function init() {
86
+ static $deactivation_hook;
87
+
88
+ if ( ! isset( $deactivation_hook ) ) {
89
+ $deactivation_hook = 'deactivate_' . plugin_basename( IMAGIFY_FILE );
90
+ }
91
+
92
+ add_action( 'init', array( $this, 'schedule_event' ) );
93
+ add_action( $this->get_event_name(), array( $this, 'do_event' ) );
94
+ add_filter( 'cron_schedules', array( $this, 'maybe_add_recurrence' ) );
95
+
96
+ if ( did_action( $deactivation_hook ) ) {
97
+ $this->unschedule_event();
98
+ } else {
99
+ add_action( $deactivation_hook, array( $this, 'unschedule_event' ) );
100
+ }
101
+ }
102
+
103
+
104
+ /** ----------------------------------------------------------------------------------------- */
105
+ /** HOOKS =================================================================================== */
106
+ /** ----------------------------------------------------------------------------------------- */
107
+
108
+ /**
109
+ * Initiate the event.
110
+ *
111
+ * @since 1.7
112
+ * @access public
113
+ * @author Grégory Viguier
114
+ */
115
+ public function schedule_event() {
116
+ if ( ! wp_next_scheduled( $this->get_event_name() ) ) {
117
+ wp_schedule_event( $this->get_event_timestamp(), $this->get_event_recurrence(), $this->get_event_name() );
118
+ }
119
+ }
120
+
121
+ /**
122
+ * The event action.
123
+ *
124
+ * @since 1.7
125
+ * @access public
126
+ * @author Grégory Viguier
127
+ */
128
+ abstract public function do_event();
129
+
130
+ /**
131
+ * Unschedule the event at plugin or submodule deactivation.
132
+ *
133
+ * @since 1.7
134
+ * @access public
135
+ * @author Grégory Viguier
136
+ */
137
+ public function unschedule_event() {
138
+ wp_clear_scheduled_hook( $this->get_event_name() );
139
+ }
140
+
141
+ /**
142
+ * Add the event recurrence schedule.
143
+ *
144
+ * @since 1.7
145
+ * @access public
146
+ * @see wp_get_schedules()
147
+ * @author Grégory Viguier
148
+ *
149
+ * @param array $schedules An array of non-default cron schedules. Default empty.
150
+ *
151
+ * @return array
152
+ */
153
+ public function maybe_add_recurrence( $schedules ) {
154
+ $default_schedules = array(
155
+ 'hourly' => 1,
156
+ 'twicedaily' => 1,
157
+ 'daily' => 1,
158
+ );
159
+
160
+ $event_recurrence = $this->get_event_recurrence();
161
+
162
+ if ( ! empty( $schedules[ $event_recurrence ] ) || ! empty( $default_schedules[ $event_recurrence ] ) ) {
163
+ return $schedules;
164
+ }
165
+
166
+ $recurrences = array(
167
+ 'weekly' => array(
168
+ 'interval' => WEEK_IN_SECONDS,
169
+ 'display' => __( 'Once Weekly', 'imagify' ),
170
+ ),
171
+ );
172
+
173
+ if ( method_exists( $this, 'get_event_recurrence_attributes' ) ) {
174
+ $recurrences[ $event_recurrence ] = $this->get_event_recurrence_attributes();
175
+ }
176
+
177
+ if ( ! empty( $recurrences[ $event_recurrence ] ) ) {
178
+ $schedules[ $event_recurrence ] = $recurrences[ $event_recurrence ];
179
+ }
180
+
181
+ return $schedules;
182
+ }
183
+
184
+
185
+ /** ----------------------------------------------------------------------------------------- */
186
+ /** TOOLS =================================================================================== */
187
+ /** ----------------------------------------------------------------------------------------- */
188
+
189
+ /**
190
+ * Get the cron name.
191
+ *
192
+ * @since 1.7
193
+ * @access public
194
+ * @author Grégory Viguier
195
+ *
196
+ * @return string
197
+ */
198
+ public function get_event_name() {
199
+ return $this->event_name;
200
+ }
201
+
202
+ /**
203
+ * Get the cron recurrence.
204
+ *
205
+ * @since 1.7
206
+ * @access public
207
+ * @author Grégory Viguier
208
+ *
209
+ * @return string
210
+ */
211
+ public function get_event_recurrence() {
212
+ /**
213
+ * Filter the recurrence of the event.
214
+ *
215
+ * @since 1.7
216
+ * @author Grégory Viguier
217
+ *
218
+ * @param string $event_recurrence The recurrence.
219
+ * @param string $event_name Name of the event this recurrence is used for.
220
+ */
221
+ return apply_filters( 'imagify_event_recurrence', $this->event_recurrence, $this->get_event_name() );
222
+ }
223
+
224
+ /**
225
+ * Get the time to schedule the event.
226
+ *
227
+ * @since 1.7
228
+ * @access public
229
+ * @author Grégory Viguier
230
+ *
231
+ * @return string
232
+ */
233
+ public function get_event_time() {
234
+ /**
235
+ * Filter the time at which the event is triggered (WordPress time).
236
+ *
237
+ * @since 1.7
238
+ * @author Grégory Viguier
239
+ *
240
+ * @param string $event_time A 24H formated time: `hour:minute`.
241
+ * @param string $event_name Name of the event this time is used for.
242
+ */
243
+ return apply_filters( 'imagify_event_time', $this->event_time, $this->get_event_name() );
244
+ }
245
+
246
+ /**
247
+ * Get the timestamp to schedule the event.
248
+ *
249
+ * @since 1.7
250
+ * @access public
251
+ * @author Grégory Viguier
252
+ *
253
+ * @return int Timestamp.
254
+ */
255
+ public function get_event_timestamp() {
256
+ return self::get_next_timestamp( $this->get_event_time() );
257
+ }
258
+
259
+ /**
260
+ * Get the timestamp of the next (event) date for the given hour.
261
+ *
262
+ * @since 1.7
263
+ * @author Grégory Viguier
264
+ * @source secupress_get_next_cron_timestamp()
265
+ *
266
+ * @param string $event_time Time when the event callback should be triggered (WordPress time), formated like `hh:mn` (hour:minute).
267
+ *
268
+ * @return int Timestamp.
269
+ */
270
+ static public function get_next_timestamp( $event_time = '00:00' ) {
271
+ $current_time_int = (int) date( 'Gis' );
272
+ $event_time_int = (int) str_replace( ':', '', $event_time . '00' );
273
+ $event_time = explode( ':', $event_time );
274
+ $event_hour = (int) $event_time[0];
275
+ $event_minute = (int) $event_time[1];
276
+ $offset = get_option( 'gmt_offset' ) * HOUR_IN_SECONDS;
277
+
278
+ if ( $event_time_int <= $current_time_int ) {
279
+ // The event time is passed, we need to schedule the event tomorrow.
280
+ return mktime( $event_hour, $event_minute, 0, (int) date( 'n' ), (int) date( 'j' ) + 1 ) - $offset;
281
+ }
282
+
283
+ // We haven't passed the event time yet, schedule the event today.
284
+ return mktime( $event_hour, $event_minute, 0 ) - $offset;
285
+ }
286
+ }
inc/classes/class-imagify-abstract-db.php CHANGED
@@ -7,14 +7,22 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
7
  * @since 1.5
8
  * @source https://gist.github.com/pippinsplugins/e220a7f0f0f2fbe64608
9
  */
10
- abstract class Imagify_Abstract_DB {
11
 
12
  /**
13
  * Class version.
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.1.1';
 
 
 
 
 
 
 
 
18
 
19
  /**
20
  * The single instance of the class.
@@ -26,31 +34,70 @@ abstract class Imagify_Abstract_DB {
26
  protected static $_instance;
27
 
28
  /**
29
- * The name of our database table.
30
  *
31
  * @var string
32
- * @since 1.5
33
- * @access public
34
  */
35
- public $table_name;
36
 
37
  /**
38
  * The version of our database table.
39
  *
40
- * @var string
41
  * @since 1.5
42
- * @access public
 
43
  */
44
- public $version;
 
 
 
 
 
 
 
 
 
45
 
46
  /**
47
  * The name of the primary column.
48
  *
49
  * @var string
50
  * @since 1.5
51
- * @access public
 
52
  */
53
- public $primary_key;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  /**
56
  * Get things started.
@@ -58,7 +105,23 @@ abstract class Imagify_Abstract_DB {
58
  * @since 1.5
59
  * @access protected
60
  */
61
- protected function __construct() {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  /**
64
  * Get the main Instance.
@@ -77,6 +140,36 @@ abstract class Imagify_Abstract_DB {
77
  return self::$_instance;
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  /**
81
  * Whitelist of columns.
82
  *
@@ -97,6 +190,39 @@ abstract class Imagify_Abstract_DB {
97
  */
98
  abstract public function get_column_defaults();
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  /**
101
  * Retrieve a row by the primary key.
102
  *
@@ -104,11 +230,14 @@ abstract class Imagify_Abstract_DB {
104
  * @access public
105
  *
106
  * @param string $row_id A primary key.
107
- * @return object
108
  */
109
  public function get( $row_id ) {
110
- global $wpdb;
111
- return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_name WHERE $this->primary_key = %s LIMIT 1;", $row_id ), ARRAY_A ); // WPCS: unprepared SQL ok.
 
 
 
112
  }
113
 
114
  /**
@@ -117,68 +246,159 @@ abstract class Imagify_Abstract_DB {
117
  * @since 1.5
118
  * @access public
119
  *
120
- * @param string $column A column name.
121
- * @param string $row_id A value.
122
- * @return object
123
  */
124
- public function get_by( $column, $row_id ) {
125
  global $wpdb;
126
- $column = esc_sql( $column );
127
- return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_name WHERE $column = %s LIMIT 1;", $row_id ), ARRAY_A ); // WPCS: unprepared SQL ok.
 
 
 
 
 
128
  }
129
 
130
  /**
131
- * Retrieve a specific column's value by the primary key.
132
  *
133
- * @since 1.5
134
  * @access public
 
135
  *
136
- * @param string $column A column name.
137
- * @param string $row_id A primary key.
138
- * @return string
139
  */
140
- public function get_column( $column, $row_id ) {
141
  global $wpdb;
142
- $column = esc_sql( $column );
143
- return $wpdb->get_var( $wpdb->prepare( "SELECT $column FROM $this->table_name WHERE $this->primary_key = %s LIMIT 1;", $row_id ) ); // WPCS: unprepared SQL ok.
 
 
 
 
 
144
  }
145
 
146
  /**
147
- * Retrieve a specific column's value by the the specified column / value.
 
148
  *
149
- * @since 1.5
150
  * @access public
 
151
  *
152
- * @param string $column A column name.
153
- * @param string $column_where A column name.
154
- * @param string $column_value A value.
155
- * @return string
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  */
157
- public function get_column_by( $column, $column_where, $column_value ) {
158
  global $wpdb;
159
- $column = esc_sql( $column );
 
 
160
  $column_where = esc_sql( $column_where );
161
- return $wpdb->get_var( $wpdb->prepare( "SELECT $column FROM $this->table_name WHERE $column_where = %s LIMIT 1;", $column_value ) ); // WPCS: unprepared SQL ok.
 
 
 
162
  }
163
 
164
  /**
165
- * Retrieve a specific column's value by the the specified column / values.
 
166
  *
167
- * @since 1.6.12
168
  * @access public
169
  * @author Grégory Viguier
170
  *
171
- * @param string $column A column name.
172
  * @param string $column_where A column name.
173
  * @param array $column_values An array of values.
174
- * @return string
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  */
176
- public function get_column_in( $column, $column_where, $column_values ) {
177
  global $wpdb;
178
- $column = esc_sql( $column );
 
179
  $column_where = esc_sql( $column_where );
180
  $column_values = Imagify_DB::prepare_values_list( $column_values );
181
- return $wpdb->get_var( "SELECT $column FROM $this->table_name WHERE $column_where IN ( $column_values ) LIMIT 1;" ); // WPCS: unprepared SQL ok.
 
 
 
182
  }
183
 
184
  /**
@@ -188,30 +408,32 @@ abstract class Imagify_Abstract_DB {
188
  * @access public
189
  *
190
  * @param string $data New data.
191
- * @return int
192
  */
193
  public function insert( $data ) {
194
  global $wpdb;
195
 
196
- // Set default values.
197
- $data = wp_parse_args( $data, $this->get_column_defaults() );
198
-
199
  // Initialise column format array.
200
  $column_formats = $this->get_columns();
201
 
 
 
 
202
  // Force fields to lower case.
203
  $data = array_change_key_case( $data );
204
 
205
  // White list columns.
206
  $data = array_intersect_key( $data, $column_formats );
207
 
 
 
 
208
  // Reorder $column_formats to match the order of columns given in $data.
209
- $data_keys = array_keys( $data );
210
- $column_formats = array_merge( array_flip( $data_keys ), $column_formats );
211
 
212
  $wpdb->insert( $this->table_name, $data, $column_formats );
213
 
214
- return $wpdb->insert_id;
215
  }
216
 
217
  /**
@@ -220,7 +442,7 @@ abstract class Imagify_Abstract_DB {
220
  * @since 1.5
221
  * @access public
222
  *
223
- * @param string $row_id A primary key.
224
  * @param array $data New data.
225
  * @param string $where A column name.
226
  * @return bool
@@ -228,13 +450,15 @@ abstract class Imagify_Abstract_DB {
228
  public function update( $row_id, $data = array(), $where = '' ) {
229
  global $wpdb;
230
 
231
- // Row ID must be positive integer.
232
- $row_id = absint( $row_id );
233
-
234
- if ( ! $row_id ) {
235
  return false;
236
  }
237
 
 
 
 
 
 
238
  if ( empty( $where ) ) {
239
  $where = $this->primary_key;
240
  }
@@ -248,16 +472,13 @@ abstract class Imagify_Abstract_DB {
248
  // White list columns.
249
  $data = array_intersect_key( $data, $column_formats );
250
 
251
- // Reorder $column_formats to match the order of columns given in $data.
252
- $data_keys = array_keys( $data );
253
- $column_formats = array_merge( array_flip( $data_keys ), $column_formats );
254
 
255
- if ( false === (bool) $this->get_column( $this->primary_key, $row_id ) ) {
256
- $this->insert( $data );
257
- return true;
258
- }
259
 
260
- return (bool) $wpdb->update( $this->table_name, $data, array( $where => $row_id ), $column_formats );
261
  }
262
 
263
  /**
@@ -272,29 +493,383 @@ abstract class Imagify_Abstract_DB {
272
  public function delete( $row_id = 0 ) {
273
  global $wpdb;
274
 
275
- // Row ID must be positive integer.
276
- $row_id = absint( $row_id );
277
-
278
- if ( ! $row_id ) {
279
  return false;
280
  }
281
 
282
- return (bool) $wpdb->query( $wpdb->prepare( "DELETE FROM $this->table_name WHERE $this->primary_key = %d", $row_id ) ); // WPCS: unprepared SQL ok.
 
 
283
  }
284
 
 
 
 
 
 
285
  /**
286
- * Check if the given table exists.
287
  *
288
- * @since 1.5
289
  * @access public
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  *
291
- * @param string $table The table name.
292
- * @return bool True if the table name exists.
 
293
  */
294
- public function table_exists( $table ) {
295
  global $wpdb;
296
- $table = sanitize_text_field( $table );
297
 
298
- return $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE '%s'", $table ) ) === $table;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
  }
7
  * @since 1.5
8
  * @source https://gist.github.com/pippinsplugins/e220a7f0f0f2fbe64608
9
  */
10
+ abstract class Imagify_Abstract_DB extends Imagify_Abstract_DB_Deprecated {
11
 
12
  /**
13
  * Class version.
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.
21
+ *
22
+ * @var string
23
+ * @since 1.7
24
+ */
25
+ const TABLE_VERSION_OPTION_SUFFIX = '_db_version';
26
 
27
  /**
28
  * The single instance of the class.
34
  protected static $_instance;
35
 
36
  /**
37
+ * The suffix used in the name of the database table (so, without the wpdb prefix).
38
  *
39
  * @var string
40
+ * @since 1.7
41
+ * @access protected
42
  */
43
+ protected $table;
44
 
45
  /**
46
  * The version of our database table.
47
  *
48
+ * @var int
49
  * @since 1.5
50
+ * @since 1.7 Not public anymore, now an integer.
51
+ * @access protected
52
  */
53
+ protected $table_version;
54
+
55
+ /**
56
+ * Tell if the table is the same for each site of a Multisite.
57
+ *
58
+ * @var bool
59
+ * @since 1.7
60
+ * @access protected
61
+ */
62
+ protected $table_is_global;
63
 
64
  /**
65
  * The name of the primary column.
66
  *
67
  * @var string
68
  * @since 1.5
69
+ * @since 1.7 Not public anymore.
70
+ * @access protected
71
  */
72
+ protected $primary_key;
73
+
74
+ /**
75
+ * The name of our database table.
76
+ *
77
+ * @var string
78
+ * @since 1.5
79
+ * @since 1.7 Not public anymore.
80
+ * @access protected
81
+ */
82
+ protected $table_name = '';
83
+
84
+ /**
85
+ * Tell if the table has been created.
86
+ *
87
+ * @var bool
88
+ * @since 1.7
89
+ * @access protected
90
+ */
91
+ protected $table_created = false;
92
+
93
+ /**
94
+ * Stores the list of columns that must be (un)serialized.
95
+ *
96
+ * @var array
97
+ * @since 1.7
98
+ * @access protected
99
+ */
100
+ protected $to_serialize;
101
 
102
  /**
103
  * Get things started.
105
  * @since 1.5
106
  * @access protected
107
  */
108
+ protected function __construct() {
109
+ global $wpdb;
110
+
111
+ $prefix = $this->table_is_global ? $wpdb->base_prefix : $wpdb->prefix;
112
+
113
+ $this->table_name = $prefix . $this->table;
114
+
115
+ if ( ! $this->table_is_up_to_date() ) {
116
+ /**
117
+ * The option doesn't exist or is not up-to-date: we must upgrade the table before declaring it ready.
118
+ * See self::maybe_upgrade_table() for the upgrade.
119
+ */
120
+ return;
121
+ }
122
+
123
+ $this->set_table_ready();
124
+ }
125
 
126
  /**
127
  * Get the main Instance.
140
  return self::$_instance;
141
  }
142
 
143
+ /**
144
+ * Init:
145
+ * - Launch hooks.
146
+ *
147
+ * @since 1.7
148
+ * @access public
149
+ * @author Grégory Viguier
150
+ */
151
+ public function init() {
152
+ add_action( 'admin_init', array( $this, 'maybe_upgrade_table' ) );
153
+ }
154
+
155
+ /**
156
+ * Tell if we can work with the tables.
157
+ *
158
+ * @since 1.7
159
+ * @access public
160
+ * @author Grégory Viguier
161
+ *
162
+ * @return bool
163
+ */
164
+ public function can_operate() {
165
+ return $this->table_created;
166
+ }
167
+
168
+
169
+ /** ----------------------------------------------------------------------------------------- */
170
+ /** TABLE SPECIFICS ========================================================================= */
171
+ /** ----------------------------------------------------------------------------------------- */
172
+
173
  /**
174
  * Whitelist of columns.
175
  *
190
  */
191
  abstract public function get_column_defaults();
192
 
193
+ /**
194
+ * Get the query to create the table fields.
195
+ *
196
+ * @since 1.7
197
+ * @access protected
198
+ * @author Grégory Viguier
199
+ *
200
+ * @return string
201
+ */
202
+ abstract protected function get_table_schema();
203
+
204
+
205
+ /** ----------------------------------------------------------------------------------------- */
206
+ /** QUERIES ================================================================================= */
207
+ /** ----------------------------------------------------------------------------------------- */
208
+
209
+ /**
210
+ * Tell if the table is empty or not.
211
+ *
212
+ * @since 1.7
213
+ * @access public
214
+ * @author Grégory Viguier
215
+ *
216
+ * @return bool True if the table contains at least one row.
217
+ */
218
+ public function has_items() {
219
+ global $wpdb;
220
+
221
+ $column = esc_sql( $this->primary_key );
222
+
223
+ return (bool) $wpdb->get_var( "SELECT $column FROM $this->table_name LIMIT 1;" ); // WPCS: unprepared SQL ok.
224
+ }
225
+
226
  /**
227
  * Retrieve a row by the primary key.
228
  *
230
  * @access public
231
  *
232
  * @param string $row_id A primary key.
233
+ * @return array
234
  */
235
  public function get( $row_id ) {
236
+ if ( $row_id <= 0 ) {
237
+ return array();
238
+ }
239
+
240
+ return $this->get_by( $this->primary_key, $row_id );
241
  }
242
 
243
  /**
246
  * @since 1.5
247
  * @access public
248
  *
249
+ * @param string $column_where A column name.
250
+ * @param mixed $column_value A value.
251
+ * @return array
252
  */
253
+ public function get_by( $column_where, $column_value ) {
254
  global $wpdb;
255
+
256
+ $placeholder = $this->get_placeholder( $column_where );
257
+ $column_where = esc_sql( $column_where );
258
+
259
+ $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_name WHERE $column_where = $placeholder LIMIT 1;", $column_value ), ARRAY_A ); // WPCS: unprepared SQL ok.
260
+
261
+ return (array) $this->cast_row( $result );
262
  }
263
 
264
  /**
265
+ * Retrieve a row by the specified column / values.
266
  *
267
+ * @since 1.7
268
  * @access public
269
+ * @author Grégory Viguier
270
  *
271
+ * @param string $column_where A column name.
272
+ * @param array $column_values An array of values.
273
+ * @return array
274
  */
275
+ public function get_in( $column_where, $column_values ) {
276
  global $wpdb;
277
+
278
+ $column_where = esc_sql( $column_where );
279
+ $column_values = Imagify_DB::prepare_values_list( $column_values );
280
+
281
+ $result = $wpdb->get_row( "SELECT * FROM $this->table_name WHERE $column_where IN ( $column_values ) LIMIT 1;", ARRAY_A ); // WPCS: unprepared SQL ok.
282
+
283
+ return (array) $this->cast_row( $result );
284
  }
285
 
286
  /**
287
+ * Retrieve a var by the primary key.
288
+ * Previously named get_column().
289
  *
290
+ * @since 1.7
291
  * @access public
292
+ * @author Grégory Viguier
293
  *
294
+ * @param string $column_select A column name.
295
+ * @param string $row_id A primary key.
296
+ * @return mixed
297
+ */
298
+ public function get_var( $column_select, $row_id ) {
299
+ if ( $row_id <= 0 ) {
300
+ return false;
301
+ }
302
+
303
+ return $this->get_var_by( $column_select, $this->primary_key, $row_id );
304
+ }
305
+
306
+ /**
307
+ * Retrieve a var by the specified column / value.
308
+ * Previously named get_column_by().
309
+ *
310
+ * @since 1.7
311
+ * @access public
312
+ * @author Grégory Viguier
313
+ *
314
+ * @param string $column_select A column name.
315
+ * @param string $column_where A column name.
316
+ * @param string $column_value A value.
317
+ * @return mixed
318
  */
319
+ public function get_var_by( $column_select, $column_where, $column_value ) {
320
  global $wpdb;
321
+
322
+ $placeholder = $this->get_placeholder( $column_where );
323
+ $column = esc_sql( $column_select );
324
  $column_where = esc_sql( $column_where );
325
+
326
+ $result = $wpdb->get_var( $wpdb->prepare( "SELECT $column FROM $this->table_name WHERE $column_where = $placeholder LIMIT 1;", $column_value ) ); // WPCS: unprepared SQL ok.
327
+
328
+ return $this->cast( $result, $column_select );
329
  }
330
 
331
  /**
332
+ * Retrieve a var by the specified column / values.
333
+ * Previously named get_column_in().
334
  *
335
+ * @since 1.7
336
  * @access public
337
  * @author Grégory Viguier
338
  *
339
+ * @param string $column_select A column name.
340
  * @param string $column_where A column name.
341
  * @param array $column_values An array of values.
342
+ * @return mixed
343
+ */
344
+ public function get_var_in( $column_select, $column_where, $column_values ) {
345
+ global $wpdb;
346
+
347
+ $column = esc_sql( $column_select );
348
+ $column_where = esc_sql( $column_where );
349
+ $column_values = Imagify_DB::prepare_values_list( $column_values );
350
+
351
+ $result = $wpdb->get_var( "SELECT $column FROM $this->table_name WHERE $column_where IN ( $column_values ) LIMIT 1;" ); // WPCS: unprepared SQL ok.
352
+
353
+ return $this->cast( $result, $column_select );
354
+ }
355
+
356
+ /**
357
+ * Retrieve values by the specified column / values.
358
+ *
359
+ * @since 1.7
360
+ * @access public
361
+ * @author Grégory Viguier
362
+ *
363
+ * @param string $column_select A column name.
364
+ * @param string $column_where A column name.
365
+ * @param array $column_values An array of values.
366
+ * @return array
367
+ */
368
+ public function get_column_in( $column_select, $column_where, $column_values ) {
369
+ global $wpdb;
370
+
371
+ $column = esc_sql( $column_select );
372
+ $column_where = esc_sql( $column_where );
373
+ $column_values = Imagify_DB::prepare_values_list( $column_values );
374
+
375
+ $result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where IN ( $column_values );" ); // WPCS: unprepared SQL ok.
376
+
377
+ return $this->cast_col( $result, $column_select );
378
+ }
379
+
380
+ /**
381
+ * Retrieve values by the specified column / values.
382
+ *
383
+ * @since 1.7
384
+ * @access public
385
+ * @author Grégory Viguier
386
+ *
387
+ * @param string $column_select A column name.
388
+ * @param string $column_where A column name.
389
+ * @param array $column_values An array of values.
390
+ * @return array
391
  */
392
+ public function get_column_not_in( $column_select, $column_where, $column_values ) {
393
  global $wpdb;
394
+
395
+ $column = esc_sql( $column_select );
396
  $column_where = esc_sql( $column_where );
397
  $column_values = Imagify_DB::prepare_values_list( $column_values );
398
+
399
+ $result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where NOT IN ( $column_values );" ); // WPCS: unprepared SQL ok.
400
+
401
+ return $this->cast_col( $result, $column_select );
402
  }
403
 
404
  /**
408
  * @access public
409
  *
410
  * @param string $data New data.
411
+ * @return int The ID.
412
  */
413
  public function insert( $data ) {
414
  global $wpdb;
415
 
 
 
 
416
  // Initialise column format array.
417
  $column_formats = $this->get_columns();
418
 
419
+ // Set default values.
420
+ $data = wp_parse_args( $data, $this->get_column_defaults() );
421
+
422
  // Force fields to lower case.
423
  $data = array_change_key_case( $data );
424
 
425
  // White list columns.
426
  $data = array_intersect_key( $data, $column_formats );
427
 
428
+ // Maybe serialize some values.
429
+ $data = $this->serialize_columns( $data );
430
+
431
  // Reorder $column_formats to match the order of columns given in $data.
432
+ $column_formats = array_merge( $data, $column_formats );
 
433
 
434
  $wpdb->insert( $this->table_name, $data, $column_formats );
435
 
436
+ return (int) $wpdb->insert_id;
437
  }
438
 
439
  /**
442
  * @since 1.5
443
  * @access public
444
  *
445
+ * @param int $row_id A primary key.
446
  * @param array $data New data.
447
  * @param string $where A column name.
448
  * @return bool
450
  public function update( $row_id, $data = array(), $where = '' ) {
451
  global $wpdb;
452
 
453
+ if ( $row_id <= 0 ) {
 
 
 
454
  return false;
455
  }
456
 
457
+ if ( ! $this->get( $row_id ) ) {
458
+ $this->insert( $data );
459
+ return true;
460
+ }
461
+
462
  if ( empty( $where ) ) {
463
  $where = $this->primary_key;
464
  }
472
  // White list columns.
473
  $data = array_intersect_key( $data, $column_formats );
474
 
475
+ // Maybe serialize some values.
476
+ $data = $this->serialize_columns( $data );
 
477
 
478
+ // Reorder $column_formats to match the order of columns given in $data.
479
+ $column_formats = array_merge( $data, $column_formats );
 
 
480
 
481
+ return (bool) $wpdb->update( $this->table_name, $data, array( $where => $row_id ), $column_formats, $this->get_placeholder( $where ) );
482
  }
483
 
484
  /**
493
  public function delete( $row_id = 0 ) {
494
  global $wpdb;
495
 
496
+ if ( $row_id <= 0 ) {
 
 
 
497
  return false;
498
  }
499
 
500
+ $placeholder = $this->get_placeholder( $this->primary_key );
501
+
502
+ return (bool) $wpdb->query( $wpdb->prepare( "DELETE FROM $this->table_name WHERE $this->primary_key = $placeholder", $row_id ) ); // WPCS: unprepared SQL ok.
503
  }
504
 
505
+
506
+ /** ----------------------------------------------------------------------------------------- */
507
+ /** TABLE CREATION ========================================================================== */
508
+ /** ----------------------------------------------------------------------------------------- */
509
+
510
  /**
511
+ * Maybe create/upgrade the table in the database.
512
  *
513
+ * @since 1.7
514
  * @access public
515
+ * @author Grégory Viguier
516
+ */
517
+ public function maybe_upgrade_table() {
518
+ global $wpdb;
519
+
520
+ if ( $this->table_is_up_to_date() ) {
521
+ // The table has the right version.
522
+ $this->set_table_ready();
523
+ return;
524
+ }
525
+
526
+ // Create the table.
527
+ $this->create_table();
528
+ }
529
+
530
+ /**
531
+ * Create/Upgrade the table in the database.
532
+ *
533
+ * @since 1.7
534
+ * @access public
535
+ * @author Grégory Viguier
536
+ */
537
+ public function create_table() {
538
+ if ( ! Imagify_DB::create_table( $this->get_table_name(), $this->get_table_schema() ) ) {
539
+ // Failure.
540
+ $this->set_table_not_ready();
541
+ $this->delete_db_version();
542
+ return;
543
+ }
544
+
545
+ // Table successfully created/upgraded.
546
+ $this->set_table_ready();
547
+ $this->update_db_version();
548
+ }
549
+
550
+ /**
551
+ * Set various properties to tell the table is ready to be used.
552
+ *
553
+ * @since 1.7
554
+ * @access public
555
+ * @author Grégory Viguier
556
+ */
557
+ protected function set_table_ready() {
558
+ global $wpdb;
559
+
560
+ $this->table_created = true;
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
+
570
+ /**
571
+ * Unset various properties to tell the table is NOT ready to be used.
572
  *
573
+ * @since 1.7
574
+ * @access public
575
+ * @author Grégory Viguier
576
  */
577
+ protected function set_table_not_ready() {
578
  global $wpdb;
 
579
 
580
+ $this->table_created = false;
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
+
590
+
591
+ /** ----------------------------------------------------------------------------------------- */
592
+ /** TABLE VERSION =========================================================================== */
593
+ /** ----------------------------------------------------------------------------------------- */
594
+
595
+ /**
596
+ * Get the table version.
597
+ *
598
+ * @since 1.7
599
+ * @access public
600
+ * @author Grégory Viguier
601
+ *
602
+ * @return int
603
+ */
604
+ public function get_table_version() {
605
+ return $this->table_version;
606
+ }
607
+
608
+ /**
609
+ * Tell if the table is up-to-date (we don't "downgrade" the tables).
610
+ *
611
+ * @since 1.7
612
+ * @access public
613
+ * @author Grégory Viguier
614
+ *
615
+ * @return bool
616
+ */
617
+ public function table_is_up_to_date() {
618
+ return $this->get_db_version() >= $this->get_table_version();
619
+ }
620
+
621
+ /**
622
+ * Get the table version stored in DB.
623
+ *
624
+ * @since 1.7
625
+ * @access public
626
+ * @author Grégory Viguier
627
+ *
628
+ * @return int|bool The version. False if not set yet.
629
+ */
630
+ public function get_db_version() {
631
+ $option_name = $this->table . self::TABLE_VERSION_OPTION_SUFFIX;
632
+
633
+ if ( $this->table_is_global && is_multisite() ) {
634
+ return get_site_option( $option_name );
635
+ }
636
+
637
+ return get_option( $option_name );
638
+ }
639
+
640
+ /**
641
+ * Update the table version stored in DB.
642
+ *
643
+ * @since 1.7
644
+ * @access protected
645
+ * @author Grégory Viguier
646
+ */
647
+ protected function update_db_version() {
648
+ $option_name = $this->table . self::TABLE_VERSION_OPTION_SUFFIX;
649
+
650
+ if ( $this->table_is_global && is_multisite() ) {
651
+ update_site_option( $option_name, $this->get_table_version() );
652
+ } else {
653
+ update_option( $option_name, $this->get_table_version() );
654
+ }
655
+ }
656
+
657
+ /**
658
+ * Delete the table version stored in DB.
659
+ *
660
+ * @since 1.7
661
+ * @access protected
662
+ * @author Grégory Viguier
663
+ */
664
+ protected function delete_db_version() {
665
+ $option_name = $this->table . self::TABLE_VERSION_OPTION_SUFFIX;
666
+
667
+ if ( $this->table_is_global && is_multisite() ) {
668
+ delete_site_option( $option_name );
669
+ } else {
670
+ delete_option( $option_name );
671
+ }
672
+ }
673
+
674
+
675
+ /** ----------------------------------------------------------------------------------------- */
676
+ /** TOOLS =================================================================================== */
677
+ /** ----------------------------------------------------------------------------------------- */
678
+
679
+ /**
680
+ * Get the table name.
681
+ *
682
+ * @since 1.7
683
+ * @access public
684
+ * @author Grégory Viguier
685
+ *
686
+ * @return string
687
+ */
688
+ public function get_table_name() {
689
+ return $this->table_name;
690
+ }
691
+
692
+ /**
693
+ * Tell if the table is the same for each site of a Multisite.
694
+ *
695
+ * @since 1.7
696
+ * @access public
697
+ * @author Grégory Viguier
698
+ *
699
+ * @return bool
700
+ */
701
+ public function is_table_global() {
702
+ return $this->table_is_global;
703
+ }
704
+
705
+ /**
706
+ * Get the primary column name.
707
+ *
708
+ * @since 1.7
709
+ * @access public
710
+ * @author Grégory Viguier
711
+ *
712
+ * @return string
713
+ */
714
+ public function get_primary_key() {
715
+ return $this->primary_key;
716
+ }
717
+
718
+ /**
719
+ * Get the formats related to the given columns.
720
+ *
721
+ * @since 1.7
722
+ * @access public
723
+ * @author Grégory Viguier
724
+ *
725
+ * @param array $columns An array of column names (as keys).
726
+ * @return array
727
+ */
728
+ public function get_column_formats( $columns ) {
729
+ if ( ! is_array( $columns ) ) {
730
+ $columns = array_flip( (array) $columns );
731
+ }
732
+
733
+ // White list columns.
734
+ return array_intersect_key( $this->get_columns(), $columns );
735
+ }
736
+
737
+ /**
738
+ * Get the placeholder corresponding to the given key.
739
+ *
740
+ * @since 1.7
741
+ * @access public
742
+ * @author Grégory Viguier
743
+ *
744
+ * @param string $key The key.
745
+ * @return string
746
+ */
747
+ public function get_placeholder( $key ) {
748
+ $columns = $this->get_columns();
749
+ return isset( $columns[ $key ] ) ? $columns[ $key ] : '%s';
750
+ }
751
+
752
+ /**
753
+ * Tell if the column value must be (un)serialized.
754
+ *
755
+ * @since 1.7
756
+ * @access public
757
+ * @author Grégory Viguier
758
+ *
759
+ * @param string $key The key.
760
+ * @return bool
761
+ */
762
+ public function is_column_serialized( $key ) {
763
+ $columns = $this->get_column_defaults();
764
+ return isset( $columns[ $key ] ) && is_array( $columns[ $key ] );
765
+ }
766
+
767
+ /**
768
+ * Cast a value.
769
+ *
770
+ * @since 1.7
771
+ * @access public
772
+ * @author Grégory Viguier
773
+ *
774
+ * @param mixed $value The value to cast.
775
+ * @param string $key The corresponding key.
776
+ * @return mixed
777
+ */
778
+ public function cast( $value, $key ) {
779
+ if ( null === $value || is_bool( $value ) ) {
780
+ return $value;
781
+ }
782
+
783
+ $placeholder = $this->get_placeholder( $key );
784
+
785
+ if ( '%d' === $placeholder ) {
786
+ return (int) $value;
787
+ }
788
+
789
+ if ( '%f' === $placeholder ) {
790
+ return (float) $value;
791
+ }
792
+
793
+ if ( $value && $this->is_column_serialized( $key ) ) {
794
+ return maybe_unserialize( $value );
795
+ }
796
+
797
+ return $value;
798
+ }
799
+
800
+ /**
801
+ * Cast a column.
802
+ *
803
+ * @since 1.7
804
+ * @access public
805
+ * @author Grégory Viguier
806
+ *
807
+ * @param array $values The values to cast.
808
+ * @param string $column The corresponding column name.
809
+ * @return array
810
+ */
811
+ public function cast_col( $values, $column ) {
812
+ if ( ! $values ) {
813
+ return $values;
814
+ }
815
+
816
+ foreach ( $values as $i => $value ) {
817
+ $values[ $i ] = $this->cast( $value, $column );
818
+ }
819
+
820
+ return $values;
821
+ }
822
+
823
+ /**
824
+ * Cast a row.
825
+ *
826
+ * @since 1.7
827
+ * @access public
828
+ * @author Grégory Viguier
829
+ *
830
+ * @param array|object $row_fields A row from the DB.
831
+ * @return array|object
832
+ */
833
+ public function cast_row( $row_fields ) {
834
+ if ( ! $row_fields ) {
835
+ return $row_fields;
836
+ }
837
+
838
+ if ( is_array( $row_fields ) ) {
839
+ foreach ( $row_fields as $field => $value ) {
840
+ $row_fields[ $field ] = $this->cast( $value, $field );
841
+ }
842
+ } elseif ( is_object( $row_fields ) ) {
843
+ foreach ( $row_fields as $field => $value ) {
844
+ $row_fields->$field = $this->cast( $value, $field );
845
+ }
846
+ }
847
+
848
+ return $row_fields;
849
+ }
850
+
851
+ /**
852
+ * Serialize columns that need to be.
853
+ *
854
+ * @since 1.7
855
+ * @access public
856
+ * @author Grégory Viguier
857
+ *
858
+ * @param array $data An array of values.
859
+ * @return array
860
+ */
861
+ public function serialize_columns( $data ) {
862
+ if ( ! isset( $this->to_serialize ) ) {
863
+ $this->to_serialize = array_filter( $this->get_column_defaults(), 'is_array' );
864
+ }
865
+
866
+ if ( ! $this->to_serialize ) {
867
+ return $data;
868
+ }
869
+
870
+ $serialized_data = array_intersect_key( $data, $this->to_serialize );
871
+ $serialized_data = array_map( 'maybe_serialize', $serialized_data );
872
+
873
+ return array_merge( $data, $serialized_data );
874
  }
875
  }
inc/classes/class-imagify-abstract-options.php ADDED
@@ -0,0 +1,623 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Abstract class to handle a part of the plugin options.
6
+ *
7
+ * @since 1.7
8
+ */
9
+ abstract class Imagify_Abstract_Options {
10
+
11
+ /**
12
+ * Class version.
13
+ *
14
+ * @var string
15
+ * @since 1.7
16
+ */
17
+ const VERSION = '1.0';
18
+
19
+ /**
20
+ * Suffix used in the name of the option.
21
+ *
22
+ * @var string
23
+ * @since 1.7
24
+ * @access protected
25
+ */
26
+ protected $identifier;
27
+
28
+ /**
29
+ * The default values for the Imagify main options.
30
+ * These are the "zero state" values.
31
+ * Don't use null as value.
32
+ *
33
+ * @var array
34
+ * @since 1.7
35
+ * @access protected
36
+ */
37
+ protected $default_values;
38
+
39
+ /**
40
+ * The Imagify main option values used when they are set the first time or reset.
41
+ * Values identical to default values are not listed.
42
+ *
43
+ * @var array
44
+ * @since 1.7
45
+ * @access protected
46
+ */
47
+ protected $reset_values = array();
48
+
49
+ /**
50
+ * Tell if the option should be autoloaded by WP.
51
+ * Possible values are 'yes' and 'no'.
52
+ *
53
+ * @var string
54
+ * @since 1.7
55
+ * @access protected
56
+ */
57
+ protected $autoload = 'yes';
58
+
59
+ /**
60
+ * Tell if the option should be a network option.
61
+ *
62
+ * @var bool
63
+ * @since 1.7
64
+ * @access protected
65
+ */
66
+ protected $network_option = false;
67
+
68
+ /**
69
+ * Identifier used in the hook names.
70
+ *
71
+ * @var string
72
+ * @since 1.7
73
+ * @access private
74
+ */
75
+ private $hook_identifier;
76
+
77
+ /**
78
+ * The single instance of the class.
79
+ *
80
+ * @var object
81
+ * @since 1.7
82
+ * @access protected
83
+ */
84
+ protected static $_instance;
85
+
86
+ /**
87
+ * The constructor.
88
+ *
89
+ * @since 1.7
90
+ * @author Grégory Viguier
91
+ * @access protected
92
+ */
93
+ protected function __construct() {
94
+ $this->hook_identifier = rtrim( strtolower( str_replace( 'Imagify_', '', get_class( $this ) ) ), 's' );
95
+
96
+ if ( ! is_string( $this->autoload ) ) {
97
+ $this->autoload = $this->autoload ? 'yes' : 'no';
98
+ }
99
+
100
+ $this->default_values = array_merge( array(
101
+ 'version' => '',
102
+ ), $this->default_values );
103
+ }
104
+
105
+ /**
106
+ * Get the main Instance.
107
+ *
108
+ * @since 1.7
109
+ * @author Grégory Viguier
110
+ * @access public
111
+ *
112
+ * @return object Main instance.
113
+ */
114
+ public static function get_instance() {
115
+ if ( ! isset( self::$_instance ) ) {
116
+ self::$_instance = new self();
117
+ }
118
+
119
+ return self::$_instance;
120
+ }
121
+
122
+ /**
123
+ * Launch the hooks.
124
+ *
125
+ * @since 1.7
126
+ * @author Grégory Viguier
127
+ * @access public
128
+ */
129
+ public function init() {
130
+ add_filter( 'sanitize_option_' . $this->get_option_name(), array( $this, 'sanitize_and_validate_on_update' ), 50 );
131
+ }
132
+
133
+
134
+ /** ----------------------------------------------------------------------------------------- */
135
+ /** GET/SET/DELETE OPTION(S) ================================================================ */
136
+ /** ----------------------------------------------------------------------------------------- */
137
+
138
+ /**
139
+ * Get an Imagify option.
140
+ *
141
+ * @since 1.7
142
+ * @author Grégory Viguier
143
+ * @access public
144
+ *
145
+ * @param string $key The option name.
146
+ * @return mixed The option value.
147
+ */
148
+ public function get( $key ) {
149
+ $default_values = $this->get_default_values();
150
+
151
+ if ( ! isset( $default_values[ $key ] ) ) {
152
+ return null;
153
+ }
154
+
155
+ $default = $default_values[ $key ];
156
+
157
+ /**
158
+ * Pre-filter any Imagify option before read.
159
+ *
160
+ * @since 1.0
161
+ *
162
+ * @param mixed $value Value to return instead of the option value. Default null to skip it.
163
+ * @param mixed $default The default value.
164
+ */
165
+ $value = apply_filters( 'pre_get_imagify_' . $this->get_hook_identifier() . '_' . $key, null, $default );
166
+
167
+ if ( isset( $value ) ) {
168
+ return $value;
169
+ }
170
+
171
+ // Get all values.
172
+ $values = $this->get_all();
173
+
174
+ // Sanitize and validate the value.
175
+ $value = $this->sanitize_and_validate( $key, $values[ $key ], $default );
176
+
177
+ /**
178
+ * Filter any Imagify option after read.
179
+ *
180
+ * @since 1.0
181
+ *
182
+ * @param mixed $value Value of the option.
183
+ * @param mixed $default The default value. Default false.
184
+ */
185
+ return apply_filters( 'get_imagify_' . $this->get_hook_identifier() . '_' . $key, $value, $default );
186
+ }
187
+
188
+ /**
189
+ * Get all options (no cast, no sanitization, no validation).
190
+ *
191
+ * @since 1.7
192
+ * @author Grégory Viguier
193
+ * @access public
194
+ *
195
+ * @return array The options.
196
+ */
197
+ public function get_all() {
198
+ $values = $this->get_raw();
199
+
200
+ if ( ! $values ) {
201
+ return $this->get_reset_values();
202
+ }
203
+
204
+ return imagify_merge_intersect( $values, $this->get_default_values() );
205
+ }
206
+
207
+ /**
208
+ * Set one or multiple options.
209
+ *
210
+ * @since 1.7
211
+ * @author Grégory Viguier
212
+ * @access public
213
+ *
214
+ * @param array $values An array of option name / option value pairs.
215
+ */
216
+ public function set( $values ) {
217
+ $args = func_get_args();
218
+
219
+ if ( isset( $args[1] ) && is_string( $args[0] ) ) {
220
+ $values = array( $args[0] => $args[1] );
221
+ }
222
+
223
+ if ( ! is_array( $values ) ) {
224
+ // PABKAC.
225
+ return;
226
+ }
227
+
228
+ $values = array_merge( $this->get_all(), $values );
229
+ $values = array_intersect_key( $values, $this->get_default_values() );
230
+
231
+ $this->set_raw( $values );
232
+ }
233
+
234
+ /**
235
+ * Delete one or multiple options.
236
+ *
237
+ * @since 1.7
238
+ * @author Grégory Viguier
239
+ * @access public
240
+ *
241
+ * @param array|string $keys An array of option names or a single option name.
242
+ */
243
+ public function delete( $keys ) {
244
+ $values = $this->get_raw();
245
+
246
+ if ( ! $values ) {
247
+ if ( false !== $values ) {
248
+ $this->delete_raw();
249
+ }
250
+ return;
251
+ }
252
+
253
+ $keys = array_flip( (array) $keys );
254
+ $values = array_diff_key( $values, $keys );
255
+
256
+ $this->set_raw( $values );
257
+ }
258
+
259
+ /**
260
+ * Checks if the option with the given name exists or not.
261
+ *
262
+ * @since 1.7
263
+ * @author Grégory Viguier
264
+ * @access public
265
+ *
266
+ * @param string $key The option name.
267
+ * @return bool
268
+ */
269
+ public function has( $key ) {
270
+ return null !== $this->get( $key );
271
+ }
272
+
273
+
274
+ /** ----------------------------------------------------------------------------------------- */
275
+ /** GET / UPDATE / DELETE RAW VALUES ======================================================== */
276
+ /** ----------------------------------------------------------------------------------------- */
277
+
278
+ /**
279
+ * Get the name of the option that stores the settings.
280
+ *
281
+ * @since 1.7
282
+ * @author Grégory Viguier
283
+ * @access public
284
+ *
285
+ * @return string
286
+ */
287
+ public function get_option_name() {
288
+ return IMAGIFY_SLUG . '_' . $this->identifier;
289
+ }
290
+
291
+ /**
292
+ * Get the identifier used in the hook names.
293
+ *
294
+ * @since 1.7
295
+ * @author Grégory Viguier
296
+ * @access public
297
+ *
298
+ * @return string
299
+ */
300
+ public function get_hook_identifier() {
301
+ return $this->hook_identifier;
302
+ }
303
+
304
+ /**
305
+ * Tell if the option is autoloaded.
306
+ *
307
+ * @since 1.7
308
+ * @author Grégory Viguier
309
+ * @access public
310
+ *
311
+ * @return bool
312
+ */
313
+ public function is_autoloaded() {
314
+ return 'yes' === $this->autoload;
315
+ }
316
+
317
+ /**
318
+ * Tell if the option is a network option.
319
+ *
320
+ * @since 1.7
321
+ * @author Grégory Viguier
322
+ * @access public
323
+ *
324
+ * @return bool
325
+ */
326
+ public function is_network_option() {
327
+ return (bool) $this->network_option;
328
+ }
329
+
330
+ /**
331
+ * Get the raw value of all Imagify options.
332
+ *
333
+ * @since 1.7
334
+ * @author Grégory Viguier
335
+ * @access public
336
+ *
337
+ * @return array|bool The options. False if not set yet. An empty array if invalid.
338
+ */
339
+ public function get_raw() {
340
+ $values = $this->is_network_option() ? get_site_option( $this->get_option_name() ) : get_option( $this->get_option_name() );
341
+
342
+ if ( false !== $values && ! is_array( $values ) ) {
343
+ return array();
344
+ }
345
+
346
+ return $values;
347
+ }
348
+
349
+ /**
350
+ * Update the Imagify options.
351
+ *
352
+ * @since 1.7
353
+ * @author Grégory Viguier
354
+ * @access public
355
+ *
356
+ * @param array $values An array of option name / option value pairs.
357
+ */
358
+ public function set_raw( $values ) {
359
+ if ( ! $values ) {
360
+ // The option is empty: delete it.
361
+ $this->delete_raw();
362
+
363
+ } elseif ( $this->is_network_option() ) {
364
+ // Network option.
365
+ update_site_option( $this->get_option_name(), $values );
366
+
367
+ } elseif ( false === get_option( $this->get_option_name() ) ) {
368
+ // Compat' with WP < 4.2 + autoload: the option doesn't exist in the database.
369
+ add_option( $this->get_option_name(), $values, '', $this->autoload );
370
+ } else {
371
+ // Update the current value.
372
+ update_option( $this->get_option_name(), $values, $this->autoload );
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Delete all Imagify options.
378
+ *
379
+ * @since 1.7
380
+ * @author Grégory Viguier
381
+ * @access public
382
+ */
383
+ public function delete_raw() {
384
+ $this->is_network_option() ? delete_site_option( $this->get_option_name() ) : delete_option( $this->get_option_name() );
385
+ }
386
+
387
+
388
+ /** ----------------------------------------------------------------------------------------- */
389
+ /** DEFAULT + RESET VALUES ================================================================== */
390
+ /** ----------------------------------------------------------------------------------------- */
391
+
392
+ /**
393
+ * Get default option values.
394
+ *
395
+ * @since 1.7
396
+ * @author Grégory Viguier
397
+ * @access public
398
+ *
399
+ * @return array
400
+ */
401
+ public function get_default_values() {
402
+ $default_values = $this->default_values;
403
+
404
+ if ( ! empty( $default_values['cached'] ) ) {
405
+ unset( $default_values['cached'] );
406
+ return $default_values;
407
+ }
408
+
409
+ /**
410
+ * Allow to add more default option values.
411
+ *
412
+ * @since 1.7
413
+ * @author Grégory Viguier
414
+ *
415
+ * @param array $new_values New default option values.
416
+ * @param array $default_values Plugin default option values.
417
+ */
418
+ $new_values = apply_filters( 'imagify_default_' . $this->get_hook_identifier() . '_values', array(), $default_values );
419
+ $new_values = is_array( $new_values ) ? $new_values : array();
420
+
421
+ if ( $new_values ) {
422
+ // Don't allow new values to overwrite the plugin values.
423
+ $new_values = array_diff_key( $new_values, $default_values );
424
+ }
425
+
426
+ if ( $new_values ) {
427
+ $default_values = array_merge( $default_values, $new_values );
428
+ $this->default_values = $default_values;
429
+ }
430
+
431
+ $this->default_values['cached'] = 1;
432
+
433
+ return $default_values;
434
+ }
435
+
436
+ /**
437
+ * Get the values used when the option is empty.
438
+ *
439
+ * @since 1.7
440
+ * @author Grégory Viguier
441
+ * @access public
442
+ *
443
+ * @return array
444
+ */
445
+ public function get_reset_values() {
446
+ $reset_values = $this->reset_values;
447
+
448
+ if ( ! empty( $reset_values['cached'] ) ) {
449
+ unset( $reset_values['cached'] );
450
+ return $reset_values;
451
+ }
452
+
453
+ $default_values = $this->get_default_values();
454
+ $reset_values = array_merge( $default_values, $reset_values );
455
+
456
+ /**
457
+ * Allow to filter the "reset" option values.
458
+ *
459
+ * @since 1.7
460
+ * @author Grégory Viguier
461
+ *
462
+ * @param array $reset_values Plugin reset option values.
463
+ */
464
+ $new_values = apply_filters( 'imagify_reset_' . $this->get_hook_identifier() . '_values', $reset_values );
465
+
466
+ if ( $new_values && is_array( $new_values ) ) {
467
+ $reset_values = array_merge( $reset_values, $new_values );
468
+ }
469
+
470
+ $this->reset_values = $reset_values;
471
+ $this->reset_values['cached'] = 1;
472
+
473
+ return $reset_values;
474
+ }
475
+
476
+
477
+ /** ----------------------------------------------------------------------------------------- */
478
+ /** SANITIZATION, VALIDATION ================================================================ */
479
+ /** ----------------------------------------------------------------------------------------- */
480
+
481
+ /**
482
+ * Sanitize and validate an option value.
483
+ *
484
+ * @since 1.7
485
+ * @author Grégory Viguier
486
+ * @access public
487
+ *
488
+ * @param string $key The option key.
489
+ * @param mixed $value The value.
490
+ * @param mixed $default The default value.
491
+ * @return mixed
492
+ */
493
+ public function sanitize_and_validate( $key, $value, $default = null ) {
494
+ if ( ! isset( $default ) ) {
495
+ $default_values = $this->get_default_values();
496
+ $default = $default_values[ $key ];
497
+ }
498
+
499
+ // Cast the value.
500
+ $value = self::cast( $value, $default );
501
+
502
+ if ( $value === $default ) {
503
+ return $value;
504
+ }
505
+
506
+ // Version.
507
+ if ( 'version' === $key ) {
508
+ return sanitize_text_field( $value );
509
+ }
510
+
511
+ return $this->sanitize_and_validate_value( $key, $value, $default );
512
+ }
513
+
514
+ /**
515
+ * Sanitize and validate an option value. Basic casts have been made.
516
+ *
517
+ * @since 1.7
518
+ * @author Grégory Viguier
519
+ * @access public
520
+ *
521
+ * @param string $key The option key.
522
+ * @param mixed $value The value.
523
+ * @param mixed $default The default value.
524
+ * @return mixed
525
+ */
526
+ abstract public function sanitize_and_validate_value( $key, $value, $default );
527
+
528
+ /**
529
+ * Sanitize and validate Imagify's options before storing them.
530
+ *
531
+ * @since 1.7
532
+ * @author Grégory Viguier
533
+ * @access public
534
+ *
535
+ * @param string $values The option value.
536
+ * @return array
537
+ */
538
+ public function sanitize_and_validate_on_update( $values ) {
539
+ $values = is_array( $values ) ? $values : array();
540
+ $default_values = $this->get_default_values();
541
+
542
+ if ( $values ) {
543
+ foreach ( $default_values as $key => $default ) {
544
+ if ( isset( $values[ $key ] ) ) {
545
+ $values[ $key ] = $this->sanitize_and_validate( $key, $values[ $key ], $default );
546
+ }
547
+ }
548
+ }
549
+
550
+ $values = array_intersect_key( $values, $default_values );
551
+
552
+ // Version.
553
+ if ( empty( $values['version'] ) ) {
554
+ $values['version'] = IMAGIFY_VERSION;
555
+ }
556
+
557
+ return $this->validate_values_on_update( $values );
558
+ }
559
+
560
+ /**
561
+ * Validate Imagify's options before storing them. Basic sanitization and validation have been made, row by row.
562
+ *
563
+ * @since 1.7
564
+ * @author Grégory Viguier
565
+ * @access public
566
+ *
567
+ * @param string $values The option value.
568
+ * @return array
569
+ */
570
+ public function validate_values_on_update( $values ) {
571
+ return $values;
572
+ }
573
+
574
+
575
+ /** ----------------------------------------------------------------------------------------- */
576
+ /** TOOLS =================================================================================== */
577
+ /** ----------------------------------------------------------------------------------------- */
578
+
579
+ /**
580
+ * Cast a value, depending on its default value type.
581
+ *
582
+ * @since 1.7
583
+ * @author Grégory Viguier
584
+ * @access public
585
+ *
586
+ * @param mixed $value The value to cast.
587
+ * @param mixed $default The default value.
588
+ * @return mixed
589
+ */
590
+ public static function cast( $value, $default ) {
591
+ if ( is_array( $default ) ) {
592
+ return is_array( $value ) ? $value : array();
593
+ }
594
+
595
+ if ( is_int( $default ) ) {
596
+ return (int) $value;
597
+ }
598
+
599
+ if ( is_bool( $default ) ) {
600
+ return (bool) $value;
601
+ }
602
+
603
+ if ( is_float( $default ) ) {
604
+ return round( (float) $value, 3 );
605
+ }
606
+
607
+ return $value;
608
+ }
609
+
610
+ /**
611
+ * Cast a float like 3.000 into an integer.
612
+ *
613
+ * @since 1.7
614
+ * @author Grégory Viguier
615
+ * @access public
616
+ *
617
+ * @param float $float The float.
618
+ * @return float|int
619
+ */
620
+ public static function maybe_cast_float_as_int( $float ) {
621
+ return ( $float / (int) $float ) === (float) 1 ? (int) $float : $float;
622
+ }
623
+ }
inc/classes/class-imagify-admin-ajax-post.php CHANGED
@@ -26,6 +26,10 @@ class Imagify_Admin_Ajax_Post {
26
  'imagify_manual_override_upload',
27
  'imagify_optimize_missing_sizes',
28
  'imagify_restore_upload',
 
 
 
 
29
  );
30
 
31
  /**
@@ -35,9 +39,11 @@ class Imagify_Admin_Ajax_Post {
35
  */
36
  protected $ajax_only_actions = array(
37
  'imagify_bulk_upload',
 
38
  'imagify_async_optimize_upload_new_media',
39
  'imagify_async_optimize_save_image_editor_file',
40
  'imagify_get_unoptimized_attachment_ids',
 
41
  'imagify_check_backup_dir_is_writable',
42
  'nopriv_imagify_rpc',
43
  'imagify_signup',
@@ -48,6 +54,10 @@ class Imagify_Admin_Ajax_Post {
48
  'imagify_get_discount',
49
  'imagify_get_images_counts',
50
  'imagify_update_estimate_sizes',
 
 
 
 
51
  );
52
 
53
  /**
@@ -55,7 +65,10 @@ class Imagify_Admin_Ajax_Post {
55
  *
56
  * @var array
57
  */
58
- protected $post_only_actions = array();
 
 
 
59
 
60
  /**
61
  * The single instance of the class.
@@ -99,7 +112,7 @@ class Imagify_Admin_Ajax_Post {
99
  * @author Grégory Viguier
100
  */
101
  public function init() {
102
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
103
  // Actions triggered only on admin ajax.
104
  $actions = array_merge( $this->ajax_post_actions, $this->ajax_only_actions );
105
 
@@ -140,7 +153,7 @@ class Imagify_Admin_Ajax_Post {
140
 
141
  $attachment = get_imagify_attachment( $context, $attachment_id, 'imagify_manual_upload' );
142
 
143
- // Optimize it!!!!!
144
  $attachment->optimize();
145
 
146
  imagify_maybe_redirect();
@@ -172,8 +185,8 @@ class Imagify_Admin_Ajax_Post {
172
  // Restore the backup file.
173
  $attachment->restore();
174
 
175
- // Optimize it!!!!!
176
- $attachment->optimize( (int) $_GET['optimization_level'] );
177
 
178
  imagify_maybe_redirect();
179
 
@@ -255,46 +268,288 @@ class Imagify_Admin_Ajax_Post {
255
  $context = imagify_sanitize_context( $_POST['context'] );
256
  $attachment_id = absint( $_POST['image'] );
257
 
258
- imagify_check_nonce( 'imagify-bulk-upload', 'imagifybulkuploadnonce' );
259
  imagify_check_user_capacity( 'bulk-optimize', $attachment_id );
260
 
261
  $attachment = get_imagify_attachment( $context, $attachment_id, 'imagify_bulk_upload' );
262
- $optimization_level = get_transient( 'imagify_bulk_optimization_level' );
263
 
264
  // Restore it if the optimization level is updated.
265
  if ( $optimization_level !== $attachment->get_optimization_level() ) {
266
  $attachment->restore();
267
  }
268
 
269
- // Optimize it!!!!!
270
  $attachment->optimize( $optimization_level );
271
 
272
  // Return the optimization statistics.
273
  $fullsize_data = $attachment->get_size_data();
274
- $stats_data = $attachment->get_stats_data();
275
- $user = new Imagify_User();
276
  $data = array();
277
 
278
  if ( ! $attachment->is_optimized() ) {
279
- $data['success'] = false;
280
- $data['error'] = $fullsize_data['error'];
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
  imagify_die( $data );
283
  }
284
 
285
- $data['success'] = true;
286
- $data['original_size'] = $fullsize_data['original_size'];
287
- $data['new_size'] = $fullsize_data['optimized_size'];
288
- $data['percent'] = $fullsize_data['percent'];
289
- $data['overall_saving'] = $stats_data['original_size'] - $stats_data['optimized_size'];
290
- $data['original_overall_size'] = $stats_data['original_size'];
291
- $data['new_overall_size'] = $stats_data['optimized_size'];
292
- $data['thumbnails'] = $attachment->get_optimized_sizes_count();
 
 
 
 
293
 
294
  wp_send_json_success( $data );
295
  }
296
 
297
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  /** ----------------------------------------------------------------------------------------- */
299
  /** AUTOMATIC OPTIMIZATION ================================================================== */
300
  /** ----------------------------------------------------------------------------------------- */
@@ -385,57 +640,47 @@ class Imagify_Admin_Ajax_Post {
385
  public function imagify_get_unoptimized_attachment_ids_callback() {
386
  global $wpdb;
387
 
388
- imagify_check_nonce( 'imagify-bulk-upload', 'imagifybulkuploadnonce' );
389
  imagify_check_user_capacity( 'bulk-optimize' );
390
-
391
- if ( ! imagify_valid_key() ) {
392
- wp_send_json_error( array( 'message' => 'invalid-api-key' ) );
393
- }
394
-
395
- $user = new Imagify_User();
396
-
397
- if ( $user->is_over_quota() ) {
398
- wp_send_json_error( array( 'message' => 'over-quota' ) );
399
- }
400
 
401
  @set_time_limit( 0 );
402
 
403
  // Get (ordered) IDs.
404
- $optimization_level = (int) $_GET['optimization_level'];
405
- $optimization_level = -1 !== $optimization_level ? $optimization_level : (int) get_imagify_option( 'optimization_level', 1 );
406
-
407
- Imagify_DB::unlimit_joins();
408
-
409
- $mime_types = Imagify_DB::get_mime_types();
410
- $ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
411
- "SELECT $wpdb->posts.ID
412
- FROM $wpdb->posts
413
- LEFT JOIN $wpdb->postmeta
414
- ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = '_imagify_optimization_level' )
415
  LEFT JOIN $wpdb->postmeta AS mt1
416
- ON ( $wpdb->posts.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
417
- INNER JOIN $wpdb->postmeta AS mt2
418
- ON ( $wpdb->posts.ID = mt2.post_id AND mt2.meta_key = '_wp_attached_file' )
419
- INNER JOIN $wpdb->postmeta AS mt3
420
- ON ( $wpdb->posts.ID = mt3.post_id AND mt3.meta_key = '_wp_attachment_metadata' )
421
  WHERE
422
- $wpdb->posts.post_mime_type IN ( $mime_types )
423
  AND (
424
- $wpdb->postmeta.meta_value != '%d'
425
  OR
426
- $wpdb->postmeta.post_id IS NULL
427
  OR
428
- mt1.meta_value = 'error'
429
  )
430
- AND $wpdb->posts.post_type = 'attachment'
431
- AND $wpdb->posts.post_status = 'inherit'
432
- GROUP BY $wpdb->posts.ID
 
433
  ORDER BY
434
  CASE mt1.meta_value
435
  WHEN 'already_optimized' THEN 2
436
  ELSE 1
437
  END ASC,
438
- $wpdb->posts.ID DESC
439
  LIMIT 0, %d",
440
  $optimization_level,
441
  imagify_get_unoptimized_attachment_limit()
@@ -446,7 +691,7 @@ class Imagify_Admin_Ajax_Post {
446
  $ids = array_filter( array_map( 'absint', $ids ) );
447
 
448
  if ( ! $ids ) {
449
- wp_send_json_error( array( 'message' => 'no-images' ) );
450
  }
451
 
452
  $results = Imagify_DB::get_metas( array(
@@ -460,9 +705,6 @@ class Imagify_Admin_Ajax_Post {
460
  'statuses' => '_imagify_status',
461
  ), $ids );
462
 
463
- // Save the optimization level in a transient to retrieve it later during the process.
464
- set_transient( 'imagify_bulk_optimization_level', $optimization_level );
465
-
466
  // First run.
467
  foreach ( $ids as $i => $id ) {
468
  $attachment_status = isset( $results['statuses'][ $id ] ) ? $results['statuses'][ $id ] : false;
@@ -494,7 +736,7 @@ class Imagify_Admin_Ajax_Post {
494
  }
495
 
496
  if ( ! $ids ) {
497
- wp_send_json_error( array( 'message' => 'no-images' ) );
498
  }
499
 
500
  $ids = array_values( $ids );
@@ -511,7 +753,8 @@ class Imagify_Admin_Ajax_Post {
511
  */
512
  do_action( 'imagify_bulk_optimize_before_file_existence_tests', $ids, $results, $optimization_level );
513
 
514
- $data = array();
 
515
 
516
  foreach ( $ids as $i => $id ) {
517
  if ( empty( $results['filenames'][ $id ] ) ) {
@@ -524,7 +767,7 @@ class Imagify_Admin_Ajax_Post {
524
  /** This filter is documented in inc/functions/process.php. */
525
  $file_path = apply_filters( 'imagify_file_path', $file_path );
526
 
527
- if ( ! $file_path || ! file_exists( $file_path ) ) {
528
  continue;
529
  }
530
 
@@ -533,7 +776,7 @@ class Imagify_Admin_Ajax_Post {
533
  $attachment_optimization_level = isset( $results['optimization_levels'][ $id ] ) ? $results['optimization_levels'][ $id ] : false;
534
 
535
  // Don't try to re-optimize if there is no backup file.
536
- if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! file_exists( $attachment_backup_path ) ) {
537
  continue;
538
  }
539
 
@@ -541,12 +784,127 @@ class Imagify_Admin_Ajax_Post {
541
  } // End foreach().
542
 
543
  if ( ! $data ) {
544
- wp_send_json_error( array( 'message' => 'no-images' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  }
546
 
547
  wp_send_json_success( $data );
548
  }
549
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
  /**
551
  * Check if the backup directory is writable.
552
  * This is used to display an error message in the plugin's settings page.
@@ -667,10 +1025,7 @@ class Imagify_Admin_Ajax_Post {
667
  imagify_die( $response );
668
  }
669
 
670
- $options = get_site_option( IMAGIFY_SETTINGS_SLUG );
671
- $options['api_key'] = sanitize_key( $_GET['api_key'] );
672
-
673
- update_site_option( IMAGIFY_SETTINGS_SLUG, $options );
674
 
675
  wp_send_json_success();
676
  }
@@ -715,7 +1070,7 @@ class Imagify_Admin_Ajax_Post {
715
  $message .= '<p>' . sprintf(
716
  /* translators: 1 is a data quota, 2 is a date. */
717
  __( 'You have consumed all your credit for this month. You will have <strong>%1$s back on %2$s</strong>.', 'imagify' ),
718
- size_format( $user->quota * 1048576 ),
719
  date_i18n( get_option( 'date_format' ), strtotime( $user->next_date_update ) )
720
  ) . '</p>';
721
  $message .= '<p class="center txt-center text-center"><a class="btn imagify-btn-ghost" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '" target="_blank">' . __( 'Upgrade My Subscription', 'imagify' ) . '</a></p>';
@@ -835,14 +1190,17 @@ class Imagify_Admin_Ajax_Post {
835
  imagify_check_nonce( 'imagify_get_pricing_' . get_current_user_id(), 'imagifynonce' );
836
  imagify_check_user_capacity();
837
 
838
- $raw_total_size_in_library = imagify_calculate_total_size_images_library();
839
- $raw_average_per_month = imagify_calculate_average_size_images_per_month();
840
- update_imagify_option( 'total_size_images_library', array( 'raw' => $raw_total_size_in_library, 'human' => size_format( $raw_total_size_in_library ) ) );
841
- update_imagify_option( 'average_size_images_per_month', array( 'raw' => $raw_average_per_month, 'human' => size_format( $raw_average_per_month ) ) );
 
 
 
842
 
843
  wp_send_json_success( array(
844
- 'total_library_size' => get_imagify_option( 'total_size_images_library', null ),
845
- 'average_month_size' => get_imagify_option( 'average_size_images_per_month', null ),
846
  ) );
847
  }
848
 
@@ -856,12 +1214,268 @@ class Imagify_Admin_Ajax_Post {
856
  imagify_check_nonce( 'update_estimate_sizes' );
857
  imagify_check_user_capacity();
858
 
859
- $raw_total_size_in_library = imagify_calculate_total_size_images_library();
860
- $raw_average_per_month = imagify_calculate_average_size_images_per_month();
861
 
862
- update_imagify_option( 'total_size_images_library', array( 'raw' => $raw_total_size_in_library, 'human' => size_format( $raw_total_size_in_library ) ) );
863
- update_imagify_option( 'average_size_images_per_month', array( 'raw' => $raw_average_per_month, 'human' => size_format( $raw_average_per_month ) ) );
 
 
864
 
865
  die( 1 );
866
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
867
  }
26
  'imagify_manual_override_upload',
27
  'imagify_optimize_missing_sizes',
28
  'imagify_restore_upload',
29
+ 'imagify_optimize_file',
30
+ 'imagify_reoptimize_file',
31
+ 'imagify_restore_file',
32
+ 'imagify_refresh_file_modified',
33
  );
34
 
35
  /**
39
  */
40
  protected $ajax_only_actions = array(
41
  'imagify_bulk_upload',
42
+ 'imagify_bulk_optimize_file',
43
  'imagify_async_optimize_upload_new_media',
44
  'imagify_async_optimize_save_image_editor_file',
45
  'imagify_get_unoptimized_attachment_ids',
46
+ 'imagify_get_unoptimized_file_ids',
47
  'imagify_check_backup_dir_is_writable',
48
  'nopriv_imagify_rpc',
49
  'imagify_signup',
54
  'imagify_get_discount',
55
  'imagify_get_images_counts',
56
  'imagify_update_estimate_sizes',
57
+ 'imagify_get_user_data',
58
+ 'imagify_get_files_tree',
59
+ 'imagify_get_folder_type_data',
60
+ 'imagify_bulk_info_seen',
61
  );
62
 
63
  /**
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.
112
  * @author Grégory Viguier
113
  */
114
  public function init() {
115
+ if ( wp_doing_ajax() ) {
116
  // Actions triggered only on admin ajax.
117
  $actions = array_merge( $this->ajax_post_actions, $this->ajax_only_actions );
118
 
153
 
154
  $attachment = get_imagify_attachment( $context, $attachment_id, 'imagify_manual_upload' );
155
 
156
+ // Optimize it.
157
  $attachment->optimize();
158
 
159
  imagify_maybe_redirect();
185
  // Restore the backup file.
186
  $attachment->restore();
187
 
188
+ // Optimize it.
189
+ $attachment->optimize( $this->get_optimization_level() );
190
 
191
  imagify_maybe_redirect();
192
 
268
  $context = imagify_sanitize_context( $_POST['context'] );
269
  $attachment_id = absint( $_POST['image'] );
270
 
271
+ imagify_check_nonce( 'imagify-bulk-upload' );
272
  imagify_check_user_capacity( 'bulk-optimize', $attachment_id );
273
 
274
  $attachment = get_imagify_attachment( $context, $attachment_id, 'imagify_bulk_upload' );
275
+ $optimization_level = $this->get_optimization_level();
276
 
277
  // Restore it if the optimization level is updated.
278
  if ( $optimization_level !== $attachment->get_optimization_level() ) {
279
  $attachment->restore();
280
  }
281
 
282
+ // Optimize it.
283
  $attachment->optimize( $optimization_level );
284
 
285
  // Return the optimization statistics.
286
  $fullsize_data = $attachment->get_size_data();
 
 
287
  $data = array();
288
 
289
  if ( ! $attachment->is_optimized() ) {
290
+ $data['success'] = false;
291
+ $data['error_code'] = '';
292
+ $data['error'] = isset( $fullsize_data['error'] ) ? (string) $fullsize_data['error'] : '';
293
+
294
+ if ( ! $attachment->has_error() ) {
295
+ $data['error_code'] = 'already-optimized';
296
+ } else {
297
+ $message = 'You\'ve consumed all your data. You have to upgrade your account to continue';
298
+
299
+ if ( $data['error'] === $message ) {
300
+ $data['error_code'] = 'over-quota';
301
+ }
302
+ }
303
+
304
+ $data['error'] = imagify_translate_api_message( $data['error'] );
305
 
306
  imagify_die( $data );
307
  }
308
 
309
+ $stats_data = $attachment->get_stats_data();
310
+
311
+ $data['success'] = true;
312
+ $data['original_size_human'] = imagify_size_format( $fullsize_data['original_size'], 2 );
313
+ $data['new_size_human'] = imagify_size_format( $fullsize_data['optimized_size'], 2 );
314
+ $data['overall_saving'] = $stats_data['original_size'] - $stats_data['optimized_size'];
315
+ $data['overall_saving_human'] = imagify_size_format( $data['overall_saving'], 2 );
316
+ $data['original_overall_size'] = $stats_data['original_size'];
317
+ $data['original_overall_size_human'] = imagify_size_format( $data['original_overall_size'], 2 );
318
+ $data['new_overall_size'] = $stats_data['optimized_size'];
319
+ $data['percent_human'] = $fullsize_data['percent'] . '%';
320
+ $data['thumbnails'] = $attachment->get_optimized_sizes_count();
321
 
322
  wp_send_json_success( $data );
323
  }
324
 
325
 
326
+ /** ----------------------------------------------------------------------------------------- */
327
+ /** CUSTOM FOLDERS ========================================================================== */
328
+ /** ----------------------------------------------------------------------------------------- */
329
+
330
+ /**
331
+ * Optimize a file.
332
+ *
333
+ * @since 1.7
334
+ * @access public
335
+ * @author Grégory Viguier
336
+ */
337
+ public function imagify_bulk_optimize_file_callback() {
338
+ imagify_check_nonce( 'imagify-bulk-upload' );
339
+ imagify_check_user_capacity( 'optimize-file' );
340
+
341
+ $file_id = (int) filter_input( INPUT_POST, 'image', FILTER_SANITIZE_NUMBER_INT );
342
+ $context = imagify_sanitize_context( filter_input( INPUT_POST, 'context', FILTER_SANITIZE_STRING ) );
343
+ $context = ! $context || 'wp' === strtolower( $context ) ? 'File' : $context;
344
+
345
+ if ( ! $file_id ) {
346
+ imagify_die( __( 'Invalid request', 'imagify' ) );
347
+ }
348
+
349
+ $file = get_imagify_attachment( $context, $file_id, 'imagify_bulk_optimize_file' );
350
+
351
+ if ( ! $file->is_valid() ) {
352
+ imagify_die( __( 'Invalid file ID', 'imagify' ) );
353
+ }
354
+
355
+ // Restore before re-optimizing.
356
+ if ( false !== $file->get_optimization_level() ) {
357
+ $file->restore();
358
+ }
359
+
360
+ // Optimize it.
361
+ $result = $file->optimize( $this->get_optimization_level() );
362
+
363
+ // Return the optimization statistics.
364
+ if ( ! $file->is_optimized() ) {
365
+ $data = array(
366
+ 'success' => false,
367
+ 'error_code' => '',
368
+ 'error' => (string) $file->get_optimized_error(),
369
+ );
370
+
371
+ if ( ! $file->has_error() ) {
372
+ $data['error_code'] = 'already-optimized';
373
+ } else {
374
+ $message = 'You\'ve consumed all your data. You have to upgrade your account to continue';
375
+
376
+ if ( $data['error'] === $message ) {
377
+ $data['error_code'] = 'over-quota';
378
+ }
379
+ }
380
+
381
+ $data['error'] = imagify_translate_api_message( $data['error'] );
382
+
383
+ imagify_die( $data );
384
+ }
385
+
386
+ $data = $file->get_size_data();
387
+
388
+ wp_send_json_success( array(
389
+ 'success' => true,
390
+ 'original_size_human' => imagify_size_format( $data['original_size'], 2 ),
391
+ 'new_size_human' => imagify_size_format( $data['optimized_size'], 2 ),
392
+ 'overall_saving' => $data['original_size'] - $data['optimized_size'],
393
+ 'overall_saving_human' => imagify_size_format( $data['original_size'] - $data['optimized_size'], 2 ),
394
+ 'original_overall_size' => $data['original_size'],
395
+ 'original_overall_size_human' => imagify_size_format( $data['original_size'], 2 ),
396
+ 'new_overall_size' => $data['optimized_size'],
397
+ 'percent_human' => $data['percent'] . '%',
398
+ 'thumbnails' => $file->get_optimized_sizes_count(),
399
+ ) );
400
+ }
401
+
402
+ /**
403
+ * Optimize a file.
404
+ *
405
+ * @since 1.7
406
+ * @access public
407
+ * @author Grégory Viguier
408
+ */
409
+ public function imagify_optimize_file_callback() {
410
+ imagify_check_nonce( 'imagify_optimize_file' );
411
+ imagify_check_user_capacity( 'optimize-file' );
412
+
413
+ $file = $this->get_file_to_optimize( 'imagify_optimize_file' );
414
+
415
+ // Optimize it.
416
+ $result = $file->optimize();
417
+
418
+ $this->file_optimization_output( $result, $file );
419
+ }
420
+
421
+ /**
422
+ * Re-optimize a file.
423
+ *
424
+ * @since 1.7
425
+ * @access public
426
+ * @author Grégory Viguier
427
+ */
428
+ public function imagify_reoptimize_file_callback() {
429
+ imagify_check_nonce( 'imagify_reoptimize_file' );
430
+ imagify_check_user_capacity( 'optimize-file' );
431
+
432
+ $file = $this->get_file_to_optimize( 'imagify_reoptimize_file' );
433
+
434
+ // Restore it.
435
+ $result = $file->restore();
436
+
437
+ if ( ! is_wp_error( $result ) ) {
438
+ // Optimize it.
439
+ $level = isset( $_GET['level'] ) && is_numeric( $_GET['level'] ) ? $_GET['level'] : null;
440
+ $result = $file->optimize( $level );
441
+ }
442
+
443
+ $this->file_optimization_output( $result, $file );
444
+ }
445
+
446
+ /**
447
+ * Restore a file.
448
+ *
449
+ * @since 1.7
450
+ * @access public
451
+ * @author Grégory Viguier
452
+ */
453
+ public function imagify_restore_file_callback() {
454
+ imagify_check_nonce( 'imagify_restore_file' );
455
+ imagify_check_user_capacity( 'optimize-file' );
456
+
457
+ $file = $this->get_file_to_optimize( 'imagify_restore_file' );
458
+
459
+ // Restore it.
460
+ $result = $file->restore();
461
+
462
+ $this->file_optimization_output( $result, $file );
463
+ }
464
+
465
+ /**
466
+ * Check if a file has been modified, and update the database accordingly.
467
+ *
468
+ * @since 1.7
469
+ * @access public
470
+ * @author Grégory Viguier
471
+ */
472
+ public function imagify_refresh_file_modified_callback() {
473
+ imagify_check_nonce( 'imagify_refresh_file_modified' );
474
+ imagify_check_user_capacity( 'optimize-file' );
475
+
476
+ $file = $this->get_file_to_optimize( 'imagify_refresh_file_modified' );
477
+ $result = Imagify_Custom_Folders::refresh_file( $file );
478
+
479
+ if ( is_wp_error( $result ) ) {
480
+ $message = $result->get_error_message();
481
+
482
+ imagify_maybe_redirect( $message );
483
+
484
+ wp_send_json_error( array(
485
+ 'row' => $message,
486
+ ) );
487
+ }
488
+
489
+ imagify_maybe_redirect();
490
+
491
+ // Return some HTML to the ajax call.
492
+ $list_table = new Imagify_Files_List_Table( array(
493
+ 'screen' => 'imagify-files',
494
+ ) );
495
+
496
+ wp_send_json_success( array(
497
+ 'folder' => $list_table->get_column( 'folder', $file ),
498
+ 'optimization' => $list_table->get_column( 'optimization', $file ),
499
+ 'status' => $list_table->get_column( 'status', $file ),
500
+ 'optimization_level' => $list_table->get_column( 'optimization_level', $file ),
501
+ 'actions' => $list_table->get_column( 'actions', $file ),
502
+ 'title' => $list_table->get_column( 'title', $file ),
503
+ ) );
504
+ }
505
+
506
+ /**
507
+ * Look for new files in custom folders.
508
+ *
509
+ * @since 1.7
510
+ * @access public
511
+ * @author Grégory Viguier
512
+ */
513
+ public function imagify_scan_custom_folders_callback() {
514
+ imagify_check_nonce( 'imagify_scan_custom_folders' );
515
+ imagify_check_user_capacity( 'optimize-file' );
516
+
517
+ $folder = (int) filter_input( INPUT_GET, 'folder', FILTER_SANITIZE_NUMBER_INT );
518
+
519
+ if ( $folder > 0 ) {
520
+ // A specific custom folder (selected or not).
521
+ $folders_db = Imagify_Folders_DB::get_instance();
522
+ $folders_key = $folders_db->get_primary_key();
523
+ $folder = $folders_db->get( $folder );
524
+
525
+ if ( ! $folder ) {
526
+ // This should not happen.
527
+ imagify_maybe_redirect( __( 'This folder is not in the database.', 'imagify' ) );
528
+ }
529
+
530
+ $folder['folder_path'] = Imagify_Files_Scan::remove_placeholder( $folder['path'] );
531
+
532
+ $folders = array(
533
+ $folder[ $folders_key ] => $folder,
534
+ );
535
+
536
+ Imagify_Custom_Folders::get_files_from_folders( $folders, array(
537
+ 'add_inactive_folder_files' => true,
538
+ ) );
539
+
540
+ imagify_maybe_redirect();
541
+ }
542
+
543
+ // All selected custom folders.
544
+ $folders = Imagify_Custom_Folders::get_folders( array(
545
+ 'active' => true,
546
+ ) );
547
+ Imagify_Custom_Folders::get_files_from_folders( $folders );
548
+
549
+ imagify_maybe_redirect();
550
+ }
551
+
552
+
553
  /** ----------------------------------------------------------------------------------------- */
554
  /** AUTOMATIC OPTIMIZATION ================================================================== */
555
  /** ----------------------------------------------------------------------------------------- */
640
  public function imagify_get_unoptimized_attachment_ids_callback() {
641
  global $wpdb;
642
 
643
+ imagify_check_nonce( 'imagify-bulk-upload' );
644
  imagify_check_user_capacity( 'bulk-optimize' );
645
+ $this->check_can_optimize();
 
 
 
 
 
 
 
 
 
646
 
647
  @set_time_limit( 0 );
648
 
649
  // Get (ordered) IDs.
650
+ $optimization_level = $this->get_optimization_level();
651
+
652
+ $mime_types = Imagify_DB::get_mime_types();
653
+ $statuses = Imagify_DB::get_post_statuses();
654
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
655
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
656
+ $ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
657
+ "
658
+ SELECT p.ID
659
+ FROM $wpdb->posts AS p
660
+ $nodata_join
661
  LEFT JOIN $wpdb->postmeta AS mt1
662
+ ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
663
+ LEFT JOIN $wpdb->postmeta AS mt2
664
+ ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_optimization_level' )
 
 
665
  WHERE
666
+ p.post_mime_type IN ( $mime_types )
667
  AND (
668
+ mt1.meta_value = 'error'
669
  OR
670
+ mt2.meta_value != '%d'
671
  OR
672
+ mt2.post_id IS NULL
673
  )
674
+ AND p.post_type = 'attachment'
675
+ AND p.post_status IN ( $statuses )
676
+ $nodata_where
677
+ GROUP BY p.ID
678
  ORDER BY
679
  CASE mt1.meta_value
680
  WHEN 'already_optimized' THEN 2
681
  ELSE 1
682
  END ASC,
683
+ p.ID DESC
684
  LIMIT 0, %d",
685
  $optimization_level,
686
  imagify_get_unoptimized_attachment_limit()
691
  $ids = array_filter( array_map( 'absint', $ids ) );
692
 
693
  if ( ! $ids ) {
694
+ wp_send_json_success( array() );
695
  }
696
 
697
  $results = Imagify_DB::get_metas( array(
705
  'statuses' => '_imagify_status',
706
  ), $ids );
707
 
 
 
 
708
  // First run.
709
  foreach ( $ids as $i => $id ) {
710
  $attachment_status = isset( $results['statuses'][ $id ] ) ? $results['statuses'][ $id ] : false;
736
  }
737
 
738
  if ( ! $ids ) {
739
+ wp_send_json_success( array() );
740
  }
741
 
742
  $ids = array_values( $ids );
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
  /** 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
  $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
 
784
  } // End foreach().
785
 
786
  if ( ! $data ) {
787
+ wp_send_json_success( array() );
788
+ }
789
+
790
+ wp_send_json_success( $data );
791
+ }
792
+
793
+ /**
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() {
800
+ imagify_check_nonce( 'imagify-bulk-upload' );
801
+ imagify_check_user_capacity( 'optimize-file' );
802
+
803
+ $this->check_can_optimize();
804
+
805
+ @set_time_limit( 0 );
806
+
807
+ $optimization_level = $this->get_optimization_level();
808
+
809
+ /**
810
+ * Get the folders from DB.
811
+ */
812
+ $folders = Imagify_Custom_Folders::get_folders( array(
813
+ 'active' => true,
814
+ ) );
815
+
816
+ if ( ! $folders ) {
817
+ wp_send_json_success( array() );
818
+ }
819
+
820
+ /**
821
+ * Triggered before getting file IDs.
822
+ *
823
+ * @since 1.7
824
+ * @author Grégory Viguier
825
+ *
826
+ * @param array $folders An array of folders data.
827
+ * @param int $optimization_level The optimization level that will be used for the optimization.
828
+ */
829
+ do_action( 'imagify_bulk_optimize_files_before_get_files', $folders, $optimization_level );
830
+
831
+ /**
832
+ * Get the files from DB, and from the folders.
833
+ */
834
+ $files = Imagify_Custom_Folders::get_files_from_folders( $folders, array(
835
+ 'optimization_level' => $optimization_level,
836
+ ) );
837
+
838
+ if ( ! $files ) {
839
+ wp_send_json_success( array() );
840
+ }
841
+
842
+ // We need to output file URLs.
843
+ foreach ( $files as $k => $file ) {
844
+ $files[ $k ] = Imagify_Files_Scan::remove_placeholder( $file['path'], 'url' );
845
+ }
846
+
847
+ wp_send_json_success( $files );
848
+ }
849
+
850
+ /**
851
+ * Get stats data for a specific folder type.
852
+ *
853
+ * @since 1.7
854
+ * @access public
855
+ * @see imagify_get_folder_type_data()
856
+ * @author Grégory Viguier
857
+ */
858
+ public function imagify_get_folder_type_data_callback() {
859
+ imagify_check_nonce( 'imagify-bulk-upload' );
860
+
861
+ $folder_type = filter_input( INPUT_GET, 'folder_type', FILTER_SANITIZE_STRING );
862
+
863
+ if ( 'library' === $folder_type ) {
864
+ imagify_check_user_capacity( 'bulk-optimize' );
865
+ } elseif ( 'custom-folders' === $folder_type ) {
866
+ imagify_check_user_capacity( 'optimize-file' );
867
+ } else {
868
+ imagify_die( __( 'Invalid request', 'imagify' ) );
869
+ }
870
+
871
+ /**
872
+ * Get the formated data.
873
+ */
874
+ $data = imagify_get_folder_type_data( $folder_type );
875
+
876
+ if ( ! $data ) {
877
+ imagify_die( __( 'Invalid request', 'imagify' ) );
878
  }
879
 
880
  wp_send_json_success( $data );
881
  }
882
 
883
+ /**
884
+ * Set the "bulk info" popup state as "seen".
885
+ *
886
+ * @since 1.7
887
+ * @access public
888
+ * @author Grégory Viguier
889
+ */
890
+ public function imagify_bulk_info_seen_callback() {
891
+ imagify_check_nonce( 'imagify-bulk-upload' );
892
+
893
+ $folder_type = filter_input( INPUT_GET, 'folder_type', FILTER_SANITIZE_STRING );
894
+
895
+ if ( 'library' === $folder_type ) {
896
+ imagify_check_user_capacity( 'bulk-optimize' );
897
+ } elseif ( 'custom-folders' === $folder_type ) {
898
+ imagify_check_user_capacity( 'optimize-file' );
899
+ } else {
900
+ imagify_die( __( 'Invalid request', 'imagify' ) );
901
+ }
902
+
903
+ set_transient( 'imagify_bulk_optimization_infos', 1, WEEK_IN_SECONDS );
904
+
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.
1025
  imagify_die( $response );
1026
  }
1027
 
1028
+ update_imagify_option( 'api_key', $_GET['api_key'] );
 
 
 
1029
 
1030
  wp_send_json_success();
1031
  }
1070
  $message .= '<p>' . sprintf(
1071
  /* translators: 1 is a data quota, 2 is a date. */
1072
  __( 'You have consumed all your credit for this month. You will have <strong>%1$s back on %2$s</strong>.', 'imagify' ),
1073
+ imagify_size_format( $user->quota * 1048576 ),
1074
  date_i18n( get_option( 'date_format' ), strtotime( $user->next_date_update ) )
1075
  ) . '</p>';
1076
  $message .= '<p class="center txt-center text-center"><a class="btn imagify-btn-ghost" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '" target="_blank">' . __( 'Upgrade My Subscription', 'imagify' ) . '</a></p>';
1190
  imagify_check_nonce( 'imagify_get_pricing_' . get_current_user_id(), 'imagifynonce' );
1191
  imagify_check_user_capacity();
1192
 
1193
+ $raw_total_size_in_library = imagify_calculate_total_size_images_library() + Imagify_Files_Stats::get_overall_original_size();
1194
+ $raw_average_per_month = imagify_calculate_average_size_images_per_month() + Imagify_Files_Stats::calculate_average_size_per_month();
1195
+
1196
+ Imagify_Data::get_instance()->set( array(
1197
+ 'total_size_images_library' => $raw_total_size_in_library,
1198
+ 'average_size_images_per_month' => $raw_average_per_month,
1199
+ ) );
1200
 
1201
  wp_send_json_success( array(
1202
+ 'total_library_size' => array( 'raw' => $raw_total_size_in_library, 'human' => imagify_size_format( $raw_total_size_in_library ) ),
1203
+ 'average_month_size' => array( 'raw' => $raw_average_per_month, 'human' => imagify_size_format( $raw_average_per_month ) ),
1204
  ) );
1205
  }
1206
 
1214
  imagify_check_nonce( 'update_estimate_sizes' );
1215
  imagify_check_user_capacity();
1216
 
1217
+ $raw_total_size_in_library = imagify_calculate_total_size_images_library() + Imagify_Files_Stats::get_overall_original_size();
1218
+ $raw_average_per_month = imagify_calculate_average_size_images_per_month() + Imagify_Files_Stats::calculate_average_size_per_month();
1219
 
1220
+ Imagify_Data::get_instance()->set( array(
1221
+ 'total_size_images_library' => $raw_total_size_in_library,
1222
+ 'average_size_images_per_month' => $raw_average_per_month,
1223
+ ) );
1224
 
1225
  die( 1 );
1226
  }
1227
+
1228
+ /**
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() {
1235
+ imagify_check_nonce( 'imagify_get_user_data' );
1236
+ imagify_check_user_capacity();
1237
+
1238
+ $user = imagify_cache_user();
1239
+
1240
+ if ( ! $user || ! $user->id ) {
1241
+ imagify_die( __( 'Couldn\'t get user data.', 'imagify' ) );
1242
+ }
1243
+
1244
+ // Remove useless sensitive data.
1245
+ unset( $user->email );
1246
+
1247
+ if ( ! $user->get_percent_unconsumed_quota ) {
1248
+ $user->best_plan_title = __( 'Oops, It\'s Over!', 'imagify' );
1249
+ } elseif ( $user->get_percent_unconsumed_quota <= 20 ) {
1250
+ $user->best_plan_title = __( 'Oops, It\'s almost over!', 'imagify' );
1251
+ } else {
1252
+ $user->best_plan_title = __( 'You\'re new to Imagify?', 'imagify' );
1253
+ }
1254
+
1255
+ wp_send_json_success( $user );
1256
+ }
1257
+
1258
+ /**
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
+
1270
+ if ( ! isset( $_POST['folder'] ) || '' === $_POST['folder'] ) {
1271
+ imagify_die( __( 'Invalid request', 'imagify' ) );
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
+
1285
+ if ( Imagify_Files_Scan::is_path_forbidden( $folder ) ) {
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.
1303
+ 'checkbox_value' => '{{ABSPATH}}/#///#' . esc_attr__( 'Site\'s root', 'imagify' ),
1304
+ 'checkbox_id' => 'ABSPATH',
1305
+ 'checkbox_selected' => isset( $selected['{{ABSPATH}}/'] ),
1306
+ 'label' => __( 'Site\'s root', 'imagify' ),
1307
+ 'no_button' => true,
1308
+ ) );
1309
+ }
1310
+
1311
+ $dir = new DirectoryIterator( $folder );
1312
+ $dir = new Imagify_Files_Iterator( $dir );
1313
+ $images = 0;
1314
+
1315
+ foreach ( new IteratorIterator( $dir ) as $file ) {
1316
+ if ( ! $file->isDir() ) {
1317
+ ++$images;
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
+
1335
+ if ( $images ) {
1336
+ /* translators: %s is a formatted number, dont use %d. */
1337
+ $output .= '<li class="imagify-number-of-images-in-folder"><em><span class="dashicons dashicons-images-alt"></span> ' . sprintf( _n( '%s image', '%s images', $images, 'imagify' ), number_format_i18n( $images ) ) . '</em></li>';
1338
+ }
1339
+
1340
+ if ( ! $output ) {
1341
+ $output .= '<li class="imagify-empty-folder"><em>' . __( 'Empty folder', 'imagify' ) . '</em></li>';
1342
+ }
1343
+
1344
+ wp_send_json_success( $output );
1345
+ }
1346
+
1347
+ /**
1348
+ * Store the "closed" status of the ads.
1349
+ *
1350
+ * @since 1.7
1351
+ * @access public
1352
+ * @author Grégory Viguier
1353
+ */
1354
+ public function imagify_dismiss_ad_callback() {
1355
+
1356
+ imagify_check_nonce( 'imagify-dismiss-ad' );
1357
+ imagify_check_user_capacity();
1358
+
1359
+ $notice = filter_input( INPUT_GET, 'ad', FILTER_SANITIZE_STRING );
1360
+
1361
+ if ( ! $notice ) {
1362
+ imagify_maybe_redirect();
1363
+ wp_send_json_error();
1364
+ }
1365
+
1366
+ $user_id = get_current_user_id();
1367
+ $notices = get_user_meta( $user_id, '_imagify_ignore_ads', true );
1368
+ $notices = $notices && is_array( $notices ) ? array_flip( $notices ) : array();
1369
+
1370
+ if ( isset( $notices[ $notice ] ) ) {
1371
+ imagify_maybe_redirect();
1372
+ wp_send_json_success();
1373
+ }
1374
+
1375
+ $notices = array_flip( $notices );
1376
+ $notices[] = $notice;
1377
+ $notices = array_filter( $notices );
1378
+ $notices = array_values( $notices );
1379
+
1380
+ update_user_meta( $user_id, '_imagify_ignore_ads', $notices );
1381
+
1382
+ imagify_maybe_redirect();
1383
+ wp_send_json_success();
1384
+ }
1385
+
1386
+
1387
+ /** ----------------------------------------------------------------------------------------- */
1388
+ /** VARIOUS HELPERS ========================================================================= */
1389
+ /** ----------------------------------------------------------------------------------------- */
1390
+
1391
+ /**
1392
+ * Get the submitted optimization level.
1393
+ *
1394
+ * @since 1.7
1395
+ * @access public
1396
+ * @author Grégory Viguier
1397
+ *
1398
+ * @return int
1399
+ */
1400
+ public function get_optimization_level() {
1401
+ $optimization_level = filter_input( INPUT_GET, 'optimization_level', FILTER_SANITIZE_NUMBER_INT );
1402
+
1403
+ if ( isset( $optimization_level ) && $optimization_level >= 0 && $optimization_level <= 2 ) {
1404
+ return (int) $optimization_level;
1405
+ }
1406
+
1407
+ return get_imagify_option( 'optimization_level' );
1408
+ }
1409
+
1410
+ /**
1411
+ * Check if the user has a valid account and has quota. Die on failure.
1412
+ *
1413
+ * @since 1.7
1414
+ * @access public
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
+ }
1428
+
1429
+ /**
1430
+ * Depending on the file ID sent, get the corresponding file object.
1431
+ *
1432
+ * @since 1.7
1433
+ * @access public
1434
+ * @author Grégory Viguier
1435
+ *
1436
+ * @param string $identifier The identifier to use in get_imagify_attachment().
1437
+ * @return object A Imagify_File_Attachment object.
1438
+ */
1439
+ protected function get_file_to_optimize( $identifier ) {
1440
+ $file_id = (int) filter_input( INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT );
1441
+
1442
+ if ( ! $file_id ) {
1443
+ imagify_die( __( 'Invalid request', 'imagify' ) );
1444
+ }
1445
+
1446
+ $file = get_imagify_attachment( 'File', $file_id, $identifier );
1447
+
1448
+ if ( ! $file->is_valid() ) {
1449
+ imagify_die( __( 'Invalid file ID', 'imagify' ) );
1450
+ }
1451
+
1452
+ return $file;
1453
+ }
1454
+
1455
+ /**
1456
+ * After a file optimization, restore, or whatever, redirect the user or output HTML for ajax.
1457
+ *
1458
+ * @since 1.7
1459
+ * @access public
1460
+ * @author Grégory Viguier
1461
+ *
1462
+ * @param bool|object $result True if the operation succeeded. A WP_Error object on failure.
1463
+ * @param object $file A Imagify_File_Attachment object.
1464
+ */
1465
+ protected function file_optimization_output( $result, $file ) {
1466
+ imagify_maybe_redirect( is_wp_error( $result ) ? $result : false );
1467
+
1468
+ // Return some HTML to the ajax call.
1469
+ $list_table = new Imagify_Files_List_Table( array(
1470
+ 'screen' => 'imagify-files',
1471
+ ) );
1472
+
1473
+ wp_send_json_success( array(
1474
+ 'optimization' => $list_table->get_column( 'optimization', $file ),
1475
+ 'status' => $list_table->get_column( 'status', $file ),
1476
+ 'optimization_level' => $list_table->get_column( 'optimization_level', $file ),
1477
+ 'actions' => $list_table->get_column( 'actions', $file ),
1478
+ 'title' => $list_table->get_column( 'title', $file ), // This one must remain after the "optimization" column, otherwize the data for the comparison tool won't be up-to-date.
1479
+ ) );
1480
+ }
1481
  }
inc/classes/class-imagify-assets.php CHANGED
@@ -14,7 +14,7 @@ class Imagify_Assets {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.0.1';
18
 
19
  /**
20
  * Prefix used for stylesheet handles.
@@ -185,6 +185,8 @@ class Imagify_Assets {
185
 
186
  $this->register_style( 'options', 'options', array( 'sweetalert', 'admin' ) );
187
 
 
 
188
  /**
189
  * 3rd Party Scripts.
190
  */
@@ -215,9 +217,11 @@ class Imagify_Assets {
215
 
216
  $this->register_script( 'async', 'imagify-gulp', array(), '2017-07-28' );
217
 
218
- $this->register_script( 'bulk', 'bulk', array( 'jquery', 'heartbeat', 'chart', 'sweetalert', 'async', 'admin' ) )->defer_localization( 'imagifyBulk' );
 
 
219
 
220
- $this->register_script( 'options', 'options', array( 'jquery', 'sweetalert', 'admin' ) )->defer_localization( 'imagifyOptions' );
221
  }
222
 
223
  /**
@@ -282,12 +286,19 @@ class Imagify_Assets {
282
  }
283
 
284
  /*
285
- * Loaded in settings page.
286
  */
287
  if ( imagify_is_screen( 'imagify-settings' ) ) {
288
  $this->enqueue_assets( array( 'sweetalert', 'notices', 'twentytwenty', 'pricing-modal', 'options' ) );
289
  }
290
 
 
 
 
 
 
 
 
291
  /**
292
  * Triggered after Imagify CSS and JS have been enqueued.
293
  *
@@ -341,7 +352,7 @@ class Imagify_Assets {
341
 
342
  $user = get_imagify_user();
343
 
344
- if ( empty( $user->is_intercom ) || false === $user->display_support ) {
345
  return;
346
  }
347
  ?>
@@ -833,8 +844,6 @@ class Imagify_Assets {
833
  return false;
834
  }
835
 
836
- $has_api_key = ( defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) || get_imagify_option( 'api_key' );
837
-
838
- return $has_api_key && is_admin_bar_showing() && imagify_current_user_can() && get_imagify_option( 'admin_bar_menu' );
839
  }
840
  }
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.0.2';
18
 
19
  /**
20
  * Prefix used for stylesheet handles.
185
 
186
  $this->register_style( 'options', 'options', array( 'sweetalert', 'admin' ) );
187
 
188
+ $this->register_style( 'files-list', 'files-list', array( 'admin' ) );
189
+
190
  /**
191
  * 3rd Party Scripts.
192
  */
217
 
218
  $this->register_script( 'async', 'imagify-gulp', array(), '2017-07-28' );
219
 
220
+ $this->register_script( 'bulk', 'bulk', array( 'jquery', 'heartbeat', 'underscore', 'chart', 'sweetalert', 'async', 'admin' ) )->defer_localization( 'imagifyBulk' );
221
+
222
+ $this->register_script( 'options', 'options', array( 'jquery', 'sweetalert', 'underscore', 'admin' ) )->defer_localization( 'imagifyOptions' );
223
 
224
+ $this->register_script( 'files-list', 'files-list', array( 'jquery', 'chart', 'admin' ) )->defer_localization( 'imagifyFiles' );
225
  }
226
 
227
  /**
286
  }
287
 
288
  /*
289
+ * Loaded in the settings page.
290
  */
291
  if ( imagify_is_screen( 'imagify-settings' ) ) {
292
  $this->enqueue_assets( array( 'sweetalert', 'notices', 'twentytwenty', 'pricing-modal', 'options' ) );
293
  }
294
 
295
+ /*
296
+ * Loaded in the files list page.
297
+ */
298
+ if ( imagify_is_screen( 'files-list' ) ) {
299
+ $this->enqueue_assets( array( 'files-list', 'twentytwenty' ) );
300
+ }
301
+
302
  /**
303
  * Triggered after Imagify CSS and JS have been enqueued.
304
  *
352
 
353
  $user = get_imagify_user();
354
 
355
+ if ( empty( $user->is_intercom ) || empty( $user->display_support ) ) {
356
  return;
357
  }
358
  ?>
844
  return false;
845
  }
846
 
847
+ return get_imagify_option( 'api_key' ) && is_admin_bar_showing() && imagify_current_user_can() && get_imagify_option( 'admin_bar_menu' );
 
 
848
  }
849
  }
inc/classes/class-imagify-attachment.php CHANGED
@@ -101,6 +101,24 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
101
  return wp_get_attachment_url( $this->id );
102
  }
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  /**
105
  * Update the metadata size of the attachment.
106
  *
@@ -111,7 +129,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
111
  */
112
  public function update_metadata_size() {
113
  // Check if the attachment extension is allowed.
114
- if ( ! $this->is_mime_type_supported() ) {
115
  return false;
116
  }
117
 
@@ -132,18 +150,18 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
132
  /**
133
  * Fills statistics data with values from $data array.
134
  *
135
- * @since 1.0
136
- * @since 1.6.5 Not static anymore.
137
- * @since 1.6.6 Removed the attachment ID parameter.
 
138
  * @access public
139
  *
140
  * @param array $data The statistics data.
141
  * @param object $response The API response.
142
- * @param int $url The attachment URL.
143
  * @param string $size The attachment size key.
144
  * @return bool|array False if the original size has an error or an array contains the data for other result.
145
  */
146
- public function fill_data( $data, $response, $url, $size = 'full' ) {
147
  $data = is_array( $data ) ? $data : array();
148
  $data['sizes'] = ! empty( $data['sizes'] ) && is_array( $data['sizes'] ) ? $data['sizes'] : array();
149
 
@@ -185,7 +203,6 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
185
 
186
  $data['sizes'][ $size ] = array(
187
  'success' => true,
188
- 'file_url' => $url,
189
  'original_size' => $response->original_size,
190
  'optimized_size' => $response->new_size,
191
  'percent' => $response->percent,
@@ -318,11 +335,11 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
318
  */
319
  public function optimize( $optimization_level = null, $metadata = array() ) {
320
  // Check if the attachment extension is allowed.
321
- if ( ! $this->is_mime_type_supported() ) {
322
  return;
323
  }
324
 
325
- $optimization_level = is_null( $optimization_level ) ? (int) get_imagify_option( 'optimization_level', 1 ) : (int) $optimization_level;
326
  $metadata = $metadata ? $metadata : wp_get_attachment_metadata( $this->id );
327
  $sizes = isset( $metadata['sizes'] ) ? (array) $metadata['sizes'] : array();
328
 
@@ -387,25 +404,25 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
387
  'original_size' => $attachment_original_size,
388
  ) );
389
 
390
- $data = $this->fill_data( null, $response, $attachment_url );
391
 
392
  // Save the optimization level.
393
  update_post_meta( $this->id, '_imagify_optimization_level', $optimization_level );
394
 
395
- if ( ! $data ) {
396
- delete_transient( 'imagify-async-in-progress-' . $this->id );
397
- return;
398
- }
399
-
400
  // If we resized the original with success, we have to update the attachment metadata.
401
  // If not, WordPress keeps the old attachment size.
402
- if ( $do_resize && $resized ) {
403
  $this->update_metadata_size();
404
  }
405
 
 
 
 
 
 
406
  // Optimize all thumbnails.
407
  if ( $sizes ) {
408
- $disallowed_sizes = get_imagify_option( 'disallowed-sizes', array() );
409
  $is_active_for_network = imagify_is_active_for_network();
410
  $attachment_path_dirname = trailingslashit( dirname( $attachment_path ) );
411
  $attachment_url_dirname = trailingslashit( dirname( $attachment_url ) );
@@ -430,7 +447,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
430
  'context' => 'wp',
431
  ) );
432
 
433
- $data = $this->fill_data( $data, $response, $thumbnail_url, $size_key );
434
 
435
  /**
436
  * Filter the optimization data of a specific thumbnail.
@@ -484,11 +501,11 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
484
  */
485
  public function optimize_missing_thumbnails( $optimization_level = null ) {
486
  // Check if the attachment extension is allowed.
487
- if ( ! $this->is_mime_type_supported() ) {
488
  return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
489
  }
490
 
491
- $optimization_level = is_null( $optimization_level ) ? (int) get_imagify_option( 'optimization_level', 1 ) : (int) $optimization_level;
492
  $missing_sizes = $this->get_unoptimized_sizes();
493
 
494
  if ( ! $missing_sizes ) {
@@ -550,7 +567,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
550
  'context' => 'wp',
551
  ) );
552
 
553
- $imagify_data = $this->fill_data( $imagify_data, $response, $thumbnail_url, $size_name );
554
 
555
  /** This filter is documented in inc/classes/class-imagify-attachment.php. */
556
  $imagify_data = apply_filters( 'imagify_fill_thumbnail_data', $imagify_data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_name, $optimization_level );
@@ -594,7 +611,7 @@ class Imagify_Attachment extends Imagify_Abstract_Attachment {
594
  */
595
  public function restore() {
596
  // Check if the attachment extension is allowed.
597
- if ( ! $this->is_mime_type_supported() ) {
598
  return;
599
  }
600
 
101
  return wp_get_attachment_url( $this->id );
102
  }
103
 
104
+ /**
105
+ * Get width and height of the original image.
106
+ *
107
+ * @since 1.7
108
+ * @author Grégory Viguier
109
+ * @access public
110
+ *
111
+ * @return array
112
+ */
113
+ public function get_dimensions() {
114
+ $values = wp_get_attachment_image_src( $this->id, 'full' );
115
+
116
+ return array(
117
+ 'width' => $values[1],
118
+ 'height' => $values[2],
119
+ );
120
+ }
121
+
122
  /**
123
  * Update the metadata size of the attachment.
124
  *
129
  */
130
  public function update_metadata_size() {
131
  // Check if the attachment extension is allowed.
132
+ if ( ! $this->is_extension_supported() ) {
133
  return false;
134
  }
135
 
150
  /**
151
  * Fills statistics data with values from $data array.
152
  *
153
+ * @since 1.0
154
+ * @since 1.6.5 Not static anymore.
155
+ * @since 1.6.6 Removed the attachment ID parameter.
156
+ * @since 1.7 Removed the image URL parameter.
157
  * @access public
158
  *
159
  * @param array $data The statistics data.
160
  * @param object $response The API response.
 
161
  * @param string $size The attachment size key.
162
  * @return bool|array False if the original size has an error or an array contains the data for other result.
163
  */
164
+ public function fill_data( $data, $response, $size = 'full' ) {
165
  $data = is_array( $data ) ? $data : array();
166
  $data['sizes'] = ! empty( $data['sizes'] ) && is_array( $data['sizes'] ) ? $data['sizes'] : array();
167
 
203
 
204
  $data['sizes'][ $size ] = array(
205
  'success' => true,
 
206
  'original_size' => $response->original_size,
207
  'optimized_size' => $response->new_size,
208
  'percent' => $response->percent,
335
  */
336
  public function optimize( $optimization_level = null, $metadata = array() ) {
337
  // Check if the attachment extension is allowed.
338
+ if ( ! $this->is_extension_supported() ) {
339
  return;
340
  }
341
 
342
+ $optimization_level = isset( $optimization_level ) ? (int) $optimization_level : get_imagify_option( 'optimization_level' );
343
  $metadata = $metadata ? $metadata : wp_get_attachment_metadata( $this->id );
344
  $sizes = isset( $metadata['sizes'] ) ? (array) $metadata['sizes'] : array();
345
 
404
  'original_size' => $attachment_original_size,
405
  ) );
406
 
407
+ $data = $this->fill_data( null, $response );
408
 
409
  // Save the optimization level.
410
  update_post_meta( $this->id, '_imagify_optimization_level', $optimization_level );
411
 
 
 
 
 
 
412
  // If we resized the original with success, we have to update the attachment metadata.
413
  // If not, WordPress keeps the old attachment size.
414
+ if ( $resized ) {
415
  $this->update_metadata_size();
416
  }
417
 
418
+ if ( ! $data ) {
419
+ delete_transient( 'imagify-async-in-progress-' . $this->id );
420
+ return;
421
+ }
422
+
423
  // Optimize all thumbnails.
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 ) );
447
  'context' => 'wp',
448
  ) );
449
 
450
+ $data = $this->fill_data( $data, $response, $size_key );
451
 
452
  /**
453
  * Filter the optimization data of a specific thumbnail.
501
  */
502
  public function optimize_missing_thumbnails( $optimization_level = null ) {
503
  // Check if the attachment extension is allowed.
504
+ if ( ! $this->is_extension_supported() ) {
505
  return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
506
  }
507
 
508
+ $optimization_level = isset( $optimization_level ) ? (int) $optimization_level : get_imagify_option( 'optimization_level' );
509
  $missing_sizes = $this->get_unoptimized_sizes();
510
 
511
  if ( ! $missing_sizes ) {
567
  'context' => 'wp',
568
  ) );
569
 
570
+ $imagify_data = $this->fill_data( $imagify_data, $response, $size_name );
571
 
572
  /** This filter is documented in inc/classes/class-imagify-attachment.php. */
573
  $imagify_data = apply_filters( 'imagify_fill_thumbnail_data', $imagify_data, $response, $this->id, $thumbnail_path, $thumbnail_url, $size_name, $optimization_level );
611
  */
612
  public function restore() {
613
  // Check if the attachment extension is allowed.
614
+ if ( ! $this->is_extension_supported() ) {
615
  return;
616
  }
617
 
inc/classes/class-imagify-cron-library-size.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that handles the cron that calculate and cache the library size.
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Cron_Library_Size extends Imagify_Abstract_Cron {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7
17
+ */
18
+ const VERSION = '1.0';
19
+
20
+ /**
21
+ * Cron name.
22
+ *
23
+ * @var string
24
+ * @since 1.7
25
+ * @access protected
26
+ */
27
+ protected $event_name = 'imagify_update_library_size_calculations_event';
28
+
29
+ /**
30
+ * Cron recurrence.
31
+ *
32
+ * @var string
33
+ * @since 1.7
34
+ * @access protected
35
+ */
36
+ protected $event_recurrence = 'weekly';
37
+
38
+ /**
39
+ * Cron time.
40
+ *
41
+ * @var string
42
+ * @since 1.7
43
+ * @access protected
44
+ */
45
+ protected $event_time = '04:00';
46
+
47
+ /**
48
+ * The single instance of the class.
49
+ *
50
+ * @var object
51
+ * @since 1.7
52
+ * @access protected
53
+ */
54
+ protected static $_instance;
55
+
56
+ /**
57
+ * Get the main Instance.
58
+ *
59
+ * @since 1.7
60
+ * @access public
61
+ * @author Grégory Viguier
62
+ *
63
+ * @return object Main instance.
64
+ */
65
+ public static function get_instance() {
66
+ if ( ! isset( self::$_instance ) ) {
67
+ self::$_instance = new self();
68
+ }
69
+
70
+ return self::$_instance;
71
+ }
72
+
73
+
74
+ /** ----------------------------------------------------------------------------------------- */
75
+ /** HOOKS =================================================================================== */
76
+ /** ----------------------------------------------------------------------------------------- */
77
+
78
+ /**
79
+ * The event action.
80
+ *
81
+ * @since 1.7
82
+ * @access public
83
+ * @author Grégory Viguier
84
+ */
85
+ public function do_event() {
86
+ imagify_do_async_job( array(
87
+ 'action' => 'imagify_update_estimate_sizes',
88
+ '_ajax_nonce' => wp_create_nonce( 'update_estimate_sizes' ),
89
+ ) );
90
+ }
91
+ }
inc/classes/class-imagify-cron-rating.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that handles the plugin rating cron.
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Cron_Rating extends Imagify_Abstract_Cron {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7
17
+ */
18
+ const VERSION = '1.0';
19
+
20
+ /**
21
+ * Cron name.
22
+ *
23
+ * @var string
24
+ * @since 1.7
25
+ * @access protected
26
+ */
27
+ protected $event_name = 'imagify_rating_event';
28
+
29
+ /**
30
+ * Cron recurrence.
31
+ *
32
+ * @var string
33
+ * @since 1.7
34
+ * @access protected
35
+ */
36
+ protected $event_recurrence = 'daily';
37
+
38
+ /**
39
+ * Cron time.
40
+ *
41
+ * @var string
42
+ * @since 1.7
43
+ * @access protected
44
+ */
45
+ protected $event_time = '15:00';
46
+
47
+ /**
48
+ * The single instance of the class.
49
+ *
50
+ * @var object
51
+ * @since 1.7
52
+ * @access protected
53
+ */
54
+ protected static $_instance;
55
+
56
+ /**
57
+ * Get the main Instance.
58
+ *
59
+ * @since 1.7
60
+ * @access public
61
+ * @author Grégory Viguier
62
+ *
63
+ * @return object Main instance.
64
+ */
65
+ public static function get_instance() {
66
+ if ( ! isset( self::$_instance ) ) {
67
+ self::$_instance = new self();
68
+ }
69
+
70
+ return self::$_instance;
71
+ }
72
+
73
+
74
+ /** ----------------------------------------------------------------------------------------- */
75
+ /** HOOKS =================================================================================== */
76
+ /** ----------------------------------------------------------------------------------------- */
77
+
78
+ /**
79
+ * Initiate the event.
80
+ *
81
+ * @since 1.7
82
+ * @access public
83
+ * @author Grégory Viguier
84
+ */
85
+ public function schedule_event() {
86
+ if ( ! wp_next_scheduled( $this->get_event_name() ) && ! get_site_transient( 'do_imagify_rating_cron' ) ) {
87
+ wp_schedule_event( $this->get_event_timestamp(), $this->get_event_recurrence(), $this->get_event_name() );
88
+ }
89
+ }
90
+
91
+ /**
92
+ * The event action.
93
+ *
94
+ * @since 1.7
95
+ * @access public
96
+ * @author Grégory Viguier
97
+ */
98
+ public function do_event() {
99
+ // Stop the process if the plugin isn't installed for 3 days.
100
+ if ( get_site_transient( 'imagify_seen_rating_notice' ) ) {
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 ) {
112
+ set_site_transient( 'imagify_user_images_count', $user->image_count );
113
+ }
114
+ }
115
+ }
inc/classes/class-imagify-cron-sync-files.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that scans the custom folders to keep files in sync in the database.
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Cron_Sync_Files extends Imagify_Abstract_Cron {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7
17
+ */
18
+ const VERSION = '1.0';
19
+
20
+ /**
21
+ * Cron name.
22
+ *
23
+ * @var string
24
+ * @since 1.7
25
+ * @access protected
26
+ */
27
+ protected $event_name = 'imagify_sync_files';
28
+
29
+ /**
30
+ * Cron recurrence.
31
+ *
32
+ * @var string
33
+ * @since 1.7
34
+ * @access protected
35
+ */
36
+ protected $event_recurrence = 'daily';
37
+
38
+ /**
39
+ * Cron time.
40
+ *
41
+ * @var string
42
+ * @since 1.7
43
+ * @access protected
44
+ */
45
+ protected $event_time = '02:00';
46
+
47
+ /**
48
+ * The single instance of the class.
49
+ *
50
+ * @var object
51
+ * @since 1.7
52
+ * @access protected
53
+ */
54
+ protected static $_instance;
55
+
56
+ /**
57
+ * Get the main Instance.
58
+ *
59
+ * @since 1.7
60
+ * @access public
61
+ * @author Grégory Viguier
62
+ *
63
+ * @return object Main instance.
64
+ */
65
+ public static function get_instance() {
66
+ if ( ! isset( self::$_instance ) ) {
67
+ self::$_instance = new self();
68
+ }
69
+
70
+ return self::$_instance;
71
+ }
72
+
73
+
74
+ /** ----------------------------------------------------------------------------------------- */
75
+ /** HOOKS =================================================================================== */
76
+ /** ----------------------------------------------------------------------------------------- */
77
+
78
+ /**
79
+ * The event action.
80
+ *
81
+ * @since 1.7
82
+ * @access public
83
+ * @author Grégory Viguier
84
+ */
85
+ public function do_event() {
86
+ global $wpdb;
87
+
88
+ $folders_db = Imagify_Folders_DB::get_instance();
89
+ $files_db = Imagify_Files_DB::get_instance();
90
+
91
+ if ( ! $folders_db->can_operate() || ! $files_db->can_operate() ) {
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
+
105
+ @set_time_limit( 0 );
106
+
107
+ /**
108
+ * Get the folders from DB.
109
+ */
110
+ $folders = Imagify_Custom_Folders::get_folders();
111
+
112
+ if ( ! $folders ) {
113
+ return;
114
+ }
115
+
116
+ Imagify_Custom_Folders::synchronize_files_from_folders( $folders );
117
+ }
118
+ }
inc/classes/class-imagify-custom-folders.php ADDED
@@ -0,0 +1,1227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that regroups things about "custom folders".
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Custom_Folders {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ */
17
+ const VERSION = '1.0';
18
+
19
+
20
+ /** ----------------------------------------------------------------------------------------- */
21
+ /** BACKUP FOLDER =========================================================================== */
22
+ /** ----------------------------------------------------------------------------------------- */
23
+
24
+ /**
25
+ * Get the path to the backups directory (custom folders).
26
+ *
27
+ * @since 1.7
28
+ * @access public
29
+ * @author Grégory Viguier
30
+ *
31
+ * @return string Path to the backups directory.
32
+ */
33
+ public static function get_backup_dir_path() {
34
+ static $backup_dir;
35
+
36
+ if ( isset( $backup_dir ) ) {
37
+ return $backup_dir;
38
+ }
39
+
40
+ $backup_dir = imagify_get_abspath() . 'imagify-backup/';
41
+
42
+ /**
43
+ * Filter the backup directory path (custom folders).
44
+ *
45
+ * @since 1.7
46
+ * @author Grégory Viguier
47
+ *
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
+ }
55
+
56
+ /**
57
+ * Tell if the folder containing the backups is writable (custom folders).
58
+ *
59
+ * @since 1.7
60
+ * @access public
61
+ * @author Grégory Viguier
62
+ *
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
+ /**
77
+ * Get the backup path of a specific file (custom folders).
78
+ *
79
+ * @since 1.7
80
+ * @access public
81
+ * @author Grégory Viguier
82
+ *
83
+ * @param string $file_path The file path.
84
+ * @return string|bool The backup path. False on failure.
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 ) {
92
+ return false;
93
+ }
94
+
95
+ return str_replace( $abspath, $backup_dir, $file_path );
96
+ }
97
+
98
+
99
+ /** ----------------------------------------------------------------------------------------- */
100
+ /** SINGLE FILE ============================================================================= */
101
+ /** ----------------------------------------------------------------------------------------- */
102
+
103
+ /**
104
+ * Insert a file into the DB.
105
+ *
106
+ * @since 1.7
107
+ * @access public
108
+ * @author Grégory Viguier
109
+ *
110
+ * @param array $args An array of arguments to pass to Imagify_Files_DB::insert(). Required values are 'folder_id' and ( 'path' or 'file_path').
111
+ * @return int The file ID on success. 0 on failure.
112
+ */
113
+ public static function insert_file( $args = array() ) {
114
+ if ( empty( $args['folder_id'] ) ) {
115
+ return 0;
116
+ }
117
+
118
+ if ( empty( $args['path'] ) ) {
119
+ if ( empty( $args['file_path'] ) ) {
120
+ return 0;
121
+ }
122
+
123
+ $args['path'] = Imagify_Files_Scan::add_placeholder( $args['file_path'] );
124
+ }
125
+
126
+ if ( empty( $args['file_path'] ) ) {
127
+ $args['file_path'] = Imagify_Files_Scan::remove_placeholder( $args['path'] );
128
+ }
129
+
130
+ $filesystem = imagify_get_filesystem();
131
+
132
+ if ( ! $filesystem->is_readable( $args['file_path'] ) ) {
133
+ return 0;
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'] ) ) {
151
+ $args['hash'] = md5_file( $args['file_path'] );
152
+ }
153
+
154
+ if ( empty( $args['original_size'] ) ) {
155
+ $args['original_size'] = (int) $filesystem->size( $args['file_path'] );
156
+ }
157
+
158
+ $files_db = Imagify_Files_DB::get_instance();
159
+ $primary_key = $files_db->get_primary_key();
160
+ unset( $args[ $primary_key ] );
161
+
162
+ return $files_db->insert( $args );
163
+ }
164
+
165
+ /**
166
+ * Delete a custom file.
167
+ *
168
+ * @since 1.7
169
+ * @access public
170
+ * @author Grégory Viguier
171
+ *
172
+ * @param array $args An array of arguments.
173
+ * At least: 'file_id'. At best (less queries): 'file_id', 'file_path' (or 'path' for the placeholder), and 'backup_path'.
174
+ */
175
+ public static function delete_file( $args = array() ) {
176
+ $args = array_merge( array(
177
+ 'file_id' => 0,
178
+ 'file_path' => '',
179
+ 'path' => '',
180
+ 'backup_path' => '',
181
+ 'file' => false,
182
+ ), $args );
183
+
184
+ $filesystem = imagify_get_filesystem();
185
+ $file = $args['file'] && is_a( $args['file'], 'Imagify_File_Attachment' ) ? $args['file'] : false;
186
+
187
+ // The file.
188
+ if ( ! $args['file_path'] && $args['path'] ) {
189
+ $args['file_path'] = Imagify_Files_Scan::remove_placeholder( $args['path'] );
190
+ }
191
+
192
+ if ( ! $args['file_path'] && $args['file_id'] ) {
193
+ $file = $file ? $file : get_imagify_attachment( 'File', $args['file_id'], 'delete_custom_file' );
194
+ $args['file_path'] = $file->get_original_path();
195
+ }
196
+
197
+ if ( $args['file_path'] && $filesystem->exists( $args['file_path'] ) ) {
198
+ $filesystem->delete( $args['file_path'] );
199
+ }
200
+
201
+ // The backup file.
202
+ if ( ! $args['backup_path'] && $args['file_path'] ) {
203
+ $args['backup_path'] = self::get_file_backup_path( $args['file_path'] );
204
+ }
205
+
206
+ if ( ! $args['backup_path'] && $args['file_id'] ) {
207
+ $file = $file ? $file : get_imagify_attachment( 'File', $args['file_id'], 'delete_custom_file' );
208
+ $args['backup_path'] = $file->get_raw_backup_path();
209
+ }
210
+
211
+ if ( $args['backup_path'] && $filesystem->exists( $args['backup_path'] ) ) {
212
+ $filesystem->delete( $args['backup_path'] );
213
+ }
214
+
215
+ // In the database.
216
+ if ( $file ) {
217
+ $file->delete_row();
218
+ } else {
219
+ Imagify_Files_DB::get_instance()->delete( $args['file_id'] );
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Check if a file has been modified, and update the database accordingly.
225
+ *
226
+ * @since 1.7
227
+ * @access public
228
+ * @author Grégory Viguier
229
+ *
230
+ * @param object $file An Imagify_File_Attachment object.
231
+ * @param bool $is_folder_active Tell if the folder is active.
232
+ * @return int|bool|object The file ID if modified. False if not modified. A WP_Error object if the entry has been removed from the database.
233
+ * The entry is removed from the database if:
234
+ * - The file doesn't exist anymore.
235
+ * - Or if its folder is not active and: the file has been modified, or the file is not optimized by Imagify, or the file is orphan (its folder is not in the database anymore).
236
+ */
237
+ public static function refresh_file( $file, $is_folder_active = null ) {
238
+ global $wpdb;
239
+
240
+ $file_path = $file->get_original_path();
241
+ $backup_path = $file->get_backup_path();
242
+ $filesystem = imagify_get_filesystem();
243
+ $modified = false;
244
+
245
+ if ( ! $file_path || ! $filesystem->exists( $file_path ) ) {
246
+ /**
247
+ * The file doesn't exist anymore.
248
+ */
249
+ if ( $backup_path ) {
250
+ // Delete the backup file.
251
+ $filesystem->delete( $backup_path );
252
+ }
253
+
254
+ // Get the folder ID before removing the row.
255
+ $folder_id = $file->get_row();
256
+ $folder_id = $folder_id['folder_id'];
257
+
258
+ // Remove the entry from the database.
259
+ $file->delete_row();
260
+
261
+ // Remove the corresponding folder if inactive and have no files left.
262
+ self::remove_empty_inactive_folders( $folder_id );
263
+
264
+ return new WP_Error( 'no-file', __( 'The file was missing or its path could not be retrieved from the database. The entry has been deleted from the database.', 'imagify' ) );
265
+ }
266
+
267
+ /**
268
+ * The file still exists.
269
+ */
270
+ $old_data = $file->get_row();
271
+ $new_data = array();
272
+
273
+ // Folder ID.
274
+ if ( $old_data['folder_id'] ) {
275
+ $folder = wp_cache_get( 'custom_folder_' . $old_data['folder_id'], 'imagify' );
276
+
277
+ if ( false === $folder ) {
278
+ // The folder is not in the cache.
279
+ $folder = Imagify_Folders_DB::get_instance()->get( $old_data['folder_id'] );
280
+ $folder = $folder ? $folder : 0;
281
+ }
282
+
283
+ if ( ! $folder ) {
284
+ // The folder is not in the database anymore.
285
+ $old_data['folder_id'] = 0;
286
+ $new_data['folder_id'] = 0;
287
+ }
288
+ } else {
289
+ $folder = 0;
290
+ }
291
+
292
+ // Hash + modified.
293
+ $current_hash = md5_file( $file_path );
294
+
295
+ if ( ! $old_data['hash'] ) {
296
+ $new_data['modified'] = 0;
297
+ } else {
298
+ $new_data['modified'] = (int) ! hash_equals( $old_data['hash'], $current_hash );
299
+ }
300
+
301
+ // The file is modified or is not optimized.
302
+ if ( $new_data['modified'] || ! $file->is_optimized() ) {
303
+ if ( ! isset( $is_folder_active ) ) {
304
+ $is_folder_active = $folder && $folder['active'];
305
+ }
306
+
307
+ // Its folder is not active: remove the entry from the database and delete the backup.
308
+ if ( ! $is_folder_active ) {
309
+ if ( $backup_path ) {
310
+ // Delete the backup file.
311
+ $filesystem->delete( $backup_path );
312
+ }
313
+
314
+ // Remove the entry from the database.
315
+ $file->delete_row();
316
+
317
+ // Remove the corresponding folder if inactive and have no files left.
318
+ if ( $old_data['folder_id'] ) {
319
+ self::remove_empty_inactive_folders( $old_data['folder_id'] );
320
+ }
321
+
322
+ return new WP_Error( 'folder-not-active', __( 'The file has been modified or was not optimized: its folder not being selected in the settings, the entry has been deleted from the database.', 'imagify' ) );
323
+ }
324
+ }
325
+
326
+ $new_data['hash'] = $current_hash;
327
+
328
+ // The file is modified.
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,
347
+ 'optimization_level' => null,
348
+ 'status' => null,
349
+ 'error' => null,
350
+ ) );
351
+
352
+ if ( $backup_path ) {
353
+ // Delete the backup of the previous file.
354
+ $filesystem->delete( $backup_path );
355
+ }
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
+ }
375
+
376
+ // Save the new data.
377
+ $old_data = array_intersect_key( $old_data, $new_data );
378
+ ksort( $old_data );
379
+ ksort( $new_data );
380
+
381
+ if ( $old_data !== $new_data ) {
382
+ $file->update_row( $new_data );
383
+ }
384
+
385
+ return $modified ? $file->get_id() : false;
386
+ }
387
+
388
+
389
+ /** ----------------------------------------------------------------------------------------- */
390
+ /** FOLDERS AND FILES ======================================================================= */
391
+ /** ----------------------------------------------------------------------------------------- */
392
+
393
+ /**
394
+ * Get folders from the DB.
395
+ *
396
+ * @since 1.7
397
+ * @access public
398
+ * @author Grégory Viguier
399
+ *
400
+ * @param array $args A list of arguments to tell more precisely what to fetch:
401
+ * - bool $active True to fetch only "active" folders (checked in the settings). False to fetch only folders that are not "active".
402
+ * @return array An array of arrays containing the following keys:
403
+ * - int $folder_id The folder ID.
404
+ * - string $path The folder path, with placeholder.
405
+ * - int $active 1 if the folder should be optimized. 0 otherwize.
406
+ * - string $folder_path The real absolute folder path.
407
+ * Example:
408
+ * Array(
409
+ * [7] => Array(
410
+ * [folder_id] => 7
411
+ * [path] => {{ABSPATH}}/custom-path/
412
+ * [active] => 1
413
+ * [folder_path] => /absolute/path/to/custom-path/
414
+ * )
415
+ * [13] => Array(
416
+ * [folder_id] => 13
417
+ * [path] => {{CONTENT}}/another-custom-path/
418
+ * [active] => 1
419
+ * [folder_path] => /absolute/path/to/wp-content/another-custom-path/
420
+ * )
421
+ * )
422
+ */
423
+ public static function get_folders( $args = array() ) {
424
+ global $wpdb;
425
+
426
+ $folders_db = Imagify_Folders_DB::get_instance();
427
+ $folders_table = $folders_db->get_table_name();
428
+ $primary_key = $folders_db->get_primary_key();
429
+ $where_active = '';
430
+
431
+ if ( isset( $args['active'] ) ) {
432
+ if ( $args['active'] ) {
433
+ $args['active'] = true;
434
+ $where_active = 'WHERE active = 1';
435
+ } else {
436
+ $args['active'] = false;
437
+ $where_active = 'WHERE active = 0';
438
+ }
439
+ }
440
+
441
+ // Get the folders from the DB.
442
+ $results = $wpdb->get_results( "SELECT * FROM $folders_table $where_active;", ARRAY_A ); // WPCS: unprepared SQL ok.
443
+
444
+ if ( ! $results || ! is_array( $results ) ) {
445
+ return array();
446
+ }
447
+
448
+ // Cast results, add absolute paths.
449
+ $folders = array();
450
+
451
+ foreach ( $results as $row_fields ) {
452
+ // Cast the row.
453
+ $row_fields = $folders_db->cast_row( $row_fields );
454
+
455
+ // Add the absolute path.
456
+ $row_fields['folder_path'] = Imagify_Files_Scan::remove_placeholder( $row_fields['path'] );
457
+
458
+ // Add the row to the list.
459
+ $folders[ $row_fields[ $primary_key ] ] = $row_fields;
460
+ }
461
+
462
+ return $folders;
463
+ }
464
+
465
+ /**
466
+ * Get files belonging to the given folders.
467
+ * Files are scanned from the folders, then:
468
+ * - If a file doesn't exist in the DB, it is added (maybe, depending on arguments provided).
469
+ * - If a file is in the DB, but with a wrong folder_id, it is fixed.
470
+ * - If a file doesn't exist, it is removed from the database and its backup is deleted.
471
+ *
472
+ * @since 1.7
473
+ * @access public
474
+ * @see Imagify_Custom_Folders::get_folders()
475
+ * @author Grégory Viguier
476
+ *
477
+ * @param array $folders An array of arrays containing at least the keys 'folder_path' and 'active'. See Imagify_Custom_Folders::get_folders() for the format.
478
+ * @param array $args A list of arguments to tell more precisely what to fetch:
479
+ * - int $optimization_level If set with an integer, only files that needs to be optimized to this level will be returned (the status is also checked).
480
+ * - bool $return_only_old_files True to return only files that have not been newly inserted.
481
+ * - bool $add_inactive_folder_files When true: if a file is not in the database and its folder is not "active", it is added to the DB. Default false: new files are not added to the database if the folder is not active.
482
+ * @return array A list of files in the following format:
483
+ * Array(
484
+ * [_2] => Array(
485
+ * [file_id] => 2
486
+ * [folder_id] => 7
487
+ * [path] => {{ABSPATH}}/custom-path/image-1.jpg
488
+ * [optimization_level] => null
489
+ * [status] => null
490
+ * [file_path] => /absolute/path/to/custom-path/image-1.jpg
491
+ * ),
492
+ * [_3] => Array(
493
+ * [file_id] => 3
494
+ * [folder_id] => 7
495
+ * [path] => {{ABSPATH}}/custom-path/image-2.jpg
496
+ * [optimization_level] => 2
497
+ * [status] => success
498
+ * [file_path] => /absolute/path/to/custom-path/image-2.jpg
499
+ * ),
500
+ * [_6] => Array(
501
+ * [file_id] => 6
502
+ * [folder_id] => 13
503
+ * [path] => {{CONTENT}}/another-custom-path/image-1.jpg
504
+ * [optimization_level] => 0
505
+ * [status] => error
506
+ * [file_path] => /absolute/path/to/wp-content/another-custom-path/image-1.jpg
507
+ * ),
508
+ * )
509
+ * The fields 'optimization_level' and 'status' are set only if the argument 'optimization_level' was set.
510
+ */
511
+ public static function get_files_from_folders( $folders, $args = array() ) {
512
+ global $wpdb;
513
+
514
+ if ( ! $folders ) {
515
+ return array();
516
+ }
517
+
518
+ $filesystem = imagify_get_filesystem();
519
+ $files_db = Imagify_Files_DB::get_instance();
520
+ $files_table = $files_db->get_table_name();
521
+ $files_key = $files_db->get_primary_key();
522
+ $files_key_esc = esc_sql( $files_key );
523
+
524
+ $optimization = isset( $args['optimization_level'] ) && is_numeric( $args['optimization_level'] );
525
+ $no_new_files = ! empty( $args['return_only_old_files'] );
526
+ $add_inactive_folder_files = ! empty( $args['add_inactive_folder_files'] );
527
+
528
+ /**
529
+ * Scan folders for files. $files_from_scan will be in the following format:
530
+ * Array(
531
+ * [7] => Array(
532
+ * [/absolute/path/to/custom-path/image-1.jpg] => 0
533
+ * [/absolute/path/to/custom-path/image-2.jpg] => 1
534
+ * )
535
+ * [13] => Array(
536
+ * [/absolute/path/to/wp-content/another-custom-path/image-1.jpg] => 0
537
+ * [/absolute/path/to/wp-content/another-custom-path/image-2.jpg] => 1
538
+ * [/absolute/path/to/wp-content/another-custom-path/image-3.jpg] => 2
539
+ * )
540
+ * )
541
+ */
542
+ $files_from_scan = array();
543
+
544
+ foreach ( $folders as $folder_id => $folder ) {
545
+ $files_from_scan[ $folder_id ] = Imagify_Files_Scan::get_files_from_folder( $folder['folder_path'] );
546
+
547
+ if ( is_wp_error( $files_from_scan[ $folder_id ] ) ) {
548
+ unset( $files_from_scan[ $folder_id ] );
549
+ }
550
+ }
551
+
552
+ $files_from_scan = array_map( 'array_flip', $files_from_scan );
553
+
554
+ /**
555
+ * Get the files from DB. $files_from_db will be in the same format as the function output.
556
+ */
557
+ $already_optimized = array();
558
+ $folder_ids = array_keys( $folders );
559
+ $files_from_db = array_fill_keys( $folder_ids, array() );
560
+ $folder_ids = Imagify_DB::prepare_values_list( $folder_ids );
561
+ $select_fields = "$files_key_esc, folder_id, path" . ( $optimization ? ', optimization_level, status' : '' );
562
+
563
+ if ( $optimization ) {
564
+ $orderby = "
565
+ CASE status
566
+ WHEN 'already_optimized' THEN 3
567
+ WHEN 'error' THEN 2
568
+ ELSE 1
569
+ END ASC,
570
+ $files_key_esc DESC";
571
+ } else {
572
+ $orderby = "folder_id, $files_key_esc";
573
+ }
574
+
575
+ $results = $wpdb->get_results( "SELECT $select_fields FROM $files_table WHERE folder_id IN ( $folder_ids ) ORDER BY $orderby;", ARRAY_A ); // WPCS: unprepared SQL ok.
576
+
577
+ if ( $results ) {
578
+ $wpdb->flush();
579
+
580
+ foreach ( $results as $i => $row_fields ) {
581
+ // Cast the row.
582
+ $row_fields = $files_db->cast_row( $row_fields );
583
+
584
+ // Add the absolute path.
585
+ $row_fields['file_path'] = Imagify_Files_Scan::remove_placeholder( $row_fields['path'] );
586
+
587
+ // Remove the file from the scan.
588
+ unset( $files_from_scan[ $row_fields['folder_id'] ][ $row_fields['file_path'] ] );
589
+
590
+ if ( $optimization ) {
591
+ if ( 'error' !== $row_fields['status'] && $row_fields['optimization_level'] === $args['optimization_level'] ) {
592
+ // Try the same level only if the status is an error.
593
+ continue;
594
+ }
595
+
596
+ if ( 'already_optimized' === $row_fields['status'] && $row_fields['optimization_level'] >= $args['optimization_level'] ) {
597
+ // If the image is already compressed, optimize only if the requested level is higher.
598
+ continue;
599
+ }
600
+
601
+ if ( 'success' === $row_fields['status'] && $args['optimization_level'] !== $row_fields['optimization_level'] ) {
602
+ $file_backup_path = self::get_file_backup_path( $row_fields['file_path'] );
603
+
604
+ if ( ! $file_backup_path || ! $filesystem->exists( $file_backup_path ) ) {
605
+ // Don't try to re-optimize if there is no backup file.
606
+ continue;
607
+ }
608
+ }
609
+ }
610
+
611
+ if ( ! $filesystem->exists( $row_fields['file_path'] ) ) {
612
+ // If the file doesn't exist: remove all traces of it and bail out.
613
+ self::delete_file( array(
614
+ 'file_id' => $row_fields[ $files_key ],
615
+ 'file_path' => $row_fields['file_path'],
616
+ ) );
617
+ continue;
618
+ }
619
+
620
+ if ( $optimization && 'already_optimized' === $row_fields['status'] ) {
621
+ $already_optimized[ '_' . $row_fields[ $files_key ] ] = 1;
622
+ }
623
+
624
+ // Add the row to the list.
625
+ $files_from_db[ $row_fields['folder_id'] ][ '_' . $row_fields[ $files_key ] ] = $row_fields;
626
+ }
627
+ }
628
+
629
+ unset( $results );
630
+ $files_from_scan = array_filter( $files_from_scan );
631
+
632
+ // Make sure files from the scan are not already in the DB with another folder (shouldn't be possible, but, you know...).
633
+ if ( $files_from_scan ) {
634
+ $folders_by_placeholder = array();
635
+
636
+ foreach ( $files_from_scan as $folder_id => $folder_files ) {
637
+ foreach ( $folder_files as $file_path => $i ) {
638
+ $placeholder = Imagify_Files_Scan::add_placeholder( $file_path );
639
+
640
+ $folders_by_placeholder[ $placeholder ] = $folder_id;
641
+ $files_from_scan[ $folder_id ][ $file_path ] = $placeholder;
642
+ }
643
+ }
644
+
645
+ $placeholders = Imagify_DB::prepare_values_list( array_keys( $folders_by_placeholder ) );
646
+ $select_fields = "$files_key_esc, folder_id, path" . ( $optimization ? ', optimization_level, status' : '' );
647
+
648
+ $results = $wpdb->get_results( "SELECT $select_fields FROM $files_table WHERE path IN ( $placeholders ) ORDER BY folder_id, $files_key_esc;", ARRAY_A ); // WPCS: unprepared SQL ok.
649
+
650
+ if ( $results ) {
651
+ // Damn...
652
+ $wpdb->flush();
653
+
654
+ foreach ( $results as $i => $row_fields ) {
655
+ // Cast the row.
656
+ $row_fields = $files_db->cast_row( $row_fields );
657
+ $old_folder_id = $row_fields['folder_id'];
658
+
659
+ // Add the absolute path.
660
+ $row_fields['file_path'] = Imagify_Files_Scan::remove_placeholder( $row_fields['path'] );
661
+
662
+ // Set the new folder ID.
663
+ $row_fields['folder_id'] = $folders_by_placeholder[ $row_fields['path'] ];
664
+
665
+ // Remove the file from everywhere.
666
+ unset(
667
+ $files_from_db[ $old_folder_id ][ '_' . $row_fields[ $files_key ] ],
668
+ $files_from_scan[ $old_folder_id ][ $row_fields['file_path'] ],
669
+ $files_from_scan[ $row_fields['folder_id'] ][ $row_fields['file_path'] ]
670
+ );
671
+
672
+ if ( $optimization ) {
673
+ if ( 'error' !== $row_fields['status'] && $row_fields['optimization_level'] === $args['optimization_level'] ) {
674
+ // Try the same level only if the status is an error.
675
+ continue;
676
+ }
677
+
678
+ if ( 'already_optimized' === $row_fields['status'] && $row_fields['optimization_level'] >= $args['optimization_level'] ) {
679
+ // If the image is already compressed, optimize only if the requested level is higher.
680
+ continue;
681
+ }
682
+
683
+ if ( 'success' === $row_fields['status'] && $args['optimization_level'] !== $row_fields['optimization_level'] ) {
684
+ $file_backup_path = self::get_file_backup_path( $row_fields['file_path'] );
685
+
686
+ if ( ! $file_backup_path || ! $filesystem->exists( $file_backup_path ) ) {
687
+ // Don't try to re-optimize if there is no backup file.
688
+ continue;
689
+ }
690
+ }
691
+ }
692
+
693
+ if ( ! $filesystem->exists( $row_fields['file_path'] ) ) {
694
+ // If the file doesn't exist: remove all traces of it and bail out.
695
+ self::delete_file( array(
696
+ 'file_id' => $row_fields[ $files_key ],
697
+ 'file_path' => $row_fields['file_path'],
698
+ ) );
699
+ continue;
700
+ }
701
+
702
+ // Set the correct folder ID in the DB.
703
+ $success = $files_db->update( $row_fields[ $files_key ], array(
704
+ 'folder_id' => $row_fields['folder_id'],
705
+ ) );
706
+
707
+ if ( $success ) {
708
+ if ( $optimization && 'already_optimized' === $row_fields['status'] ) {
709
+ $already_optimized[ '_' . $row_fields[ $files_key ] ] = 1;
710
+ }
711
+
712
+ $files_from_db[ $row_fields['folder_id'] ][ '_' . $row_fields[ $files_key ] ] = $row_fields;
713
+ }
714
+ }
715
+ }
716
+
717
+ unset( $results, $folders_by_placeholder );
718
+ }
719
+
720
+ $files_from_scan = array_filter( $files_from_scan );
721
+
722
+ // Insert the remaining files into the DB.
723
+ if ( $files_from_scan ) {
724
+ foreach ( $files_from_scan as $folder_id => $placeholders ) {
725
+ // Don't add the file to the DB if its folder is not "active".
726
+ if ( ! $add_inactive_folder_files && empty( $folders[ $folder_id ]['active'] ) ) {
727
+ unset( $files_from_scan[ $folder_id ] );
728
+ continue;
729
+ }
730
+
731
+ foreach ( $placeholders as $file_path => $placeholder ) {
732
+ $file_id = self::insert_file( array(
733
+ 'folder_id' => $folder_id,
734
+ 'path' => $placeholder,
735
+ 'file_path' => $file_path,
736
+ ) );
737
+
738
+ if ( $file_id && ! $no_new_files ) {
739
+ $files_from_db[ $folder_id ][ '_' . $file_id ] = array(
740
+ 'file_id' => $file_id,
741
+ 'folder_id' => $folder_id,
742
+ 'path' => $placeholder,
743
+ 'optimization_level' => null,
744
+ 'status' => null,
745
+ 'file_path' => $file_path,
746
+ );
747
+ }
748
+ }
749
+
750
+ unset( $files_from_scan[ $folder_id ] );
751
+ }
752
+ }
753
+
754
+ $files_from_db = array_filter( $files_from_db );
755
+
756
+ if ( ! $files_from_db ) {
757
+ return array();
758
+ }
759
+
760
+ $files_from_db = call_user_func_array( 'array_merge', $files_from_db );
761
+
762
+ if ( $already_optimized ) {
763
+ // Put the files already optimized at the end of the list.
764
+ $already_optimized = array_intersect_key( $files_from_db, $already_optimized );
765
+ $files_from_db = array_diff_key( $files_from_db, $already_optimized );
766
+ $files_from_db = array_merge( $files_from_db, $already_optimized );
767
+ }
768
+
769
+ return $files_from_db;
770
+ }
771
+
772
+ /**
773
+ * Check if files inside the given folders have been modified, and update the database accordingly.
774
+ *
775
+ * @since 1.7
776
+ * @access public
777
+ * @author Grégory Viguier
778
+ *
779
+ * @param array $folders A list of folders. See Imagify_Custom_Folders::get_folders() for the format.
780
+ */
781
+ public static function synchronize_files_from_folders( $folders ) {
782
+ global $wpdb;
783
+ /**
784
+ * Get the files from DB, and from the folder.
785
+ */
786
+ $files = self::get_files_from_folders( $folders, array(
787
+ 'return_only_old_files' => true,
788
+ ) );
789
+
790
+ if ( ! $files ) {
791
+ // This folder doesn't have (new) images.
792
+ return;
793
+ }
794
+
795
+ $files_db = Imagify_Files_DB::get_instance();
796
+ $files_table = $files_db->get_table_name();
797
+ $files_key = $files_db->get_primary_key();
798
+ $files_key_esc = esc_sql( $files_key );
799
+ $file_ids = wp_list_pluck( $files, $files_key );
800
+ $file_ids = Imagify_DB::prepare_values_list( $file_ids );
801
+ $results = $wpdb->get_results( "SELECT * FROM $files_table WHERE $files_key IN ( $file_ids ) ORDER BY $files_key_esc;", ARRAY_A ); // WPCS: unprepared SQL ok.
802
+
803
+ if ( ! $results ) {
804
+ // WAT?!
805
+ return;
806
+ }
807
+
808
+ // Caching the folders will prevent unecessary SQL queries in Imagify_Custom_Folders::refresh_file().
809
+ foreach ( $folders as $folder_id => $folder ) {
810
+ wp_cache_set( 'custom_folder_' . $folder_id, $folder, 'imagify' );
811
+ }
812
+
813
+ // Finally, refresh the files data.
814
+ foreach ( $results as $file ) {
815
+ $file = $files_db->cast_row( $file );
816
+ $folder_id = $file['folder_id'];
817
+ $file = get_imagify_attachment( 'File', $file, 'synchronize_files_from_folders' );
818
+
819
+ self::refresh_file( $file, $folders[ $folder_id ]['active'] );
820
+ }
821
+
822
+ foreach ( $folders as $folder_id => $folder ) {
823
+ wp_cache_delete( 'custom_folder_' . $folder_id, 'imagify' );
824
+ }
825
+ }
826
+
827
+
828
+ /** ----------------------------------------------------------------------------------------- */
829
+ /** WHEN SAVING SELECTED FOLDERS ============================================================ */
830
+ /** ----------------------------------------------------------------------------------------- */
831
+
832
+ /**
833
+ * Dectivate all active folders.
834
+ *
835
+ * @since 1.7
836
+ * @access public
837
+ * @author Grégory Viguier
838
+ */
839
+ public static function deactivate_all_folders() {
840
+ self::deactivate_not_selected_folders();
841
+ }
842
+
843
+ /**
844
+ * Dectivate folders that are not selected.
845
+ *
846
+ * @since 1.7
847
+ * @access public
848
+ * @author Grégory Viguier
849
+ *
850
+ * @param array|object|string $selected_paths A list of "placeholdered" paths corresponding to the selected folders.
851
+ */
852
+ public static function deactivate_not_selected_folders( $selected_paths = array() ) {
853
+ global $wpdb;
854
+
855
+ $folders_table = Imagify_Folders_DB::get_instance()->get_table_name();
856
+
857
+ if ( $selected_paths ) {
858
+ if ( is_array( $selected_paths ) || is_object( $selected_paths ) ) {
859
+ $selected_paths = Imagify_DB::prepare_values_list( $selected_paths );
860
+ }
861
+
862
+ $selected_paths_clause = "AND path NOT IN ( $selected_paths )";
863
+ } else {
864
+ $selected_paths_clause = '';
865
+ }
866
+
867
+ // Remove the active status from the folders that are not selected.
868
+ $wpdb->query( "UPDATE $folders_table SET active = 0 WHERE active != 0 $selected_paths_clause" ); // WPCS: unprepared SQL ok.
869
+ }
870
+
871
+ /**
872
+ * Activate folders that are selected.
873
+ *
874
+ * @since 1.7
875
+ * @access public
876
+ * @author Grégory Viguier
877
+ *
878
+ * @param array|object $selected_paths A list of "placeholdered" paths corresponding to the selected folders.
879
+ * @return array An array of paths of folders that are not in the DB.
880
+ */
881
+ public static function activate_selected_folders( $selected_paths ) {
882
+ global $wpdb;
883
+
884
+ if ( ! $selected_paths ) {
885
+ return $selected_paths;
886
+ }
887
+
888
+ $folders_db = Imagify_Folders_DB::get_instance();
889
+ $folders_table = $folders_db->get_table_name();
890
+ $folders_key = $folders_db->get_primary_key();
891
+
892
+ $selected_paths = (array) $selected_paths;
893
+ $selected_in = Imagify_DB::prepare_values_list( $selected_paths );
894
+
895
+ // Get folders that already are in the DB.
896
+ $folders = $wpdb->get_results( "SELECT * FROM $folders_table WHERE path IN ( $selected_in );", ARRAY_A ); // WPCS: unprepared SQL ok.
897
+
898
+ if ( ! $folders ) {
899
+ return $selected_paths;
900
+ }
901
+
902
+ $selected_paths = array_flip( $selected_paths );
903
+
904
+ foreach ( $folders as $folder ) {
905
+ $folder = $folders_db->cast_row( $folder );
906
+
907
+ if ( Imagify_Files_Scan::placeholder_path_exists( $folder['path'] ) ) {
908
+ if ( ! $folder['active'] ) {
909
+ // Add the active status only if not already set and if the folder exists.
910
+ $folders_db->update( $folder[ $folders_key ], array(
911
+ 'active' => 1,
912
+ ) );
913
+ }
914
+ } else {
915
+ // Remove the active status if the folder does not exist.
916
+ $folders_db->update( $folder[ $folders_key ], array(
917
+ 'active' => 0,
918
+ ) );
919
+ }
920
+
921
+ // Remove the path from the selected list, so the remaining will be created.
922
+ unset( $selected_paths[ $folder['path'] ] );
923
+ }
924
+
925
+ // Paths of folders that are not in the DB.
926
+ return array_flip( $selected_paths );
927
+ }
928
+
929
+ /**
930
+ * Insert folders into the database.
931
+ *
932
+ * @since 1.7
933
+ * @access public
934
+ * @author Grégory Viguier
935
+ *
936
+ * @param array $folders An array of "placeholdered" paths.
937
+ * @return array An array of folder IDs.
938
+ */
939
+ public static function insert_folders( $folders ) {
940
+ if ( ! $folders ) {
941
+ return array();
942
+ }
943
+
944
+ $folder_ids = array();
945
+ $filesystem = imagify_get_filesystem();
946
+ $folders_db = Imagify_Folders_DB::get_instance();
947
+
948
+ foreach ( $folders as $placeholder ) {
949
+ $full_path = Imagify_Files_Scan::remove_placeholder( $placeholder );
950
+ $full_path = realpath( $full_path );
951
+
952
+ if ( ! $full_path || ! $filesystem->is_readable( $full_path ) || ! $filesystem->is_dir( $full_path ) ) {
953
+ continue;
954
+ }
955
+
956
+ if ( Imagify_Files_Scan::is_path_forbidden( $full_path ) ) {
957
+ continue;
958
+ }
959
+
960
+ $folder_ids[] = $folders_db->insert( array(
961
+ 'path' => $placeholder,
962
+ 'active' => 1,
963
+ ) );
964
+ }
965
+
966
+ return array_filter( $folder_ids );
967
+ }
968
+
969
+ /**
970
+ * Remove files that are in inactive folders and are not optimized.
971
+ *
972
+ * @since 1.7
973
+ * @access public
974
+ * @author Grégory Viguier
975
+ */
976
+ public static function remove_unoptimized_files_from_inactive_folders() {
977
+ global $wpdb;
978
+
979
+ $folders_db = Imagify_Folders_DB::get_instance();
980
+ $folders_key = $folders_db->get_primary_key();
981
+ $files_table = Imagify_Files_DB::get_instance()->get_table_name();
982
+ $folder_ids = $folders_db->get_active_folders_column( $folders_key );
983
+
984
+ if ( $folder_ids ) {
985
+ $folder_ids = Imagify_DB::prepare_values_list( $folder_ids );
986
+
987
+ $wpdb->query( "DELETE FROM $files_table WHERE folder_id NOT IN ( $folder_ids ) AND ( status != 'success' OR status IS NULL )" ); // WPCS: unprepared SQL ok.
988
+ } else {
989
+ $wpdb->query( "DELETE FROM $files_table WHERE status != 'success' OR status IS NULL" ); // WPCS: unprepared SQL ok.
990
+ }
991
+ }
992
+
993
+ /**
994
+ * Reassign inactive files to active folders.
995
+ * Example:
996
+ * - Consider the file "/a/b/c/d/file.png".
997
+ * - The folder "/a/b/c/", previously active, becomes inactive.
998
+ * - The folder "/a/b/", previously inactive, becomes active.
999
+ * - The file is reassigned to the folder "/a/b/".
1000
+ *
1001
+ * @since 1.7
1002
+ * @access public
1003
+ * @author Grégory Viguier
1004
+ */
1005
+ public static function reassign_inactive_files() {
1006
+ global $wpdb;
1007
+
1008
+ $folders_db = Imagify_Folders_DB::get_instance();
1009
+ $folders_table = $folders_db->get_table_name();
1010
+ $folders_key = $folders_db->get_primary_key();
1011
+ $folders_key_esc = esc_sql( $folders_key );
1012
+
1013
+ $files_db = Imagify_Files_DB::get_instance();
1014
+ $files_table = $files_db->get_table_name();
1015
+ $files_key = $files_db->get_primary_key();
1016
+ $files_key_esc = esc_sql( $files_key );
1017
+
1018
+ // All active folders.
1019
+ $active_folders = $wpdb->get_results( "SELECT $folders_key_esc, path FROM $folders_table WHERE active = 1;", ARRAY_A ); // WPCS: unprepared SQL ok.
1020
+
1021
+ if ( ! $active_folders ) {
1022
+ return;
1023
+ }
1024
+
1025
+ $active_folder_ids = array();
1026
+ $has_abspath = false;
1027
+
1028
+ foreach ( $active_folders as $i => $active_folder ) {
1029
+ $active_folders[ $i ] = $folders_db->cast_row( $active_folder );
1030
+ $active_folder_ids[] = $active_folders[ $i ][ $folders_key ];
1031
+
1032
+ if ( '{{ABSPATH}}/' === $active_folders[ $i ]['path'] ) {
1033
+ $has_abspath = true;
1034
+ }
1035
+ }
1036
+
1037
+ // Files not in active folders.
1038
+ $active_folder_ids = Imagify_DB::prepare_values_list( $active_folder_ids );
1039
+ $inactive_files = $wpdb->get_results( "SELECT $files_key_esc, path FROM $files_table WHERE folder_id NOT IN ( $active_folder_ids )", ARRAY_A ); // WPCS: unprepared SQL ok.
1040
+
1041
+ if ( ! $inactive_files ) {
1042
+ return;
1043
+ }
1044
+
1045
+ $file_ids_by_folder = array();
1046
+ $active_folders = self::sort_folders( $active_folders, true );
1047
+
1048
+ foreach ( $inactive_files as $inactive_file ) {
1049
+ $inactive_file = $files_db->cast_row( $inactive_file );
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 ) {
1057
+ $folder_id = $active_folder[ $folders_key ];
1058
+
1059
+ if ( strpos( $inactive_file['full_path'], $active_folder['full_path'] ) !== 0 ) {
1060
+ // The file is not in this folder.
1061
+ continue;
1062
+ }
1063
+
1064
+ if ( ! isset( $file_ids_by_folder[ $folder_id ] ) ) {
1065
+ $file_ids_by_folder[ $folder_id ] = array();
1066
+ }
1067
+
1068
+ if ( '{{ABSPATH}}/' === $active_folder['path'] ) {
1069
+ // For the site's root: only direct childs.
1070
+ if ( $inactive_file['dirname'] === $active_folder['full_path'] ) {
1071
+ // This file is in the site's root folder.
1072
+ $file_ids_by_folder[ $folder_id ][] = $inactive_file[ $files_key ];
1073
+ }
1074
+ break;
1075
+ }
1076
+
1077
+ // This file is not in the site's root, but still a grand-child of this folder.
1078
+ $file_ids_by_folder[ $folder_id ][] = $inactive_file[ $files_key ];
1079
+ break;
1080
+ }
1081
+ }
1082
+
1083
+ $file_ids_by_folder = array_filter( $file_ids_by_folder );
1084
+
1085
+ if ( ! $file_ids_by_folder ) {
1086
+ return;
1087
+ }
1088
+
1089
+ // Set the new folder ID.
1090
+ foreach ( $file_ids_by_folder as $folder_id => $file_ids ) {
1091
+ $file_ids = Imagify_DB::prepare_values_list( $file_ids );
1092
+
1093
+ $wpdb->query( "UPDATE $files_table SET folder_id = $folder_id WHERE $files_key_esc IN ( $file_ids )" ); // WPCS: unprepared SQL ok.
1094
+ }
1095
+ }
1096
+
1097
+ /**
1098
+ * Remove the given folders from the DB if they are inactive and have no files.
1099
+ *
1100
+ * @since 1.7
1101
+ * @access public
1102
+ * @author Grégory Viguier
1103
+ *
1104
+ * @param array $folder_ids An array of folder IDs.
1105
+ * @return int Number of removed folders.
1106
+ */
1107
+ public static function remove_empty_inactive_folders( $folder_ids = null ) {
1108
+ global $wpdb;
1109
+
1110
+ $folders_db = Imagify_Folders_DB::get_instance();
1111
+ $folders_table = $folders_db->get_table_name();
1112
+ $folders_key = $folders_db->get_primary_key();
1113
+ $folders_key_esc = esc_sql( $folders_key );
1114
+ $files_table = Imagify_Files_DB::get_instance()->get_table_name();
1115
+
1116
+ $folder_ids = array_filter( (array) $folder_ids );
1117
+
1118
+ if ( $folder_ids ) {
1119
+ $folder_ids = $folders_db->cast_col( $folder_ids, $folders_key );
1120
+ $folder_ids = Imagify_DB::prepare_values_list( $folder_ids );
1121
+ $in_clause = "folders.$folders_key_esc IN ( $folder_ids )";
1122
+ } else {
1123
+ $in_clause = '1=1';
1124
+ }
1125
+
1126
+ // Within the range of given folder IDs, filter the ones that are inactive and have no files.
1127
+ $results = $wpdb->get_col( // WPCS: unprepared SQL ok.
1128
+ "
1129
+ SELECT folders.$folders_key_esc FROM $folders_table AS folders
1130
+ LEFT JOIN $files_table AS files ON folders.$folders_key_esc = files.folder_id
1131
+ WHERE $in_clause
1132
+ AND folders.active != 1
1133
+ AND files.folder_id IS NULL"
1134
+ );
1135
+
1136
+ if ( ! $results ) {
1137
+ return 0;
1138
+ }
1139
+
1140
+ $results = $folders_db->cast_col( $results, $folders_key );
1141
+ $results = Imagify_DB::prepare_values_list( $results );
1142
+
1143
+ // Remove inactive folders with no files.
1144
+ $wpdb->query( "DELETE FROM $folders_table WHERE $folders_key_esc IN ( $results )" ); // WPCS: unprepared SQL ok.
1145
+
1146
+ return (int) $wpdb->rows_affected;
1147
+ }
1148
+
1149
+
1150
+ /** ----------------------------------------------------------------------------------------- */
1151
+ /** TOOLS =================================================================================== */
1152
+ /** ----------------------------------------------------------------------------------------- */
1153
+
1154
+ /**
1155
+ * Sort folders by full path.
1156
+ * The row "full_path" is added to each folder.
1157
+ *
1158
+ * @since 1.7
1159
+ * @access public
1160
+ * @author Grégory Viguier
1161
+ *
1162
+ * @param array $folders An array of folders with at least a "path" row.
1163
+ * @param bool $reverse Reverse the order.
1164
+ * @return array
1165
+ */
1166
+ public static function sort_folders( $folders, $reverse = false ) {
1167
+ if ( ! $folders ) {
1168
+ return array();
1169
+ }
1170
+
1171
+ $keyed_folders = array();
1172
+ $keyed_paths = array();
1173
+
1174
+ foreach ( $folders as $folder ) {
1175
+ $folder = (array) $folder;
1176
+ $folder['full_path'] = Imagify_Files_Scan::remove_placeholder( $folder['path'] );
1177
+
1178
+ $keyed_folders[ $folder['path'] ] = $folder;
1179
+ $keyed_paths[ $folder['path'] ] = $folder['full_path'];
1180
+ }
1181
+
1182
+ natcasesort( $keyed_paths );
1183
+
1184
+ if ( $reverse ) {
1185
+ $keyed_paths = array_reverse( $keyed_paths, true );
1186
+ }
1187
+
1188
+ $keyed_folders = array_merge( $keyed_paths, $keyed_folders );
1189
+
1190
+ return array_values( $keyed_folders );
1191
+ }
1192
+
1193
+ /**
1194
+ * Remove sub-paths: if 'a/b/' and 'a/b/c/' are in the array, we keep only the "parent" 'a/b/'.
1195
+ *
1196
+ * @since 1.7
1197
+ * @access public
1198
+ * @author Grégory Viguier
1199
+ *
1200
+ * @param array $placeholders A list of "placeholdered" paths.
1201
+ * @return array
1202
+ */
1203
+ public static function remove_sub_paths( $placeholders ) {
1204
+ sort( $placeholders );
1205
+
1206
+ foreach ( $placeholders as $i => $placeholder_path ) {
1207
+ if ( '{{ABSPATH}}/' === $placeholder_path ) {
1208
+ continue;
1209
+ }
1210
+
1211
+ if ( ! isset( $prev_path ) ) {
1212
+ $prev_path = strtolower( Imagify_Files_Scan::remove_placeholder( $placeholder_path ) );
1213
+ continue;
1214
+ }
1215
+
1216
+ $placeholder_path = strtolower( Imagify_Files_Scan::remove_placeholder( $placeholder_path ) );
1217
+
1218
+ if ( strpos( $placeholder_path, $prev_path ) === 0 ) {
1219
+ unset( $placeholders[ $i ] );
1220
+ } else {
1221
+ $prev_path = $placeholder_path;
1222
+ }
1223
+ }
1224
+
1225
+ return $placeholders;
1226
+ }
1227
+ }
inc/classes/class-imagify-data.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that handles the plugin data.
6
+ *
7
+ * @since 1.7
8
+ */
9
+ class Imagify_Data extends Imagify_Abstract_Options {
10
+
11
+ /**
12
+ * Class version.
13
+ *
14
+ * @var string
15
+ * @since 1.7
16
+ */
17
+ const VERSION = '1.0';
18
+
19
+ /**
20
+ * Suffix used in the name of the option.
21
+ *
22
+ * @var string
23
+ * @since 1.7
24
+ * @access protected
25
+ */
26
+ protected $identifier = 'data';
27
+
28
+ /**
29
+ * The default values for the Imagify main options.
30
+ * These are the "zero state" values.
31
+ * Don't use null as value.
32
+ *
33
+ * @var array
34
+ * @since 1.7
35
+ * @access protected
36
+ */
37
+ protected $default_values = array(
38
+ 'total_size_images_library' => 0.0,
39
+ 'average_size_images_per_month' => 0.0,
40
+ 'previous_quota_percent' => 0.0,
41
+ );
42
+
43
+ /**
44
+ * The single instance of the class.
45
+ *
46
+ * @var object
47
+ * @since 1.7
48
+ * @access protected
49
+ */
50
+ protected static $_instance;
51
+
52
+ /**
53
+ * Get the main Instance.
54
+ *
55
+ * @since 1.7
56
+ * @author Grégory Viguier
57
+ * @access public
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
+ /** ----------------------------------------------------------------------------------------- */
71
+ /** SANITIZATION, VALIDATION ================================================================ */
72
+ /** ----------------------------------------------------------------------------------------- */
73
+
74
+ /**
75
+ * Sanitize and validate an option value. Basic casts have been made.
76
+ *
77
+ * @since 1.7
78
+ * @author Grégory Viguier
79
+ * @access public
80
+ *
81
+ * @param string $key The option key.
82
+ * @param mixed $value The value.
83
+ * @param mixed $default The default value.
84
+ * @return mixed
85
+ */
86
+ public function sanitize_and_validate_value( $key, $value, $default ) {
87
+ switch ( $key ) {
88
+ case 'total_size_images_library':
89
+ case 'average_size_images_per_month':
90
+ if ( $value <= 0 ) {
91
+ // Invalid.
92
+ return 0.0;
93
+ }
94
+ return $value;
95
+
96
+ case 'previous_quota_percent':
97
+ $value = round( $value, 1 );
98
+ return min( max( 0, $value ), 100 );
99
+ }
100
+
101
+ return false;
102
+ }
103
+ }
inc/classes/class-imagify-db.php CHANGED
@@ -14,7 +14,7 @@ class Imagify_DB {
14
  *
15
  * @var string
16
  */
17
- const VERSION = '1.0';
18
 
19
  /**
20
  * Some hosts limit the number of JOINs in SQL queries, but we need them.
@@ -79,6 +79,37 @@ class Imagify_DB {
79
  return is_numeric( $value ) ? $value : "'" . addcslashes( $value, "'" ) . "'";
80
  }
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  /**
83
  * Get Imagify mime types, ready to be used in a `IN ()` clause.
84
  *
@@ -92,12 +123,181 @@ class Imagify_DB {
92
  static $mime_types;
93
 
94
  if ( ! isset( $mime_types ) ) {
95
- $mime_types = self::prepare_values_list( get_imagify_mime_type() );
96
  }
97
 
98
  return $mime_types;
99
  }
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  /**
102
  * Combine two arrays with some specific keys.
103
  * We use this function to combine the result of 2 SQL queries.
@@ -185,4 +385,47 @@ class Imagify_DB {
185
 
186
  return $metas;
187
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }
14
  *
15
  * @var string
16
  */
17
+ const VERSION = '1.0.1';
18
 
19
  /**
20
  * Some hosts limit the number of JOINs in SQL queries, but we need them.
79
  return is_numeric( $value ) ? $value : "'" . addcslashes( $value, "'" ) . "'";
80
  }
81
 
82
+ /**
83
+ * First half of escaping for LIKE special characters % and _ before preparing for MySQL.
84
+ * Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.
85
+ *
86
+ * Example Prepared Statement:
87
+ * $wild = '%';
88
+ * $find = 'only 43% of planets';
89
+ * $like = $wild . $wpdb->esc_like( $find ) . $wild;
90
+ * $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE %s", $like );
91
+ *
92
+ * Example Escape Chain:
93
+ * $sql = esc_sql( $wpdb->esc_like( $input ) );
94
+ *
95
+ * @since 1.7
96
+ * @access public
97
+ * @author Grégory Viguier
98
+ *
99
+ * @param string $text The raw text to be escaped. The input typed by the user should have no extra or deleted slashes.
100
+ * @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare() or real_escape next.
101
+ */
102
+ public static function esc_like( $text ) {
103
+ global $wpdb;
104
+
105
+ if ( method_exists( $wpdb, 'esc_like' ) ) {
106
+ // Introduced in WP 4.0.0.
107
+ return $wpdb->esc_like( $text );
108
+ }
109
+
110
+ return addcslashes( $text, '_%\\' );
111
+ }
112
+
113
  /**
114
  * Get Imagify mime types, ready to be used in a `IN ()` clause.
115
  *
123
  static $mime_types;
124
 
125
  if ( ! isset( $mime_types ) ) {
126
+ $mime_types = self::prepare_values_list( imagify_get_mime_types() );
127
  }
128
 
129
  return $mime_types;
130
  }
131
 
132
+ /**
133
+ * Get post statuses related to attachments, ready to be used in a `IN ()` clause.
134
+ *
135
+ * @since 1.7
136
+ * @access public
137
+ * @author Grégory Viguier
138
+ *
139
+ * @return string A comma separated list of post statuses.
140
+ */
141
+ public static function get_post_statuses() {
142
+ static $statuses;
143
+
144
+ if ( ! isset( $statuses ) ) {
145
+ $statuses = self::prepare_values_list( imagify_get_post_statuses() );
146
+ }
147
+
148
+ return $statuses;
149
+ }
150
+
151
+ /**
152
+ * Get the SQL JOIN clause to use to get only attachments that have the required WP metadata.
153
+ * It returns an empty string if the database has no attachments without the required metadada.
154
+ * It also triggers Imagify_DB::unlimit_joins().
155
+ *
156
+ * @since 1.7
157
+ * @access public
158
+ * @author Grégory Viguier
159
+ *
160
+ * @param string $id_field An ID field to match the metadata ID against in the JOIN clause.
161
+ * Default is the posts table `ID` field, using the `p` alias: `p.ID`.
162
+ * In case of "false" value or PEBKAC, fallback to the same field without alias.
163
+ * @param bool $matching Set to false to get a query to fetch metas NOT matching the file extensions.
164
+ * @param bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
165
+ * @return string
166
+ */
167
+ public static function get_required_wp_metadata_join_clause( $id_field = 'p.ID', $matching = true, $test = true ) {
168
+ global $wpdb;
169
+
170
+ if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
171
+ return '';
172
+ }
173
+
174
+ self::unlimit_joins();
175
+ $clause = '';
176
+
177
+ if ( ! $id_field || ! is_string( $id_field ) ) {
178
+ $id_field = "$wpdb->posts.ID";
179
+ }
180
+
181
+ $join = $matching ? 'INNER' : 'LEFT';
182
+
183
+ foreach ( self::get_required_wp_metadata_aliases() as $meta_name => $alias ) {
184
+ $clause .= "
185
+ $join JOIN $wpdb->postmeta AS $alias
186
+ ON ( $id_field = $alias.post_id AND $alias.meta_key = '$meta_name' )";
187
+ }
188
+
189
+ return $clause;
190
+ }
191
+
192
+ /**
193
+ * Get the SQL part to be used in a WHERE clause, to get only attachments that have (in)valid '_wp_attached_file' and '_wp_attachment_metadata' metadatas.
194
+ * It returns an empty string if the database has no attachments without the required metadada.
195
+ *
196
+ * @since 1.7
197
+ * @access public
198
+ * @author Grégory Viguier
199
+ *
200
+ * @param string $aliases The aliases to use for the meta values.
201
+ * @param bool $matching Set to false to get a query to fetch invalid metas.
202
+ * @param bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
203
+ * @return string A query.
204
+ */
205
+ public static function get_required_wp_metadata_where_clause( $aliases = array(), $matching = true, $test = true ) {
206
+ static $query = array();
207
+
208
+ if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
209
+ return '';
210
+ }
211
+
212
+ if ( is_string( $aliases ) ) {
213
+ $aliases = array(
214
+ '_wp_attached_file' => $aliases,
215
+ );
216
+ }
217
+
218
+ $aliases = imagify_merge_intersect( $aliases, self::get_required_wp_metadata_aliases() );
219
+ $key = implode( '|', $aliases ) . '|' . (int) $matching;
220
+
221
+ if ( isset( $query[ $key ] ) ) {
222
+ return $query[ $key ];
223
+ }
224
+
225
+ $alias_1 = $aliases['_wp_attached_file'];
226
+ $alias_2 = $aliases['_wp_attachment_metadata'];
227
+ $extensions = self::get_extensions_where_clause( $alias_1, $matching, $test );
228
+
229
+ if ( $matching ) {
230
+ $query[ $key ] = "AND $alias_1.meta_value NOT LIKE '%://%' AND $alias_1.meta_value NOT LIKE '_:\\\\\%' $extensions";
231
+ } else {
232
+ $query[ $key ] = "AND ( $alias_2.meta_value IS NULL OR $alias_1.meta_value IS NULL OR $alias_1.meta_value LIKE '%://%' OR $alias_1.meta_value LIKE '_:\\\\\%' $extensions )";
233
+ }
234
+
235
+ return $query[ $key ];
236
+ }
237
+
238
+ /**
239
+ * Get the SQL part to be used in a WHERE clause, to get only attachments that have a valid file extensions.
240
+ * It returns an empty string if the database has no attachments without the required metadada.
241
+ *
242
+ * @since 1.7
243
+ * @access public
244
+ * @author Grégory Viguier
245
+ *
246
+ * @param string $alias The alias to use for the meta value.
247
+ * @param bool $matching Set to false to get a query to fetch metas NOT matching the file extensions.
248
+ * @param bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
249
+ * @return string A query.
250
+ */
251
+ public static function get_extensions_where_clause( $alias = false, $matching = true, $test = true ) {
252
+ static $extensions;
253
+ static $query = array();
254
+
255
+ if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
256
+ return '';
257
+ }
258
+
259
+ if ( ! isset( $extensions ) ) {
260
+ $extensions = array_keys( imagify_get_mime_types() );
261
+ $extensions = implode( '|', $extensions );
262
+ $extensions = explode( '|', $extensions );
263
+ }
264
+
265
+ if ( ! $alias ) {
266
+ $alias = self::get_required_wp_metadata_aliases();
267
+ $alias = $alias['_wp_attached_file'];
268
+ }
269
+
270
+ $key = $alias . '|' . (int) $matching;
271
+
272
+ if ( isset( $query[ $key ] ) ) {
273
+ return $query[ $key ];
274
+ }
275
+
276
+ if ( $matching ) {
277
+ $query[ $key ] = "AND ( LOWER( $alias.meta_value ) LIKE '%." . implode( "' OR LOWER( $alias.meta_value ) LIKE '%.", $extensions ) . "' )";
278
+ } else {
279
+ $query[ $key ] = "OR ( LOWER( $alias.meta_value ) NOT LIKE '%." . implode( "' AND LOWER( $alias.meta_value ) NOT LIKE '%.", $extensions ) . "' )";
280
+ }
281
+
282
+ return $query[ $key ];
283
+ }
284
+
285
+ /**
286
+ * Get the aliases used for the metas in self::get_required_wp_metadata_join_clause(), self::get_required_wp_metadata_where_clause(), and self::get_extensions_where_clause().
287
+ *
288
+ * @since 1.7
289
+ * @access public
290
+ * @author Grégory Viguier
291
+ *
292
+ * @return array An array with the meta name as key and its alias as value.
293
+ */
294
+ public static function get_required_wp_metadata_aliases() {
295
+ return array(
296
+ '_wp_attached_file' => 'imrwpmt1',
297
+ '_wp_attachment_metadata' => 'imrwpmt2',
298
+ );
299
+ }
300
+
301
  /**
302
  * Combine two arrays with some specific keys.
303
  * We use this function to combine the result of 2 SQL queries.
385
 
386
  return $metas;
387
  }
388
+
389
+ /**
390
+ * Create/Upgrade the table in the database.
391
+ *
392
+ * @since 1.7
393
+ * @access public
394
+ * @author Grégory Viguier
395
+ *
396
+ * @param string $table_name The (prefixed) table name.
397
+ * @param string $schema_query Query representing the table schema.
398
+ * @return bool True on success. False otherwise.
399
+ */
400
+ public static function create_table( $table_name, $schema_query ) {
401
+ global $wpdb;
402
+
403
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
404
+
405
+ $schema_query = trim( $schema_query );
406
+ $charset_collate = $wpdb->get_charset_collate();
407
+
408
+ dbDelta( "CREATE TABLE $table_name ($schema_query) $charset_collate;" );
409
+
410
+ return empty( $wpdb->last_error ) && self::table_exists( $table_name );
411
+ }
412
+
413
+ /**
414
+ * Tell if the given table exists.
415
+ *
416
+ * @since 1.7
417
+ * @access public
418
+ * @author Grégory Viguier
419
+ *
420
+ * @param string $table_name Full name of the table (with DB prefix).
421
+ * @return bool
422
+ */
423
+ public static function table_exists( $table_name ) {
424
+ global $wpdb;
425
+
426
+ $escaped_table = self::esc_like( $table_name );
427
+ $result = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $escaped_table ) );
428
+
429
+ return $result === $table_name;
430
+ }
431
  }
inc/classes/class-imagify-file-attachment.php ADDED
@@ -0,0 +1,746 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Imagify Attachment class for custom folders.
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_File_Attachment extends Imagify_Attachment {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7
17
+ * @author Grégory Viguier
18
+ */
19
+ const VERSION = '1.0';
20
+
21
+ /**
22
+ * The attachment SQL DB class.
23
+ *
24
+ * @var string
25
+ * @since 1.7
26
+ * @access protected
27
+ */
28
+ protected $db_class_name = 'Imagify_Files_DB';
29
+
30
+ /**
31
+ * The constructor.
32
+ *
33
+ * @since 1.7
34
+ * @access public
35
+ * @author Grégory Viguier
36
+ *
37
+ * @param int|array|object $id Thefile ID.
38
+ */
39
+ public function __construct( $id = 0 ) {
40
+ if ( is_numeric( $id ) ) {
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
+ /**
54
+ * Get the original file path.
55
+ *
56
+ * @since 1.7
57
+ * @access public
58
+ * @author Grégory Viguier
59
+ *
60
+ * @return string
61
+ */
62
+ public function get_original_path() {
63
+ $row = $this->get_row();
64
+
65
+ if ( ! $row || empty( $row['path'] ) ) {
66
+ return '';
67
+ }
68
+
69
+ return Imagify_Files_Scan::remove_placeholder( $row['path'] );
70
+ }
71
+
72
+ /**
73
+ * Get the original file URL.
74
+ *
75
+ * @since 1.7
76
+ * @access public
77
+ * @author Grégory Viguier
78
+ *
79
+ * @return string
80
+ */
81
+ public function get_original_url() {
82
+ $row = $this->get_row();
83
+
84
+ if ( ! $row || empty( $row['path'] ) ) {
85
+ return '';
86
+ }
87
+
88
+ return Imagify_Files_Scan::remove_placeholder( $row['path'], 'url' );
89
+ }
90
+
91
+ /**
92
+ * Get the backup file path, even if the file doesn't exist.
93
+ *
94
+ * @since 1.7
95
+ * @access public
96
+ * @author Grégory Viguier
97
+ *
98
+ * @return string|bool The file path. False on failure.
99
+ */
100
+ public function get_raw_backup_path() {
101
+ if ( ! $this->is_valid() ) {
102
+ return false;
103
+ }
104
+
105
+ return Imagify_Custom_Folders::get_file_backup_path( $this->get_original_path() );
106
+ }
107
+
108
+ /**
109
+ * Get the backup URL.
110
+ *
111
+ * @since 1.7
112
+ * @access public
113
+ * @author Grégory Viguier
114
+ *
115
+ * @return string|bool The file URL. False on failure.
116
+ */
117
+ public function get_backup_url() {
118
+ if ( ! $this->is_valid() ) {
119
+ return false;
120
+ }
121
+
122
+ return site_url( '/' ) . imagify_make_file_path_relative( $this->get_raw_backup_path() );
123
+ }
124
+
125
+ /**
126
+ * Get the optimization data.
127
+ *
128
+ * @since 1.7
129
+ * @access public
130
+ * @author Grégory Viguier
131
+ *
132
+ * @return array
133
+ */
134
+ public function get_data() {
135
+ if ( ! $this->is_valid() ) {
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;
144
+ }
145
+
146
+ /**
147
+ * Get the optimization level.
148
+ *
149
+ * @since 1.7
150
+ * @access public
151
+ * @author Grégory Viguier
152
+ *
153
+ * @return int|bool
154
+ */
155
+ public function get_optimization_level() {
156
+ $row = $this->get_row();
157
+ return isset( $row['optimization_level'] ) && is_int( $row['optimization_level'] ) ? $row['optimization_level'] : false;
158
+ }
159
+
160
+ /**
161
+ * Get the file optimization status (success, already_optimized, or error).
162
+ *
163
+ * @since 1.7
164
+ * @access public
165
+ * @author Grégory Viguier
166
+ *
167
+ * @return string
168
+ */
169
+ public function get_status() {
170
+ $row = $this->get_row();
171
+ return ! empty( $row['status'] ) ? $row['status'] : '';
172
+ }
173
+
174
+ /**
175
+ * Get width and height of the original image.
176
+ *
177
+ * @since 1.7
178
+ * @access public
179
+ * @author Grégory Viguier
180
+ *
181
+ * @return array
182
+ */
183
+ public function get_dimensions() {
184
+ $row = $this->get_row();
185
+
186
+ return array(
187
+ 'width' => ! empty( $row['width'] ) ? $row['width'] : 0,
188
+ 'height' => ! empty( $row['height'] ) ? $row['height'] : 0,
189
+ );
190
+ }
191
+
192
+ /**
193
+ * Get the attachment error if there is one.
194
+ *
195
+ * @since 1.7
196
+ * @access public
197
+ * @author Grégory Viguier
198
+ *
199
+ * @return string The message error
200
+ */
201
+ public function get_optimized_error() {
202
+ $row = $this->get_row();
203
+ return ! empty( $row['error'] ) && is_string( $row['error'] ) ? trim( $row['error'] ) : '';
204
+ }
205
+
206
+ /**
207
+ * Count number of optimized sizes.
208
+ *
209
+ * @since 1.7
210
+ * @access public
211
+ * @author Grégory Viguier
212
+ *
213
+ * @return int
214
+ */
215
+ public function get_optimized_sizes_count() {
216
+ return $this->get_status() === 'success' ? 1 : 0;
217
+ }
218
+
219
+ /**
220
+ * Delete the data related to optimization.
221
+ *
222
+ * @since 1.7
223
+ * @access public
224
+ * @author Grégory Viguier
225
+ */
226
+ public function delete_imagify_data() {
227
+ if ( ! $this->is_valid() ) {
228
+ return;
229
+ }
230
+
231
+ $this->update_row( $this->get_reset_imagify_data() );
232
+ }
233
+
234
+ /**
235
+ * Tell if the current file extension is supported.
236
+ * If it's in the DB, it's supported.
237
+ *
238
+ * @since 1.7
239
+ * @access public
240
+ * @author Grégory Viguier
241
+ *
242
+ * @return bool
243
+ */
244
+ public function is_extension_supported() {
245
+ return $this->is_valid();
246
+ }
247
+
248
+ /**
249
+ * Tell if the current file mime type is supported.
250
+ * If it's in the DB, it's supported.
251
+ *
252
+ * @since 1.7
253
+ * @access public
254
+ * @author Grégory Viguier
255
+ *
256
+ * @return bool
257
+ */
258
+ public function is_mime_type_supported() {
259
+ return $this->is_valid();
260
+ }
261
+
262
+ /**
263
+ * Tell if the current attachment has the required WP metadata.
264
+ * Well, these are not attachments, so...
265
+ *
266
+ * @since 1.7
267
+ * @access public
268
+ * @author Grégory Viguier
269
+ *
270
+ * @return bool
271
+ */
272
+ public function has_required_metadata() {
273
+ return $this->is_valid();
274
+ }
275
+
276
+ /**
277
+ * Get the original file size.
278
+ *
279
+ * @since 1.7
280
+ * @access public
281
+ * @author Grégory Viguier
282
+ *
283
+ * @param bool $human_format True to display the image human format size (1Mb).
284
+ * @param int $decimals Precision of number of decimal places.
285
+ * @return string|int
286
+ */
287
+ public function get_original_size( $human_format = true, $decimals = 2 ) {
288
+ if ( ! $this->is_valid() ) {
289
+ return $human_format ? imagify_size_format( 0, $decimals ) : 0;
290
+ }
291
+
292
+ $row = $this->get_row();
293
+
294
+ if ( ! empty( $row['original_size'] ) ) {
295
+ $size = $row['original_size'];
296
+ } else {
297
+ // Check for the backup file first.
298
+ $file_path = $this->get_backup_path();
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 ) {
309
+ return imagify_size_format( (int) $size, $decimals );
310
+ }
311
+
312
+ return (int) $size;
313
+ }
314
+
315
+ /**
316
+ * Get the optimized file size.
317
+ *
318
+ * @since 1.7
319
+ * @access public
320
+ * @author Grégory Viguier
321
+ *
322
+ * @param bool $human_format True to display the image human format size (1Mb).
323
+ * @param int $decimals Precision of number of decimal places.
324
+ * @return string|int
325
+ */
326
+ public function get_optimized_size( $human_format = true, $decimals = 2 ) {
327
+ if ( ! $this->is_valid() ) {
328
+ return $human_format ? imagify_size_format( 0, $decimals ) : 0;
329
+ }
330
+
331
+ $row = $this->get_row();
332
+
333
+ if ( ! empty( $row['optimized_size'] ) ) {
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 ) {
342
+ return imagify_size_format( (int) $size, $decimals );
343
+ }
344
+
345
+ return (int) $size;
346
+ }
347
+
348
+ /**
349
+ * Get the optimized attachment size.
350
+ *
351
+ * @since 1.7
352
+ * @access public
353
+ * @author Grégory Viguier
354
+ *
355
+ * @return float A 2-decimals float.
356
+ */
357
+ public function get_saving_percent() {
358
+ if ( ! $this->is_valid() ) {
359
+ return round( (float) 0, 2 );
360
+ }
361
+
362
+ $row = $this->get_row();
363
+
364
+ if ( ! empty( $row['percent'] ) ) {
365
+ return $row['percent'] / 100;
366
+ }
367
+
368
+ $original_size = $this->get_original_size( false );
369
+
370
+ if ( ! $original_size ) {
371
+ return round( (float) 0, 2 );
372
+ }
373
+
374
+ $optimized_size = $this->get_optimized_size( false );
375
+
376
+ if ( ! $optimized_size ) {
377
+ return round( (float) 0, 2 );
378
+ }
379
+
380
+ return round( ( $original_size - $optimized_size ) / $original_size * 100, 2 );
381
+ }
382
+
383
+ /**
384
+ * Get the overall optimized size (all thumbnails).
385
+ * And since we don't have thumbnails...
386
+ *
387
+ * @since 1.7
388
+ * @access public
389
+ * @author Grégory Viguier
390
+ *
391
+ * @return float A 2-decimals float.
392
+ */
393
+ public function get_overall_saving_percent() {
394
+ return $this->get_saving_percent();
395
+ }
396
+
397
+ /**
398
+ * Get the statistics of the file.
399
+ *
400
+ * @since 1.7
401
+ * @access public
402
+ * @author Grégory Viguier
403
+ *
404
+ * @param string $size The thumbnail slug. Not used here.
405
+ * @param string $key The specific data slug.
406
+ * @return array|string
407
+ */
408
+ public function get_size_data( $size = null, $key = null ) {
409
+ if ( ! isset( $key ) ) {
410
+ $key = $size;
411
+ }
412
+
413
+ if ( ! $this->is_valid() ) {
414
+ return isset( $key ) ? '' : array();
415
+ }
416
+
417
+ $data = imagify_merge_intersect( $this->get_row(), array(
418
+ 'original_size' => 0,
419
+ 'optimized_size' => false,
420
+ 'percent' => 0,
421
+ 'status' => false,
422
+ 'error' => false,
423
+ ) );
424
+
425
+ $data['success'] = 'success' === $data['status'];
426
+
427
+ if ( $data['status'] ) {
428
+ unset( $data['status'], $data['error'] );
429
+
430
+ if ( empty( $data['percent'] ) && $data['original_size'] && $data['optimized_size'] ) {
431
+ $data['percent'] = round( ( $data['original_size'] - $data['optimized_size'] ) / $data['original_size'] * 100, 2 );
432
+ } elseif ( ! empty( $data['percent'] ) ) {
433
+ $data['percent'] = $data['percent'] / 100;
434
+ }
435
+ } else {
436
+ unset( $data['status'], $data['original_size'], $data['optimized_size'], $data['percent'] );
437
+ }
438
+
439
+ if ( isset( $key ) ) {
440
+ return isset( $data[ $key ] ) ? $data[ $key ] : '';
441
+ }
442
+
443
+ return $data;
444
+ }
445
+
446
+ /**
447
+ * Get the global statistics data or a specific one.
448
+ *
449
+ * @since 1.7
450
+ * @access public
451
+ * @author Grégory Viguier
452
+ *
453
+ * @param string $key The specific data slug.
454
+ * @return array|string
455
+ */
456
+ public function get_stats_data( $key = null ) {
457
+ if ( ! $this->is_valid() ) {
458
+ return isset( $key ) ? '' : array();
459
+ }
460
+
461
+ $stats = $this->get_size_data( $key );
462
+ $default = array(
463
+ 'original_size' => 0,
464
+ 'optimized_size' => 0,
465
+ 'percent' => 0,
466
+ );
467
+
468
+ return imagify_merge_intersect( $stats, $default );
469
+ }
470
+
471
+ /**
472
+ * Since these files are not resized, this method is not needed.
473
+ *
474
+ * @since 1.7
475
+ * @access public
476
+ * @author Grégory Viguier
477
+ *
478
+ * @return bool
479
+ */
480
+ public function update_metadata_size() {
481
+ return false;
482
+ }
483
+
484
+ /**
485
+ * Get the unoptimized sizes for a specific attachment.
486
+ *
487
+ * @since 1.7
488
+ * @access public
489
+ * @author Grégory Viguier
490
+ *
491
+ * @return array
492
+ */
493
+ public function get_unoptimized_sizes() {
494
+ return array();
495
+ }
496
+
497
+ /**
498
+ * Get default values used to reset Imagify data.
499
+ *
500
+ * @since 1.7
501
+ * @access public
502
+ * @author Grégory Viguier
503
+ */
504
+ public function get_reset_imagify_data() {
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 ) {
513
+ if ( 'hash' === $column_name || 'modified' === $column_name ) {
514
+ continue;
515
+ }
516
+
517
+ if ( isset( $value ) ) {
518
+ unset( $column_defaults[ $column_name ] );
519
+ }
520
+ }
521
+ }
522
+
523
+ $imagify_columns = $column_defaults;
524
+
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
+
532
+ return $imagify_columns;
533
+ }
534
+
535
+ /**
536
+ * Fills statistics data with values from $data array.
537
+ *
538
+ * @since 1.7
539
+ * @access public
540
+ * @author Grégory Viguier
541
+ *
542
+ * @param array $data The statistics data.
543
+ * @param object $response The API response.
544
+ * @param string $size The attachment size key. Not used here.
545
+ * @return bool|array False if the original size has an error or an array contains the data for other result.
546
+ */
547
+ public function fill_data( $data, $response, $size = null ) {
548
+ $data = is_array( $data ) ? $data : array();
549
+ $data = imagify_merge_intersect( $data, $this->get_reset_imagify_data() );
550
+
551
+ if ( is_wp_error( $response ) ) {
552
+ // Error or already optimized.
553
+ $data['error'] = $response->get_error_message();
554
+
555
+ if ( false !== strpos( $data['error'], 'This image is already compressed' ) ) {
556
+ $data['status'] = 'already_optimized';
557
+ } else {
558
+ $data['status'] = 'error';
559
+ }
560
+
561
+ return $data;
562
+ }
563
+
564
+ // Success.
565
+ $old_data = $this->get_data();
566
+ $original_size = $old_data['original_size'];
567
+ $data['percent'] = 0;
568
+ $data['status'] = 'success';
569
+
570
+ if ( ! empty( $response->original_size ) && ! $original_size ) {
571
+ $data['original_size'] = (int) $response->original_size;
572
+ $original_size = $data['original_size'];
573
+ }
574
+
575
+ if ( ! empty( $response->new_size ) ) {
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'] ) {
585
+ $data['percent'] = round( ( $original_size - $data['optimized_size'] ) / $original_size * 10000 );
586
+ }
587
+
588
+ return $data;
589
+ }
590
+
591
+ /**
592
+ * Optimize the file with Imagify.
593
+ *
594
+ * @since 1.7
595
+ * @access public
596
+ * @author Grégory Viguier
597
+ *
598
+ * @param int $optimization_level The optimization level (2=ultra, 1=aggressive, 0=normal).
599
+ * @param array $metadata The attachment meta data. Not used here.
600
+ * @return bool|object True on success (status success or already_optimized). A WP_Error object on failure.
601
+ */
602
+ public function optimize( $optimization_level = null, $metadata = null ) {
603
+ // Check if the file extension is allowed.
604
+ if ( ! $this->is_extension_supported() ) {
605
+ return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
606
+ }
607
+
608
+ $optimization_level = is_numeric( $optimization_level ) ? (int) $optimization_level : get_imagify_option( 'optimization_level' );
609
+
610
+ // Check if the image is already optimized.
611
+ if ( $this->is_optimized() && ( $this->get_optimization_level() === $optimization_level ) ) {
612
+ return new WP_Error( 'same_optimization_level', __( 'This file is already optimized with this level.', 'imagify' ) );
613
+ }
614
+
615
+ /**
616
+ * Fires before optimizing a file.
617
+ *
618
+ * @since 1.7
619
+ * @author Grégory Viguier
620
+ *
621
+ * @param int $id The file ID.
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(
629
+ 'optimization_level' => $optimization_level,
630
+ 'context' => 'File',
631
+ 'original_size' => $this->get_original_size( false ),
632
+ 'backup_path' => $this->get_raw_backup_path(),
633
+ ) );
634
+
635
+ // Fill the data.
636
+ $data = $this->fill_data( array(
637
+ 'optimization_level' => $optimization_level,
638
+ ), $response );
639
+
640
+ // Save the data.
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;
648
+ }
649
+
650
+ // Already optimized.
651
+ return true;
652
+ }
653
+
654
+ /**
655
+ * Fires after optimizing an attachment.
656
+ *
657
+ * @since 1.7
658
+ * @author Grégory Viguier
659
+ *
660
+ * @param int $id The attachment ID.
661
+ * @param array $optimized_data The optimization data.
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
+ }
669
+
670
+ /**
671
+ * Process an attachment restoration from the backup file.
672
+ *
673
+ * @since 1.7
674
+ * @access public
675
+ * @author Grégory Viguier
676
+ *
677
+ * @return bool|object True on success (status success or already_optimized). A WP_Error object on failure.
678
+ */
679
+ public function restore() {
680
+ // Check if the file extension is allowed.
681
+ if ( ! $this->is_extension_supported() ) {
682
+ return new WP_Error( 'mime_type_not_supported', __( 'This type of file is not supported.', 'imagify' ) );
683
+ }
684
+
685
+ $backup_path = $this->get_backup_path();
686
+
687
+ // Stop the process if there is no backup file to restore.
688
+ if ( ! $backup_path ) {
689
+ return new WP_Error( 'source_doesnt_exist', __( 'The backup file does not exist.', 'imagify' ) );
690
+ }
691
+
692
+ $file_path = $this->get_original_path();
693
+
694
+ if ( ! $file_path ) {
695
+ return new WP_Error( 'empty_path', __( 'The file path is empty.', 'imagify' ) );
696
+ }
697
+
698
+ /**
699
+ * Fires before restoring a file.
700
+ *
701
+ * @since 1.7
702
+ * @author Grégory Viguier
703
+ *
704
+ * @param int $id The file ID.
705
+ */
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();
714
+
715
+ /**
716
+ * Fires after restoring a file.
717
+ *
718
+ * @since 1.7
719
+ * @author Grégory Viguier
720
+ *
721
+ * @param int $id The file ID.
722
+ */
723
+ do_action( 'after_imagify_restore_file', $this->id );
724
+ }
725
+
726
+
727
+ /** ----------------------------------------------------------------------------------------- */
728
+ /** DB ROW ================================================================================== */
729
+ /** ----------------------------------------------------------------------------------------- */
730
+
731
+ /**
732
+ * Invalidate the row.
733
+ *
734
+ * @since 1.7
735
+ * @author Grégory Viguier
736
+ * @access public
737
+ *
738
+ * @return array The row
739
+ */
740
+ public function invalidate_row() {
741
+ // Since the ID doesn't exist in any other table (not a Post ID, not a NGG gallery ID), it must be reset.
742
+ $this->id = 0;
743
+ $this->row = array();
744
+ return $this->row;
745
+ }
746
+ }
inc/classes/class-imagify-files-db.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * DB class that handles files in "custom folders".
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Files_DB extends Imagify_Abstract_DB {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ */
17
+ const VERSION = '1.0';
18
+
19
+ /**
20
+ * The single instance of the class.
21
+ *
22
+ * @var object
23
+ * @since 1.7
24
+ * @access protected
25
+ */
26
+ protected static $_instance;
27
+
28
+ /**
29
+ * The suffix used in the name of the database table (so, without the wpdb prefix).
30
+ *
31
+ * @var string
32
+ * @since 1.7
33
+ * @access protected
34
+ */
35
+ protected $table = 'imagify_files';
36
+
37
+ /**
38
+ * The version of our database table.
39
+ *
40
+ * @var int
41
+ * @since 1.7
42
+ * @access protected
43
+ */
44
+ protected $table_version = 100;
45
+
46
+ /**
47
+ * Tell if the table is the same for each site of a Multisite.
48
+ *
49
+ * @var bool
50
+ * @since 1.7
51
+ * @access protected
52
+ */
53
+ protected $table_is_global = true;
54
+
55
+ /**
56
+ * The name of the primary column.
57
+ *
58
+ * @var string
59
+ * @since 1.7
60
+ * @access protected
61
+ */
62
+ protected $primary_key = 'file_id';
63
+
64
+ /**
65
+ * Get the main Instance.
66
+ *
67
+ * @since 1.7
68
+ * @access public
69
+ * @author Grégory Viguier
70
+ *
71
+ * @return object Main instance.
72
+ */
73
+ public static function get_instance() {
74
+ if ( ! isset( self::$_instance ) ) {
75
+ self::$_instance = new self();
76
+ }
77
+
78
+ return self::$_instance;
79
+ }
80
+
81
+ /**
82
+ * Whitelist of columns.
83
+ *
84
+ * @since 1.7
85
+ * @access public
86
+ * @author Grégory Viguier
87
+ *
88
+ * @return array
89
+ */
90
+ public function get_columns() {
91
+ return array(
92
+ 'file_id' => '%d',
93
+ 'folder_id' => '%d',
94
+ 'file_date' => '%s',
95
+ 'path' => '%s',
96
+ 'hash' => '%s',
97
+ 'mime_type' => '%s',
98
+ 'modified' => '%d',
99
+ 'width' => '%d',
100
+ 'height' => '%d',
101
+ 'original_size' => '%d',
102
+ 'optimized_size' => '%d',
103
+ 'percent' => '%d',
104
+ 'optimization_level' => '%d',
105
+ 'status' => '%s',
106
+ 'error' => '%s',
107
+ );
108
+ }
109
+
110
+ /**
111
+ * Default column values.
112
+ *
113
+ * @since 1.7
114
+ * @access public
115
+ * @author Grégory Viguier
116
+ *
117
+ * @return array
118
+ */
119
+ public function get_column_defaults() {
120
+ return array(
121
+ 'file_id' => 0,
122
+ 'folder_id' => 0,
123
+ 'file_date' => '0000-00-00 00:00:00',
124
+ 'path' => '',
125
+ 'hash' => '',
126
+ 'mime_type' => '',
127
+ 'modified' => 0,
128
+ 'width' => 0,
129
+ 'height' => 0,
130
+ 'original_size' => 0,
131
+ 'optimized_size' => null,
132
+ 'percent' => null,
133
+ 'optimization_level' => null,
134
+ 'status' => null,
135
+ 'error' => null,
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Get the query to create the table fields.
141
+ *
142
+ * For with and height: `smallint(2) unsigned` means 65,535px max.
143
+ *
144
+ * @since 1.7
145
+ * @access protected
146
+ * @author Grégory Viguier
147
+ *
148
+ * @return string
149
+ */
150
+ protected function get_table_schema() {
151
+ return "
152
+ file_id bigint(20) unsigned NOT NULL auto_increment,
153
+ folder_id bigint(20) unsigned NOT NULL default 0,
154
+ file_date datetime NOT NULL default '0000-00-00 00:00:00',
155
+ path varchar(191) NOT NULL default '',
156
+ hash varchar(32) NOT NULL default '',
157
+ mime_type varchar(100) NOT NULL default '',
158
+ modified tinyint(1) unsigned NOT NULL default 0,
159
+ width smallint(2) unsigned NOT NULL default 0,
160
+ height smallint(2) unsigned NOT NULL default 0,
161
+ original_size int(4) unsigned NOT NULL default 0,
162
+ optimized_size int(4) unsigned default NULL,
163
+ percent smallint(2) unsigned default NULL,
164
+ optimization_level tinyint(1) unsigned default NULL,
165
+ status varchar(20) default NULL,
166
+ error varchar(255) default NULL,
167
+ PRIMARY KEY (file_id),
168
+ UNIQUE KEY path (path),
169
+ KEY folder_id (folder_id),
170
+ KEY optimization_level (optimization_level),
171
+ KEY status (status),
172
+ KEY modified (modified)";
173
+ }
174
+ }
inc/classes/class-imagify-files-iterator.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class allowing to filter DirectoryIterator, to return only files that Imagify can optimize and folders.
6
+ * It also allows to remove forbidden folders.
7
+ *
8
+ * @since 1.7
9
+ * @author Grégory Viguier
10
+ */
11
+ class Imagify_Files_Iterator extends FilterIterator {
12
+
13
+ /**
14
+ * Class version.
15
+ *
16
+ * @var string
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.
24
+ *
25
+ * @var bool
26
+ * @since 1.7
27
+ */
28
+ protected $include_folders;
29
+
30
+ /**
31
+ * Check whether the current element of the iterator is acceptable.
32
+ *
33
+ * @since 1.7
34
+ * @access public
35
+ * @author Grégory Viguier
36
+ *
37
+ * @param object $iterator The iterator that is being filtered.
38
+ * @param bool $include_folders True to return both folders and images. False to return only images.
39
+ */
40
+ public function __construct( $iterator, $include_folders = true ) {
41
+ parent::__construct( $iterator );
42
+ $this->include_folders = (bool) $include_folders;
43
+ }
44
+
45
+ /**
46
+ * Check whether the current element of the iterator is acceptable.
47
+ *
48
+ * @since 1.7
49
+ * @access public
50
+ * @author Grégory Viguier
51
+ *
52
+ * @return bool Returns whether the current element of the iterator is acceptable through this filter.
53
+ */
54
+ public function accept() {
55
+ static $extensions, $has_extension_method;
56
+
57
+ // Forbidden file/folder paths and names.
58
+ $file_path = $this->current()->getPathname();
59
+
60
+ if ( Imagify_Files_Scan::is_path_forbidden( $file_path ) ) {
61
+ return false;
62
+ }
63
+
64
+ // OK for folders.
65
+ if ( $this->include_folders && $this->isDir() ) {
66
+ return true;
67
+ }
68
+
69
+ // Only files.
70
+ if ( ! $this->current()->isFile() ) {
71
+ return false;
72
+ }
73
+
74
+ // Only files with the required extension.
75
+ if ( ! isset( $extensions ) ) {
76
+ $extensions = array_keys( imagify_get_mime_types() );
77
+ $extensions = implode( '|', $extensions );
78
+ }
79
+
80
+ if ( ! isset( $has_extension_method ) ) {
81
+ // This method was introduced in php 5.3.6.
82
+ $has_extension_method = method_exists( $this->current(), 'getExtension' );
83
+ }
84
+
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 );
92
+ }
93
+ }
inc/classes/class-imagify-files-list-table.php ADDED
@@ -0,0 +1,1028 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that display the "custom folders" files.
6
+ *
7
+ * @package Imagify
8
+ * @since 1.7
9
+ * @author Grégory Viguier
10
+ */
11
+ class Imagify_Files_List_Table extends WP_List_Table {
12
+
13
+ /**
14
+ * Class version.
15
+ *
16
+ * @var string
17
+ * @since 1.7
18
+ * @author Grégory Viguier
19
+ */
20
+ const VERSION = '1.0';
21
+
22
+ /**
23
+ * Class version.
24
+ *
25
+ * @var string
26
+ * @since 1.7
27
+ * @author Grégory Viguier
28
+ */
29
+ const PER_PAGE_OPTION = 'imagify_files_per_page';
30
+
31
+ /**
32
+ * List of the folders containing the listed files.
33
+ *
34
+ * @var array
35
+ * @since 1.7
36
+ * @access protected
37
+ * @author Grégory Viguier
38
+ */
39
+ protected $folders = array();
40
+
41
+ /**
42
+ * Constructor.
43
+ *
44
+ * @since 1.7
45
+ * @access public
46
+ * @author Grégory Viguier
47
+ *
48
+ * @param array $args An associative array of arguments.
49
+ */
50
+ public function __construct( $args = array() ) {
51
+ parent::__construct( array(
52
+ 'plural' => 'imagify-files',
53
+ 'screen' => isset( $args['screen'] ) ? convert_to_screen( $args['screen'] ) : null,
54
+ ) );
55
+
56
+ $this->modes = array(
57
+ 'list' => __( 'List View', 'imagify' ),
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Prepares the list of items for displaying.
63
+ *
64
+ * @since 1.7
65
+ * @access public
66
+ * @author Grégory Viguier
67
+ */
68
+ public function prepare_items() {
69
+ global $wpdb;
70
+
71
+ add_screen_option( 'per_page', array(
72
+ 'label' => __( 'Number of files per page', 'imagify' ),
73
+ 'default' => 20,
74
+ 'option' => self::PER_PAGE_OPTION,
75
+ ) );
76
+
77
+ $files_db = Imagify_Files_DB::get_instance();
78
+ $files_table = $files_db->get_table_name();
79
+ $files_key = $files_db->get_primary_key();
80
+ $files_key_esc = esc_sql( $files_key );
81
+ $per_page = $this->get_items_per_page( self::PER_PAGE_OPTION );
82
+
83
+ // Prepare the query to get items.
84
+ $page = $this->get_pagenum();
85
+ $offset = ( $page - 1 ) * $per_page;
86
+ $orderbys = $this->get_sortable_columns();
87
+ $orderby = 'path';
88
+ $order = 'ASC';
89
+ $folders = array();
90
+ $file_ids = array();
91
+ $where = '';
92
+
93
+ $sent_orderby = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_STRING );
94
+ $sent_order = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
95
+ $folder_filter = self::get_folder_filter();
96
+ $status_filter = self::get_status_filter();
97
+
98
+ if ( ! empty( $sent_orderby ) && isset( $orderbys[ $sent_orderby ] ) ) {
99
+ $orderby = $sent_orderby;
100
+ $order = is_array( $orderbys[ $orderby ] ) ? 'DESC' : 'ASC';
101
+
102
+ if ( 'optimization' === $orderby ) {
103
+ $orderby = 'percent';
104
+ }
105
+ }
106
+
107
+ if ( $sent_order ) {
108
+ $order = 'ASC' === strtoupper( $sent_order ) ? 'ASC' : 'DESC';
109
+ }
110
+
111
+ if ( $folder_filter ) {
112
+ // Display only files from a specific custom folder.
113
+ $where = "WHERE folder_id = $folder_filter";
114
+ }
115
+
116
+ if ( $status_filter ) {
117
+ // Display files optimized, not optimized, or with error.
118
+ $where .= $where ? ' AND ' : 'WHERE ';
119
+
120
+ switch ( $status_filter ) {
121
+ case 'optimized':
122
+ $where .= "( status = 'success' OR status = 'already_optimized' )";
123
+ break;
124
+ case 'unoptimized':
125
+ $where .= 'status IS NULL';
126
+ break;
127
+ case 'errors':
128
+ $where .= "status = 'error'";
129
+ break;
130
+ }
131
+ }
132
+
133
+ // Pagination.
134
+ $this->set_pagination_args( array(
135
+ 'total_items' => (int) $wpdb->get_var( "SELECT COUNT($files_key_esc) FROM $files_table $where" ), // WPCS: unprepared SQL ok.
136
+ 'per_page' => $per_page,
137
+ ) );
138
+
139
+ // Get items.
140
+ $this->items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $files_table $where ORDER BY $orderby $order LIMIT %d, %d", $offset, $per_page ) ); // WPCS: unprepared SQL ok.
141
+
142
+ if ( ! $this->items ) {
143
+ return;
144
+ }
145
+
146
+ // Prepare items.
147
+ foreach ( $this->items as $i => $item ) {
148
+ // Cast values.
149
+ $item = $files_db->cast_row( $item );
150
+
151
+ // Store the folders used by the items to get their data later in 1 query.
152
+ $folders[ $item->folder_id ] = $item->folder_id;
153
+
154
+ // Store the item IDs to store transients later in 1 query.
155
+ $file_ids[ $item->$files_key ] = $item->$files_key;
156
+
157
+ // Use Imagify objects + add related folder ID and path (set later).
158
+ $this->items[ $i ] = get_imagify_attachment( 'File', $item, 'files_list_row' );
159
+ $this->items[ $i ]->folder_id = $item->folder_id;
160
+ $this->items[ $i ]->folder_path = false;
161
+ $this->items[ $i ]->is_folder_active = true;
162
+ }
163
+
164
+ $folders = array_filter( $folders );
165
+
166
+ // Cache transient values.
167
+ imagify_load_network_options( $file_ids, array(
168
+ '_site_transient_imagify-file-async-in-progress-',
169
+ '_site_transient_timeout_imagify-file-async-in-progress-',
170
+ ) );
171
+
172
+ if ( ! $folders ) {
173
+ return;
174
+ }
175
+
176
+ // Get folders data.
177
+ $folders_db = Imagify_Folders_DB::get_instance();
178
+ $folders_table = $folders_db->get_table_name();
179
+ $folders_key_esc = esc_sql( $folders_db->get_primary_key() );
180
+ $folders = Imagify_DB::prepare_values_list( $folders );
181
+ $folders = $wpdb->get_results( "SELECT * FROM $folders_table WHERE $folders_key_esc IN ( $folders )" ); // WPCS: unprepared SQL ok.
182
+
183
+ if ( ! $folders ) {
184
+ return;
185
+ }
186
+
187
+ // Cast folders data and store data into a property.
188
+ foreach ( $folders as $folder ) {
189
+ $folder = $folders_db->cast_row( $folder );
190
+
191
+ $this->folders[ $folder->folder_id ] = $folder;
192
+ }
193
+
194
+ // Set folders path to each item.
195
+ foreach ( $this->items as $i => $item ) {
196
+ if ( $item->folder_id && isset( $this->folders[ $item->folder_id ] ) ) {
197
+ $item->folder_path = $this->folders[ $item->folder_id ]->path;
198
+ $item->is_folder_active = (bool) $this->folders[ $item->folder_id ]->active;
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Message to be displayed when there are no items.
205
+ *
206
+ * @since 1.7
207
+ * @access public
208
+ * @author Grégory Viguier
209
+ */
210
+ public function no_items() {
211
+ if ( self::get_status_filter() ) {
212
+ // Filter by status.
213
+ switch ( self::get_status_filter() ) {
214
+ case 'optimized':
215
+ /* translators: 1 is a link tag start, 2 is the link tag end. */
216
+ printf( esc_html__( 'No optimized files. Have you tried the %1$sbulk optimization%2$s yet?', 'imagify' ), '<a href="' . esc_url( get_imagify_admin_url( 'files-bulk-optimization' ) ) . '">', '</a>' );
217
+ return;
218
+
219
+ case 'unoptimized':
220
+ esc_html_e( 'No unoptimized files, hurray!', 'imagify' );
221
+ return;
222
+
223
+ case 'errors':
224
+ esc_html_e( 'No errors, hurray!', 'imagify' );
225
+ return;
226
+ }
227
+ }
228
+
229
+ $args = array(
230
+ 'action' => 'imagify_scan_custom_folders',
231
+ '_wpnonce' => wp_create_nonce( 'imagify_scan_custom_folders' ),
232
+ '_wp_http_referer' => get_imagify_admin_url( 'files-list' ),
233
+ );
234
+
235
+ if ( self::get_folder_filter() ) {
236
+ // A specific custom folder (selected or not).
237
+ $args['folder'] = self::get_folder_filter();
238
+ $args['_wp_http_referer'] = rawurlencode( add_query_arg( 'folder-filter', self::get_folder_filter(), $args['_wp_http_referer'] ) );
239
+
240
+ printf(
241
+ /* translators: 1 and 2 are link tag starts, 3 is a link tag end. */
242
+ esc_html__( 'No files yet. Do you want to %1$sscan this folder%3$s for new files or launch a %2$sbulk optimization%3$s directly?', 'imagify' ),
243
+ '<a href="' . esc_url( add_query_arg( $args, admin_url( 'admin-post.php' ) ) ) . '">',
244
+ '<a href="' . esc_url( get_imagify_admin_url( 'files-bulk-optimization' ) ) . '">',
245
+ '</a>'
246
+ );
247
+ return;
248
+ }
249
+
250
+ if ( Imagify_Folders_DB::get_instance()->has_active_folders() ) {
251
+ // All selected custom folders.
252
+ $args['_wp_http_referer'] = rawurlencode( $args['_wp_http_referer'] );
253
+ printf(
254
+ /* translators: 1 and 2 are link tag starts, 3 is a link tag end. */
255
+ esc_html__( 'No files yet. Do you want to %1$sscan your selected folders%3$s for new files or launch a %2$sbulk optimization%3$s directly?', 'imagify' ),
256
+ '<a href="' . esc_url( add_query_arg( $args, admin_url( 'admin-post.php' ) ) ) . '">',
257
+ '<a href="' . esc_url( get_imagify_admin_url( 'files-bulk-optimization' ) ) . '">',
258
+ '</a>'
259
+ );
260
+ return;
261
+ }
262
+
263
+ // Nothing selected in the settings.
264
+ printf(
265
+ /* translators: 1 is a link tag start, 2 is the link tag end. */
266
+ esc_html__( 'To see things appear here, you must select folders in the settings page first :)', 'imagify' ),
267
+ '<a href="' . esc_url( get_imagify_admin_url() ) . '">',
268
+ '</a>'
269
+ );
270
+ }
271
+
272
+ /**
273
+ * Display views.
274
+ *
275
+ * @since 1.7
276
+ * @access public
277
+ * @author Grégory Viguier
278
+ */
279
+ public function views() {
280
+ global $wpdb;
281
+
282
+ // Get all folders.
283
+ $folders_table = Imagify_Folders_DB::get_instance()->get_table_name();
284
+ $folders = $wpdb->get_results( "SELECT folder_id, path FROM $folders_table" ); // WPCS: unprepared SQL ok.
285
+
286
+ if ( ! $folders ) {
287
+ return;
288
+ }
289
+
290
+ $files_db = Imagify_Files_DB::get_instance();
291
+ $files_table = $files_db->get_table_name();
292
+ $files_key_esc = esc_sql( $files_db->get_primary_key() );
293
+
294
+ // Filter files by folder.
295
+ $folder_filters = array();
296
+ $root_id = 0;
297
+ $counts = $wpdb->get_results( "SELECT folder_id, COUNT( $files_key_esc ) AS count FROM $files_table GROUP BY folder_id", OBJECT_K ); // WPCS: unprepared SQL ok.
298
+
299
+ foreach ( $folders as $folder ) {
300
+ if ( '{{ABSPATH}}/' === $folder->path ) {
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
+
308
+ natcasesort( $folder_filters );
309
+
310
+ if ( $root_id ) {
311
+ $folder_filters[ $root_id ] = __( 'Site\'s root', 'imagify' );
312
+ }
313
+
314
+ foreach ( $folder_filters as $folder_id => $label ) {
315
+ $folder_filters[ $folder_id ] .= ' (' . ( isset( $counts[ $folder_id ] ) ? (int) $counts[ $folder_id ]->count : 0 ) . ')';
316
+ }
317
+
318
+ // Filter files by status.
319
+ $counts = $wpdb->get_results( "SELECT status, COUNT( $files_key_esc ) AS count FROM $files_table GROUP BY status", OBJECT_K ); // WPCS: unprepared SQL ok.
320
+ $status_filters = array(
321
+ 'optimized' => 0,
322
+ 'unoptimized' => 0,
323
+ 'errors' => 0,
324
+ );
325
+
326
+ if ( isset( $counts['success'] ) ) {
327
+ $status_filters['optimized'] += $counts['success']->count;
328
+ }
329
+
330
+ if ( isset( $counts['already_optimized'] ) ) {
331
+ $status_filters['optimized'] += $counts['already_optimized']->count;
332
+ }
333
+
334
+ if ( isset( $counts[''] ) ) {
335
+ $status_filters['unoptimized'] += $counts['']->count;
336
+ }
337
+
338
+ if ( isset( $counts['error'] ) ) {
339
+ $status_filters['errors'] += $counts['error']->count;
340
+ }
341
+
342
+ $status_filters = array(
343
+ '' => __( 'All images', 'imagify' ),
344
+ 'optimized' => __( 'Optimized','imagify' ) . ' (' . $status_filters['optimized'] . ')',
345
+ 'unoptimized' => __( 'Unoptimized','imagify' ) . ' (' . $status_filters['unoptimized'] . ')',
346
+ 'errors' => __( 'Errors','imagify' ) . ' (' . $status_filters['errors'] . ')',
347
+ );
348
+
349
+ // Get submitted values.
350
+ $folder_filter = self::get_folder_filter();
351
+ $status_filter = self::get_status_filter();
352
+
353
+ // Display the filters.
354
+ $this->screen->render_screen_reader_content( 'heading_views' );
355
+ ?>
356
+ <div class="wp-filter">
357
+ <div class="filter-items">
358
+
359
+ <label for="folder-filter" class="screen-reader-text"><?php _e( 'Filter by folder', 'imagify' ); ?></label>
360
+ <select class="folder-filters" name="folder-filter" id="folder-filter">
361
+ <?php
362
+ printf( '<option value="%s"%s>%s</option>', '', selected( $folder_filter, 0, false ), esc_html__( 'All Folders', 'imagify' ) );
363
+
364
+ foreach ( $folder_filters as $folder_id => $label ) {
365
+ printf( '<option value="%d"%s>%s</option>', $folder_id, selected( $folder_filter, $folder_id, false ), esc_html( $label ) );
366
+ }
367
+ ?>
368
+ </select>
369
+
370
+ <label for="status-filter" class="screen-reader-text"><?php _e( 'Filter by status', 'imagify' ); ?></label>
371
+ <select class="folder-filters" name="status-filter" id="status-filter">
372
+ <?php
373
+ foreach ( $status_filters as $status => $label ) {
374
+ printf( '<option value="%s"%s>%s</option>', $status, selected( $status_filter, $status, false ), esc_html( $label ) );
375
+ }
376
+ ?>
377
+ </select>
378
+
379
+ <?php submit_button( _x( 'Filter', 'verb', 'imagify' ), '', 'filter_action', false, array( 'id' => 'folders-query-submit' ) ); ?>
380
+
381
+ <?php $this->extra_tablenav( 'bar' ); ?>
382
+ </div>
383
+ </div>
384
+ <?php
385
+ }
386
+
387
+ /**
388
+ * Get an associative array ( option_name => option_title ) with the list of bulk actions available on this table.
389
+ *
390
+ * @since 1.7
391
+ * @access public
392
+ * @author Grégory Viguier
393
+ *
394
+ * @return array
395
+ */
396
+ public function get_bulk_actions() {
397
+ return array(
398
+ 'imagify-bulk-refresh-status' => __( 'Refresh status', 'imagify' ),
399
+ );
400
+ }
401
+
402
+ /**
403
+ * Get a list of columns. The format is:
404
+ * 'internal-name' => 'Title'
405
+ *
406
+ * @since 1.7
407
+ * @access public
408
+ * @author Grégory Viguier
409
+ *
410
+ * @return array
411
+ */
412
+ public function get_columns() {
413
+ return array(
414
+ 'cb' => '<input type="checkbox" />',
415
+ 'title' => __( 'File', 'imagify' ),
416
+ 'folder' => __( 'Folder', 'imagify' ),
417
+ 'optimization' => __( 'Optimization', 'imagify' ),
418
+ 'status' => __( 'Status', 'imagify' ),
419
+ 'optimization_level' => __( 'Optimization Level', 'imagify' ),
420
+ 'actions' => __( 'Actions', 'imagify' ),
421
+ );
422
+ }
423
+
424
+ /**
425
+ * Get a list of sortable columns. The format is:
426
+ * 'internal-name' => 'orderby'
427
+ * or
428
+ * 'internal-name' => array( 'orderby', true )
429
+ *
430
+ * The second format will make the initial sorting order be descending.
431
+ *
432
+ * @since 1.7
433
+ * @access public
434
+ * @author Grégory Viguier
435
+ *
436
+ * @return array
437
+ */
438
+ public function get_sortable_columns() {
439
+ return array(
440
+ 'folder' => 'folder',
441
+ 'optimization' => array( 'optimization', true ),
442
+ 'status' => 'status',
443
+ 'optimization_level' => array( 'optimization_level', true ),
444
+ );
445
+ }
446
+
447
+ /**
448
+ * Get a column contents.
449
+ *
450
+ * @since 1.7
451
+ * @access public
452
+ * @author Grégory Viguier
453
+ *
454
+ * @param string $column The column "name": "cb", "title", "optimization_level", etc.
455
+ * @param object $item The current File object.
456
+ * @return string HTML contents,
457
+ */
458
+ public function get_column( $column, $item ) {
459
+ if ( ! method_exists( $this, 'column_' . $column ) ) {
460
+ return '';
461
+ }
462
+
463
+ ob_start();
464
+ call_user_func( array( $this, 'column_' . $column ), $item );
465
+ return ob_get_clean();
466
+ }
467
+
468
+ /**
469
+ * Handles the checkbox column output.
470
+ *
471
+ * @since 1.7
472
+ * @access public
473
+ * @author Grégory Viguier
474
+ *
475
+ * @param object $item The current File object.
476
+ */
477
+ public function column_cb( $item ) {
478
+ ?>
479
+ <label class="screen-reader-text" for="cb-select-<?php echo $item->get_id(); ?>"><?php _ex( 'Select file', 'checkbox label', 'imagify' ); ?></label>
480
+ <input id="cb-select-<?php echo $item->get_id(); ?>" type="checkbox" name="bulk_select[]" value="<?php echo $item->get_id(); ?>" />
481
+ <?php
482
+ }
483
+
484
+ /**
485
+ * Handles the title column output.
486
+ *
487
+ * @since 1.7
488
+ * @access public
489
+ * @author Grégory Viguier
490
+ *
491
+ * @param object $item The current File object.
492
+ */
493
+ public function column_title( $item ) {
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 : '';
501
+
502
+ if ( ! wp_doing_ajax() && $item->get_optimized_size( false ) > 100000 ) {
503
+ // LazyLoad.
504
+ $image_tag = '<img src="' . esc_url( IMAGIFY_ASSETS_IMG_URL . 'lazyload.png' ) . '" data-lazy-src="' . esc_url( $url ) . '" alt="" />';
505
+ $image_tag .= '<noscript><img src="' . esc_url( $url ) . '" alt="" /></noscript>';
506
+ } else {
507
+ $image_tag = '<img src="' . esc_url( $url ) . '" class="hide-if-no-js" alt="" />';
508
+ }
509
+ ?>
510
+ <strong class="has-media-icon">
511
+ <a href="<?php echo esc_url( $url ); ?>" target="_blank">
512
+ <span class="media-icon image-icon<?php echo $orientation; ?>">
513
+ <span class="centered">
514
+ <?php echo $image_tag; ?>
515
+ </span>
516
+ </span>
517
+ <?php echo esc_html( $title ); ?>
518
+ </a>
519
+ </strong>
520
+ <p class="filename">
521
+ <?php $this->comparison_tool_button( $item ); ?>
522
+ </p>
523
+ <?php
524
+ }
525
+
526
+ /**
527
+ * Handles the parent folder column output.
528
+ *
529
+ * @since 1.7
530
+ * @access public
531
+ * @author Grégory Viguier
532
+ *
533
+ * @param object $item The current File object.
534
+ */
535
+ public function column_folder( $item ) {
536
+ $item = $this->maybe_set_item_folder( $item );
537
+
538
+ if ( empty( $item->folder_path ) ) {
539
+ return;
540
+ }
541
+
542
+ $format = '%s';
543
+ $filter = self::get_folder_filter();
544
+
545
+ if ( $filter !== $item->folder_id ) {
546
+ $format = '<a href="' . esc_url( add_query_arg( 'folder-filter', $item->folder_id, get_imagify_admin_url( 'files-list' ) ) ) . '">%s</a>';
547
+ }
548
+
549
+ if ( '{{ABSPATH}}/' === $item->folder_path ) {
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 ) {
557
+ echo '<br/>';
558
+ _e( 'This folder is not selected for bulk optimization.', 'imagify' );
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Handles the optimization data column output.
564
+ *
565
+ * @since 1.7
566
+ * @access public
567
+ * @author Grégory Viguier
568
+ *
569
+ * @param object $item The current File object.
570
+ */
571
+ public function column_optimization( $item ) {
572
+ ?>
573
+ <ul class="imagify-datas-list">
574
+ <li class="imagify-data-item">
575
+ <span class="data"><?php esc_html_e( 'Original Filesize:', 'imagify' ); ?></span>
576
+ <strong class="data-value original"><?php echo esc_html( $item->get_original_size() ); ?></strong>
577
+ </li>
578
+ <?php if ( $item->is_optimized() ) { ?>
579
+ <li class="imagify-data-item">
580
+ <span class="data"><?php esc_html_e( 'New Filesize:', 'imagify' ); ?></span>
581
+ <strong class="data-value big optimized"><?php echo esc_html( $item->get_optimized_size() ); ?></strong>
582
+ </li>
583
+ <li class="imagify-data-item">
584
+ <span class="data"><?php esc_html_e( 'Original Saving:', 'imagify' ); ?></span>
585
+ <strong class="data-value">
586
+ <span class="imagify-chart">
587
+ <span class="imagify-chart-container">
588
+ <canvas class="imagify-consumption-chart imagify-consumption-chart-<?php echo $item->get_id(); ?>" width="15" height="15"></canvas>
589
+ <?php if ( wp_doing_ajax() ) { ?>
590
+ <script type="text/javascript">jQuery( window ).trigger( "canvasprinted.imagify", [ ".imagify-consumption-chart-<?php echo $item->get_id(); ?>" ] ); </script>
591
+ <?php } ?>
592
+ </span>
593
+ </span>
594
+ <span class="imagify-chart-value"><?php echo $item->get_saving_percent(); ?></span>%
595
+ </strong>
596
+ </li>
597
+ <?php } ?>
598
+ </ul>
599
+ <?php
600
+ }
601
+
602
+ /**
603
+ * Handles the status column output.
604
+ *
605
+ * @since 1.7
606
+ * @access public
607
+ * @author Grégory Viguier
608
+ *
609
+ * @param object $item The current File object.
610
+ */
611
+ public function column_status( $item ) {
612
+ $status = $item->get_status();
613
+ $error_text = $item->get_optimized_error();
614
+ $row = $item->get_row();
615
+ $messages = array();
616
+
617
+ if ( ! $status ) {
618
+ // File is not optimized.
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 ) {
626
+ // No need to display this if we already have another message to display.
627
+ $messages[] = '<em class="imagify-status-no-changes">' . esc_html__( 'No changes found', 'imagify' ) . '</em>';
628
+ } elseif ( $row['modified'] ) {
629
+ // The file has changed or is missing.
630
+ $messages[] = '<strong class="imagify-status-changed">' . esc_html__( 'The file has changed', 'imagify' ) . '</strong>';
631
+ }
632
+
633
+ echo implode( '<br/>', $messages );
634
+
635
+ $this->refresh_status_button( $item );
636
+ }
637
+
638
+ /**
639
+ * Handles the optimization level column output.
640
+ *
641
+ * @since 1.7
642
+ * @access public
643
+ * @author Grégory Viguier
644
+ *
645
+ * @param object $item The current File object.
646
+ */
647
+ public function column_optimization_level( $item ) {
648
+ if ( ! $item->has_error() ) {
649
+ echo $item->get_optimization_level_label( '%ICON% %s' );
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Handles the actions column output.
655
+ *
656
+ * @since 1.7
657
+ * @access public
658
+ * @author Grégory Viguier
659
+ *
660
+ * @param object $item The current File object.
661
+ */
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.
669
+ $done = true;
670
+ esc_html_e( 'Invalid API key', 'imagify' );
671
+ echo '<br/><a href="' . esc_url( get_imagify_admin_url() ) . '">' . __( 'Check your Settings', 'imagify' ) . '</a>';
672
+ }
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
+ }
682
+
683
+ $this->optimize_button( $item );
684
+ $this->retry_button( $item );
685
+ $this->reoptimize_buttons( $item );
686
+ $this->restore_button( $item );
687
+ }
688
+
689
+ /**
690
+ * Prints a button to optimize the file.
691
+ *
692
+ * @since 1.7
693
+ * @access protected
694
+ * @author Grégory Viguier
695
+ *
696
+ * @param object $item The current File object.
697
+ */
698
+ protected function optimize_button( $item ) {
699
+ if ( $item->get_status() ) {
700
+ // Already optimized.
701
+ return;
702
+ }
703
+
704
+ $url = get_imagify_admin_url( 'optimize-file', array(
705
+ 'attachment_id' => $item->get_id(),
706
+ ) );
707
+ $level = imagify_get_optimization_level_label( Imagify_Options::get_instance()->get( 'optimization_level' ) );
708
+ /* translators: %s is an optimization level. */
709
+ $title = sprintf( __( 'Optimize this file to %s.' ), $level );
710
+ $class = 'button-primary button-imagify-optimize' . ( $item->has_backup() ? ' file-has-backup' : '' );
711
+ ?>
712
+ <a id="imagify-optimize-<?php echo $item->get_id(); ?>" href="<?php echo esc_url( $url ); ?>" title="<?php echo esc_attr( $title ); ?>" class="<?php echo $class; ?>" data-waiting-label="<?php esc_attr_e( 'Optimizing...', 'imagify' ); ?>">
713
+ <?php esc_html_e( 'Optimize', 'imagify' ); ?>
714
+ </a>
715
+ <?php
716
+ }
717
+
718
+ /**
719
+ * Prints a button to retry to optimize the file.
720
+ *
721
+ * @since 1.7
722
+ * @access protected
723
+ * @author Grégory Viguier
724
+ *
725
+ * @param object $item The current File object.
726
+ */
727
+ protected function retry_button( $item ) {
728
+ if ( ! $item->is_already_optimized() && ! $item->has_error() ) {
729
+ // Not optimized or successfully optimized.
730
+ return;
731
+ }
732
+
733
+ $url = get_imagify_admin_url( 'optimize-file', array(
734
+ 'attachment_id' => $item->get_id(),
735
+ ) );
736
+ $level = imagify_get_optimization_level_label( Imagify_Options::get_instance()->get( 'optimization_level' ) );
737
+ /* translators: %s is an optimization level. */
738
+ $title = sprintf( __( 'Optimize this file to %s.' ), $level );
739
+ $class = 'button button-imagify-optimize' . ( $item->has_backup() ? ' file-has-backup' : '' );
740
+ ?>
741
+ <a id="imagify-optimize-<?php echo $item->get_id(); ?>" href="<?php echo esc_url( $url ); ?>" title="<?php echo esc_attr( $title ); ?>" class="<?php echo $class; ?>" data-waiting-label="<?php esc_attr_e( 'Optimizing...', 'imagify' ); ?>">
742
+ <?php esc_html_e( 'Try again', 'imagify' ); ?>
743
+ </a><br/>
744
+ <?php
745
+ }
746
+
747
+ /**
748
+ * Prints buttons to re-optimize the file to other levels.
749
+ *
750
+ * @since 1.7
751
+ * @access protected
752
+ * @author Grégory Viguier
753
+ *
754
+ * @param object $item The current File object.
755
+ */
756
+ protected function reoptimize_buttons( $item ) {
757
+ if ( ! $item->get_status() ) {
758
+ // Not optimized yet.
759
+ return;
760
+ }
761
+
762
+ $is_optimized = $item->is_optimized();
763
+ $has_backup = $item->has_backup();
764
+ $can_reoptimize = $has_backup || ! $is_optimized;
765
+
766
+ // Don't display anything if there is no backup or the image has been optimized.
767
+ if ( ! $can_reoptimize ) {
768
+ return;
769
+ }
770
+
771
+ /**
772
+ * If the image is optimized, don't display the Retry button for the level the image is optimized in.
773
+ * If not, don't display the Retry button for the level set in the plugin settings.
774
+ */
775
+ $skip_level = $is_optimized ? $item->get_optimization_level() : Imagify_Options::get_instance()->get( 'optimization_level' );
776
+ $args = array(
777
+ 'attachment_id' => $item->get_id(),
778
+ );
779
+ $labels = array(
780
+ 0 => __( 'Normal', 'imagify' ),
781
+ 1 => __( 'Aggressive', 'imagify' ),
782
+ 2 => __( 'Ultra', 'imagify' ),
783
+ );
784
+
785
+ foreach ( $labels as $level => $label ) {
786
+ if ( $skip_level === $level ) {
787
+ continue;
788
+ }
789
+
790
+ $args['optimization_level'] = $level;
791
+ ?>
792
+ <a href="<?php echo esc_url( get_imagify_admin_url( 'reoptimize-file', $args ) ); ?>" class="button-imagify-reoptimize" data-waiting-label="<?php esc_attr_e( 'Optimizing...', 'imagify' ); ?>">
793
+ <span class="dashicons dashicons-admin-generic"></span>
794
+ <span class="imagify-hide-if-small">
795
+ <?php
796
+ /* translators: %s is an optimization level. */
797
+ printf( esc_html__( 'Re-Optimize to %s', 'imagify' ), '</span>' . esc_html( $label ) . '<span class="imagify-hide-if-small">' );
798
+ ?>
799
+ </span>
800
+ </a><br/>
801
+ <?php
802
+ }
803
+ }
804
+
805
+ /**
806
+ * Prints a button to restore the file.
807
+ *
808
+ * @since 1.7
809
+ * @access protected
810
+ * @author Grégory Viguier
811
+ *
812
+ * @param object $item The current File object.
813
+ */
814
+ protected function restore_button( $item ) {
815
+ if ( ! $item->is_optimized() || ! $item->has_backup() ) {
816
+ return;
817
+ }
818
+
819
+ $url = get_imagify_admin_url( 'restore-file', array(
820
+ 'attachment_id' => $item->get_id(),
821
+ ) );
822
+ ?>
823
+ <a id="imagify-restore-<?php echo $item->get_id(); ?>" href="<?php echo esc_url( $url ); ?>" class="button-imagify-restore file-has-backup" data-waiting-label="<?php esc_attr_e( 'Restoring...', 'imagify' ); ?>">
824
+ <span class="dashicons dashicons-image-rotate"></span>
825
+ <?php esc_html_e( 'Restore Original', 'imagify' ); ?>
826
+ </a>
827
+ <?php
828
+ }
829
+
830
+ /**
831
+ * Prints a button to check if the file has been modified or not.
832
+ *
833
+ * @since 1.7
834
+ * @access protected
835
+ * @author Grégory Viguier
836
+ *
837
+ * @param object $item The current File object.
838
+ */
839
+ protected function refresh_status_button( $item ) {
840
+ $url = get_imagify_admin_url( 'refresh-file-modified', array(
841
+ 'attachment_id' => $item->get_id(),
842
+ ) );
843
+ ?>
844
+ <br/>
845
+ <a id="imagify-refresh-status-<?php echo $item->get_id(); ?>" href="<?php echo esc_url( $url ); ?>" class="button-imagify-refresh-status" data-waiting-label="<?php esc_attr_e( 'Refreshing status...', 'imagify' ); ?>">
846
+ <span class="dashicons dashicons-image-rotate"></span>
847
+ <?php esc_html_e( 'Refresh status', 'imagify' ); ?>
848
+ </a>
849
+ <?php
850
+ }
851
+
852
+ /**
853
+ * Prints a button for the comparison tool (before / after optimization).
854
+ *
855
+ * @since 1.7
856
+ * @access protected
857
+ * @author Grégory Viguier
858
+ *
859
+ * @param object $item The current File object.
860
+ */
861
+ protected function comparison_tool_button( $item ) {
862
+ if ( ! $item->is_optimized() || ! $item->has_backup() ) {
863
+ return;
864
+ }
865
+
866
+ $file_path = $item->get_original_path();
867
+
868
+ if ( ! $file_path || ! imagify_get_filesystem()->exists( $file_path ) ) {
869
+ return;
870
+ }
871
+
872
+ $dimensions = $item->get_dimensions();
873
+
874
+ if ( $dimensions['width'] < 360 ) {
875
+ return;
876
+ }
877
+
878
+ $backup_url = $item->get_backup_url();
879
+
880
+ printf(
881
+ '<a href="%1$s" data-id="%2$d" data-backup-src="%3$s" data-full-src="%4$s" data-full-width="%5$d" data-full-height="%6$d" data-target="#imagify-comparison-%2$d" class="imagify-compare-images imagify-modal-trigger" target="_blank">%7$s</a>',
882
+ esc_url( $backup_url ),
883
+ $item->get_id(),
884
+ esc_url( $backup_url ),
885
+ esc_url( $item->get_original_url() ),
886
+ $dimensions['width'],
887
+ $dimensions['height'],
888
+ esc_html__( 'Compare Original VS Optimized', 'imagify' )
889
+ );
890
+
891
+ if ( wp_doing_ajax() ) {
892
+ ?>
893
+ <script type="text/javascript">jQuery( window ).trigger( 'comparisonprinted.imagify', [ <?php echo $item->get_id(); ?> ] ); </script>
894
+ <?php
895
+ }
896
+ }
897
+
898
+ /**
899
+ * Add the folder_id and folder_path properties to the $item if not set yet.
900
+ * It may happen if the $item doesn't come from the prepare() method.
901
+ *
902
+ * @since 1.7
903
+ * @access protected
904
+ * @author Grégory Viguier
905
+ *
906
+ * @param object $item The current File object.
907
+ * @return object The current File object.
908
+ */
909
+ protected function maybe_set_item_folder( $item ) {
910
+ if ( isset( $item->folder_path ) ) {
911
+ return $item;
912
+ }
913
+
914
+ $item->folder_id = 0;
915
+ $item->folder_path = false;
916
+
917
+ $row = $item->get_row();
918
+
919
+ if ( ! $row['folder_id'] ) {
920
+ return $item;
921
+ }
922
+
923
+ $folder = Imagify_Folders_DB::get_instance()->get( $row['folder_id'] );
924
+
925
+ if ( ! $folder ) {
926
+ return $item;
927
+ }
928
+
929
+ $item->folder_id = $folder['folder_id'];
930
+ $item->folder_path = $folder['path'];
931
+ $item->is_folder_active = (bool) $folder['active'];
932
+
933
+ return $item;
934
+ }
935
+
936
+ /**
937
+ * Get the name of the default primary column.
938
+ *
939
+ * @since 1.7
940
+ * @access protected
941
+ * @author Grégory Viguier
942
+ *
943
+ * @return string Name of the default primary column, in this case, 'title'.
944
+ */
945
+ protected function get_default_primary_column_name() {
946
+ return 'title';
947
+ }
948
+
949
+ /**
950
+ * Get a list of CSS classes for the WP_List_Table table tag.
951
+ *
952
+ * @since 1.7
953
+ * @access protected
954
+ * @author Grégory Viguier
955
+ *
956
+ * @return array List of CSS classes for the table tag.
957
+ */
958
+ protected function get_table_classes() {
959
+ return array( 'widefat', 'fixed', 'striped', 'media', $this->_args['plural'] );
960
+ }
961
+
962
+ /**
963
+ * Allow to save the screen options when submitted by the user.
964
+ *
965
+ * @since 1.7
966
+ * @access public
967
+ * @author Grégory Viguier
968
+ *
969
+ * @param bool|int $status Screen option value. Default false to skip.
970
+ * @param string $option The option name.
971
+ * @param int $value The number of rows to use.
972
+ * @return int|bool
973
+ */
974
+ public static function save_screen_options( $status, $option, $value ) {
975
+ if ( self::PER_PAGE_OPTION === $option ) {
976
+ return (int) $value;
977
+ }
978
+
979
+ return $status;
980
+ }
981
+
982
+ /**
983
+ * Get the requested folder filter.
984
+ *
985
+ * @since 1.7
986
+ * @access public
987
+ * @author Grégory Viguier
988
+ *
989
+ * @return string
990
+ */
991
+ public static function get_folder_filter() {
992
+ static $filter;
993
+
994
+ if ( ! isset( $filter ) ) {
995
+ $filter = (int) filter_input( INPUT_GET, 'folder-filter', FILTER_SANITIZE_NUMBER_INT );
996
+ $filter = max( 0, $filter );
997
+ }
998
+
999
+ return $filter;
1000
+ }
1001
+
1002
+ /**
1003
+ * Get the requested status filter.
1004
+ *
1005
+ * @since 1.7
1006
+ * @access public
1007
+ * @author Grégory Viguier
1008
+ *
1009
+ * @return string
1010
+ */
1011
+ public static function get_status_filter() {
1012
+ static $filter;
1013
+
1014
+ if ( isset( $filter ) ) {
1015
+ return $filter;
1016
+ }
1017
+
1018
+ $values = array(
1019
+ 'optimized' => 1,
1020
+ 'unoptimized' => 1,
1021
+ 'errors' => 1,
1022
+ );
1023
+ $filter = trim( filter_input( INPUT_GET, 'status-filter', FILTER_SANITIZE_STRING ) );
1024
+ $filter = isset( $values[ $filter ] ) ? $filter : '';
1025
+
1026
+ return $filter;
1027
+ }
1028
+ }
inc/classes/class-imagify-files-recursive-iterator.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class allowing to filter RecursiveDirectoryIterator, to return only files that Imagify can optimize.
6
+ * It also allows to remove forbidden folders.
7
+ *
8
+ * @since 1.7
9
+ * @author Grégory Viguier
10
+ */
11
+ class Imagify_Files_Recursive_Iterator extends RecursiveFilterIterator {
12
+
13
+ /**
14
+ * Class version.
15
+ *
16
+ * @var string
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.
24
+ *
25
+ * @since 1.7
26
+ * @access public
27
+ * @author Grégory Viguier
28
+ *
29
+ * @return bool Returns whether the current element of the iterator is acceptable through this filter.
30
+ */
31
+ public function accept() {
32
+ static $extensions, $has_extension_method;
33
+
34
+ // Forbidden file/folder paths and names.
35
+ $file_path = $this->current()->getPathname();
36
+
37
+ if ( Imagify_Files_Scan::is_path_forbidden( $file_path ) ) {
38
+ return false;
39
+ }
40
+
41
+ // OK for folders.
42
+ if ( $this->hasChildren() ) {
43
+ return true;
44
+ }
45
+
46
+ // Only files.
47
+ if ( ! $this->current()->isFile() ) {
48
+ return false;
49
+ }
50
+
51
+ // Only files with the required extension.
52
+ if ( ! isset( $extensions ) ) {
53
+ $extensions = array_keys( imagify_get_mime_types() );
54
+ $extensions = implode( '|', $extensions );
55
+ }
56
+
57
+ if ( ! isset( $has_extension_method ) ) {
58
+ // This method was introduced in php 5.3.6.
59
+ $has_extension_method = method_exists( $this->current(), 'getExtension' );
60
+ }
61
+
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 );
69
+ }
70
+ }
inc/classes/class-imagify-files-scan.php ADDED
@@ -0,0 +1,544 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class handling everything that is related to "custom folders optimization".
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Files_Scan {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
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.
23
+ *
24
+ * @since 1.7
25
+ * @access public
26
+ * @author Grégory Viguier
27
+ *
28
+ * @param string $folder An absolute path to a 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
+ 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
+
39
+ $folder = realpath( $folder );
40
+
41
+ if ( ! $folder ) {
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
+
49
+ if ( self::is_path_forbidden( $folder ) ) {
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 );
62
+ $images = array();
63
+
64
+ foreach ( new IteratorIterator( $dir ) as $file ) {
65
+ $images[] = $file->getPathname();
66
+ }
67
+
68
+ return $images;
69
+ }
70
+
71
+ /**
72
+ * 4096 stands for FilesystemIterator::SKIP_DOTS, which was introduced in php 5.3.0.
73
+ * 8192 stands for FilesystemIterator::UNIX_PATHS, which was introduced in php 5.3.0.
74
+ */
75
+ $dir = new RecursiveDirectoryIterator( $folder, 4096 | 8192 );
76
+ $dir = new Imagify_Files_Recursive_Iterator( $dir );
77
+ $images = new RecursiveIteratorIterator( $dir );
78
+ $images = array_keys( iterator_to_array( $images ) );
79
+
80
+ return $images;
81
+ }
82
+
83
+ /**
84
+ * Tell if a path is forbidden.
85
+ *
86
+ * @since 1.7
87
+ * @access public
88
+ * @author Grégory Viguier
89
+ *
90
+ * @param string $file_path A file or folder absolute path.
91
+ * @return bool
92
+ */
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
+ }
104
+
105
+ if ( ! isset( $folders ) ) {
106
+ $folders = self::get_forbidden_folders();
107
+ $folders = array_map( 'strtolower', $folders );
108
+ $folders = array_flip( $folders );
109
+ }
110
+
111
+ $file_path = self::normalize_path_for_comparison( $file_path );
112
+
113
+ if ( isset( $folders[ $file_path ] ) ) {
114
+ return true;
115
+ }
116
+
117
+ if ( self::get_forbidden_folder_patterns() ) {
118
+ foreach ( self::get_forbidden_folder_patterns() as $pattern ) {
119
+ if ( preg_match( '@^' . $pattern . '@', $file_path ) ) {
120
+ return true;
121
+ }
122
+ }
123
+ }
124
+
125
+ foreach ( $folders as $folder ) {
126
+ if ( strpos( $file_path, $folder ) === 0 ) {
127
+ return true;
128
+ }
129
+ }
130
+
131
+ return false;
132
+ }
133
+
134
+ /**
135
+ * Get the list of folders where Imagify won't look for files to optimize.
136
+ *
137
+ * @since 1.7
138
+ * @access public
139
+ * @author Grégory Viguier
140
+ *
141
+ * @return array A list of absolute paths.
142
+ */
143
+ public static function get_forbidden_folders() {
144
+ static $folders;
145
+
146
+ if ( isset( $folders ) ) {
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.
159
+ WP_CONTENT_DIR . '/upgrade', // Upgrade.
160
+ // Plugins.
161
+ WP_CONTENT_DIR . '/backups', // A folder commonly used by backup plugins.
162
+ WP_CONTENT_DIR . '/cache', // A folder commonly used by cache plugins.
163
+ WP_CONTENT_DIR . '/bps-backup', // BulletProof Security.
164
+ WP_CONTENT_DIR . '/ngg', // NextGen Gallery.
165
+ WP_CONTENT_DIR . '/ngg_styles', // NextGen Gallery.
166
+ WP_CONTENT_DIR . '/w3tc-config', // W3 Total Cache.
167
+ WP_CONTENT_DIR . '/wfcache', // WP Fastest Cache.
168
+ WP_CONTENT_DIR . '/wp-rocket-config', // WP Rocket.
169
+ Imagify_Custom_Folders::get_backup_dir_path(), // Imagify "Custom folders" backup.
170
+ IMAGIFY_PATH, // Imagify plugin.
171
+ self::get_wc_logs_path(), // WooCommerce Logs.
172
+ self::get_ewww_tools_path(), // EWWW.
173
+ );
174
+
175
+ // NextGen Gallery.
176
+ if ( ! is_multisite() ) {
177
+ $folders[] = self::get_ngg_galleries_path();
178
+ }
179
+
180
+ $folders = array_map( 'trailingslashit', $folders );
181
+ $folders = array_map( 'wp_normalize_path', $folders );
182
+
183
+ /**
184
+ * Add folders to the list of forbidden ones.
185
+ *
186
+ * @since 1.7
187
+ * @author Grégory Viguier
188
+ *
189
+ * @param array $added_folders List of absolute paths.
190
+ * @param array $folders List of folders already forbidden.
191
+ */
192
+ $added_folders = apply_filters( 'imagify_add_forbidden_folders', array(), $folders );
193
+ $added_folders = array_filter( (array) $added_folders );
194
+ $added_folders = array_filter( $added_folders, 'is_string' );
195
+
196
+ if ( ! $added_folders ) {
197
+ return $folders;
198
+ }
199
+
200
+ $added_folders = array_map( 'trailingslashit', $added_folders );
201
+ $added_folders = array_map( 'wp_normalize_path', $added_folders );
202
+
203
+ $folders = array_merge( $folders, $added_folders );
204
+ $folders = array_flip( array_flip( $folders ) );
205
+
206
+ return $folders;
207
+ }
208
+
209
+ /**
210
+ * Get the list of folder patterns where Imagify won't look for files to optimize.
211
+ * `^` will be prepended to each pattern (aka, the pattern must match an absolute path). Pattern delimiter is `@`. Paths tested against these patterns are lower-cased.
212
+ *
213
+ * @since 1.7
214
+ * @access public
215
+ * @author Grégory Viguier
216
+ *
217
+ * @return array A list of regex patterns.
218
+ */
219
+ public static function get_forbidden_folder_patterns() {
220
+ static $folders;
221
+
222
+ if ( isset( $folders ) ) {
223
+ return $folders;
224
+ }
225
+
226
+ $folders = array();
227
+
228
+ // NextGen Gallery.
229
+ if ( is_multisite() ) {
230
+ $folders[] = self::get_ngg_galleries_path();
231
+ }
232
+
233
+ if ( $folders ) {
234
+ $folders = array_map( 'trailingslashit', $folders );
235
+ $folders = array_map( 'wp_normalize_path', $folders );
236
+ $folders = array_map( 'preg_quote', $folders, array_fill( 1, count( $folders ), '@' ) );
237
+
238
+ // Must be done after `wp_normalize_path()` and `preg_quote()`.
239
+ foreach ( $folders as $i => $folder ) {
240
+ $folders[ $i ] = str_replace( '%BLOG_ID%', '\d+', $folder );
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Add folder patterns to the list of forbidden ones.
246
+ * Don't forget to use `trailingslashit()`, `wp_normalize_path()` and `preg_quote()`!
247
+ *
248
+ * @since 1.7
249
+ * @author Grégory Viguier
250
+ *
251
+ * @param array $added_folders List of patterns.
252
+ * @param array $folders List of patterns already forbidden.
253
+ */
254
+ $added_folders = apply_filters( 'imagify_add_forbidden_folder_patterns', array(), $folders );
255
+ $added_folders = array_filter( (array) $added_folders );
256
+ $added_folders = array_filter( $added_folders, 'is_string' );
257
+
258
+ if ( ! $added_folders ) {
259
+ return $folders;
260
+ }
261
+
262
+ $folders = array_merge( $folders, $added_folders );
263
+ $folders = array_flip( array_flip( $folders ) );
264
+
265
+ return $folders;
266
+ }
267
+
268
+ /**
269
+ * Tell if a file/folder name is forbidden.
270
+ *
271
+ * @since 1.7
272
+ * @access public
273
+ * @author Grégory Viguier
274
+ *
275
+ * @param string $file_name A file or folder name.
276
+ * @return bool
277
+ */
278
+ public static function is_filename_forbidden( $file_name ) {
279
+ static $file_names;
280
+
281
+ if ( ! isset( $file_names ) ) {
282
+ $file_names = array_flip( self::get_forbidden_file_names() );
283
+ }
284
+
285
+ return isset( $file_names[ strtolower( $file_name ) ] );
286
+ }
287
+
288
+ /**
289
+ * Get the list of file names that Imagify won't optimize.
290
+ * It can contain folder names. Names are case-lowered.
291
+ *
292
+ * @since 1.7
293
+ * @access public
294
+ * @author Grégory Viguier
295
+ *
296
+ * @return array A list of file names
297
+ */
298
+ public static function get_forbidden_file_names() {
299
+ static $file_names;
300
+
301
+ if ( isset( $file_names ) ) {
302
+ return $file_names;
303
+ }
304
+
305
+ $file_names = array(
306
+ '.',
307
+ '..',
308
+ '.DS_Store',
309
+ '.git',
310
+ '.svn',
311
+ 'node_modules',
312
+ 'Thumbs.db',
313
+ );
314
+ $file_names = array_map( 'strtolower', $file_names );
315
+
316
+ /**
317
+ * Add file names to the list of forbidden ones.
318
+ *
319
+ * @since 1.7
320
+ * @author Grégory Viguier
321
+ *
322
+ * @param array $added_file_names List of file names.
323
+ * @param array $file_names List of file names already forbidden.
324
+ */
325
+ $added_file_names = apply_filters( 'imagify_add_forbidden_file_names', array(), $file_names );
326
+
327
+ if ( ! $added_file_names || ! is_array( $added_file_names ) ) {
328
+ return $file_names;
329
+ }
330
+
331
+ $added_file_names = array_filter( $added_file_names, 'is_string' );
332
+ $added_file_names = array_map( 'strtolower', $added_file_names );
333
+
334
+ $file_names = array_merge( $file_names, $added_file_names );
335
+ $file_names = array_flip( array_flip( $file_names ) );
336
+
337
+ return $file_names;
338
+ }
339
+
340
+ /**
341
+ * Add a placeholder to a path.
342
+ *
343
+ * @since 1.7
344
+ * @access public
345
+ * @author Grégory Viguier
346
+ *
347
+ * @param string $file_path An absolute path.
348
+ * @return string A "placeholdered" path.
349
+ */
350
+ public static function add_placeholder( $file_path ) {
351
+ $file_path = wp_normalize_path( $file_path );
352
+ $locations = self::get_placeholder_paths();
353
+
354
+ foreach ( $locations as $placeholder => $location_path ) {
355
+ if ( strpos( $file_path, $location_path ) === 0 ) {
356
+ return str_replace( $location_path, $placeholder, $file_path );
357
+ }
358
+ }
359
+
360
+ // Should not happen.
361
+ return $file_path;
362
+ }
363
+
364
+ /**
365
+ * Change a path with a placeholder into a real path or URL.
366
+ *
367
+ * @since 1.7
368
+ * @access public
369
+ * @author Grégory Viguier
370
+ *
371
+ * @param string $file_path A path with a placeholder.
372
+ * @param string $type What to return: 'path' or 'url'.
373
+ * @return string An absolute path or a URL.
374
+ */
375
+ public static function remove_placeholder( $file_path, $type = 'path' ) {
376
+ if ( 'path' === $type ) {
377
+ $locations = self::get_placeholder_paths();
378
+ } else {
379
+ $locations = self::get_placeholder_urls();
380
+ }
381
+
382
+ foreach ( $locations as $placeholder => $location_path ) {
383
+ if ( strpos( $file_path, $placeholder ) === 0 ) {
384
+ return str_replace( $placeholder, $location_path, $file_path );
385
+ }
386
+ }
387
+
388
+ // Should not happen.
389
+ return $file_path;
390
+ }
391
+
392
+ /**
393
+ * Get array of pairs of placeholder => corresponding path.
394
+ *
395
+ * @since 1.7
396
+ * @access public
397
+ * @author Grégory Viguier
398
+ *
399
+ * @return array
400
+ */
401
+ public static function get_placeholder_paths() {
402
+ static $replacements;
403
+
404
+ if ( isset( $replacements ) ) {
405
+ return $replacements;
406
+ }
407
+
408
+ $replacements = array(
409
+ '{{PLUGINS}}/' => WP_PLUGIN_DIR . '/',
410
+ '{{MU_PLUGINS}}/' => WPMU_PLUGIN_DIR . '/',
411
+ '{{THEMES}}/' => WP_CONTENT_DIR . '/themes/',
412
+ '{{CONTENT}}/' => WP_CONTENT_DIR . '/',
413
+ '{{ABSPATH}}/' => ABSPATH,
414
+ );
415
+ $replacements = array_map( 'wp_normalize_path', $replacements );
416
+
417
+ return $replacements;
418
+ }
419
+
420
+ /**
421
+ * Get array of pairs of placeholder => corresponding URL.
422
+ *
423
+ * @since 1.7
424
+ * @access public
425
+ * @author Grégory Viguier
426
+ *
427
+ * @return array
428
+ */
429
+ public static function get_placeholder_urls() {
430
+ static $replacements;
431
+
432
+ if ( isset( $replacements ) ) {
433
+ return $replacements;
434
+ }
435
+
436
+ $replacements = array(
437
+ '{{PLUGINS}}/' => plugins_url( '/' ),
438
+ '{{MU_PLUGINS}}/' => plugins_url( '/', WPMU_PLUGIN_DIR . '/.' ),
439
+ '{{THEMES}}/' => content_url( 'themes/' ),
440
+ '{{CONTENT}}/' => content_url( '/' ),
441
+ '{{ABSPATH}}/' => site_url( '/' ),
442
+ );
443
+
444
+ return $replacements;
445
+ }
446
+
447
+ /**
448
+ * A file_exists() for paths with a placeholder.
449
+ *
450
+ * @since 1.7
451
+ * @access public
452
+ * @author Grégory Viguier
453
+ *
454
+ * @param string $file_path The file path.
455
+ * @return bool
456
+ */
457
+ public static function placeholder_path_exists( $file_path ) {
458
+ return imagify_get_filesystem()->is_readable( self::remove_placeholder( $file_path ) );
459
+ }
460
+
461
+ /**
462
+ * Normalize a file path, aiming for path comparison.
463
+ * The path is normalized, case-lowered, and a trailing slash is added.
464
+ *
465
+ * @since 1.7
466
+ * @access public
467
+ * @author Grégory Viguier
468
+ *
469
+ * @param string $file_path The file path.
470
+ * @return string The normalized file path.
471
+ */
472
+ public static function normalize_path_for_comparison( $file_path ) {
473
+ return strtolower( wp_normalize_path( trailingslashit( $file_path ) ) );
474
+ }
475
+
476
+ /**
477
+ * Get the path to NextGen galleries. On multisite, the path contains `%BLOG_ID%`, and must be used as a regex pattern.
478
+ *
479
+ * @since 1.7
480
+ * @access public
481
+ * @author Grégory Viguier
482
+ *
483
+ * @return string An absolute path.
484
+ */
485
+ public static function get_ngg_galleries_path() {
486
+ $ngg_options = get_site_option( 'ngg_options' );
487
+
488
+ if ( empty( $ngg_options['gallerypath'] ) ) {
489
+ if ( is_multisite() ) {
490
+ return get_imagify_upload_basedir( true ) . 'sites/%BLOG_ID%/nggallery';
491
+ }
492
+
493
+ return WP_CONTENT_DIR . '/gallery/';
494
+ }
495
+
496
+ $ngg_root = defined( 'NGG_GALLERY_ROOT_TYPE' ) ? NGG_GALLERY_ROOT_TYPE : 'site';
497
+
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() ) {
505
+ return $ngg_root . str_replace( '%BLOG_NAME%', get_bloginfo( 'name' ), $ngg_options['gallerypath'] );
506
+ }
507
+
508
+ return $ngg_root . $ngg_options['gallerypath'];
509
+ }
510
+
511
+ /**
512
+ * Get the path to WooCommerce logs.
513
+ *
514
+ * @since 1.7
515
+ * @access public
516
+ * @author Grégory Viguier
517
+ *
518
+ * @return string An absolute path.
519
+ */
520
+ public static function get_wc_logs_path() {
521
+ if ( defined( 'WC_LOG_DIR' ) ) {
522
+ return WC_LOG_DIR;
523
+ }
524
+
525
+ return get_imagify_upload_basedir( true ) . 'wc-logs/';
526
+ }
527
+
528
+ /**
529
+ * Get the path to EWWW optimization tools.
530
+ *
531
+ * @since 1.7
532
+ * @access public
533
+ * @author Grégory Viguier
534
+ *
535
+ * @return string An absolute path.
536
+ */
537
+ public static function get_ewww_tools_path() {
538
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_TOOL_PATH' ) ) {
539
+ return EWWW_IMAGE_OPTIMIZER_TOOL_PATH;
540
+ }
541
+
542
+ return WP_CONTENT_DIR . '/ewww/';
543
+ }
544
+ }
inc/classes/class-imagify-files-stats.php ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class handling stats related to "custom folders optimization".
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Files_Stats {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7
17
+ * @author Grégory Viguier
18
+ */
19
+ const VERSION = '1.0';
20
+
21
+
22
+ /** ----------------------------------------------------------------------------------------- */
23
+ /** COUNT FILES ============================================================================= */
24
+ /** ----------------------------------------------------------------------------------------- */
25
+
26
+ /**
27
+ * Count number of images in custom folders.
28
+ *
29
+ * @since 1.7
30
+ * @access public
31
+ * @author Grégory Viguier
32
+ *
33
+ * @return int The number of images.
34
+ */
35
+ public static function count_all_files() {
36
+ /**
37
+ * Filter the number of images in custom folders.
38
+ *
39
+ * @since 1.7
40
+ * @author Grégory Viguier
41
+ *
42
+ * @param int|bool $pre_count Default is false. Provide an integer.
43
+ */
44
+ $pre_count = apply_filters( 'imagify_count_files', false );
45
+
46
+ if ( false !== $pre_count ) {
47
+ return (int) $pre_count;
48
+ }
49
+
50
+ return self::count_files( 'all' );
51
+ }
52
+
53
+ /**
54
+ * Count number of images in custom folders with an error.
55
+ *
56
+ * @since 1.7
57
+ * @access public
58
+ * @author Grégory Viguier
59
+ *
60
+ * @return int The number of images.
61
+ */
62
+ public static function count_error_files() {
63
+ /**
64
+ * Filter the number of images in custom folders with an error.
65
+ *
66
+ * @since 1.7
67
+ * @author Grégory Viguier
68
+ *
69
+ * @param int|bool $pre_count Default is false. Provide an integer.
70
+ */
71
+ $pre_count = apply_filters( 'imagify_count_error_files', false );
72
+
73
+ if ( false !== $pre_count ) {
74
+ return (int) $pre_count;
75
+ }
76
+
77
+ return self::count_files( 'error' );
78
+ }
79
+
80
+ /**
81
+ * Count number of images successfully optimized by Imagify in custom folders.
82
+ *
83
+ * @since 1.7
84
+ * @access public
85
+ * @author Grégory Viguier
86
+ *
87
+ * @return int The number of images.
88
+ */
89
+ public static function count_success_files() {
90
+ /**
91
+ * Filter the number of images successfully optimized by Imagify in custom folders.
92
+ *
93
+ * @since 1.7
94
+ * @author Grégory Viguier
95
+ *
96
+ * @param int|bool $pre_count Default is false. Provide an integer.
97
+ */
98
+ $pre_count = apply_filters( 'imagify_count_success_files', false );
99
+
100
+ if ( false !== $pre_count ) {
101
+ return (int) $pre_count;
102
+ }
103
+
104
+ return self::count_files( 'success' );
105
+ }
106
+
107
+ /**
108
+ * Count number of optimized images in custom folders.
109
+ *
110
+ * @since 1.7
111
+ * @access public
112
+ * @author Grégory Viguier
113
+ *
114
+ * @return int The number of images.
115
+ */
116
+ public static function count_optimized_files() {
117
+ /**
118
+ * Filter the number of optimized images in custom folders.
119
+ *
120
+ * @since 1.7
121
+ * @author Grégory Viguier
122
+ *
123
+ * @param int|bool $pre_count Default is false. Provide an integer.
124
+ */
125
+ $pre_count = apply_filters( 'imagify_count_optimized_files', false );
126
+
127
+ if ( false !== $pre_count ) {
128
+ return (int) $pre_count;
129
+ }
130
+
131
+ return self::count_files( 'optimized' );
132
+ }
133
+
134
+ /**
135
+ * Count number of unoptimized images in custom folders.
136
+ *
137
+ * @since 1.7
138
+ * @access public
139
+ * @author Grégory Viguier
140
+ *
141
+ * @return int The number of images.
142
+ */
143
+ public static function count_unoptimized_files() {
144
+ /**
145
+ * Filter the number of unoptimized images in custom folders.
146
+ *
147
+ * @since 1.7
148
+ * @author Grégory Viguier
149
+ *
150
+ * @param int|bool $pre_count Default is false. Provide an integer.
151
+ */
152
+ $pre_count = apply_filters( 'imagify_count_unoptimized_files', false );
153
+
154
+ if ( false !== $pre_count ) {
155
+ return (int) $pre_count;
156
+ }
157
+
158
+ return self::count_files( 'unoptimized' );
159
+ }
160
+
161
+ /**
162
+ * Count number of images without status in custom folders.
163
+ *
164
+ * @since 1.7
165
+ * @access public
166
+ * @author Grégory Viguier
167
+ *
168
+ * @return int The number of images.
169
+ */
170
+ public static function count_no_status_files() {
171
+ /**
172
+ * Filter the number of images without status in custom folders.
173
+ *
174
+ * @since 1.7
175
+ * @author Grégory Viguier
176
+ *
177
+ * @param int|bool $pre_count Default is false. Provide an integer.
178
+ */
179
+ $pre_count = apply_filters( 'imagify_count_no_status_files', false );
180
+
181
+ if ( false !== $pre_count ) {
182
+ return (int) $pre_count;
183
+ }
184
+
185
+ return self::count_files( 'none' );
186
+ }
187
+
188
+ /**
189
+ * Count number of images in custom folders.
190
+ *
191
+ * @since 1.7
192
+ * @access public
193
+ * @author Grégory Viguier
194
+ *
195
+ * @param string $status The status of these folders: all, success, already_optimized, optimized, error, none, unoptimized.
196
+ * "none" if for files without status.
197
+ * "optimized" regroups "success" and "already_optimized".
198
+ * "unoptimized" regroups "error" and "none".
199
+ * @return int The number of images.
200
+ */
201
+ public static function count_files( $status = 'all' ) {
202
+ global $wpdb;
203
+ static $count = array();
204
+
205
+ $status = self::validate_status( $status );
206
+
207
+ if ( isset( $count[ $status ] ) ) {
208
+ return $count[ $status ];
209
+ }
210
+
211
+ $files_db = Imagify_Files_DB::get_instance();
212
+
213
+ if ( ! $files_db->can_operate() ) {
214
+ $count[ $status ] = 0;
215
+ return $count[ $status ];
216
+ }
217
+
218
+ switch ( $status ) {
219
+ case 'all':
220
+ $status = '';
221
+ break;
222
+
223
+ case 'none':
224
+ $status = 'status IS NULL';
225
+ break;
226
+
227
+ case 'optimized':
228
+ $status = "status IN ('success','already_optimized')";
229
+ break;
230
+
231
+ case 'unoptimized':
232
+ $status = "( status = 'error' OR status IS NULL )";
233
+ break;
234
+
235
+ default:
236
+ // "success", "already_optimized", "error".
237
+ $status = "status = '$status'";
238
+ }
239
+
240
+ $table_name = $files_db->get_table_name();
241
+ $status = $status ? "WHERE $status" : '';
242
+
243
+ $count[ $status ] = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
244
+ "SELECT COUNT( file_id ) FROM $table_name $status"
245
+ );
246
+
247
+ return $count[ $status ];
248
+ }
249
+
250
+
251
+ /** ----------------------------------------------------------------------------------------- */
252
+ /** PERCENTS ================================================================================ */
253
+ /** ----------------------------------------------------------------------------------------- */
254
+
255
+ /**
256
+ * Count percent of optimized images in custom folders.
257
+ *
258
+ * @since 1.7
259
+ * @access public
260
+ * @author Grégory Viguier
261
+ *
262
+ * @return int The percent of optimized images.
263
+ */
264
+ public static function percent_optimized_files() {
265
+ /**
266
+ * Filter the percent of optimized images in custom folders.
267
+ *
268
+ * @since 1.7
269
+ * @author Grégory Viguier
270
+ *
271
+ * @param int|bool $percent Default is false. Provide an integer.
272
+ */
273
+ $percent = apply_filters( 'imagify_percent_optimized_files', false );
274
+
275
+ if ( false !== $percent ) {
276
+ return (int) $percent;
277
+ }
278
+
279
+ $total_files = self::count_all_files();
280
+ $total_optimized_files = self::count_optimized_files();
281
+
282
+ if ( ! $total_files || ! $total_optimized_files ) {
283
+ return 0;
284
+ }
285
+
286
+ return min( round( 100 * $total_optimized_files / $total_files ), 100 );
287
+ }
288
+
289
+
290
+ /** ----------------------------------------------------------------------------------------- */
291
+ /** GET FILE SIZES ========================================================================== */
292
+ /** ----------------------------------------------------------------------------------------- */
293
+
294
+ /**
295
+ * Sum up all optimized sizes of all successfully optimized files.
296
+ *
297
+ * @since 1.7
298
+ * @access public
299
+ * @author Grégory Viguier
300
+ *
301
+ * @return int The sizes sum in bytes.
302
+ */
303
+ public static function get_optimized_size() {
304
+ /**
305
+ * Filter the optimized sizes of all successfully optimized files.
306
+ *
307
+ * @since 1.7
308
+ * @author Grégory Viguier
309
+ *
310
+ * @param int|bool $pre_size Default is false. Provide an integer.
311
+ */
312
+ $pre_size = apply_filters( 'imagify_get_optimized_files_size', false );
313
+
314
+ if ( false !== $pre_size ) {
315
+ return (int) $pre_size;
316
+ }
317
+
318
+ return self::get_size( 'optimized' );
319
+ }
320
+
321
+ /**
322
+ * Sum up all original sizes of all successfully optimized files.
323
+ *
324
+ * @since 1.7
325
+ * @access public
326
+ * @author Grégory Viguier
327
+ *
328
+ * @return int The sizes sum in bytes.
329
+ */
330
+ public static function get_original_size() {
331
+ /**
332
+ * Filter the original sizes of all successfully optimized files.
333
+ *
334
+ * @since 1.7
335
+ * @author Grégory Viguier
336
+ *
337
+ * @param int|bool $pre_size Default is false. Provide an integer.
338
+ */
339
+ $pre_size = apply_filters( 'imagify_get_original_files_size', false );
340
+
341
+ if ( false !== $pre_size ) {
342
+ return (int) $pre_size;
343
+ }
344
+
345
+ return self::get_size( 'original' );
346
+ }
347
+
348
+ /**
349
+ * Sum up all (optimized|original) sizes of all successfully optimized files.
350
+ *
351
+ * @since 1.7
352
+ * @access public
353
+ * @author Grégory Viguier
354
+ *
355
+ * @param string $type "optimized" or "original".
356
+ * @return int The sizes sum in bytes.
357
+ */
358
+ public static function get_size( $type = null ) {
359
+ global $wpdb;
360
+ static $sizes = array();
361
+
362
+ $type = 'optimized' === $type ? 'optimized_size' : 'original_size';
363
+
364
+ if ( isset( $sizes[ $type ] ) ) {
365
+ return $sizes[ $type ];
366
+ }
367
+
368
+ $files_db = Imagify_Files_DB::get_instance();
369
+
370
+ if ( ! $files_db->can_operate() ) {
371
+ $sizes[ $type ] = 0;
372
+ return $sizes[ $type ];
373
+ }
374
+
375
+ $table_name = $files_db->get_table_name();
376
+ $sizes[ $type ] = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
377
+ "SELECT SUM( $type ) FROM $table_name WHERE status = 'success'"
378
+ );
379
+
380
+ return $sizes[ $type ];
381
+ }
382
+
383
+ /**
384
+ * Sum up all original sizes.
385
+ *
386
+ * @since 1.7
387
+ * @access public
388
+ * @author Grégory Viguier
389
+ *
390
+ * @return int The sizes sum in bytes.
391
+ */
392
+ public static function get_overall_original_size() {
393
+ global $wpdb;
394
+ static $size;
395
+
396
+ if ( isset( $size ) ) {
397
+ return $size;
398
+ }
399
+
400
+ $files_db = Imagify_Files_DB::get_instance();
401
+
402
+ if ( ! $files_db->can_operate() ) {
403
+ $size = 0;
404
+ return $size;
405
+ }
406
+
407
+ $table_name = $files_db->get_table_name();
408
+ $size = round( $wpdb->get_var( "SELECT SUM( original_size ) FROM $table_name" ) ); // WPCS: unprepared SQL ok.
409
+
410
+ return $size;
411
+ }
412
+
413
+ /**
414
+ * Calculate the average size of the images uploaded per month.
415
+ *
416
+ * @since 1.7
417
+ * @access public
418
+ * @author Grégory Viguier
419
+ *
420
+ * @return int The current average size of images uploaded per month in bytes.
421
+ */
422
+ public static function calculate_average_size_per_month() {
423
+ global $wpdb;
424
+ static $average;
425
+
426
+ if ( isset( $average ) ) {
427
+ return $average;
428
+ }
429
+
430
+ $files_db = Imagify_Files_DB::get_instance();
431
+
432
+ if ( ! $files_db->can_operate() ) {
433
+ $average = 0;
434
+ return $average;
435
+ }
436
+
437
+ $table_name = $files_db->get_table_name();
438
+ $average = round( $wpdb->get_var( "SELECT AVG( size ) AS average_size_per_month FROM ( SELECT SUM( original_size ) AS size FROM $table_name GROUP BY YEAR( file_date ), MONTH( file_date ) ) AS size_per_month" ) ); // WPCS: unprepared SQL ok.
439
+
440
+ return $average;
441
+ }
442
+
443
+
444
+ /** ----------------------------------------------------------------------------------------- */
445
+ /** TOOLS =================================================================================== */
446
+ /** ----------------------------------------------------------------------------------------- */
447
+
448
+ /**
449
+ * Validate a status.
450
+ *
451
+ * @since 1.7
452
+ * @access public
453
+ * @author Grégory Viguier
454
+ *
455
+ * @param string $status The status of these folders: all, success, already_optimized, optimized, error, none, unoptimized.
456
+ * "none" if for files without status.
457
+ * "optimized" regroups "success" and "already_optimized".
458
+ * "unoptimized" regroups "error" and "none".
459
+ * @return string Fallback to 'all' if the status is not valid.
460
+ */
461
+ public static function validate_status( $status = 'all' ) {
462
+ $statuses = array(
463
+ 'all' => 1,
464
+ 'success' => 1,
465
+ 'already_optimized' => 1,
466
+ 'error' => 1,
467
+ 'none' => 1,
468
+ 'optimized' => 1,
469
+ 'unoptimized' => 1,
470
+ );
471
+
472
+ return isset( $statuses[ $status ] ) ? $status : 'all';
473
+ }
474
+ }
inc/classes/class-imagify-folders-db.php ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * DB class that handles files in "custom folders".
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Folders_DB extends Imagify_Abstract_DB {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ */
17
+ const VERSION = '1.0';
18
+
19
+ /**
20
+ * The single instance of the class.
21
+ *
22
+ * @var object
23
+ * @since 1.7
24
+ * @access protected
25
+ */
26
+ protected static $_instance;
27
+
28
+ /**
29
+ * The suffix used in the name of the database table (so, without the wpdb prefix).
30
+ *
31
+ * @var string
32
+ * @since 1.7
33
+ * @access protected
34
+ */
35
+ protected $table = 'imagify_folders';
36
+
37
+ /**
38
+ * The version of our database table.
39
+ *
40
+ * @var int
41
+ * @since 1.7
42
+ * @access protected
43
+ */
44
+ protected $table_version = 100;
45
+
46
+ /**
47
+ * Tell if the table is the same for each site of a Multisite.
48
+ *
49
+ * @var bool
50
+ * @since 1.7
51
+ * @access protected
52
+ */
53
+ protected $table_is_global = true;
54
+
55
+ /**
56
+ * The name of the primary column.
57
+ *
58
+ * @var string
59
+ * @since 1.7
60
+ * @access protected
61
+ */
62
+ protected $primary_key = 'folder_id';
63
+
64
+ /**
65
+ * Get the main Instance.
66
+ *
67
+ * @since 1.7
68
+ * @access public
69
+ * @author Grégory Viguier
70
+ *
71
+ * @return object Main instance.
72
+ */
73
+ public static function get_instance() {
74
+ if ( ! isset( self::$_instance ) ) {
75
+ self::$_instance = new self();
76
+ }
77
+
78
+ return self::$_instance;
79
+ }
80
+
81
+ /**
82
+ * Whitelist of columns.
83
+ *
84
+ * @since 1.7
85
+ * @access public
86
+ * @author Grégory Viguier
87
+ *
88
+ * @return array
89
+ */
90
+ public function get_columns() {
91
+ return array(
92
+ 'folder_id' => '%d',
93
+ 'path' => '%s',
94
+ 'active' => '%d',
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Default column values.
100
+ *
101
+ * @since 1.7
102
+ * @access public
103
+ * @author Grégory Viguier
104
+ *
105
+ * @return array
106
+ */
107
+ public function get_column_defaults() {
108
+ return array(
109
+ 'folder_id' => 0,
110
+ 'path' => '',
111
+ 'active' => 0,
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Get the query to create the table fields.
117
+ *
118
+ * @since 1.7
119
+ * @access protected
120
+ * @author Grégory Viguier
121
+ *
122
+ * @return string
123
+ */
124
+ protected function get_table_schema() {
125
+ return "
126
+ folder_id bigint(20) unsigned NOT NULL auto_increment,
127
+ path varchar(191) NOT NULL default '',
128
+ active tinyint(1) unsigned NOT NULL default 0,
129
+ PRIMARY KEY (folder_id),
130
+ UNIQUE KEY path (path),
131
+ KEY active (active)";
132
+ }
133
+
134
+ /**
135
+ * Tell if folders are selected in the plugin settings.
136
+ *
137
+ * @since 1.7
138
+ * @access public
139
+ * @author Grégory Viguier
140
+ *
141
+ * @return bool
142
+ */
143
+ public function has_active_folders() {
144
+ global $wpdb;
145
+
146
+ $column = esc_sql( $this->get_primary_key() );
147
+
148
+ return (bool) $wpdb->get_var( "SELECT $column FROM $this->table_name WHERE active = 1 LIMIT 1;" ); // WPCS: unprepared SQL ok.
149
+ }
150
+
151
+ /**
152
+ * Retrieve active folders (checked in the settings).
153
+ *
154
+ * @since 1.7
155
+ * @access public
156
+ * @author Grégory Viguier
157
+ *
158
+ * @param string $column_select A column name.
159
+ * @return array
160
+ */
161
+ public function get_active_folders_column( $column_select ) {
162
+ global $wpdb;
163
+
164
+ $column = esc_sql( $column_select );
165
+
166
+ $result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE active = 1;" ); // WPCS: unprepared SQL ok.
167
+
168
+ return $this->cast_col( $result, $column_select );
169
+ }
170
+
171
+ /**
172
+ * Retrieve active folders (checked in the settings) by the specified column / values.
173
+ *
174
+ * @since 1.7
175
+ * @access public
176
+ * @author Grégory Viguier
177
+ *
178
+ * @param string $column_select A column name.
179
+ * @param string $column_where A column name.
180
+ * @param array $column_values An array of values.
181
+ * @return array
182
+ */
183
+ public function get_active_folders_column_in( $column_select, $column_where, $column_values ) {
184
+ global $wpdb;
185
+
186
+ $column = esc_sql( $column_select );
187
+ $column_where = esc_sql( $column_where );
188
+ $column_values = Imagify_DB::prepare_values_list( $column_values );
189
+
190
+ $result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where IN ( $column_values ) AND active = 1;" ); // WPCS: unprepared SQL ok.
191
+
192
+ return $this->cast_col( $result, $column_select );
193
+ }
194
+
195
+ /**
196
+ * Retrieve active folders (checked in the settings) by the specified column / values.
197
+ *
198
+ * @since 1.7
199
+ * @access public
200
+ * @author Grégory Viguier
201
+ *
202
+ * @param string $column_select A column name.
203
+ * @param string $column_where A column name.
204
+ * @param array $column_values An array of values.
205
+ * @return array
206
+ */
207
+ public function get_active_folders_column_not_in( $column_select, $column_where, $column_values ) {
208
+ global $wpdb;
209
+
210
+ $column = esc_sql( $column_select );
211
+ $column_where = esc_sql( $column_where );
212
+ $column_values = Imagify_DB::prepare_values_list( $column_values );
213
+
214
+ $result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE $column_where NOT IN ( $column_values ) AND active = 1;" ); // WPCS: unprepared SQL ok.
215
+
216
+ return $this->cast_col( $result, $column_select );
217
+ }
218
+
219
+ /**
220
+ * Retrieve not active folders (not checked in the settings).
221
+ *
222
+ * @since 1.7
223
+ * @access public
224
+ * @author Grégory Viguier
225
+ *
226
+ * @param string $column_select A column name.
227
+ * @return array
228
+ */
229
+ public function get_inactive_folders_column( $column_select ) {
230
+ global $wpdb;
231
+
232
+ $column = esc_sql( $column_select );
233
+
234
+ $result = $wpdb->get_col( "SELECT $column FROM $this->table_name WHERE active != 1;" ); // WPCS: unprepared SQL ok.
235
+
236
+ return $this->cast_col( $result, $column_select );
237
+ }
238
+ }
inc/classes/class-imagify-notices.php CHANGED
@@ -7,7 +7,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
7
  * @since 1.6.10
8
  * @author Grégory Viguier
9
  */
10
- class Imagify_Notices {
11
 
12
  /**
13
  * Class version.
@@ -16,6 +16,13 @@ class Imagify_Notices {
16
  */
17
  const VERSION = '1.0';
18
 
 
 
 
 
 
 
 
19
  /**
20
  * Name of the user meta that stores the dismissed notice IDs.
21
  *
@@ -37,13 +44,6 @@ class Imagify_Notices {
37
  */
38
  const DEACTIVATE_PLUGIN_NONCE_ACTION = 'imagify-deactivate-plugin';
39
 
40
- /**
41
- * The path to the folder containing the views.
42
- *
43
- * @var string
44
- */
45
- protected static $views_folder = IMAGIFY_ADMIN_UI_PATH;
46
-
47
  /**
48
  * List of notice IDs.
49
  * They correspond to method names and IDs stored in the "dismissed" transient.
@@ -62,8 +62,8 @@ class Imagify_Notices {
62
  'http-block-external',
63
  // This warning is displayed when the grid view is active on the library. Dismissible.
64
  'grid-view',
65
- // This warning is displayed to warn the user that its quota is consumed for the current month. Dismissible.
66
- 'free-over-quota',
67
  // This warning is displayed if the backup folder is not writable. NOT dismissible.
68
  'backup-folder-not-writable',
69
  // This notice is displayed to rate the plugin after 100 optimizations & 7 days after the first installation. Dismissible.
@@ -143,11 +143,14 @@ class Imagify_Notices {
143
  * @author Grégory Viguier
144
  */
145
  public function init() {
146
- add_action( 'all_admin_notices', array( $this, 'render_notices' ) );
147
- add_action( 'wp_ajax_imagify_dismiss_notice', array( $this, 'admin_post_dismiss_notice' ) );
148
- add_action( 'admin_post_imagify_dismiss_notice', array( $this, 'admin_post_dismiss_notice' ) );
149
- add_action( 'imagify_dismiss_notice', array( $this, 'clear_scheduled_rating' ) );
150
- add_action( 'admin_post_imagify_deactivate_plugin', array( $this, 'deactivate_plugin' ) );
 
 
 
151
  }
152
 
153
 
@@ -174,9 +177,12 @@ class Imagify_Notices {
174
 
175
  if ( $data ) {
176
  // The notice must be displayed: render the view.
177
- $this->render_view( str_replace( '_', '-', $notice_id ), $data );
178
  }
179
  }
 
 
 
180
  }
181
 
182
  /**
@@ -224,7 +230,7 @@ class Imagify_Notices {
224
  public function clear_scheduled_rating( $notice ) {
225
  if ( 'rating' === $notice ) {
226
  set_site_transient( 'do_imagify_rating_cron', 'no' );
227
- wp_clear_scheduled_hook( 'imagify_rating_event' );
228
  }
229
  }
230
 
@@ -256,6 +262,40 @@ class Imagify_Notices {
256
  wp_send_json_success();
257
  }
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
  /** ----------------------------------------------------------------------------------------- */
261
  /** NOTICES ================================================================================= */
@@ -426,14 +466,14 @@ class Imagify_Notices {
426
  }
427
 
428
  /**
429
- * Tell if the 'over-quota' notice should be displayed.
430
  *
431
- * @since 1.6.10
432
- * @author Grégory Viguier
433
  *
434
  * @return bool|object An Imagify user object. False otherwise.
435
  */
436
- public function display_free_over_quota() {
437
  static $display;
438
 
439
  if ( isset( $display ) ) {
@@ -442,7 +482,7 @@ class Imagify_Notices {
442
 
443
  $display = false;
444
 
445
- if ( ! $this->user_can( 'free-over-quota' ) ) {
446
  return $display;
447
  }
448
 
@@ -450,14 +490,14 @@ class Imagify_Notices {
450
  return $display;
451
  }
452
 
453
- if ( self::notice_is_dismissed( 'free-over-quota' ) ) {
454
  return $display;
455
  }
456
 
457
  $user = new Imagify_User();
458
 
459
- // Don't display the notice if the user doesn't use all his quota or the API key isn't valid.
460
- if ( ! $user->is_over_quota() || ! imagify_valid_key() ) {
461
  return $display;
462
  }
463
 
@@ -577,6 +617,145 @@ class Imagify_Notices {
577
  }
578
 
579
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
  /** ----------------------------------------------------------------------------------------- */
581
  /** PUBLIC TOOLS ============================================================================ */
582
  /** ----------------------------------------------------------------------------------------- */
@@ -586,10 +765,9 @@ class Imagify_Notices {
586
  *
587
  * @since 1.6.10
588
  * @author Grégory Viguier
589
- * @see imagify_renew_notice()
590
  *
591
- * @param string $notice A notice ID.
592
- * @param int $user_id A user ID.
593
  */
594
  public static function renew_notice( $notice, $user_id = 0 ) {
595
  $user_id = $user_id ? (int) $user_id : get_current_user_id();
@@ -674,19 +852,6 @@ class Imagify_Notices {
674
  return false;
675
  }
676
 
677
- /**
678
- * Include the view file.
679
- *
680
- * @since 1.6.10
681
- * @author Grégory Viguier
682
- *
683
- * @param string $view The view ID.
684
- * @param mixed $data Some data to pass to the view.
685
- */
686
- public function render_view( $view, $data = array() ) {
687
- require self::$views_folder . 'notice-' . $view . '.php';
688
- }
689
-
690
 
691
  /** ----------------------------------------------------------------------------------------- */
692
  /** INTERNAL TOOLS ========================================================================== */
7
  * @since 1.6.10
8
  * @author Grégory Viguier
9
  */
10
+ class Imagify_Notices extends Imagify_Notices_Deprecated {
11
 
12
  /**
13
  * Class version.
16
  */
17
  const VERSION = '1.0';
18
 
19
+ /**
20
+ * Name of the transient storing temporary notices.
21
+ *
22
+ * @var string
23
+ */
24
+ const TEMPORARY_NOTICES_TRANSIENT_NAME = 'imagify_temporary_notices';
25
+
26
  /**
27
  * Name of the user meta that stores the dismissed notice IDs.
28
  *
44
  */
45
  const DEACTIVATE_PLUGIN_NONCE_ACTION = 'imagify-deactivate-plugin';
46
 
 
 
 
 
 
 
 
47
  /**
48
  * List of notice IDs.
49
  * They correspond to method names and IDs stored in the "dismissed" transient.
62
  'http-block-external',
63
  // This warning is displayed when the grid view is active on the library. Dismissible.
64
  'grid-view',
65
+ // This warning is displayed to warn the user that the quota is almost consumed for the current month. Dismissible.
66
+ 'almost-over-quota',
67
  // This warning is displayed if the backup folder is not writable. NOT dismissible.
68
  'backup-folder-not-writable',
69
  // This notice is displayed to rate the plugin after 100 optimizations & 7 days after the first installation. Dismissible.
143
  * @author Grégory Viguier
144
  */
145
  public function init() {
146
+ // For generic purpose.
147
+ add_action( 'all_admin_notices', array( $this, 'render_notices' ) );
148
+ add_action( 'wp_ajax_imagify_dismiss_notice', array( $this, 'admin_post_dismiss_notice' ) );
149
+ add_action( 'admin_post_imagify_dismiss_notice', array( $this, 'admin_post_dismiss_notice' ) );
150
+ // For specific notices.
151
+ add_action( 'imagify_dismiss_notice', array( $this, 'clear_scheduled_rating' ) );
152
+ add_action( 'admin_post_imagify_deactivate_plugin', array( $this, 'deactivate_plugin' ) );
153
+ add_action( 'imagify_not_almost_over_quota_anymore', array( $this, 'renew_almost_over_quota_notice' ) );
154
  }
155
 
156
 
177
 
178
  if ( $data ) {
179
  // The notice must be displayed: render the view.
180
+ Imagify_Views::get_instance()->print_template( 'notice-' . $notice_id, $data );
181
  }
182
  }
183
+
184
+ // Temporary notices.
185
+ $this->render_temporary_notices();
186
  }
187
 
188
  /**
230
  public function clear_scheduled_rating( $notice ) {
231
  if ( 'rating' === $notice ) {
232
  set_site_transient( 'do_imagify_rating_cron', 'no' );
233
+ Imagify_Cron_Rating::get_instance()->unschedule_event();
234
  }
235
  }
236
 
262
  wp_send_json_success();
263
  }
264
 
265
+ /**
266
+ * Renew the "almost-over-quota" notice when the consumed quota percent decreases back below 80%.
267
+ *
268
+ * @since 1.7
269
+ * @author Grégory Viguier
270
+ */
271
+ public function renew_almost_over_quota_notice() {
272
+ global $wpdb;
273
+
274
+ $results = $wpdb->get_results( $wpdb->prepare( "SELECT umeta_id, user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value LIKE '%almost-over-quota%'", self::DISMISS_META_NAME ) );
275
+
276
+ if ( ! $results ) {
277
+ return;
278
+ }
279
+
280
+ // Prevent multiple queries to the DB by caching user metas.
281
+ $not_cached = array();
282
+
283
+ foreach ( $results as $result ) {
284
+ if ( ! wp_cache_get( $result->umeta_id, 'user_meta' ) ) {
285
+ $not_cached[] = $result->umeta_id;
286
+ }
287
+ }
288
+
289
+ if ( $not_cached ) {
290
+ update_meta_cache( 'user', $not_cached );
291
+ }
292
+
293
+ // Renew the notice for all users.
294
+ foreach ( $results as $result ) {
295
+ self::renew_notice( 'almost-over-quota', $result->user_id );
296
+ }
297
+ }
298
+
299
 
300
  /** ----------------------------------------------------------------------------------------- */
301
  /** NOTICES ================================================================================= */
466
  }
467
 
468
  /**
469
+ * Tell if the 'almost-over-quota' notice should be displayed.
470
  *
471
+ * @since 1.7.0
472
+ * @author Geoffrey Crofte
473
  *
474
  * @return bool|object An Imagify user object. False otherwise.
475
  */
476
+ public function display_almost_over_quota() {
477
  static $display;
478
 
479
  if ( isset( $display ) ) {
482
 
483
  $display = false;
484
 
485
+ if ( ! $this->user_can( 'almost-over-quota' ) ) {
486
  return $display;
487
  }
488
 
490
  return $display;
491
  }
492
 
493
+ if ( self::notice_is_dismissed( 'almost-over-quota' ) ) {
494
  return $display;
495
  }
496
 
497
  $user = new Imagify_User();
498
 
499
+ // Don't display the notice if the user's unconsumed quota is superior to 20%.
500
+ if ( $user->get_percent_unconsumed_quota() > 20 ) {
501
  return $display;
502
  }
503
 
617
  }
618
 
619
 
620
+ /** ----------------------------------------------------------------------------------------- */
621
+ /** TEMPORARY NOTICES ======================================================================= */
622
+ /** ----------------------------------------------------------------------------------------- */
623
+
624
+ /**
625
+ * Maybe display some notices.
626
+ *
627
+ * @since 1.7
628
+ * @access public
629
+ * @author Grégory Viguier
630
+ */
631
+ public function render_temporary_notices() {
632
+ if ( is_network_admin() ) {
633
+ $notices = $this->get_network_temporary_notices();
634
+ } else {
635
+ $notices = $this->get_site_temporary_notices();
636
+ }
637
+
638
+ if ( ! $notices ) {
639
+ return;
640
+ }
641
+
642
+ $views = Imagify_Views::get_instance();
643
+
644
+ foreach ( $notices as $i => $notice_data ) {
645
+ $notices[ $i ]['type'] = ! empty( $notice_data['type'] ) ? $notice_data['type'] : 'error';
646
+ }
647
+
648
+ $views->print_template( 'notice-temporary', $notices );
649
+ }
650
+
651
+ /**
652
+ * Get temporary notices for the network.
653
+ *
654
+ * @since 1.7
655
+ * @access public
656
+ * @author Grégory Viguier
657
+ *
658
+ * @return array
659
+ */
660
+ public function get_network_temporary_notices() {
661
+ $notices = get_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
662
+
663
+ if ( false === $notices ) {
664
+ return array();
665
+ }
666
+
667
+ delete_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
668
+
669
+ return $notices && is_array( $notices ) ? $notices : array();
670
+ }
671
+
672
+ /**
673
+ * Create a temporary notice for the network.
674
+ *
675
+ * @since 1.7
676
+ * @access public
677
+ * @author Grégory Viguier
678
+ *
679
+ * @param array|object|string $notice_data Some data, with the message to display.
680
+ */
681
+ public function add_network_temporary_notice( $notice_data ) {
682
+ $notices = get_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
683
+ $notices = is_array( $notices ) ? $notices : array();
684
+
685
+ if ( is_wp_error( $notice_data ) ) {
686
+ $notice_data = $notice_data->get_error_messages();
687
+ $notice_data = implode( '<br/>', $notice_data );
688
+ }
689
+
690
+ if ( is_string( $notice_data ) ) {
691
+ $notice_data = array(
692
+ 'message' => $notice_data,
693
+ );
694
+ } elseif ( is_object( $notice_data ) ) {
695
+ $notice_data = (array) $notice_data;
696
+ }
697
+
698
+ if ( ! is_array( $notice_data ) || empty( $notice_data['message'] ) ) {
699
+ return;
700
+ }
701
+
702
+ $notices[] = $notice_data;
703
+
704
+ set_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME, $notices, 30 );
705
+ }
706
+
707
+ /**
708
+ * Get temporary notices for the current site.
709
+ *
710
+ * @since 1.7
711
+ * @access public
712
+ * @author Grégory Viguier
713
+ *
714
+ * @return array
715
+ */
716
+ public function get_site_temporary_notices() {
717
+ $notices = get_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
718
+
719
+ if ( false === $notices ) {
720
+ return array();
721
+ }
722
+
723
+ delete_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
724
+
725
+ return $notices && is_array( $notices ) ? $notices : array();
726
+ }
727
+
728
+ /**
729
+ * Create a temporary notice for the current site.
730
+ *
731
+ * @since 1.7
732
+ * @access public
733
+ * @author Grégory Viguier
734
+ *
735
+ * @param array|string $notice_data Some data, with the message to display.
736
+ */
737
+ public function add_site_temporary_notice( $notice_data ) {
738
+ $notices = get_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
739
+ $notices = is_array( $notices ) ? $notices : array();
740
+
741
+ if ( is_string( $notice_data ) ) {
742
+ $notice_data = array(
743
+ 'message' => $notice_data,
744
+ );
745
+ } elseif ( is_object( $notice_data ) ) {
746
+ $notice_data = (array) $notice_data;
747
+ }
748
+
749
+ if ( ! is_array( $notice_data ) || empty( $notice_data['message'] ) ) {
750
+ return;
751
+ }
752
+
753
+ $notices[] = $notice_data;
754
+
755
+ set_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME, $notices, 30 );
756
+ }
757
+
758
+
759
  /** ----------------------------------------------------------------------------------------- */
760
  /** PUBLIC TOOLS ============================================================================ */
761
  /** ----------------------------------------------------------------------------------------- */
765
  *
766
  * @since 1.6.10
767
  * @author Grégory Viguier
 
768
  *
769
+ * @param string $notice A notice ID.
770
+ * @param int $user_id A user ID.
771
  */
772
  public static function renew_notice( $notice, $user_id = 0 ) {
773
  $user_id = $user_id ? (int) $user_id : get_current_user_id();
852
  return false;
853
  }
854
 
 
 
 
 
 
 
 
 
 
 
 
 
 
855
 
856
  /** ----------------------------------------------------------------------------------------- */
857
  /** INTERNAL TOOLS ========================================================================== */
inc/classes/class-imagify-options.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that handles the plugin options.
6
+ *
7
+ * @since 1.7
8
+ */
9
+ class Imagify_Options extends Imagify_Abstract_Options {
10
+
11
+ /**
12
+ * Class version.
13
+ *
14
+ * @var string
15
+ * @since 1.7
16
+ */
17
+ const VERSION = '1.0';
18
+
19
+ /**
20
+ * Suffix used in the name of the option.
21
+ *
22
+ * @var string
23
+ * @since 1.7
24
+ * @access protected
25
+ */
26
+ protected $identifier = 'settings';
27
+
28
+ /**
29
+ * The default values for the Imagify main options.
30
+ * These are the "zero state" values.
31
+ * Don't use null as value.
32
+ *
33
+ * @var array
34
+ * @since 1.7
35
+ * @access protected
36
+ */
37
+ protected $default_values = array(
38
+ 'api_key' => '',
39
+ 'optimization_level' => 0,
40
+ 'auto_optimize' => 0,
41
+ 'backup' => 0,
42
+ 'resize_larger' => 0,
43
+ 'resize_larger_w' => 0,
44
+ 'exif' => 0,
45
+ 'disallowed-sizes' => array(),
46
+ 'admin_bar_menu' => 0,
47
+ );
48
+
49
+ /**
50
+ * The Imagify main option values used when they are set the first time or reset.
51
+ * Values identical to default values are not listed.
52
+ *
53
+ * @var array
54
+ * @since 1.7
55
+ * @access protected
56
+ */
57
+ protected $reset_values = array(
58
+ 'optimization_level' => 1,
59
+ 'auto_optimize' => 1,
60
+ 'backup' => 1,
61
+ 'admin_bar_menu' => 1,
62
+ );
63
+
64
+ /**
65
+ * The single instance of the class.
66
+ *
67
+ * @var object
68
+ * @since 1.7
69
+ * @access protected
70
+ */
71
+ protected static $_instance;
72
+
73
+ /**
74
+ * The constructor.
75
+ * Side note: $this->hook_identifier value is "option".
76
+ *
77
+ * @since 1.7
78
+ * @author Grégory Viguier
79
+ * @access protected
80
+ */
81
+ protected function __construct() {
82
+ if ( defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) {
83
+ $this->default_values['api_key'] = (string) IMAGIFY_API_KEY;
84
+ }
85
+
86
+ $this->network_option = imagify_is_active_for_network();
87
+
88
+ parent::__construct();
89
+ }
90
+
91
+ /**
92
+ * Get the main Instance.
93
+ *
94
+ * @since 1.7
95
+ * @author Grégory Viguier
96
+ * @access public
97
+ *
98
+ * @return object Main instance.
99
+ */
100
+ public static function get_instance() {
101
+ if ( ! isset( self::$_instance ) ) {
102
+ self::$_instance = new self();
103
+ }
104
+
105
+ return self::$_instance;
106
+ }
107
+
108
+
109
+ /** ----------------------------------------------------------------------------------------- */
110
+ /** SANITIZATION, VALIDATION ================================================================ */
111
+ /** ----------------------------------------------------------------------------------------- */
112
+
113
+ /**
114
+ * Sanitize and validate an option value. Basic casts have been made.
115
+ *
116
+ * @since 1.7
117
+ * @author Grégory Viguier
118
+ * @access public
119
+ *
120
+ * @param string $key The option key.
121
+ * @param mixed $value The value.
122
+ * @param mixed $default The default value.
123
+ * @return mixed
124
+ */
125
+ public function sanitize_and_validate_value( $key, $value, $default ) {
126
+ static $max_sizes;
127
+
128
+ switch ( $key ) {
129
+ case 'api_key':
130
+ if ( defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) {
131
+ return (string) IMAGIFY_API_KEY;
132
+ }
133
+ return $value ? sanitize_key( $value ) : '';
134
+
135
+ case 'optimization_level':
136
+ if ( $value < 0 || $value > 2 ) {
137
+ // For an invalid value, return the "reset" value.
138
+ $reset_values = $this->get_reset_values();
139
+ return $reset_values[ $key ];
140
+ }
141
+ return $value;
142
+
143
+ case 'auto_optimize':
144
+ case 'backup':
145
+ case 'resize_larger':
146
+ case 'exif':
147
+ case 'admin_bar_menu':
148
+ return 1;
149
+
150
+ case 'resize_larger_w':
151
+ if ( $value <= 0 ) {
152
+ // Invalid.
153
+ return 0;
154
+ }
155
+ if ( ! isset( $max_sizes ) ) {
156
+ $max_sizes = get_imagify_max_intermediate_image_size();
157
+ }
158
+ if ( $value < $max_sizes['width'] ) {
159
+ // Invalid.
160
+ return $max_sizes['width'];
161
+ }
162
+ return $value;
163
+
164
+ case 'disallowed-sizes':
165
+ if ( ! $value ) {
166
+ return $default;
167
+ }
168
+
169
+ $value = array_keys( $value );
170
+ $value = array_map( 'sanitize_text_field', $value );
171
+ return array_fill_keys( $value, 1 );
172
+ }
173
+
174
+ return false;
175
+ }
176
+
177
+ /**
178
+ * Validate Imagify's options before storing them. Basic sanitization and validation have been made, row by row.
179
+ *
180
+ * @since 1.7
181
+ * @author Grégory Viguier
182
+ * @access public
183
+ *
184
+ * @param string $values The option value.
185
+ * @return array
186
+ */
187
+ public function validate_values_on_update( $values ) {
188
+ // The max width for the "Resize larger images" option can't be 0.
189
+ if ( empty( $values['resize_larger_w'] ) ) {
190
+ unset( $values['resize_larger'], $values['resize_larger_w'] );
191
+ }
192
+
193
+ return $values;
194
+ }
195
+ }
inc/classes/class-imagify-settings.php ADDED
@@ -0,0 +1,662 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that handles the plugin settings.
6
+ *
7
+ * @since 1.7
8
+ */
9
+ class Imagify_Settings {
10
+
11
+ /**
12
+ * Class version.
13
+ *
14
+ * @var string
15
+ * @since 1.7
16
+ */
17
+ const VERSION = '1.0';
18
+
19
+ /**
20
+ * The settings group.
21
+ *
22
+ * @var string
23
+ * @since 1.7
24
+ */
25
+ protected $settings_group;
26
+
27
+ /**
28
+ * The option name.
29
+ *
30
+ * @var string
31
+ * @since 1.7
32
+ */
33
+ protected $option_name;
34
+
35
+ /**
36
+ * The options instance.
37
+ *
38
+ * @var object
39
+ * @since 1.7
40
+ */
41
+ protected $options;
42
+
43
+ /**
44
+ * The single instance of the class.
45
+ *
46
+ * @var object
47
+ * @since 1.7
48
+ * @access protected
49
+ */
50
+ protected static $_instance;
51
+
52
+ /**
53
+ * The constructor.
54
+ *
55
+ * @since 1.7
56
+ * @author Grégory Viguier
57
+ * @access protected
58
+ */
59
+ protected function __construct() {
60
+ $this->options = Imagify_Options::get_instance();
61
+ $this->option_name = $this->options->get_option_name();
62
+ $this->settings_group = IMAGIFY_SLUG;
63
+ }
64
+
65
+ /**
66
+ * Get the main Instance.
67
+ *
68
+ * @since 1.7
69
+ * @author Grégory Viguier
70
+ * @access public
71
+ *
72
+ * @return object Main instance.
73
+ */
74
+ public static function get_instance() {
75
+ if ( ! isset( self::$_instance ) ) {
76
+ self::$_instance = new self();
77
+ }
78
+
79
+ return self::$_instance;
80
+ }
81
+
82
+ /**
83
+ * Launch the hooks.
84
+ *
85
+ * @since 1.7
86
+ * @author Grégory Viguier
87
+ * @access public
88
+ */
89
+ public function init() {
90
+ add_filter( 'sanitize_option_' . $this->option_name, array( $this, 'populate_values_on_save' ), 5 );
91
+ add_action( 'admin_init', array( $this, 'register' ) );
92
+ add_filter( 'option_page_capability_' . $this->settings_group, array( $this, 'get_capability' ) );
93
+
94
+ if ( imagify_is_active_for_network() ) {
95
+ add_filter( 'pre_update_site_option_' . $this->option_name, array( $this, 'maybe_set_redirection' ), 10, 2 );
96
+ add_action( 'update_site_option_' . $this->option_name, array( $this, 'after_save_network_options' ), 10, 3 );
97
+ add_action( 'admin_post_update', array( $this, 'update_site_option_on_network' ) );
98
+ } else {
99
+ add_filter( 'pre_update_option_' . $this->option_name, array( $this, 'maybe_set_redirection' ), 10, 2 );
100
+ add_action( 'update_option_' . $this->option_name, array( $this, 'after_save_options' ), 10, 2 );
101
+ }
102
+ }
103
+
104
+
105
+ /** ----------------------------------------------------------------------------------------- */
106
+ /** VARIOUS HELPERS ========================================================================= */
107
+ /** ----------------------------------------------------------------------------------------- */
108
+
109
+ /**
110
+ * Get the name of the settings group.
111
+ *
112
+ * @since 1.7
113
+ * @author Grégory Viguier
114
+ * @access public
115
+ *
116
+ * @return string
117
+ */
118
+ public function get_settings_group() {
119
+ return $this->settings_group;
120
+ }
121
+
122
+ /**
123
+ * Get the URL to use as form action.
124
+ *
125
+ * @since 1.7
126
+ * @author Grégory Viguier
127
+ * @access public
128
+ *
129
+ * @return string
130
+ */
131
+ public function get_form_action() {
132
+ return imagify_is_active_for_network() ? admin_url( 'admin-post.php' ) : admin_url( 'options.php' );
133
+ }
134
+
135
+ /**
136
+ * Tell if we're submitting the settings form.
137
+ *
138
+ * @since 1.7
139
+ * @author Grégory Viguier
140
+ * @access public
141
+ *
142
+ * @return bool
143
+ */
144
+ public function is_form_submit() {
145
+ return filter_input( INPUT_POST, 'option_page', FILTER_SANITIZE_STRING ) === $this->settings_group && filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING ) === 'update';
146
+ }
147
+
148
+
149
+ /** ----------------------------------------------------------------------------------------- */
150
+ /** ON FORM SUBMIT ========================================================================== */
151
+ /** ----------------------------------------------------------------------------------------- */
152
+
153
+ /**
154
+ * On form submit, handle some specific values.
155
+ * This must be hooked before Imagify_Options::sanitize_and_validate_on_update().
156
+ *
157
+ * @since 1.7
158
+ * @author Grégory Viguier
159
+ * @access public
160
+ *
161
+ * @param array $values The option values.
162
+ * @return array
163
+ */
164
+ public function populate_values_on_save( $values ) {
165
+ if ( ! $this->is_form_submit() ) {
166
+ return $values;
167
+ }
168
+
169
+ $values = is_array( $values ) ? $values : array();
170
+
171
+ /**
172
+ * Disabled thumbnail sizes.
173
+ */
174
+ $values = $this->populate_disallowed_sizes( $values );
175
+
176
+ /**
177
+ * Custom folders.
178
+ */
179
+ $values = $this->populate_custom_folders( $values );
180
+
181
+ return $values;
182
+ }
183
+
184
+ /**
185
+ * On form submit, handle disallowed thumbnail sizes.
186
+ *
187
+ * @since 1.7
188
+ * @access protected
189
+ * @author Grégory Viguier
190
+ *
191
+ * @param array $values The option values.
192
+ * @return array
193
+ */
194
+ protected function populate_disallowed_sizes( $values ) {
195
+ $values['disallowed-sizes'] = array();
196
+
197
+ if ( isset( $values['disallowed-sizes-reversed'] ) && is_array( $values['disallowed-sizes-reversed'] ) ) {
198
+ $checked = ! empty( $values['disallowed-sizes-checked'] ) && is_array( $values['disallowed-sizes-checked'] ) ? array_flip( $values['disallowed-sizes-checked'] ) : array();
199
+
200
+ if ( ! empty( $values['disallowed-sizes-reversed'] ) ) {
201
+ foreach ( $values['disallowed-sizes-reversed'] as $size_key ) {
202
+ if ( ! isset( $checked[ $size_key ] ) ) {
203
+ // The checkbox is not checked: the size is disabled.
204
+ $values['disallowed-sizes'][ $size_key ] = 1;
205
+ }
206
+ }
207
+ }
208
+ }
209
+
210
+ unset( $values['disallowed-sizes-reversed'], $values['disallowed-sizes-checked'] );
211
+
212
+ return $values;
213
+ }
214
+
215
+ /**
216
+ * On form submit, handle the custom folders.
217
+ *
218
+ * @since 1.7
219
+ * @access protected
220
+ * @author Grégory Viguier
221
+ *
222
+ * @param array $values The option values.
223
+ * @return array
224
+ */
225
+ protected function populate_custom_folders( $values ) {
226
+ if ( ! imagify_can_optimize_custom_folders() ) {
227
+ // The databases are not ready or the user has not the permission.
228
+ unset( $values['custom_folders'] );
229
+ return $values;
230
+ }
231
+
232
+ if ( ! isset( $values['custom_folders'] ) ) {
233
+ // No selected folders: set them all inactive.
234
+ Imagify_Custom_Folders::deactivate_all_folders();
235
+ // Remove files that are in inactive folders and are not optimized.
236
+ Imagify_Custom_Folders::remove_unoptimized_files_from_inactive_folders();
237
+ // Remove empty inactive folders.
238
+ Imagify_Custom_Folders::remove_empty_inactive_folders();
239
+
240
+ return $values;
241
+ }
242
+
243
+ if ( ! is_array( $values['custom_folders'] ) ) {
244
+ // Invalid value.
245
+ unset( $values['custom_folders'] );
246
+ return $values;
247
+ }
248
+
249
+ $selected = array_filter( $values['custom_folders'] );
250
+ unset( $values['custom_folders'] );
251
+
252
+ if ( ! $selected ) {
253
+ // No selected folders: set them all inactive.
254
+ Imagify_Custom_Folders::deactivate_all_folders();
255
+ // Remove files that are in inactive folders and are not optimized.
256
+ Imagify_Custom_Folders::remove_unoptimized_files_from_inactive_folders();
257
+ // Remove empty inactive folders.
258
+ Imagify_Custom_Folders::remove_empty_inactive_folders();
259
+
260
+ return $values;
261
+ }
262
+
263
+ // Normalize the paths, remove duplicates, and remove sub-paths.
264
+ $selected = array_map( 'sanitize_text_field', $selected );
265
+ $selected = array_map( 'wp_normalize_path', $selected );
266
+ $selected = array_map( 'trailingslashit', $selected );
267
+ $selected = array_flip( array_flip( $selected ) );
268
+ $selected = Imagify_Custom_Folders::remove_sub_paths( $selected );
269
+
270
+ // Remove the active status from the folders that are not selected.
271
+ Imagify_Custom_Folders::deactivate_not_selected_folders( $selected );
272
+
273
+ // Add the active status to the folders that are selected (and already in the DB).
274
+ $selected = Imagify_Custom_Folders::activate_selected_folders( $selected );
275
+
276
+ // If we still have paths here, they need to be added to the DB with an active status.
277
+ Imagify_Custom_Folders::insert_folders( $selected );
278
+
279
+ // Remove files that are in inactive folders and are not optimized.
280
+ Imagify_Custom_Folders::remove_unoptimized_files_from_inactive_folders();
281
+
282
+ // Reassign files to active folders.
283
+ Imagify_Custom_Folders::reassign_inactive_files();
284
+
285
+ // Remove empty inactive folders.
286
+ Imagify_Custom_Folders::remove_empty_inactive_folders();
287
+
288
+ return $values;
289
+ }
290
+
291
+
292
+ /** ----------------------------------------------------------------------------------------- */
293
+ /** SETTINGS API ============================================================================ */
294
+ /** ----------------------------------------------------------------------------------------- */
295
+
296
+ /**
297
+ * Add Imagify' settings to the settings API whitelist.
298
+ *
299
+ * @since 1.7
300
+ * @author Grégory Viguier
301
+ * @access public
302
+ */
303
+ public function register() {
304
+ register_setting( $this->settings_group, $this->option_name );
305
+ }
306
+
307
+ /**
308
+ * Set the user capacity needed to save Imagify's main options from the settings page.
309
+ *
310
+ * @since 1.7
311
+ * @author Grégory Viguier
312
+ * @access public
313
+ */
314
+ public function get_capability() {
315
+ return imagify_get_capacity();
316
+ }
317
+
318
+ /**
319
+ * If the user clicked the "Save & Go to Bulk Optimizer" button, set a redirection to the bulk optimizer.
320
+ * We use this hook because it can be triggered even if the option value hasn't changed.
321
+ *
322
+ * @since 1.7
323
+ * @author Grégory Viguier
324
+ * @access public
325
+ *
326
+ * @param mixed $value The new, unserialized option value.
327
+ * @param mixed $old_value The old option value.
328
+ * @return mixed The option value.
329
+ */
330
+ public function maybe_set_redirection( $value, $old_value ) {
331
+ if ( isset( $_POST['submit-goto-bulk'] ) ) { // WPCS: CSRF ok.
332
+ $_REQUEST['_wp_http_referer'] = esc_url_raw( get_admin_url( get_current_blog_id(), 'upload.php?page=imagify-bulk-optimization' ) );
333
+ }
334
+
335
+ return $value;
336
+ }
337
+
338
+ /**
339
+ * Used to launch some actions after saving the network options.
340
+ *
341
+ * @since 1.7
342
+ * @author Grégory Viguier
343
+ * @access public
344
+ *
345
+ * @param string $option Name of the network option.
346
+ * @param mixed $value Current value of the network option.
347
+ * @param mixed $old_value Old value of the network option.
348
+ */
349
+ public function after_save_network_options( $option, $value, $old_value ) {
350
+ $this->after_save_options( $old_value, $value );
351
+ }
352
+
353
+ /**
354
+ * Used to launch some actions after saving the options.
355
+ *
356
+ * @since 1.7
357
+ * @author Grégory Viguier
358
+ * @access public
359
+ *
360
+ * @param mixed $old_value The old option value.
361
+ * @param mixed $value The new option value.
362
+ */
363
+ public function after_save_options( $old_value, $value ) {
364
+ if ( ! $value || isset( $old_value['api_key'], $value['api_key'] ) && $old_value['api_key'] === $value['api_key'] ) {
365
+ return;
366
+ }
367
+
368
+ if ( is_wp_error( get_imagify_user() ) ) {
369
+ Imagify_Notices::renew_notice( 'wrong-api-key' );
370
+ delete_site_transient( 'imagify_check_licence_1' );
371
+ } else {
372
+ Imagify_Notices::dismiss_notice( 'wrong-api-key' );
373
+ }
374
+ }
375
+
376
+ /**
377
+ * `options.php` does not handle network options. Let's use `admin-post.php` for multisite installations.
378
+ *
379
+ * @since 1.7
380
+ * @author Grégory Viguier
381
+ * @access public
382
+ */
383
+ public function update_site_option_on_network() {
384
+ if ( empty( $_POST['option_page'] ) || $_POST['option_page'] !== $this->settings_group ) { // WPCS: CSRF ok.
385
+ return;
386
+ }
387
+
388
+ $capability = apply_filters( 'option_page_capability_' . $this->settings_group, 'manage_network_options' );
389
+
390
+ if ( ! current_user_can( $capability ) ) {
391
+ imagify_die();
392
+ }
393
+
394
+ imagify_check_nonce( $this->settings_group . '-options' );
395
+
396
+ $whitelist_options = apply_filters( 'whitelist_options', array() );
397
+
398
+ if ( ! isset( $whitelist_options[ $this->settings_group ] ) ) {
399
+ imagify_die( __( '<strong>ERROR</strong>: options page not found.' ) );
400
+ }
401
+
402
+ $options = $whitelist_options[ $this->settings_group ];
403
+
404
+ if ( $options ) {
405
+ foreach ( $options as $option ) {
406
+ $option = trim( $option );
407
+ $value = null;
408
+
409
+ if ( isset( $_POST[ $option ] ) ) {
410
+ $value = $_POST[ $option ];
411
+ if ( ! is_array( $value ) ) {
412
+ $value = trim( $value );
413
+ }
414
+ $value = wp_unslash( $value );
415
+ }
416
+
417
+ update_site_option( $option, $value );
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Redirect back to the settings page that was submitted.
423
+ */
424
+ imagify_maybe_redirect( false, array( 'settings-updated' => 'true' ) );
425
+ }
426
+
427
+
428
+ /** ----------------------------------------------------------------------------------------- */
429
+ /** FIELDS ================================================================================== */
430
+ /** ----------------------------------------------------------------------------------------- */
431
+
432
+ /**
433
+ * Display a single checkbox.
434
+ *
435
+ * @since 1.7
436
+ * @author Grégory Viguier
437
+ * @access public
438
+ *
439
+ * @param array $args Arguments:
440
+ * {option_name} string The option name. E.g. 'disallowed-sizes'. Mandatory.
441
+ * {label} string The label to use.
442
+ * {info} string Text to display in an "Info box" after the field. A 'aria-describedby' attribute will automatically be created.
443
+ * {attributes} array A list of HTML attributes, as 'attribute' => 'value'.
444
+ * {current_value} int|bool USE ONLY WHEN DEALING WITH DATA THAT IS NOT SAVED IN THE PLUGIN OPTIONS. If not provided, the field will automatically get the value from the options.
445
+ */
446
+ public function field_checkbox( $args ) {
447
+ $args = array_merge( array(
448
+ 'option_name' => '',
449
+ 'label' => '',
450
+ 'info' => '',
451
+ 'attributes' => array(),
452
+ // To not use the plugin settings: use an integer.
453
+ 'current_value' => null,
454
+ ), $args );
455
+
456
+ if ( ! $args['option_name'] || ! $args['label'] ) {
457
+ return;
458
+ }
459
+
460
+ if ( is_numeric( $args['current_value'] ) || is_bool( $args['current_value'] ) ) {
461
+ // We don't use the plugin settings.
462
+ $current_value = (int) (bool) $args['current_values'];
463
+ } else {
464
+ // This is a normal plugin setting.
465
+ $current_value = $this->options->get( $args['option_name'] );
466
+ }
467
+
468
+ $option_name_class = sanitize_html_class( $args['option_name'] );
469
+ $attributes = array(
470
+ 'name' => $this->option_name . '[' . $args['option_name'] . ']',
471
+ 'id' => 'imagify_' . $option_name_class,
472
+ );
473
+
474
+ if ( $args['info'] && empty( $attributes['aria-describedby'] ) ) {
475
+ $attributes['aria-describedby'] = 'describe-' . $option_name_class;
476
+ }
477
+
478
+ $attributes = array_merge( $attributes, $args['attributes'] );
479
+ $args['attributes'] = self::build_attributes( $attributes );
480
+ ?>
481
+ <input type="checkbox" value="1" <?php checked( $current_value, 1 ); ?><?php echo $args['attributes']; ?> />
482
+ <!-- Empty onclick attribute to make clickable labels on iTruc & Mac -->
483
+ <label for="<?php echo $attributes['id']; ?>" onclick=""><?php echo $args['label']; ?></label>
484
+ <?php
485
+ if ( ! $args['info'] ) {
486
+ return;
487
+ }
488
+ ?>
489
+ <span id="<?php echo $attributes['aria-describedby']; ?>" class="imagify-info">
490
+ <span class="dashicons dashicons-info"></span>
491
+ <?php echo $args['info']; ?>
492
+ </span>
493
+ <?php
494
+ }
495
+
496
+ /**
497
+ * Display a checkbox group.
498
+ *
499
+ * @since 1.7
500
+ * @author Grégory Viguier
501
+ * @access public
502
+ *
503
+ * @param array $args Arguments:
504
+ * {option_name} string The option name. E.g. 'disallowed-sizes'. Mandatory.
505
+ * {legend} string Label to use for the <legend> tag.
506
+ * {values} array List of values to display, in the form of 'value' => 'Label'. Mandatory.
507
+ * {disabled_values} array Values to be disabled. Values are the array keys.
508
+ * {reverse_check} bool If true, the values that will be stored in the option are the ones that are unchecked. It requires special treatment when saving (detect what values are unchecked).
509
+ * {attributes} array A list of HTML attributes, as 'attribute' => 'value'.
510
+ * {current_values} array USE ONLY WHEN DEALING WITH DATA THAT IS NOT SAVED IN THE PLUGIN OPTIONS. If not provided, the field will automatically get the value from the options.
511
+ */
512
+ public function field_checkbox_list( $args ) {
513
+
514
+ $args = array_merge( array(
515
+ 'option_name' => '',
516
+ 'legend' => '',
517
+ 'values' => array(),
518
+ 'disabled_values' => array(),
519
+ 'reverse_check' => false,
520
+ 'attributes' => array(),
521
+ // To not use the plugin settings: use an array.
522
+ 'current_values' => false,
523
+ ), $args );
524
+
525
+ if ( ! $args['option_name'] || ! $args['values'] ) {
526
+ return;
527
+ }
528
+
529
+ if ( is_array( $args['current_values'] ) ) {
530
+ // We don't use the plugin settings.
531
+ $current_values = $args['current_values'];
532
+ } else {
533
+ // This is a normal plugin setting.
534
+ $current_values = $this->options->get( $args['option_name'] );
535
+ }
536
+
537
+ $option_name_class = sanitize_html_class( $args['option_name'] );
538
+ $attributes = array_merge( array(
539
+ 'name' => $this->option_name . '[' . $args['option_name'] . ( $args['reverse_check'] ? '-checked' : '' ) . '][]',
540
+ 'id' => 'imagify_' . $option_name_class . '_%s',
541
+ 'class' => 'imagify-row-check',
542
+ ), $args['attributes'] );
543
+
544
+ $id_attribute = $attributes['id'];
545
+ unset( $attributes['id'] );
546
+ $args['attributes'] = self::build_attributes( $attributes );
547
+
548
+ $current_values = array_diff_key( $current_values, $args['disabled_values'] );
549
+ $nb_of_values = count( $args['values'] );
550
+ $display_check_all = $nb_of_values > 3;
551
+ $nb_of_checked = 0;
552
+ ?>
553
+ <fieldset class="imagify-check-group<?php echo $nb_of_values > 5 ? ' imagify-is-scrollable' : ''; ?>">
554
+ <?php
555
+ if ( $args['legend'] ) {
556
+ ?>
557
+ <legend class="screen-reader-text"><?php echo $args['legend']; ?></legend>
558
+ <?php
559
+ }
560
+
561
+ foreach ( $args['values'] as $value => $label ) {
562
+ $input_id = sprintf( $id_attribute, sanitize_html_class( $value ) );
563
+ $disabled = isset( $args['disabled_values'][ $value ] );
564
+
565
+ if ( $args['reverse_check'] ) {
566
+ $checked = ! $disabled && ! isset( $current_values[ $value ] );
567
+ } else {
568
+ $checked = ! $disabled && isset( $current_values[ $value ] );
569
+ }
570
+
571
+ $nb_of_checked = $checked ? $nb_of_checked + 1 : $nb_of_checked;
572
+
573
+ if ( $args['reverse_check'] ) {
574
+ echo '<input type="hidden" name="' . $this->option_name . '[' . $args['option_name'] . '-reversed][]" value="' . esc_attr( $value ) . '" />';
575
+ }
576
+ ?>
577
+ <p>
578
+ <input type="checkbox" value="<?php echo esc_attr( $value ); ?>" id="<?php echo $input_id; ?>"<?php echo $args['attributes']; ?> <?php checked( $checked ); ?> <?php disabled( $disabled ); ?>/>
579
+ <label for="<?php echo $input_id; ?>" onclick=""><?php echo $label; ?></label>
580
+ </p>
581
+ <?php
582
+ }
583
+ ?>
584
+ </fieldset>
585
+ <?php
586
+ if ( $display_check_all ) {
587
+ if ( $args['reverse_check'] ) {
588
+ $all_checked = ! array_intersect_key( $args['values'], $current_values );
589
+ } else {
590
+ $all_checked = ! array_diff_key( $args['values'], $current_values );
591
+ }
592
+ ?>
593
+ <p class="hide-if-no-js imagify-select-all-buttons">
594
+ <button type="button" class="imagify-link-like imagify-select-all<?php echo $all_checked ? ' imagify-is-inactive" aria-disabled="true' : ''; ?>" data-action="select"><?php _e( 'Select All', 'imagify' ); ?></button>
595
+
596
+ <span class="imagify-pipe"></span>
597
+
598
+ <button type="button" class="imagify-link-like imagify-select-all<?php echo $nb_of_checked ? '' : ' imagify-is-inactive" aria-disabled="true'; ?>" data-action="unselect"><?php _e( 'Unselect All', 'imagify' ); ?></button>
599
+ </p>
600
+ <?php
601
+ }
602
+ }
603
+
604
+
605
+ /** ----------------------------------------------------------------------------------------- */
606
+ /** FIELD VALUES ============================================================================ */
607
+ /** ----------------------------------------------------------------------------------------- */
608
+
609
+ /**
610
+ * Get the thumbnail sizes.
611
+ *
612
+ * @since 1.7
613
+ * @author Grégory Viguier
614
+ * @access public
615
+ *
616
+ * @return array A list of thumbnail sizes in the form of 'medium' => 'medium - 300 × 300'.
617
+ */
618
+ public static function get_thumbnail_sizes() {
619
+ static $sizes;
620
+
621
+ if ( isset( $sizes ) ) {
622
+ return $sizes;
623
+ }
624
+
625
+ $sizes = get_imagify_thumbnail_sizes();
626
+
627
+ foreach ( $sizes as $size_key => $size_data ) {
628
+ $sizes[ $size_key ] = sprintf( '%s - %d &times; %d', esc_html( stripslashes( $size_data['name'] ) ), $size_data['width'], $size_data['height'] );
629
+ }
630
+
631
+ return $sizes;
632
+ }
633
+
634
+
635
+ /** ----------------------------------------------------------------------------------------- */
636
+ /** TOOLS =================================================================================== */
637
+ /** ----------------------------------------------------------------------------------------- */
638
+
639
+ /**
640
+ * Create HTML attributes from an array.
641
+ *
642
+ * @since 1.7
643
+ * @access public
644
+ * @author Grégory Viguier
645
+ *
646
+ * @param array $attributes A list of attribute pairs.
647
+ * @return string HTML attributes.
648
+ */
649
+ public static function build_attributes( $attributes ) {
650
+ if ( ! $attributes || ! is_array( $attributes ) ) {
651
+ return '';
652
+ }
653
+
654
+ $out = '';
655
+
656
+ foreach ( $attributes as $attribute => $value ) {
657
+ $out .= ' ' . $attribute . '="' . esc_attr( $value ) . '"';
658
+ }
659
+
660
+ return $out;
661
+ }
662
+ }
inc/classes/class-imagify-user.php CHANGED
@@ -147,9 +147,11 @@ class Imagify_User {
147
  * @since 1.0
148
  *
149
  * @access public
150
- * @return int
151
  */
152
  public function get_percent_consumed_quota() {
 
 
153
  $quota = $this->quota;
154
  $consumed_quota = $this->consumed_current_month_quota;
155
 
@@ -159,12 +161,50 @@ class Imagify_User {
159
  }
160
 
161
  if ( ! $quota || ! $consumed_quota ) {
162
- return 0;
 
 
 
 
163
  }
164
 
165
- $percent = 100 * $consumed_quota / $quota;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
- return min( round( $percent, 1 ), 100 );
168
  }
169
 
170
  /**
@@ -173,7 +213,7 @@ class Imagify_User {
173
  * @since 1.0
174
  *
175
  * @access public
176
- * @return int
177
  */
178
  public function get_percent_unconsumed_quota() {
179
  $percent = 100 - $this->get_percent_consumed_quota();
147
  * @since 1.0
148
  *
149
  * @access public
150
+ * @return float|int
151
  */
152
  public function get_percent_consumed_quota() {
153
+ static $done = false;
154
+
155
  $quota = $this->quota;
156
  $consumed_quota = $this->consumed_current_month_quota;
157
 
161
  }
162
 
163
  if ( ! $quota || ! $consumed_quota ) {
164
+ $percent = 0;
165
+ } else {
166
+ $percent = 100 * $consumed_quota / $quota;
167
+ $percent = round( $percent, 1 );
168
+ $percent = min( max( 0, $percent ), 100 );
169
  }
170
 
171
+ if ( ! $done ) {
172
+ $previous_percent = Imagify_Data::get_instance()->get( 'previous_quota_percent' );
173
+
174
+ // Percent is not 100% anymore.
175
+ if ( 100 === $previous_percent && $percent < 100 ) {
176
+ /**
177
+ * Triggered when the consumed quota percent decreases below 100%.
178
+ *
179
+ * @since 1.7
180
+ * @author Grégory Viguier
181
+ *
182
+ * @param float|int $percent The current percentage of consumed quota.
183
+ */
184
+ do_action( 'imagify_not_over_quota_anymore', $percent );
185
+ }
186
+ // Percent is not >= 80% anymore.
187
+ if ( $previous_percent >= 80 && $percent < 80 ) {
188
+ /**
189
+ * Triggered when the consumed quota percent decreases below 80%.
190
+ *
191
+ * @since 1.7
192
+ * @author Grégory Viguier
193
+ *
194
+ * @param float|int $percent The current percentage of consumed quota.
195
+ * @param float|int $previous_percent The previous percentage of consumed quota.
196
+ */
197
+ do_action( 'imagify_not_almost_over_quota_anymore', $percent, $previous_percent );
198
+ }
199
+
200
+ if ( $previous_percent !== $percent ) {
201
+ Imagify_Data::get_instance()->set( 'previous_quota_percent', $percent );
202
+ }
203
+
204
+ $done = true;
205
+ }
206
 
207
+ return $percent;
208
  }
209
 
210
  /**
213
  * @since 1.0
214
  *
215
  * @access public
216
+ * @return float|int
217
  */
218
  public function get_percent_unconsumed_quota() {
219
  $percent = 100 - $this->get_percent_consumed_quota();
inc/classes/class-imagify-views.php ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ /**
5
+ * Class that handles templates and menus.
6
+ *
7
+ * @since 1.7
8
+ * @author Grégory Viguier
9
+ */
10
+ class Imagify_Views {
11
+
12
+ /**
13
+ * Class version.
14
+ *
15
+ * @var string
16
+ * @since 1.7
17
+ */
18
+ const VERSION = '1.0';
19
+
20
+ /**
21
+ * Slug used for the settings page URL.
22
+ *
23
+ * @var string
24
+ * @since 1.7
25
+ * @access protected
26
+ */
27
+ protected $slug_settings;
28
+
29
+ /**
30
+ * Slug used for the bulk optimization page URL.
31
+ *
32
+ * @var string
33
+ * @since 1.7
34
+ * @access protected
35
+ */
36
+ protected $slug_bulk;
37
+
38
+ /**
39
+ * Slug used for the "custom folders" page URL.
40
+ *
41
+ * @var string
42
+ * @since 1.7
43
+ * @access protected
44
+ */
45
+ protected $slug_files;
46
+
47
+ /**
48
+ * Stores the "custom folders" files list instance.
49
+ *
50
+ * @var object Imagify_Files_List_Table
51
+ * @since 1.7
52
+ * @access protected
53
+ */
54
+ protected $list_table;
55
+
56
+ /**
57
+ * The single instance of the class.
58
+ *
59
+ * @var object
60
+ * @since 1.7
61
+ * @access protected
62
+ */
63
+ protected static $_instance;
64
+
65
+
66
+ /** ----------------------------------------------------------------------------------------- */
67
+ /** INSTANCE/INIT =========================================================================== */
68
+ /** ----------------------------------------------------------------------------------------- */
69
+
70
+ /**
71
+ * The constructor.
72
+ *
73
+ * @since 1.7
74
+ * @author Grégory Viguier
75
+ * @access protected
76
+ */
77
+ protected function __construct() {
78
+ $this->slug_settings = IMAGIFY_SLUG;
79
+ $this->slug_bulk = IMAGIFY_SLUG . '-bulk-optimization';
80
+ $this->slug_files = IMAGIFY_SLUG . '-files';
81
+ }
82
+
83
+ /**
84
+ * Get the main Instance.
85
+ *
86
+ * @since 1.7
87
+ * @author Grégory Viguier
88
+ * @access public
89
+ *
90
+ * @return object Main instance.
91
+ */
92
+ public static function get_instance() {
93
+ if ( ! isset( self::$_instance ) ) {
94
+ self::$_instance = new self();
95
+ }
96
+
97
+ return self::$_instance;
98
+ }
99
+
100
+ /**
101
+ * Launch the hooks.
102
+ *
103
+ * @since 1.7
104
+ * @author Grégory Viguier
105
+ * @access public
106
+ */
107
+ public function init() {
108
+ // Menu items.
109
+ add_action( 'admin_menu', array( $this, 'add_site_menus' ) );
110
+
111
+ if ( imagify_is_active_for_network() ) {
112
+ add_action( 'network_admin_menu', array( $this, 'add_network_menus' ) );
113
+ }
114
+
115
+ // Action links in plugins list.
116
+ $basename = plugin_basename( IMAGIFY_FILE );
117
+ add_filter( 'plugin_action_links_' . $basename, array( $this, 'plugin_action_links' ) );
118
+ add_filter( 'network_admin_plugin_action_links_' . $basename, array( $this, 'plugin_action_links' ) );
119
+
120
+ // Save the "per page" option value from the files list screen.
121
+ add_filter( 'set-screen-option', array( 'Imagify_Files_List_Table', 'save_screen_options' ), 10, 3 );
122
+ }
123
+
124
+
125
+ /** ----------------------------------------------------------------------------------------- */
126
+ /** MENU ITEMS ============================================================================== */
127
+ /** ----------------------------------------------------------------------------------------- */
128
+
129
+ /**
130
+ * Add sub-menus for all sites.
131
+ *
132
+ * @since 1.7
133
+ * @author Grégory Viguier
134
+ * @access public
135
+ */
136
+ public function add_site_menus() {
137
+ // Sub-menu item: bulk optimization.
138
+ add_media_page( __( 'Bulk Optimization', 'imagify' ), __( 'Bulk Optimization', 'imagify' ), imagify_get_capacity( 'bulk-optimize' ), $this->get_bulk_page_slug(), array( $this, 'display_bulk_page' ) );
139
+
140
+ if ( imagify_is_active_for_network() ) {
141
+ return;
142
+ }
143
+
144
+ /**
145
+ * Plugin is not network activated.
146
+ */
147
+ if ( imagify_can_optimize_custom_folders() ) {
148
+ // Sub-menu item: custom folders list.
149
+ $screen_id = add_media_page( __( 'Other Media optimized by Imagify', 'imagify' ), __( 'Other Media', 'imagify' ), imagify_get_capacity( 'optimize-file' ), $this->get_files_page_slug(), array( $this, 'display_files_list' ) );
150
+
151
+ if ( $screen_id ) {
152
+ // Load the data for this page.
153
+ add_action( 'load-' . $screen_id, array( $this, 'load_files_list' ) );
154
+ }
155
+ }
156
+
157
+ // Sub-menu item: settings.
158
+ add_options_page( 'Imagify', 'Imagify', imagify_get_capacity(), $this->get_settings_page_slug(), array( $this, 'display_settings_page' ) );
159
+ }
160
+
161
+ /**
162
+ * Add menu and sub-menus in the network admin when Imagify is network-activated.
163
+ *
164
+ * @since 1.7
165
+ * @author Grégory Viguier
166
+ * @access public
167
+ */
168
+ public function add_network_menus() {
169
+ global $submenu;
170
+
171
+ if ( ! imagify_can_optimize_custom_folders() ) {
172
+ // Main item: settings (edge case).
173
+ add_menu_page( 'Imagify', 'Imagify', imagify_get_capacity(), $this->get_settings_page_slug(), array( $this, 'display_settings_page' ) );
174
+ return;
175
+ }
176
+
177
+ // Main item: bulk optimization (custom folders).
178
+ add_menu_page( __( 'Bulk Optimization', 'imagify' ), 'Imagify', imagify_get_capacity( 'optimize-file' ), $this->get_bulk_page_slug(), array( $this, 'display_bulk_page' ) );
179
+
180
+ // Sub-menu item: custom folders list.
181
+ $screen_id = add_submenu_page( $this->get_bulk_page_slug(), __( 'Other Media optimized by Imagify', 'imagify' ), __( 'Other Media', 'imagify' ), imagify_get_capacity( 'optimize-file' ), $this->get_files_page_slug(), array( $this, 'display_files_list' ) );
182
+
183
+ // Sub-menu item: settings.
184
+ add_submenu_page( $this->get_bulk_page_slug(), 'Imagify', __( 'Settings', 'imagify' ), imagify_get_capacity(), $this->get_settings_page_slug(), array( $this, 'display_settings_page' ) );
185
+
186
+ // Change the sub-menu label.
187
+ if ( ! empty( $submenu[ $this->get_bulk_page_slug() ] ) ) {
188
+ $submenu[ $this->get_bulk_page_slug() ][0][0] = __( 'Bulk Optimization', 'imagify' ); // WPCS: override ok.
189
+ }
190
+
191
+ if ( $screen_id ) {
192
+ // On the "Other Media optimized by Imagify" page, load the data.
193
+ add_action( 'load-' . $screen_id, array( $this, 'load_files_list' ) );
194
+ }
195
+ }
196
+
197
+
198
+ /** ----------------------------------------------------------------------------------------- */
199
+ /** PLUGIN ACTION LINKS ===================================================================== */
200
+ /** ----------------------------------------------------------------------------------------- */
201
+
202
+ /**
203
+ * Add links to the plugin row in the plugins list.
204
+ *
205
+ * @since 1.7
206
+ * @author Grégory Viguier
207
+ * @access public
208
+ *
209
+ * @param array $actions An array of action links.
210
+ * @return array
211
+ */
212
+ public function plugin_action_links( $actions ) {
213
+ array_unshift( $actions, sprintf( '<a href="%s" target="_blank">%s</a>', esc_url( imagify_get_external_url( 'documentation' ) ), __( 'Documentation', 'imagify' ) ) );
214
+ array_unshift( $actions, sprintf( '<a href="%s">%s</a>', esc_url( get_imagify_admin_url( 'bulk-optimization' ) ), __( 'Bulk Optimization', 'imagify' ) ) );
215
+ array_unshift( $actions, sprintf( '<a href="%s">%s</a>', esc_url( get_imagify_admin_url() ), __( 'Settings', 'imagify' ) ) );
216
+ return $actions;
217
+ }
218
+
219
+
220
+ /** ----------------------------------------------------------------------------------------- */
221
+ /** MAIN PAGE TEMPLATES ===================================================================== */
222
+ /** ----------------------------------------------------------------------------------------- */
223
+
224
+ /**
225
+ * The main settings page.
226
+ *
227
+ * @since 1.7
228
+ * @author Grégory Viguier
229
+ * @access public
230
+ */
231
+ public function display_settings_page() {
232
+ $this->print_template( 'page-settings' );
233
+ }
234
+
235
+ /**
236
+ * The bulk optimization page.
237
+ *
238
+ * @since 1.7
239
+ * @author Grégory Viguier
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' ) ) {
263
+ if ( ! is_network_admin() ) {
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
+
352
+ /**
353
+ * The page displaying the "custom folders" files.
354
+ *
355
+ * @since 1.7
356
+ * @author Grégory Viguier
357
+ * @access public
358
+ */
359
+ public function display_files_list() {
360
+ $this->print_template( 'page-files-list' );
361
+ }
362
+
363
+ /**
364
+ * Initiate the "custom folders" list table data.
365
+ *
366
+ * @since 1.7
367
+ * @author Grégory Viguier
368
+ * @access public
369
+ */
370
+ public function load_files_list() {
371
+ // Instantiate the list.
372
+ $this->list_table = new Imagify_Files_List_Table( array(
373
+ 'screen' => 'imagify-files',
374
+ ) );
375
+
376
+ // Query the Items.
377
+ $this->list_table->prepare_items();
378
+ }
379
+
380
+
381
+ /** ----------------------------------------------------------------------------------------- */
382
+ /** GETTERS ================================================================================= */
383
+ /** ----------------------------------------------------------------------------------------- */
384
+
385
+ /**
386
+ * Get the settings page slug.
387
+ *
388
+ * @since 1.7
389
+ * @author Grégory Viguier
390
+ * @access public
391
+ *
392
+ * @return string
393
+ */
394
+ public function get_settings_page_slug() {
395
+ return $this->slug_settings;
396
+ }
397
+
398
+ /**
399
+ * Get the bulk optimization page slug.
400
+ *
401
+ * @since 1.7
402
+ * @author Grégory Viguier
403
+ * @access public
404
+ *
405
+ * @return string
406
+ */
407
+ public function get_bulk_page_slug() {
408
+ return $this->slug_bulk;
409
+ }
410
+
411
+ /**
412
+ * Get the "custom folders" files page slug.
413
+ *
414
+ * @since 1.7
415
+ * @author Grégory Viguier
416
+ * @access public
417
+ *
418
+ * @return string
419
+ */
420
+ public function get_files_page_slug() {
421
+ return $this->slug_files;
422
+ }
423
+
424
+
425
+ /** ----------------------------------------------------------------------------------------- */
426
+ /** GENERIC TEMPLATE TOOLS ================================================================== */
427
+ /** ----------------------------------------------------------------------------------------- */
428
+
429
+ /**
430
+ * Get a template contents.
431
+ *
432
+ * @since 1.7
433
+ * @author Grégory Viguier
434
+ * @access public
435
+ *
436
+ * @param string $template The template name.
437
+ * @param mixed $data Some data to pass to the template.
438
+ * @return string|bool The page contents. False if the template doesn't exist.
439
+ */
440
+ public function get_template( $template, $data = array() ) {
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
+
448
+ ob_start();
449
+ include $path;
450
+ $contents = ob_get_clean();
451
+
452
+ return trim( (string) $contents );
453
+ }
454
+
455
+ /**
456
+ * Print a template.
457
+ *
458
+ * @since 1.7
459
+ * @author Grégory Viguier
460
+ * @access public
461
+ *
462
+ * @param string $template The template name.
463
+ * @param mixed $data Some data to pass to the template.
464
+ */
465
+ public function print_template( $template, $data = array() ) {
466
+ echo $this->get_template( $template, $data );
467
+ }
468
+ }
inc/classes/class-imagify.php CHANGED
@@ -53,16 +53,7 @@ class Imagify extends Imagify_Deprecated {
53
  * The constructor.
54
  */
55
  protected function __construct() {
56
- // Check if the WordPress plugin is activated and the API key is stored in the options.
57
- if ( defined( 'IMAGIFY_VERSION' ) && function_exists( 'get_imagify_option' ) ) {
58
- $api_key = get_imagify_option( 'api_key', false );
59
- $this->api_key = $api_key ? $api_key : $this->api_key;
60
- }
61
-
62
- // Check if the API key is defined with the PHP constant (it's ovveride the WordPress plugin option.
63
- if ( defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) {
64
- $this->api_key = IMAGIFY_API_KEY;
65
- }
66
 
67
  $this->all_headers['Accept'] = 'Accept: application/json';
68
  $this->all_headers['Content-Type'] = 'Content-Type: application/json';
@@ -285,7 +276,7 @@ class Imagify extends Imagify_Deprecated {
285
  }
286
 
287
  /**
288
- * Get prices for packs (one time).
289
  *
290
  * @access public
291
  * @since 1.6.5
@@ -503,8 +494,15 @@ class Imagify extends Imagify_Deprecated {
503
  private function handle_response( $response, $http_code, $error = '' ) {
504
  $response = json_decode( $response );
505
 
506
- if ( 200 !== $http_code && isset( $response->code, $response->detail ) ) {
507
- return new WP_Error( $http_code, $response->detail );
 
 
 
 
 
 
 
508
  }
509
 
510
  if ( 413 === $http_code ) {
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';
276
  }
277
 
278
  /**
279
+ * Get prices for packs (One Time).
280
  *
281
  * @access public
282
  * @since 1.6.5
494
  private function handle_response( $response, $http_code, $error = '' ) {
495
  $response = json_decode( $response );
496
 
497
+ if ( 200 !== $http_code && ! empty( $response->code ) ) {
498
+ if ( ! empty( $response->detail ) ) {
499
+ return new WP_Error( $http_code, $response->detail );
500
+ }
501
+ if ( ! empty( $response->image ) ) {
502
+ $error = (array) $response->image;
503
+ $error = reset( $error );
504
+ return new WP_Error( $http_code, $error );
505
+ }
506
  }
507
 
508
  if ( 413 === $http_code ) {
inc/common/admin-bar.php CHANGED
@@ -63,15 +63,13 @@ function _imagify_admin_bar( $wp_admin_bar ) {
63
  ) );
64
 
65
  // Quota & Profile informations.
66
- if ( defined( 'IMAGIFY_HIDDEN_ACCOUNT' ) && IMAGIFY_HIDDEN_ACCOUNT ) {
67
  return;
68
  }
69
 
70
- if ( ( defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) || get_imagify_option( 'api_key', false ) ) {
71
- $wp_admin_bar->add_menu( array(
72
- 'parent' => 'imagify',
73
- 'id' => 'imagify-profile',
74
- 'title' => wp_nonce_field( 'imagify-get-admin-bar-profile', 'imagifygetadminbarprofilenonce', false, false ) . '<div id="wp-admin-bar-imagify-profile-loading" class="hide-if-no-js">' . __( 'Loading...', 'imagify' ) . '</div><div id="wp-admin-bar-imagify-profile-content" class="hide-if-no-js"></div>',
75
- ) );
76
- }
77
  }
63
  ) );
64
 
65
  // Quota & Profile informations.
66
+ if ( defined( 'IMAGIFY_HIDDEN_ACCOUNT' ) && IMAGIFY_HIDDEN_ACCOUNT || ! get_imagify_option( 'api_key' ) ) {
67
  return;
68
  }
69
 
70
+ $wp_admin_bar->add_menu( array(
71
+ 'parent' => 'imagify',
72
+ 'id' => 'imagify-profile',
73
+ 'title' => wp_nonce_field( 'imagify-get-admin-bar-profile', 'imagifygetadminbarprofilenonce', false, false ) . '<div id="wp-admin-bar-imagify-profile-loading" class="hide-if-no-js">' . __( 'Loading...', 'imagify' ) . '</div><div id="wp-admin-bar-imagify-profile-content" class="hide-if-no-js"></div>',
74
+ ) );
 
 
75
  }
inc/common/cron.php DELETED
@@ -1,90 +0,0 @@
1
- <?php
2
- defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
-
4
- add_action( 'init', '_imagify_rating_scheduled' );
5
- /**
6
- * Planning cron.
7
- * If the task is not programmed, it is automatically triggered.
8
- *
9
- * @since 1.4.2
10
- */
11
- function _imagify_rating_scheduled() {
12
- if ( ! wp_next_scheduled( 'imagify_rating_event' ) && ! get_site_transient( 'do_imagify_rating_cron' ) ) {
13
- wp_schedule_event( time(), 'daily', 'imagify_rating_event' );
14
- }
15
- }
16
-
17
- add_action( 'imagify_rating_event', '_do_imagify_rating_cron' );
18
- /**
19
- * Save the user images count to display it later in a notice message to ask him to rate Imagify on WordPress.org.
20
- *
21
- * @since 1.4.2
22
- */
23
- function _do_imagify_rating_cron() {
24
- // Stop the process if the plugin isn't installed for 3 days.
25
- if ( get_site_transient( 'imagify_seen_rating_notice' ) ) {
26
- return;
27
- }
28
-
29
- // Check if the Imagify servers & the API are accessible.
30
- if ( ! is_imagify_servers_up() ) {
31
- return;
32
- }
33
-
34
- $user = get_imagify_user();
35
-
36
- if ( isset( $user ) && (int) $user->image_count > 100 ) {
37
- set_site_transient( 'imagify_user_images_count', $user->image_count );
38
- }
39
- }
40
-
41
- add_filter( 'cron_schedules', 'imagify_purge_cron_schedule' );
42
- /**
43
- * Adds weekly interval for cron jobs.
44
- *
45
- * @since 1.6
46
- * @author Remy Perona
47
- *
48
- * @param Array $schedules An array of intervals used by cron jobs.
49
- * @return Array Updated array of intervals.
50
- */
51
- function imagify_purge_cron_schedule( $schedules ) {
52
- if ( array_key_exists( 'weekly', $schedules ) ) {
53
- return $schedules;
54
- }
55
-
56
- $schedules['weekly'] = array(
57
- 'interval' => 604800,
58
- 'display' => __( 'Once Weekly', 'imagify' ),
59
- );
60
-
61
- return $schedules;
62
- }
63
-
64
- add_action( 'init', '_imagify_update_library_size_calculations_scheduled' );
65
- /**
66
- * Planning cron task to update weekly the size of the images and the size of images uploaded by month.
67
- * If the task is not programmed, it is automatically triggered.
68
- *
69
- * @since 1.6
70
- * @author Remy Perona
71
- */
72
- function _imagify_update_library_size_calculations_scheduled() {
73
- if ( ! wp_next_scheduled( 'imagify_update_library_size_calculations_event' ) ) {
74
- wp_schedule_event( time(), 'weekly', 'imagify_update_library_size_calculations_event' );
75
- }
76
- }
77
-
78
- add_action( 'imagify_update_library_size_calculations_event', '_do_imagify_update_library_size_calculations' );
79
- /**
80
- * Cron task to update weekly the size of the images and the size of images uploaded by month.
81
- *
82
- * @since 1.6
83
- * @author Remy Perona
84
- */
85
- function _do_imagify_update_library_size_calculations() {
86
- imagify_do_async_job( array(
87
- 'action' => 'imagify_update_estimate_sizes',
88
- '_ajax_nonce' => wp_create_nonce( 'update_estimate_sizes' ),
89
- ) );
90
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/common/partners.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
- add_action( 'update_option_' . IMAGIFY_SETTINGS_SLUG, 'imagify_maybe_delete_partner_on_option_update', 10, 2 );
5
  /**
6
  * After the first API key has been successfully added, make sure the partner ID is deleted.
7
  *
@@ -17,7 +17,7 @@ function imagify_maybe_delete_partner_on_option_update( $old_value, $new_value )
17
  }
18
  }
19
 
20
- add_action( 'update_site_option_' . IMAGIFY_SETTINGS_SLUG, 'imagify_maybe_delete_partner_on_network_option_update', 10, 3 );
21
  /**
22
  * After the first API key has been successfully added to the network option, make sure the partner ID is deleted.
23
  *
1
  <?php
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
+ add_action( 'update_option_' . Imagify_Options::get_instance()->get_option_name(), 'imagify_maybe_delete_partner_on_option_update', 10, 2 );
5
  /**
6
  * After the first API key has been successfully added, make sure the partner ID is deleted.
7
  *
17
  }
18
  }
19
 
20
+ add_action( 'update_site_option_' . Imagify_Options::get_instance()->get_option_name(), 'imagify_maybe_delete_partner_on_network_option_update', 10, 3 );
21
  /**
22
  * After the first API key has been successfully added to the network option, make sure the partner ID is deleted.
23
  *
inc/functions/admin-stats.php CHANGED
@@ -4,7 +4,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
4
  /**
5
  * Count number of attachments.
6
  *
7
- * @since 1.0
8
  * @author Jonathan Buttigieg
9
  *
10
  * @return int The number of attachments.
@@ -31,18 +31,19 @@ function imagify_count_attachments() {
31
  return $count;
32
  }
33
 
34
- $mime_types = Imagify_DB::get_mime_types();
35
- $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
 
 
 
36
  "
37
- SELECT COUNT( ID )
38
- FROM $wpdb->posts
39
- INNER JOIN $wpdb->postmeta
40
- ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = '_wp_attached_file' )
41
- INNER JOIN $wpdb->postmeta AS mt1
42
- ON ( $wpdb->posts.ID = mt1.post_id AND mt1.meta_key = '_wp_attachment_metadata' )
43
- WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
44
- AND $wpdb->posts.post_type = 'attachment'
45
- AND $wpdb->posts.post_status = 'inherit'"
46
  );
47
 
48
  if ( $count > imagify_get_unoptimized_attachment_limit() ) {
@@ -58,7 +59,7 @@ function imagify_count_attachments() {
58
  /**
59
  * Count number of optimized attachments with an error.
60
  *
61
- * @since 1.0
62
  * @author Jonathan Buttigieg
63
  *
64
  * @return int The number of attachments.
@@ -85,23 +86,22 @@ function imagify_count_error_attachments() {
85
  return $count;
86
  }
87
 
88
- Imagify_DB::unlimit_joins();
89
-
90
- $mime_types = Imagify_DB::get_mime_types();
91
- $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
 
92
  "
93
- SELECT COUNT( $wpdb->posts.ID )
94
- FROM $wpdb->posts
95
- INNER JOIN $wpdb->postmeta
96
- ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = '_imagify_status' )
97
  INNER JOIN $wpdb->postmeta AS mt1
98
- ON ( $wpdb->posts.ID = mt1.post_id AND mt1.meta_key = '_wp_attached_file' )
99
- INNER JOIN $wpdb->postmeta AS mt2
100
- ON ( $wpdb->posts.ID = mt2.post_id AND mt2.meta_key = '_wp_attachment_metadata' )
101
- WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
102
- AND $wpdb->posts.post_type = 'attachment'
103
- AND $wpdb->posts.post_status = 'inherit'
104
- AND $wpdb->postmeta.meta_value = 'error'"
105
  );
106
 
107
  return $count;
@@ -110,7 +110,7 @@ function imagify_count_error_attachments() {
110
  /**
111
  * Count number of optimized attachments (by Imagify or an other tool before).
112
  *
113
- * @since 1.0
114
  * @author Jonathan Buttigieg
115
  *
116
  * @return int The number of attachments.
@@ -137,27 +137,22 @@ function imagify_count_optimized_attachments() {
137
  return $count;
138
  }
139
 
140
- Imagify_DB::unlimit_joins();
141
-
142
- $mime_types = Imagify_DB::get_mime_types();
143
- $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
 
144
  "
145
- SELECT COUNT( $wpdb->posts.ID )
146
- FROM $wpdb->posts
147
- INNER JOIN $wpdb->postmeta
148
- ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = '_imagify_status' )
149
  INNER JOIN $wpdb->postmeta AS mt1
150
- ON ( $wpdb->posts.ID = mt1.post_id AND mt1.meta_key = '_wp_attached_file' )
151
- INNER JOIN $wpdb->postmeta AS mt2
152
- ON ( $wpdb->posts.ID = mt2.post_id AND mt2.meta_key = '_wp_attachment_metadata' )
153
- WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
154
- AND $wpdb->posts.post_type = 'attachment'
155
- AND $wpdb->posts.post_status = 'inherit'
156
- AND (
157
- $wpdb->postmeta.meta_value = 'success'
158
- OR
159
- $wpdb->postmeta.meta_value = 'already_optimized'
160
- )"
161
  );
162
 
163
  return $count;
@@ -166,7 +161,7 @@ function imagify_count_optimized_attachments() {
166
  /**
167
  * Count number of unoptimized attachments.
168
  *
169
- * @since 1.0
170
  * @author Jonathan Buttigieg
171
  *
172
  * @return int The number of attachments.
@@ -192,7 +187,7 @@ function imagify_count_unoptimized_attachments() {
192
  /**
193
  * Count percent of optimized attachments.
194
  *
195
- * @since 1.0
196
  * @author Jonathan Buttigieg
197
  *
198
  * @return int The percent of optimized attachments.
@@ -303,20 +298,27 @@ function imagify_count_saving_data( $key = '' ) {
303
  $limit = apply_filters( 'imagify_count_saving_data_limit', 15000 );
304
  $limit = absint( $limit );
305
 
306
- $attachment_ids = $wpdb->get_col(
307
- "SELECT $wpdb->postmeta.post_id
308
- FROM $wpdb->postmeta
309
- INNER JOIN $wpdb->postmeta AS mt1
310
- ON ( $wpdb->postmeta.post_id = mt1.post_id AND mt1.meta_key = '_wp_attached_file' )
311
- INNER JOIN $wpdb->postmeta AS mt2
312
- ON ( $wpdb->postmeta.post_id = mt2.post_id AND mt2.meta_key = '_wp_attachment_metadata' )
313
- WHERE $wpdb->postmeta.meta_key = '_imagify_status'
314
- AND $wpdb->postmeta.meta_value = 'success'
315
- ORDER BY CAST( $wpdb->postmeta.post_id AS UNSIGNED )"
 
 
 
 
 
 
 
316
  );
317
  $wpdb->flush();
318
 
319
- $attachment_ids = array_map( 'absint', $attachment_ids );
320
  $attachment_ids = array_chunk( $attachment_ids, $limit );
321
 
322
  while ( $attachment_ids ) {
@@ -324,10 +326,11 @@ function imagify_count_saving_data( $key = '' ) {
324
  $limit_ids = implode( ',', $limit_ids );
325
 
326
  $attachments = $wpdb->get_col( // WPCS: unprepared SQL ok.
327
- "SELECT meta_value
328
- FROM $wpdb->postmeta
329
- WHERE post_id IN ( $limit_ids )
330
- AND meta_key = '_imagify_data'"
 
331
  );
332
  $wpdb->flush();
333
 
@@ -345,20 +348,29 @@ function imagify_count_saving_data( $key = '' ) {
345
  continue;
346
  }
347
 
348
- ++$count;
349
  $original_data = $attachment_data['sizes']['full'];
350
 
 
 
 
 
 
 
 
 
 
 
351
  // Increment the original sizes.
352
- $original_size += $original_data['original_size'] ? $original_data['original_size'] : 0;
353
- $optimized_size += $original_data['optimized_size'] ? $original_data['optimized_size'] : 0;
354
 
355
  unset( $attachment_data['sizes']['full'], $original_data );
356
 
357
  // Increment the thumbnails sizes.
358
  foreach ( $attachment_data['sizes'] as $size_data ) {
359
  if ( ! empty( $size_data['success'] ) ) {
360
- $original_size += $size_data['original_size'] ? $size_data['original_size'] : 0;
361
- $optimized_size += $size_data['optimized_size'] ? $size_data['optimized_size'] : 0;
362
  }
363
  }
364
 
@@ -397,18 +409,19 @@ function imagify_count_saving_data( $key = '' ) {
397
  function imagify_calculate_total_size_images_library() {
398
  global $wpdb;
399
 
400
- $mime_types = Imagify_DB::get_mime_types();
401
- $image_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
 
 
 
402
  "
403
- SELECT ID
404
- FROM $wpdb->posts
405
- INNER JOIN $wpdb->postmeta
406
- ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = '_wp_attached_file' )
407
- INNER JOIN $wpdb->postmeta AS mt1
408
- ON ( $wpdb->posts.ID = mt1.post_id AND mt1.meta_key = '_wp_attachment_metadata' )
409
- WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
410
- AND $wpdb->posts.post_type = 'attachment'
411
- AND $wpdb->posts.post_status = 'inherit'
412
  LIMIT 250
413
  " );
414
 
@@ -416,11 +429,10 @@ function imagify_calculate_total_size_images_library() {
416
  return 0;
417
  }
418
 
419
- $partial_total_images = count( $image_ids );
420
- $total_images = imagify_count_attachments();
421
- $total_size_images = imagify_calculate_total_image_size( $image_ids, $partial_total_images, $total_images );
422
 
423
- return $total_size_images;
424
  }
425
 
426
  /**
@@ -430,84 +442,84 @@ function imagify_calculate_total_size_images_library() {
430
  * for the 3 latest months, add up their filesizes, and doing some maths to get the total average size.
431
  *
432
  * @since 1.6
 
433
  * @author Remy Perona
434
  *
435
  * @return int The current estimated average size of images uploaded per month.
436
  */
437
  function imagify_calculate_average_size_images_per_month() {
438
- $query = array(
439
- 'is_imagify' => true,
440
- 'post_type' => 'attachment',
441
- 'post_status' => 'inherit',
442
- 'post_mime_type' => get_imagify_mime_type(),
443
- 'posts_per_page' => 250,
444
- 'fields' => 'ids',
445
- 'meta_query' => array(
446
- array(
447
- 'key' => '_wp_attached_file',
448
- 'compare' => 'EXISTS',
449
- ),
450
- array(
451
- 'key' => '_wp_attachment_metadata',
452
- 'compare' => 'EXISTS',
453
- ),
454
- ),
455
- );
456
 
457
- $partial_images_uploaded_last_month = new WP_Query( array_merge( $query, array(
458
- 'date_query' => array(
459
- array(
460
- 'before' => 'now',
461
- 'after' => '1 month ago',
462
- ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  ),
464
- ) ) );
465
-
466
- $partial_images_uploaded_two_months_ago = new WP_Query( array_merge( $query, array(
467
- 'date_query' => array(
468
- array(
469
- 'before' => '1 month ago',
470
- 'after' => '2 months ago',
471
- ),
472
  ),
473
- ) ) );
474
-
475
- $partial_images_uploaded_three_months_ago = new WP_Query( array_merge( $query, array(
476
- 'date_query' => array(
477
- array(
478
- 'before' => '2 months ago',
479
- 'after' => '3 months ago',
480
- ),
481
  ),
482
- ) ) );
483
 
484
- $partial_images_uploaded_id = array_merge( $partial_images_uploaded_last_month->posts, $partial_images_uploaded_two_months_ago->posts, $partial_images_uploaded_three_months_ago->posts );
 
 
 
485
 
486
  if ( ! $partial_images_uploaded_id ) {
487
  return 0;
488
  }
489
 
490
- $images_uploaded_id = new WP_Query( array_merge( $query, array(
491
- 'posts_per_page' => -1,
492
- 'date_query' => array(
493
- array(
494
- 'before' => 'now',
495
- 'after' => '3 months ago',
496
- ),
497
  ),
498
- ) ) );
 
 
499
 
500
- if ( ! $images_uploaded_id->posts ) {
501
  return 0;
502
  }
503
 
504
  // Number of image attachments uploaded for the 3 latest months, limited to 250 per month.
505
  $partial_total_images_uploaded = count( $partial_images_uploaded_id );
506
  // Total number of image attachments uploaded for the 3 latest months.
507
- $total_images_uploaded = (int) $images_uploaded_id->post_count;
508
- $average_size_images_per_month = imagify_calculate_total_image_size( $partial_images_uploaded_id, $partial_total_images_uploaded, $total_images_uploaded ) / 3;
509
 
510
- return $average_size_images_per_month;
511
  }
512
 
513
  /**
@@ -549,7 +561,7 @@ function imagify_calculate_total_image_size( $image_ids, $partial_total_images,
549
  $partial_total_intermediate_images = 0;
550
 
551
  $is_active_for_network = imagify_is_active_for_network();
552
- $disallowed_sizes = array_filter( (array) get_imagify_option( 'disallowed-sizes', array() ) );
553
 
554
  foreach ( $image_ids as $i => $image_id ) {
555
  $attachment_status = isset( $results['statuses'][ $image_id ] ) ? $results['statuses'][ $image_id ] : false;
4
  /**
5
  * Count number of attachments.
6
  *
7
+ * @since 1.0
8
  * @author Jonathan Buttigieg
9
  *
10
  * @return int The number of attachments.
31
  return $count;
32
  }
33
 
34
+ $mime_types = Imagify_DB::get_mime_types();
35
+ $statuses = Imagify_DB::get_post_statuses();
36
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
37
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
38
+ $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
39
  "
40
+ SELECT COUNT( p.ID )
41
+ FROM $wpdb->posts AS p
42
+ $nodata_join
43
+ WHERE p.post_mime_type IN ( $mime_types )
44
+ AND p.post_type = 'attachment'
45
+ AND p.post_status IN ( $statuses )
46
+ $nodata_where"
 
 
47
  );
48
 
49
  if ( $count > imagify_get_unoptimized_attachment_limit() ) {
59
  /**
60
  * Count number of optimized attachments with an error.
61
  *
62
+ * @since 1.0
63
  * @author Jonathan Buttigieg
64
  *
65
  * @return int The number of attachments.
86
  return $count;
87
  }
88
 
89
+ $mime_types = Imagify_DB::get_mime_types();
90
+ $statuses = Imagify_DB::get_post_statuses();
91
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
92
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
93
+ $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
94
  "
95
+ SELECT COUNT( DISTINCT p.ID )
96
+ FROM $wpdb->posts AS p
97
+ $nodata_join
 
98
  INNER JOIN $wpdb->postmeta AS mt1
99
+ ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
100
+ WHERE p.post_mime_type IN ( $mime_types )
101
+ AND p.post_type = 'attachment'
102
+ AND p.post_status IN ( $statuses )
103
+ AND mt1.meta_value = 'error'
104
+ $nodata_where"
 
105
  );
106
 
107
  return $count;
110
  /**
111
  * Count number of optimized attachments (by Imagify or an other tool before).
112
  *
113
+ * @since 1.0
114
  * @author Jonathan Buttigieg
115
  *
116
  * @return int The number of attachments.
137
  return $count;
138
  }
139
 
140
+ $mime_types = Imagify_DB::get_mime_types();
141
+ $statuses = Imagify_DB::get_post_statuses();
142
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
143
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
144
+ $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
145
  "
146
+ SELECT COUNT( DISTINCT p.ID )
147
+ FROM $wpdb->posts AS p
148
+ $nodata_join
 
149
  INNER JOIN $wpdb->postmeta AS mt1
150
+ ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
151
+ WHERE p.post_mime_type IN ( $mime_types )
152
+ AND p.post_type = 'attachment'
153
+ AND p.post_status IN ( $statuses )
154
+ AND mt1.meta_value IN ( 'success', 'already_optimized' )
155
+ $nodata_where"
 
 
 
 
 
156
  );
157
 
158
  return $count;
161
  /**
162
  * Count number of unoptimized attachments.
163
  *
164
+ * @since 1.0
165
  * @author Jonathan Buttigieg
166
  *
167
  * @return int The number of attachments.
187
  /**
188
  * Count percent of optimized attachments.
189
  *
190
+ * @since 1.0
191
  * @author Jonathan Buttigieg
192
  *
193
  * @return int The percent of optimized attachments.
298
  $limit = apply_filters( 'imagify_count_saving_data_limit', 15000 );
299
  $limit = absint( $limit );
300
 
301
+ $mime_types = Imagify_DB::get_mime_types();
302
+ $statuses = Imagify_DB::get_post_statuses();
303
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
304
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
305
+ $attachment_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
306
+ "
307
+ SELECT p.ID
308
+ FROM $wpdb->posts AS p
309
+ $nodata_join
310
+ INNER JOIN $wpdb->postmeta AS mt1
311
+ ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
312
+ WHERE p.post_mime_type IN ( $mime_types )
313
+ AND p.post_type = 'attachment'
314
+ AND p.post_status IN ( $statuses )
315
+ AND mt1.meta_value = 'success'
316
+ $nodata_where
317
+ ORDER BY CAST( p.ID AS UNSIGNED )"
318
  );
319
  $wpdb->flush();
320
 
321
+ $attachment_ids = array_map( 'absint', array_unique( $attachment_ids ) );
322
  $attachment_ids = array_chunk( $attachment_ids, $limit );
323
 
324
  while ( $attachment_ids ) {
326
  $limit_ids = implode( ',', $limit_ids );
327
 
328
  $attachments = $wpdb->get_col( // WPCS: unprepared SQL ok.
329
+ "
330
+ SELECT meta_value
331
+ FROM $wpdb->postmeta
332
+ WHERE post_id IN ( $limit_ids )
333
+ AND meta_key = '_imagify_data'"
334
  );
335
  $wpdb->flush();
336
 
348
  continue;
349
  }
350
 
 
351
  $original_data = $attachment_data['sizes']['full'];
352
 
353
+ if ( empty( $original_data['success'] ) ) {
354
+ /**
355
+ * Case where this attachment has multiple '_imagify_status' metas, and is fetched (in the above query) as a "success" while the '_imagify_data' says otherwise.
356
+ * Don't ask how it's possible, I don't know.
357
+ */
358
+ continue;
359
+ }
360
+
361
+ ++$count;
362
+
363
  // Increment the original sizes.
364
+ $original_size += ! empty( $original_data['original_size'] ) ? $original_data['original_size'] : 0;
365
+ $optimized_size += ! empty( $original_data['optimized_size'] ) ? $original_data['optimized_size'] : 0;
366
 
367
  unset( $attachment_data['sizes']['full'], $original_data );
368
 
369
  // Increment the thumbnails sizes.
370
  foreach ( $attachment_data['sizes'] as $size_data ) {
371
  if ( ! empty( $size_data['success'] ) ) {
372
+ $original_size += ! empty( $size_data['original_size'] ) ? $size_data['original_size'] : 0;
373
+ $optimized_size += ! empty( $size_data['optimized_size'] ) ? $size_data['optimized_size'] : 0;
374
  }
375
  }
376
 
409
  function imagify_calculate_total_size_images_library() {
410
  global $wpdb;
411
 
412
+ $mime_types = Imagify_DB::get_mime_types();
413
+ $statuses = Imagify_DB::get_post_statuses();
414
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
415
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
416
+ $image_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
417
  "
418
+ SELECT p.ID
419
+ FROM $wpdb->posts AS p
420
+ $nodata_join
421
+ WHERE p.post_mime_type IN ( $mime_types )
422
+ AND p.post_type = 'attachment'
423
+ AND p.post_status IN ( $statuses )
424
+ $nodata_where
 
 
425
  LIMIT 250
426
  " );
427
 
429
  return 0;
430
  }
431
 
432
+ $count_latest_images = count( $image_ids );
433
+ $count_total_images = imagify_count_attachments();
 
434
 
435
+ return imagify_calculate_total_image_size( $image_ids, $count_latest_images, $count_total_images );
436
  }
437
 
438
  /**
442
  * for the 3 latest months, add up their filesizes, and doing some maths to get the total average size.
443
  *
444
  * @since 1.6
445
+ * @since 1.7 Use wpdb instead of WP_Query.
446
  * @author Remy Perona
447
  *
448
  * @return int The current estimated average size of images uploaded per month.
449
  */
450
  function imagify_calculate_average_size_images_per_month() {
451
+ global $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
+ $mime_types = Imagify_DB::get_mime_types();
454
+ $statuses = Imagify_DB::get_post_statuses();
455
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause( "$wpdb->posts.ID" );
456
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
457
+ $limit = ' LIMIT 0, 250';
458
+ $query = "
459
+ SELECT $wpdb->posts.ID
460
+ FROM $wpdb->posts
461
+ $nodata_join
462
+ WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
463
+ AND $wpdb->posts.post_type = 'attachment'
464
+ AND $wpdb->posts.post_status IN ( $statuses )
465
+ $nodata_where
466
+ %date_query%";
467
+
468
+ // Queries per month.
469
+ $date_query = new WP_Date_Query( array(
470
+ array(
471
+ 'before' => 'now',
472
+ 'after' => '1 month ago',
473
  ),
474
+ ) );
475
+
476
+ $partial_images_uploaded_last_month = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
477
+
478
+ $date_query = new WP_Date_Query( array(
479
+ array(
480
+ 'before' => '1 month ago',
481
+ 'after' => '2 months ago',
482
  ),
483
+ ) );
484
+
485
+ $partial_images_uploaded_two_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
486
+
487
+ $date_query = new WP_Date_Query( array(
488
+ array(
489
+ 'before' => '2 month ago',
490
+ 'after' => '3 months ago',
491
  ),
492
+ ) );
493
 
494
+ $partial_images_uploaded_three_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
495
+
496
+ // Total for the 3 months.
497
+ $partial_images_uploaded_id = array_merge( $partial_images_uploaded_last_month, $partial_images_uploaded_two_months_ago, $partial_images_uploaded_three_months_ago );
498
 
499
  if ( ! $partial_images_uploaded_id ) {
500
  return 0;
501
  }
502
 
503
+ // Total for the 3 months, without the "250" limit.
504
+ $date_query = new WP_Date_Query( array(
505
+ array(
506
+ 'before' => 'now',
507
+ 'after' => '3 month ago',
 
 
508
  ),
509
+ ) );
510
+
511
+ $images_uploaded_id = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query ) ); // WPCS: unprepared SQL ok.
512
 
513
+ if ( ! $images_uploaded_id ) {
514
  return 0;
515
  }
516
 
517
  // Number of image attachments uploaded for the 3 latest months, limited to 250 per month.
518
  $partial_total_images_uploaded = count( $partial_images_uploaded_id );
519
  // Total number of image attachments uploaded for the 3 latest months.
520
+ $total_images_uploaded = count( $images_uploaded_id );
 
521
 
522
+ return imagify_calculate_total_image_size( $partial_images_uploaded_id, $partial_total_images_uploaded, $total_images_uploaded ) / 3;
523
  }
524
 
525
  /**
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
 
566
  foreach ( $image_ids as $i => $image_id ) {
567
  $attachment_status = isset( $results['statuses'][ $image_id ] ) ? $results['statuses'][ $image_id ] : false;
inc/functions/admin-ui.php CHANGED
@@ -37,11 +37,10 @@ function get_imagify_attachment_optimization_text( $attachment, $context = 'wp'
37
  }
38
 
39
  $attachment_id = $attachment->id;
40
- $data = $attachment->get_data();
41
  $optimization_level = $attachment->get_optimization_level_label();
42
 
43
  if ( ! $is_media_page ) {
44
- $output .= $output_before . '<span class="data">' . __( 'New Filesize:', 'imagify' ) . '</span> <strong class="big">' . size_format( $data['sizes']['full']['optimized_size'], 2 ) . '</strong>' . $output_after;
45
  }
46
 
47
  $chart = '';
@@ -62,7 +61,7 @@ function get_imagify_attachment_optimization_text( $attachment, $context = 'wp'
62
 
63
  $output .= $output_before;
64
  $output .= '<span class="data">' . __( 'Original Saving:', 'imagify' ) . '</span> ';
65
- $output .= '<strong>' . $chart . '<span class="imagify-chart-value">' . $data['sizes']['full']['percent'] . '</span>%</strong>';
66
  $output .= $output_after;
67
 
68
  // More details section.
@@ -87,7 +86,7 @@ function get_imagify_attachment_optimization_text( $attachment, $context = 'wp'
87
 
88
  if ( $total_optimized_thumbnails ) {
89
  $output .= $output_before . '<span class="data">' . __( 'Thumbnails Optimized:', 'imagify' ) . '</span> <strong>' . $total_optimized_thumbnails . '</strong>' . $output_after;
90
- $output .= $output_before . '<span class="data">' . __( 'Overall Saving:', 'imagify' ) . '</span> <strong>' . $data['stats']['percent'] . '%</strong>' . $output_after;
91
  }
92
 
93
  // End of list.
@@ -110,13 +109,13 @@ function get_imagify_attachment_optimization_text( $attachment, $context = 'wp'
110
  $output .= '</a>';
111
 
112
  if ( ! $is_library_page ) {
113
- $image = wp_get_attachment_image_src( $attachment_id, 'full' );
114
 
115
  $output .= '<input id="imagify-original-src" type="hidden" value="' . esc_url( $attachment->get_backup_url() ) . '">';
116
  $output .= '<input id="imagify-original-size" type="hidden" value="' . $attachment->get_original_size() . '">';
117
- $output .= '<input id="imagify-full-src" type="hidden" value="' . $image[0] . '">';
118
- $output .= '<input id="imagify-full-width" type="hidden" value="' . $image[1] . '">';
119
- $output .= '<input id="imagify-full-height" type="hidden" value="' . $image[2] . '">';
120
  }
121
  }
122
 
@@ -287,11 +286,15 @@ function get_imagify_media_column_content( $attachment, $context = 'wp' ) {
287
  }
288
 
289
  // Check if the attachment extension is allowed.
290
- if ( ! $attachment->is_mime_type_supported() ) {
291
- $attachment_ext = $attachment->get_extension();
 
 
 
 
292
 
293
  /* translators: %s is a file extension. */
294
- return sprintf( __( '%s can\'t be optimized', 'imagify' ), strtoupper( $attachment_ext ) );
295
  }
296
 
297
  // Check if the attachment has the required WP metadata.
@@ -303,7 +306,7 @@ function get_imagify_media_column_content( $attachment, $context = 'wp' ) {
303
  if ( ! imagify_valid_key() && ! $attachment->is_optimized() ) {
304
  $output = __( 'Invalid API key', 'imagify' );
305
  $output .= '<br/>';
306
- $output .= '<a href="' . esc_url( get_imagify_admin_url( 'options-general' ) ) . '">' . __( 'Check your Settings', 'imagify' ) . '</a>';
307
  return $output;
308
  }
309
 
@@ -315,7 +318,7 @@ function get_imagify_media_column_content( $attachment, $context = 'wp' ) {
315
  }
316
 
317
  // Check if the image was optimized.
318
- if ( ! $attachment->is_optimized() && ! $attachment->has_error() ) {
319
  $args = array(
320
  'attachment_id' => $attachment->id,
321
  'context' => $context,
@@ -333,533 +336,85 @@ function get_imagify_media_column_content( $attachment, $context = 'wp' ) {
333
  }
334
 
335
  /**
336
- * Display the plan chooser section.
337
  *
338
- * @since 1.6
339
- * @author Geoffrey
340
- *
341
- * @todo Add only for no-payable users?
342
  *
343
- * @return string HTML.
 
344
  */
345
- function get_imagify_new_to_imagify() {
346
- if ( ! imagify_current_user_can() || ! imagify_valid_key() ) {
347
- return '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
 
350
  /**
351
- * Filter whether the plan chooser section is displayed.
352
- *
353
- * @param $show_new bool Default to true: display the section.
354
  */
355
- $show_new = apply_filters( 'imagify_show_new_to_imagify', true );
356
-
357
- if ( ! $show_new ) {
358
- return '';
 
 
 
 
 
359
  }
360
 
361
- return '
362
- <div class="imagify-section imagify-section-positive">
363
- <div class="imagify-start imagify-mr2">
364
- <button id="imagify-get-pricing-modal" data-nonce="' . wp_create_nonce( 'imagify_get_pricing_' . get_current_user_id() ) . '" data-target="#imagify-pricing-modal" type="button" class="imagify-modal-trigger imagify-button imagify-button-light imagify-button-big">
365
- <i class="dashicons dashicons-dashboard" aria-hidden="true"></i>
366
- <span class="button-text">' . esc_html__( 'What plan do I need?', 'imagify' ) . '</span>
367
- </button>
368
- </div>
369
- <div class="imagify-oh">
370
- <p class="imagify-section-title">' . esc_html__( 'You\'re new to Imagify?', 'imagify' ) . '</p>
371
- <p>' . esc_html__( 'Let us help you by analyzing your existing images and determine the best plan for you', 'imagify' ) . '</p>
372
- </div>
373
- </div>
374
- ';
375
- }
376
-
377
- /**
378
- * Return the formatted price present in pricing tables.
379
- *
380
- * @since 1.6
381
- * @author Geoffrey
382
- *
383
- * @param float $value The price value.
384
- * @return string The markuped price.
385
- */
386
- function get_imagify_price_table_format( $value ) {
387
- $v = explode( '.', (string) $value );
388
 
389
- return '<span class="imagify-price-big">' . $v[0] . '</span> <span class="imagify-price-mini">.' . ( strlen( $v[1] ) === 1 ? $v[1] . '0' : $v[1] ) . '</span>';
390
- }
 
 
 
391
 
392
- /**
393
- * Get the payment modal HTML.
394
- *
395
- * @since 1.6
396
- * @since 1.6.3 Include discount banners.
397
- * @author Geoffrey
398
- *
399
- * @todo Make first offers dynamic thanks to consumption estimation.
400
- */
401
- function imagify_payment_modal() {
402
- ?>
403
- <div id="imagify-pricing-modal" class="imagify-modal imagify-payment-modal hide-if-no-js" aria-hidden="false" role="dialog">
404
- <div class="imagify-modal-content">
405
- <div class="imagify-modal-main">
406
- <div class="imagify-modal-views imagify-pre-checkout-view" id="imagify-pre-checkout-view" aria-hidden="false">
407
-
408
- <?php
409
- $attachments_number = imagify_count_attachments();
410
- $total_size = get_imagify_option( 'total_size_images_library', false );
411
- $per_month = get_imagify_option( 'average_size_images_per_month', false );
412
- ?>
413
-
414
- <div class="imagify-modal-section section-gray imagify-estimation-block<?php echo false === $total_size ? ' imagify-analyzing' : ''; ?>">
415
- <p class="imagify-modal-title">
416
- <span class="imagify-numbers-calc"><?php esc_html_e( 'We analysed your images', 'imagify' ); ?></span>
417
- <span class="imagify-numbers-notcalc"><?php esc_html_e( 'We are analysing your images', 'imagify' ); ?></span>
418
- </p>
419
-
420
- <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>loader-balls.svg" width="77" height="48" alt="<?php esc_attr_e( 'Analyzing', 'imagify' ); ?>" class="imagify-loader">
421
-
422
- <div class="imagify-modal-cols">
423
- <div class="imagify-col">
424
- <p>
425
- <span class="imagify-border-styled">
426
- <?php
427
- printf(
428
- /* translators: %s is a formatted number (don't use %d). */
429
- _n( 'You have %s image', 'You have %s images', $attachments_number, 'imagify' ),
430
- '</span><span class="imagify-big-number">' . number_format_i18n( $attachments_number ) . '</span><span class="imagify-border-styled">'
431
- );
432
- ?>
433
- </span>
434
- </p>
435
- </div>
436
- <div class="imagify-col">
437
- <p class="imagify-iconed">
438
- <i class="dashicons dashicons-images-alt2" aria-hidden="true"></i>
439
- <?php
440
- printf(
441
- /* translators: %s is a formatted file size. */
442
- esc_html__( 'You currently have %s of images in your library.', 'imagify' ),
443
- '<strong class="imagify-dark total-library-size">' . ( isset( $total_size['human'] ) ? $total_size['human'] : $total_size ) . '</strong>'
444
- );
445
- ?>
446
- </p>
447
- <p class="imagify-iconed">
448
- <i class="dashicons dashicons-cloud" aria-hidden="true"></i>
449
- <?php
450
- printf(
451
- /* translators: %s is a formatted file size. */
452
- esc_html__( 'You upload around %s of images per month.', 'imagify' ),
453
- '<strong class="imagify-dark average-month-size">' . ( isset( $per_month['human'] ) ? $per_month['human'] : $per_month ) . '</strong>'
454
- );
455
- ?>
456
- </p>
457
- </div>
458
- </div>
459
- </div><!-- .imagify-modal-section -->
460
-
461
- <?php imagify_print_discount_banner(); ?>
462
-
463
- <div class="imagify-modal-section imagify-pre-checkout-offers">
464
- <p class="imagify-modal-title">
465
- <span class="imagify-not-enough-title"><?php esc_html_e( 'We recommend you this plan', 'imagify' ); ?></span>
466
- <span class="imagify-enough-title"><?php esc_html_e( 'The free plan is enough to optimize your images', 'imagify' ); ?></span>
467
-
468
- </p>
469
-
470
- <div class="imagify-offer-line imagify-offer-monthly imagify-offer-selected imagify-month-selected" data-offer='{"lite":{"id":3,"name":"Lite","data":1073741824,"dataf":"1 GB","imgs":5000,"prices":{"monthly":4.99,"yearly":4.16,"add":4}}}'>
471
- <div class="imagify-offer-header">
472
- <p class="imagify-offer-title imagify-switch-my">
473
- <span aria-hidden="false" class="imagify-monthly"><?php esc_html_e( 'Subscribe a monthly plan', 'imagify' ); ?></span>
474
- <span aria-hidden="true" class="imagify-yearly"><?php esc_html_e( 'Subscribe a yearly plan', 'imagify' ); ?></span>
475
- </p>
476
- <div class="imagify-inline-options imagify-radio-line">
477
- <input id="imagify-subscription-monthly" type="radio" value="monthly" name="plan-subscription" checked="checked">
478
- <label for="imagify-subscription-monthly"><?php esc_html_e( 'Monthly' , 'imagify' ); ?></label>
479
-
480
- <input id="imagify-subscription-yearly" type="radio" value="yearly" name="plan-subscription">
481
- <label for="imagify-subscription-yearly"><?php esc_html_e( 'Yearly' , 'imagify' ); ?><span class="imagify-2-free"><?php esc_html_e( '2 months free', 'imagify' ); ?></span></label>
482
- </div><!-- .imagify-radio-line -->
483
- </div><!-- .imagify-offer-header -->
484
-
485
- <div class="imagify-offer-content imagify-flex-table">
486
-
487
- <div class="imagify-col-checkbox">
488
- <input type="checkbox" name="imagify-offer" id="imagify-offer-1gb" value="1Gb" checked="checked" class="imagify-checkbox medium">
489
- <label for="imagify-offer-1gb">
490
- <span class="imagify-the-offer">
491
- <span class="imagify-offer-size">1 GB</span>
492
- <span class="imagify-offer-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
493
- </span>
494
- <span class="imagify-approx">
495
- <?php
496
- printf(
497
- /* translators: %s is a formatted number (don't use %d). */
498
- esc_html__( 'approx: %s images', 'imagify' ),
499
- '<span class="imagify-approx-nb">' . number_format_i18n( 5000 ) . '</span>'
500
- );
501
- ?>
502
- </span>
503
- </label>
504
- </div>
505
- <div class="imagify-col-price imagify-flex-table">
506
- <span class="imagify-price-block">
507
- <span class="imagify-dollars">$</span>
508
- <span class="imagify-number-block">
509
- <span class="imagify-switch-my">
510
- <span class="imagify-monthly" aria-hidden="false">
511
- <span class="imagify-price-big">3</span>
512
- <span class="imagify-price-mini">.99</span>
513
- </span>
514
- <span class="imagify-yearly" aria-hidden="true">
515
- <span class="imagify-price-big">3</span>
516
- <span class="imagify-price-mini">.16</span>
517
- </span>
518
- </span>
519
- <span class="imagify-price-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
520
- </span>
521
- </span>
522
-
523
- <p class="imagify-price-complement">
524
- <?php
525
- printf(
526
- /* translators: %s is a formatted price. */
527
- __( '%s per<br>additionnal Gb', 'imagify' ),
528
- '<span class="imagify-price-add-data"></span>'
529
- );
530
- ?>
531
- </p>
532
-
533
- </div>
534
- <div class="imagify-col-other-actions">
535
- <a href="#imagify-plans-selection-view" class="imagify-choose-another-plan" data-imagify-choose="plan"><?php esc_html_e( 'Choose another plan', 'imagify' ); ?></a>
536
- </div>
537
-
538
- </div><!-- .imagify-offer-content -->
539
-
540
- </div><!-- .imagify-offer-line -->
541
-
542
- <div class="imagify-offer-line imagify-offer-onetime" data-offer='{"recommended":{"id":999,"name":"Customized","data":3000001337,"dataf":"3 GB","imgs":54634,"price":28.98}}'>
543
- <div class="imagify-offer-header">
544
- <p class="imagify-offer-title">
545
- <?php esc_html_e( 'Optimize the images you already have, buy a one-time plan', 'imagify' ); ?>
546
- </p>
547
- </div><!-- .imagify-offer-header -->
548
-
549
- <div class="imagify-offer-content imagify-flex-table">
550
-
551
- <div class="imagify-col-checkbox">
552
- <input type="checkbox" name="imagify-offer" id="imagify-offer-custom" value="1Gb" checked="checked" class="imagify-checkbox medium">
553
- <label for="imagify-offer-custom">
554
- <span class="imagify-the-offer">
555
- <span class="imagify-offer-size">3 GB</span>
556
- </span>
557
- <span class="imagify-approx">
558
- <?php
559
- printf(
560
- /* translators: %s is a formatted number (don't use %d). */
561
- esc_html__( 'approx: %s images', 'imagify' ),
562
- '<span class="imagify-approx-nb">' . number_format_i18n( 54000 ) . '</span>'
563
- );
564
- ?>
565
- </span>
566
- </label>
567
- </div>
568
- <div class="imagify-col-price imagify-flex-table">
569
- <span class="imagify-price-block">
570
- <span class="imagify-dollars">$</span>
571
- <span class="imagify-number-block">
572
- <span class="imagify-price-big"></span>
573
- <span class="imagify-price-mini"></span>
574
- </span>
575
- </span>
576
- </div>
577
- <div class="imagify-col-other-actions">
578
- <a href="#imagify-plans-selection-view" class="imagify-choose-another-plan" data-imagify-choose="onetime"><?php esc_html_e( 'Choose another plan', 'imagify' ); ?></a>
579
- </div>
580
-
581
- </div><!-- .imagify-offer-content -->
582
-
583
- </div><!-- .imagify-offer-line -->
584
-
585
-
586
- <div class="imagify-submit-line">
587
- <div class="imagify-coupon-section">
588
- <p class="imagify-coupon-text">
589
-
590
- <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>loader-balls.svg" width="60" height="36" alt="<?php esc_attr_e( 'Checking Coupon', 'imagify' ); ?>" class="imagify-coupon-loader">
591
-
592
- <label for="imagify-coupon-code"><?php _e( 'If you have a <strong>coupon code</strong><br> use it there:', 'imagify' ); ?></label>
593
- </p>
594
- <p class="imagify-coupon-input">
595
- <input type="text" class="imagify-coupon-code" name="imagify-coupon-code" id="imagify-coupon-code" value="" placeholder="<?php _e( 'Coupon Code', 'imagify' ); ?>" autocomplete="off">
596
- <button type="button" class="button button-secondary imagify-button-secondary" id="imagify-coupon-validate"><?php _e( 'OK' ); ?></button>
597
- </p>
598
- </div>
599
- <div class="imagify-submit-section">
600
- <button type="button" class="button button-secondary imagify-button-secondary" id="imagify-modal-checkout-btn">
601
- <i class="dashicons dashicons-cart" aria-hidden="true"></i>
602
- <?php _e( 'Checkout', 'imagify' ); ?>
603
- </button>
604
- </div>
605
- </div>
606
-
607
- <p class="imagify-footer-lines"><?php esc_html_e( 'Monthly plans come with credits which is renewed every months. The billing happens automatically each month or year depending the billing period you choose.', 'imagify' ); ?></p>
608
- </div>
609
- </div><!-- .imagify-pre-checkout-view -->
610
-
611
- <?php
612
- /**
613
- * SECOND MODAL VIEW.
614
- */
615
- ?>
616
-
617
- <div class="imagify-modal-views imagify-plans-selection-view" id="imagify-plans-selection-view" aria-hidden="true">
618
- <p class="imagify-modal-title"><?php _e( 'Choose a plan', 'imagify' ); ?></p>
619
- <ul class="imagify-tabs" role="tablist">
620
- <li class="imagify-tab imagify-current">
621
- <a href="#imagify-pricing-tab-monthly" role="tab" aria-controls="imagify-pricing-tab-monthly" aria-selected="true">
622
- <?php esc_html_e( 'Monthly Plans', 'imagify' ); ?>
623
- </a>
624
- </li>
625
- <li class="imagify-tab">
626
- <a href="#imagify-pricing-tab-onetime" role="tab" aria-controls="imagify-pricing-tab-onetime" aria-selected="false">
627
- <?php esc_html_e( 'One Time Plans', 'imagify' ); ?>
628
- </a>
629
- </li>
630
- </ul><!-- .imagify-tabs -->
631
-
632
- <div class="imagify-tabs-contents">
633
-
634
- <div class="imagify-tab-content imagify-current" id="imagify-pricing-tab-monthly" role="tabpanel">
635
-
636
- <div class="imagify-modal-section section-gray">
637
- <p><?php esc_html_e( 'Monthly plans come with credits which is renewed every months. The billing happens automatically each month or year depending the billing period you choose.', 'imagify' ); ?></p>
638
- </div>
639
-
640
- <?php imagify_print_discount_banner(); ?>
641
-
642
- <div class="imagify-inline-options imagify-small-options imagify-radio-line">
643
- <input id="imagify-pricing-montly" type="radio" value="monthly" name="plan-pricing" checked="checked">
644
- <label for="imagify-pricing-montly"><?php esc_html_e( 'Monthly' , 'imagify' ); ?></label>
645
-
646
- <input id="imagify-pricing-yearly" type="radio" value="yearly" name="plan-pricing">
647
- <label for="imagify-pricing-yearly"><?php esc_html_e( 'Yearly' , 'imagify' ); ?><span class="imagify-2-free imagify-b-right"><?php esc_html_e( '2 months free', 'imagify' ); ?></span></label>
648
- </div><!-- .imagify-radio-line -->
649
-
650
-
651
- <div class="imagify-pricing-table imagify-month-selected">
652
-
653
- <script type="text/template" id="imagify-offer-monthly-template"><div class="imagify-offer-line imagify-offer-monthlies imagify-flex-table">
654
- <div class="imagify-col-details">
655
- <p class="imagify-label">
656
- <span class="imagify-the-offer">
657
- <span class="imagify-offer-size"></span>
658
- <span class="imagify-offer-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
659
- </span>
660
- <span class="imagify-approx">
661
- <?php
662
- printf(
663
- /* translators: %s is a formatted number (don't use %d). */
664
- __( 'approx: %s images', 'imagify' ),
665
- '<span class="imagify-approx-nb"></span>'
666
- );
667
- ?>
668
- </span>
669
- </p>
670
- </div>
671
- <div class="imagify-col-price imagify-flex-table">
672
- <span class="imagify-price-block">
673
- <span class="imagify-dollars">$</span>
674
- <span class="imagify-number-block">
675
- <span class="imagify-switch-my"></span>
676
- <span class="imagify-price-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
677
- </span>
678
- </span>
679
-
680
- <span class="imagify-recommend" aria-hidden="true"><?php esc_html_e( 'we recommend for you', 'imagify' ); ?></span>
681
-
682
- <p class="imagify-price-complement">
683
- <?php
684
- printf(
685
- /* translators: %s is a formatted price. */
686
- __( '%s per<br>additionnal Gb', 'imagify' ),
687
- '<span class="imagify-price-add-data"></span>'
688
- );
689
- ?>
690
- </p>
691
-
692
- </div><!-- .imagify-col-price -->
693
-
694
- <div class="imagify-col-other-actions">
695
- <button type="button" class="button imagify-button-secondary mini imagify-payment-btn-select-plan"><?php esc_html_e( 'Choose plan', 'imagify' ); ?></button>
696
- </div>
697
- </div><!-- .imagify-offer-line --></script>
698
- </div><!-- .imagify-pricing-table -->
699
-
700
- <div class="imagify-cols">
701
- <div class="imagify-col imagify-txt-start">
702
- <p class="imagify-special-needs">
703
- <strong><?php esc_html_e( 'Need more?', 'imagify' ); ?></strong>
704
- <span><?php esc_html_e( 'for special needs', 'imagify' ); ?></span>
705
- </p>
706
- </div>
707
- <div class="imagify-col imagify-txt-end">
708
- <p><a class="button imagify-button-ghost imagify-button-medium imagify-mt1 imagify-mb1 imagify-mr1" href="<?php echo esc_html( imagify_get_external_url( 'contact' ) ); ?>" target="_blank"><i class="dashicons dashicons-email" aria-hidden="true"></i>&nbsp;<?php esc_html_e( 'Contact Us', 'imagify' ); ?></a></p>
709
- </div>
710
- </div>
711
-
712
- </div><!-- .imagify-tab-content -->
713
- <div class="imagify-tab-content" id="imagify-pricing-tab-onetime" role="tabpanel">
714
- <div class="imagify-modal-section section-gray">
715
- <p><?php esc_html_e( 'One time plans are useful if you have a lots of existing images which need to be optimized. You can use it for bulk optimizing all your past images. You will pay only once.', 'imagify' ); ?></p>
716
- </div>
717
-
718
- <div class="imagify-pricing-table imagify-month-selected">
719
- <script type="text/template" id="imagify-offer-onetime-template"><div class="imagify-offer-line imagify-flex-table imagify-offer-onetimes">
720
- <div class="imagify-col-details">
721
- <p class="imagify-label">
722
- <span class="imagify-the-offer">
723
- <span class="imagify-offer-size"></span>
724
- </span>
725
- <span class="imagify-approx">
726
- <?php
727
- printf(
728
- /* translators: %s is a formatted number (don't use %d). */
729
- __( 'approx: %s images', 'imagify' ),
730
- '<span class="imagify-approx-nb"></span>'
731
- );
732
- ?>
733
- </span>
734
- </p>
735
- </div>
736
- <div class="imagify-col-price">
737
- <span class="imagify-price-block">
738
- <span class="imagify-dollars">$</span>
739
- <span class="imagify-number-block"></span>
740
- </span>
741
- <span class="imagify-recommend"><?php esc_html_e( 'we recommend for you', 'imagify' ); ?></span>
742
- </div><!-- .imagify-col-price -->
743
-
744
- <div class="imagify-col-other-actions">
745
- <button type="button" class="button imagify-button-secondary mini imagify-payment-btn-select-plan"><?php esc_html_e( 'Choose plan', 'imagify' ); ?></button>
746
- </div>
747
- </div><!-- .imagify-offer-line --></script>
748
- </div><!-- .imagify-pricing-table -->
749
-
750
- </div><!-- .imagify-tab-content -->
751
-
752
- </div><!-- .imagify-tabs-contents -->
753
- </div><!-- .imagify-plans-selection-view -->
754
-
755
-
756
- <?php
757
- /**
758
- * THIRD MODAL VIEW.
759
- */
760
- ?>
761
-
762
- <div class="imagify-modal-views imagify-payment-process-view" id="imagify-payment-process-view" aria-hidden="true">
763
-
764
- <iframe data-imagify-api="<?php echo esc_attr( get_imagify_option( 'api_key' ) ); ?>" id="imagify-payment-iframe" data-src="<?php echo esc_url( imagify_get_external_url( 'payment' ) ); ?>" name="imagify-payment-iframe" src="" frameborder="0"></iframe>
765
-
766
- </div><!-- .imagify-modal-views -->
767
-
768
- <?php
769
- /**
770
- * SUCCESS VIEW.
771
- */
772
- ?>
773
-
774
- <div class="imagify-modal-views imagify-success-view" id="imagify-success-view" aria-hidden="true">
775
- <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>big-blue-check.png" width="113" height="109" alt="">
776
- <p><?php esc_html_e( 'Thank you for being awesome!', 'imagify' ); ?></p>
777
- </div><!-- .imagify-modal-views -->
778
-
779
- <button class="close-btn" type="button">
780
- <i aria-hidden="true" class="dashicons dashicons-no-alt"></i>
781
- <span class="screen-reader-text"><?php esc_html_e( 'Close', 'imagify' ); ?></span>
782
- </button>
783
-
784
- </div><!-- .imagify-modal-main -->
785
-
786
- <div class="imagify-modal-sidebar">
787
- <div class="imagify-modal-sidebar-content imagify-txt-start">
788
- <p class="imagify-modal-sidebar-title"><?php esc_html_e( 'What do our user think about Imagify', 'imagify' ); ?></p>
789
-
790
- <div class="imagify-modal-testimony">
791
- <div class="imagify-modal-testimony-person">
792
- <span class="imagify-modal-avatar">
793
- <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>pic-srhdesign.jpg" alt="">
794
- </span>
795
- <p class="imagify-modal-identity">
796
- <a href="https://twitter.com/SRHDesign" target="_blank">@SRHDesign</a>
797
- <a href="https://twitter.com/SRHDesign/status/686486119249260544" target="_blank"><time datetime="2016-01-11">11 jan. 2016 @ 17:40</time></a>
798
- </p>
799
- </div>
800
- <div class="imagify-modal-testimony-content">
801
- <p>@imagify is an awesome tool that is powerful &amp; easy to use. It's fast, rivals and surpasses other established plugins/software. Awesome!</p>
802
- </div>
803
- </div>
804
-
805
- <div class="imagify-modal-testimony">
806
- <div class="imagify-modal-testimony-person">
807
- <span class="imagify-modal-avatar">
808
- <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>pic-ericwaltr.jpg" alt="">
809
- </span>
810
- <p class="imagify-modal-identity">
811
- <a href="https://twitter.com/EricWaltr" target="_blank">@EricWaltr</a>
812
- <a href="https://twitter.com/EricWaltR/status/679053496382038016" target="_blank"><time datetime="2016-01-11">21 dec. 2015 @ 22:39</time></a>
813
- </p>
814
- </div>
815
- <div class="imagify-modal-testimony-content">
816
- <p>Clearly @imagify is the most awesome tool to compress images on your website! A must try</p>
817
- </div>
818
- </div>
819
-
820
- <div class="imagify-modal-sidebar-trust imagify-txt-center">
821
- <p class="imagify-secondary">
822
- <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>icon-lock.png" srcset="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>icon-lock.svg 2x" width="16" height="19" alt="">&nbsp;<?php esc_html_e( 'Secure Credit Card Payment', 'imagify' ); ?></p>
823
- <p><?php esc_html_e( 'This is secure 128-bits SSL encrypted payment', 'imagify' ); ?></p>
824
- </div>
825
- </div>
826
- </div><!-- .imagify-modal-sidebar -->
827
-
828
- <div class="imagify-modal-loader"></div>
829
- </div><!-- .imagify-modal-content-->
830
- </div><!-- .imagify-payment-modal -->
831
- <?php
832
- }
833
 
834
- /**
835
- * Print the discount banner used inside Payment Modal.
836
- *
837
- * @since 1.6.3
838
- * @author Geoffrey Crofte
839
- *
840
- * @return void
841
- */
842
- function imagify_print_discount_banner() {
843
- ?>
844
- <div class="imagify-modal-promotion" aria-hidden="true">
845
- <p class="imagify-promo-title">
846
- <?php
847
- printf(
848
- /* translators: %s is a formatted percentage. */
849
- __( '%s OFF on all the subscriptions', 'secupress' ),
850
- '<span class="imagify-promotion-number"></span>'
851
- );
852
- ?>
853
- </p>
854
- <p class="imagify-until-date">
855
- <?php
856
- printf(
857
- /* translators: %s is a formatted date. */
858
- __( 'Special Offer<br><strong>Until %s</strong>', 'secupress' ),
859
- '<span class="imagify-promotion-date"></span>'
860
- );
861
- ?>
862
- </p>
863
- </div>
864
- <?php
865
  }
37
  }
38
 
39
  $attachment_id = $attachment->id;
 
40
  $optimization_level = $attachment->get_optimization_level_label();
41
 
42
  if ( ! $is_media_page ) {
43
+ $output .= $output_before . '<span class="data">' . __( 'New Filesize:', 'imagify' ) . '</span> <strong class="big">' . $attachment->get_optimized_size() . '</strong>' . $output_after;
44
  }
45
 
46
  $chart = '';
61
 
62
  $output .= $output_before;
63
  $output .= '<span class="data">' . __( 'Original Saving:', 'imagify' ) . '</span> ';
64
+ $output .= '<strong>' . $chart . '<span class="imagify-chart-value">' . $attachment->get_saving_percent() . '</span>%</strong>';
65
  $output .= $output_after;
66
 
67
  // More details section.
86
 
87
  if ( $total_optimized_thumbnails ) {
88
  $output .= $output_before . '<span class="data">' . __( 'Thumbnails Optimized:', 'imagify' ) . '</span> <strong>' . $total_optimized_thumbnails . '</strong>' . $output_after;
89
+ $output .= $output_before . '<span class="data">' . __( 'Overall Saving:', 'imagify' ) . '</span> <strong>' . $attachment->get_overall_saving_percent() . '%</strong>' . $output_after;
90
  }
91
 
92
  // End of list.
109
  $output .= '</a>';
110
 
111
  if ( ! $is_library_page ) {
112
+ $dimensions = $attachment->get_dimensions();
113
 
114
  $output .= '<input id="imagify-original-src" type="hidden" value="' . esc_url( $attachment->get_backup_url() ) . '">';
115
  $output .= '<input id="imagify-original-size" type="hidden" value="' . $attachment->get_original_size() . '">';
116
+ $output .= '<input id="imagify-full-src" type="hidden" value="' . esc_url( $attachment->get_original_url() ) . '">';
117
+ $output .= '<input id="imagify-full-width" type="hidden" value="' . $dimensions['width'] . '">';
118
+ $output .= '<input id="imagify-full-height" type="hidden" value="' . $dimensions['height'] . '">';
119
  }
120
  }
121
 
286
  }
287
 
288
  // Check if the attachment extension is allowed.
289
+ if ( ! $attachment->is_extension_supported() ) {
290
+ $extension = $attachment->get_extension();
291
+
292
+ if ( '' === $extension ) {
293
+ return __( 'With no extension, this file cannot be optimized', 'imagify' );
294
+ }
295
 
296
  /* translators: %s is a file extension. */
297
+ return sprintf( __( '%s cannot be optimized', 'imagify' ), strtoupper( $extension ) );
298
  }
299
 
300
  // Check if the attachment has the required WP metadata.
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
 
318
  }
319
 
320
  // Check if the image was optimized.
321
+ if ( ! $attachment->get_status() ) {
322
  $args = array(
323
  'attachment_id' => $attachment->id,
324
  'context' => $context,
336
  }
337
 
338
  /**
339
+ * Get stats data for a specific folder type.
340
  *
341
+ * @since 1.7
342
+ * @see Imagify_Admin_Ajax_Post::imagify_get_folder_type_data_callback()
343
+ * @author Grégory Viguier
 
344
  *
345
+ * @param string $folder_type A folder type.
346
+ * @return array
347
  */
348
+ function imagify_get_folder_type_data( $folder_type ) {
349
+ /**
350
+ * Get the data.
351
+ */
352
+ switch ( $folder_type ) {
353
+ case 'library':
354
+ $total_saving_data = imagify_count_saving_data();
355
+ $data = array(
356
+ 'images-optimized' => imagify_count_optimized_attachments(),
357
+ 'errors' => imagify_count_error_attachments(),
358
+ 'optimized' => $total_saving_data['optimized_size'],
359
+ 'original' => $total_saving_data['original_size'],
360
+ 'errors_url' => get_imagify_admin_url( 'folder-errors', $folder_type ),
361
+ );
362
+ break;
363
+
364
+ case 'custom-folders':
365
+ $data = array(
366
+ 'images-optimized' => Imagify_Files_Stats::count_optimized_files(),
367
+ 'errors' => Imagify_Files_Stats::count_error_files(),
368
+ 'optimized' => Imagify_Files_Stats::get_optimized_size(),
369
+ 'original' => Imagify_Files_Stats::get_original_size(),
370
+ 'errors_url' => get_imagify_admin_url( 'folder-errors', $folder_type ),
371
+ );
372
+ break;
373
+
374
+ default:
375
+ /**
376
+ * Provide custom folder type data.
377
+ *
378
+ * @since 1.7
379
+ * @author Grégory Viguier
380
+ *
381
+ * @param array $data An array with keys corresponding to cell classes, and values formatted with HTML.
382
+ * @param string $folder_type A folder type.
383
+ */
384
+ $data = apply_filters( 'imagify_get_folder_type_data', array(), $folder_type );
385
+
386
+ if ( ! $data || ! is_array( $data ) ) {
387
+ return array();
388
+ }
389
  }
390
 
391
  /**
392
+ * Format the data.
 
 
393
  */
394
+ /* translators: %s is a formatted number, dont use %d. */
395
+ $data['images-optimized'] = sprintf( _n( '%s Image Optimized', '%s Images Optimized', $data['images-optimized'], 'imagify' ), '<span>' . number_format_i18n( $data['images-optimized'] ) . '</span>' );
396
+
397
+ if ( $data['errors'] ) {
398
+ /* translators: %s is a formatted number, dont use %d. */
399
+ $data['errors'] = sprintf( _n( '%s Error', '%s Errors', $data['errors'], 'imagify' ), '<span>' . number_format_i18n( $data['errors'] ) . '</span>' );
400
+ $data['errors'] .= ' <a href="' . esc_url( $data['errors_url'] ) . '">' . __( 'View Errors', 'imagify' ) . '</a>';
401
+ } else {
402
+ $data['errors'] = '';
403
  }
404
 
405
+ if ( $data['optimized'] ) {
406
+ $data['optimized'] = '<span class="imagify-cell-label">' . __( 'Optimized Filesize', 'imagify' ) . '</span> ' . imagify_size_format( $data['optimized'], 2 );
407
+ } else {
408
+ $data['optimized'] = '';
409
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
 
411
+ if ( $data['original'] ) {
412
+ $data['original'] = '<span class="imagify-cell-label">' . __( 'Original Filesize', 'imagify' ) . '</span> ' . imagify_size_format( $data['original'], 2 );
413
+ } else {
414
+ $data['original'] = '';
415
+ }
416
 
417
+ unset( $data['errors_url'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
 
419
+ return $data;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  }
inc/functions/admin.php CHANGED
@@ -54,11 +54,13 @@ function imagify_is_screen( $identifier ) {
54
  switch ( $identifier ) {
55
  case 'imagify-settings':
56
  // Imagify Settings or Imagify Network Settings.
57
- return 'settings_page_' . IMAGIFY_SLUG === $current_screen->id || 'settings_page_' . IMAGIFY_SLUG . '-network' === $current_screen->id;
 
58
 
59
  case 'imagify-network-settings':
60
  // Imagify Network Settings.
61
- return 'settings_page_' . IMAGIFY_SLUG . '-network' === $current_screen->id;
 
62
 
63
  case 'library':
64
  // Media Library.
@@ -79,8 +81,20 @@ function imagify_is_screen( $identifier ) {
79
 
80
  case 'bulk':
81
  case 'bulk-optimization':
82
- // Bulk Optimization.
83
- return 'media_page_' . IMAGIFY_SLUG . '-bulk-optimization' === $current_screen->id;
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  case 'media-modal':
86
  // Media modal.
@@ -100,11 +114,11 @@ function imagify_is_screen( $identifier ) {
100
  * @param array|string $arg An array of arguments. It can contain an attachment ID and/or a context.
101
  * @return string The URL of the specific admin page or action.
102
  */
103
- function get_imagify_admin_url( $action = 'options-general', $arg = array() ) {
104
  if ( is_array( $arg ) ) {
105
  $id = isset( $arg['attachment_id'] ) ? $arg['attachment_id'] : 0;
106
  $context = isset( $arg['context'] ) ? $arg['context'] : 'wp';
107
- $level = isset( $arg['optimization_level'] ) ? $arg['optimization_level'] : 0;
108
  }
109
 
110
  switch ( $action ) {
@@ -123,12 +137,48 @@ function get_imagify_admin_url( $action = 'options-general', $arg = array() ) {
123
  case 'dismiss-notice':
124
  return wp_nonce_url( admin_url( 'admin-post.php?action=imagify_dismiss_notice&notice=' . $arg ), Imagify_Notices::DISMISS_NONCE_ACTION );
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  case 'bulk-optimization':
127
- return admin_url( 'upload.php?page=' . IMAGIFY_SLUG . '-bulk-optimization' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
  default:
130
- $page = imagify_is_active_for_network() ? network_admin_url( 'settings.php' ) : admin_url( 'options-general.php' );
131
- return $page . '?page=' . IMAGIFY_SLUG;
132
  }
133
  }
134
 
@@ -164,12 +214,16 @@ function get_imagify_max_intermediate_image_size() {
164
  * Get the default Bulk Optimization buffer size.
165
  *
166
  * @since 1.5.10
 
167
  * @author Jonathan Buttigieg
168
  *
169
- * @return int The buffer size.
 
170
  */
171
- function get_imagify_bulk_buffer_size() {
172
- $sizes = count( get_imagify_thumbnail_sizes() );
 
 
173
 
174
  switch ( true ) {
175
  case ( $sizes >= 10 ):
@@ -259,10 +313,10 @@ function imagify_check_nonce( $action, $query_arg = false ) {
259
  *
260
  * @since 1.6.10
261
  * @since 1.6.11 Uses a capacity describer instead of a capacity itself.
262
- * @author Grégory Viguier
263
  * @see imagify_get_capacity()
 
264
  *
265
- * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', and 'auto-optimize'. Can also be a "real" user capacity.
266
  * @param int $post_id A post ID.
267
  */
268
  function imagify_check_user_capacity( $describer = 'manage', $post_id = null ) {
@@ -295,7 +349,7 @@ function imagify_die( $message = null ) {
295
  }
296
  }
297
 
298
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
299
  wp_send_json_error( $message );
300
  }
301
 
@@ -332,7 +386,7 @@ function imagify_die( $message = null ) {
332
  * @param array|string $args_or_url An array of query args to add to the redirection URL. If a string, the complete URL.
333
  */
334
  function imagify_maybe_redirect( $message = false, $args_or_url = array() ) {
335
- if ( defined( 'DOING_AJAX' ) ) {
336
  return;
337
  }
338
 
@@ -354,6 +408,73 @@ function imagify_maybe_redirect( $message = false, $args_or_url = array() ) {
354
  */
355
  $redirect = apply_filters( 'imagify_redirect_to', $redirect );
356
 
 
 
 
 
 
 
 
 
357
  wp_safe_redirect( esc_url_raw( $redirect ) );
358
  die();
359
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  switch ( $identifier ) {
55
  case 'imagify-settings':
56
  // Imagify Settings or Imagify Network Settings.
57
+ $slug = Imagify_Views::get_instance()->get_settings_page_slug();
58
+ return 'settings_page_' . $slug === $current_screen->id || $slug . '_page_' . $slug . '-network' === $current_screen->id || 'toplevel_page_' . $slug . '-network' === $current_screen->id;
59
 
60
  case 'imagify-network-settings':
61
  // Imagify Network Settings.
62
+ $slug = Imagify_Views::get_instance()->get_settings_page_slug();
63
+ return $slug . '_page_' . $slug . '-network' === $current_screen->id || 'toplevel_page_' . $slug . '-network' === $current_screen->id;
64
 
65
  case 'library':
66
  // Media Library.
81
 
82
  case 'bulk':
83
  case 'bulk-optimization':
84
+ // Bulk Optimization (any).
85
+ $slug = Imagify_Views::get_instance()->get_bulk_page_slug();
86
+ return 'toplevel_page_' . $slug . '-network' === $current_screen->id || 'media_page_' . $slug === $current_screen->id;
87
+
88
+ case 'files-bulk-optimization':
89
+ // Bulk Optimization (custom folders).
90
+ $slug = Imagify_Views::get_instance()->get_bulk_page_slug();
91
+ return 'toplevel_page_' . $slug . '-network' === $current_screen->id || 'media_page_' . $slug === $current_screen->id;
92
+
93
+ case 'files':
94
+ case 'files-list':
95
+ // "Custom folders" files list.
96
+ $slug = Imagify_Views::get_instance()->get_files_page_slug();
97
+ return 'imagify_page_' . $slug . '-network' === $current_screen->id || 'media_page_' . $slug === $current_screen->id;
98
 
99
  case 'media-modal':
100
  // Media modal.
114
  * @param array|string $arg An array of arguments. It can contain an attachment ID and/or a context.
115
  * @return string The URL of the specific admin page or action.
116
  */
117
+ function get_imagify_admin_url( $action = 'settings', $arg = array() ) {
118
  if ( is_array( $arg ) ) {
119
  $id = isset( $arg['attachment_id'] ) ? $arg['attachment_id'] : 0;
120
  $context = isset( $arg['context'] ) ? $arg['context'] : 'wp';
121
+ $level = isset( $arg['optimization_level'] ) ? $arg['optimization_level'] : '';
122
  }
123
 
124
  switch ( $action ) {
137
  case 'dismiss-notice':
138
  return wp_nonce_url( admin_url( 'admin-post.php?action=imagify_dismiss_notice&notice=' . $arg ), Imagify_Notices::DISMISS_NONCE_ACTION );
139
 
140
+ case 'optimize-file':
141
+ case 'restore-file':
142
+ case 'refresh-file-modified':
143
+ $action = 'imagify_' . str_replace( '-', '_', $action );
144
+ return wp_nonce_url( admin_url( 'admin-post.php?action=' . $action . '&id=' . $id ), $action );
145
+
146
+ case 'reoptimize-file':
147
+ $action = 'imagify_' . str_replace( '-', '_', $action );
148
+ return wp_nonce_url( admin_url( 'admin-post.php?action=' . $action . '&id=' . $id . '&level=' . $level ), $action );
149
+
150
+ case 'get-files-tree':
151
+ return wp_nonce_url( admin_url( 'admin-ajax.php?action=imagify_get_files_tree' ), 'get-files-tree' );
152
+
153
  case 'bulk-optimization':
154
+ return admin_url( 'upload.php?page=' . Imagify_Views::get_instance()->get_bulk_page_slug() );
155
+
156
+ case 'files-bulk-optimization':
157
+ $page = '?page=' . Imagify_Views::get_instance()->get_bulk_page_slug();
158
+ return imagify_is_active_for_network() ? network_admin_url( 'admin.php' . $page ) : admin_url( 'upload.php' . $page );
159
+
160
+ case 'files-list':
161
+ $page = '?page=' . Imagify_Views::get_instance()->get_files_page_slug();
162
+ return imagify_is_active_for_network() ? network_admin_url( 'admin.php' . $page ) : admin_url( 'upload.php' . $page );
163
+
164
+ case 'folder-errors':
165
+ switch ( $arg ) {
166
+ case 'library':
167
+ return add_query_arg( array(
168
+ 'mode' => 'list',
169
+ 'imagify-status' => 'errors',
170
+ ), admin_url( 'upload.php' ) );
171
+
172
+ case 'custom-folders':
173
+ return add_query_arg( array(
174
+ 'status-filter' => 'errors',
175
+ ), get_imagify_admin_url( 'files-list' ) );
176
+ }
177
+ return '';
178
 
179
  default:
180
+ $page = '?page=' . Imagify_Views::get_instance()->get_settings_page_slug();
181
+ return imagify_is_active_for_network() ? network_admin_url( 'admin.php' . $page ) : admin_url( 'options-general.php' . $page );
182
  }
183
  }
184
 
214
  * Get the default Bulk Optimization buffer size.
215
  *
216
  * @since 1.5.10
217
+ * @since 1.7 Added $sizes parameter.
218
  * @author Jonathan Buttigieg
219
  *
220
+ * @param int $sizes Number of image sizes per item (attachment).
221
+ * @return int The buffer size.
222
  */
223
+ function get_imagify_bulk_buffer_size( $sizes = false ) {
224
+ if ( ! $sizes ) {
225
+ $sizes = count( get_imagify_thumbnail_sizes() );
226
+ }
227
 
228
  switch ( true ) {
229
  case ( $sizes >= 10 ):
313
  *
314
  * @since 1.6.10
315
  * @since 1.6.11 Uses a capacity describer instead of a capacity itself.
 
316
  * @see imagify_get_capacity()
317
+ * @author Grégory Viguier
318
  *
319
+ * @param string $describer Capacity describer. See imagify_get_capacity() for possible values. Can also be a "real" user capacity.
320
  * @param int $post_id A post ID.
321
  */
322
  function imagify_check_user_capacity( $describer = 'manage', $post_id = null ) {
349
  }
350
  }
351
 
352
+ if ( wp_doing_ajax() ) {
353
  wp_send_json_error( $message );
354
  }
355
 
386
  * @param array|string $args_or_url An array of query args to add to the redirection URL. If a string, the complete URL.
387
  */
388
  function imagify_maybe_redirect( $message = false, $args_or_url = array() ) {
389
+ if ( wp_doing_ajax() ) {
390
  return;
391
  }
392
 
408
  */
409
  $redirect = apply_filters( 'imagify_redirect_to', $redirect );
410
 
411
+ if ( $message ) {
412
+ if ( is_multisite() && strpos( $redirect, network_admin_url( '/' ) ) === 0 ) {
413
+ Imagify_Notices::get_instance()->add_network_temporary_notice( $message );
414
+ } else {
415
+ Imagify_Notices::get_instance()->add_site_temporary_notice( $message );
416
+ }
417
+ }
418
+
419
  wp_safe_redirect( esc_url_raw( $redirect ) );
420
  die();
421
  }
422
+
423
+ /**
424
+ * Get cached Imagify user data.
425
+ * This is usefull to prevent triggering an HTTP request to our server on every page load, but it can be used only where the data doesn't need to be in real time.
426
+ *
427
+ * @since 1.7
428
+ * @author Grégory Viguier
429
+ *
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
+
437
+ if ( imagify_is_active_for_network() ) {
438
+ $user = get_site_transient( 'imagify_user' );
439
+ } else {
440
+ $user = get_transient( 'imagify_user' );
441
+ }
442
+
443
+ return is_object( $user ) ? $user : false;
444
+ }
445
+
446
+ /**
447
+ * Cache Imagify user data for 5 minutes.
448
+ * Runs every methods to store the results. Also stores formatted data like the quota and the next update date.
449
+ *
450
+ * @since 1.7
451
+ * @author Grégory Viguier
452
+ *
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
+
460
+ $user = new Imagify_User();
461
+ $data = (object) get_object_vars( $user );
462
+ $methods = get_class_methods( $user );
463
+
464
+ foreach ( $methods as $method ) {
465
+ if ( '__construct' !== $method ) {
466
+ $data->$method = $user->$method();
467
+ }
468
+ }
469
+
470
+ $data->quota_formatted = imagify_size_format( $user->quota * 1048576 );
471
+ $data->next_date_update_formatted = date_i18n( get_option( 'date_format' ), strtotime( $user->next_date_update ) );
472
+
473
+ if ( imagify_is_active_for_network() ) {
474
+ set_site_transient( 'imagify_user', $data, 5 * MINUTE_IN_SECONDS );
475
+ } else {
476
+ set_transient( 'imagify_user', $data, 5 * MINUTE_IN_SECONDS );
477
+ }
478
+
479
+ return $data;
480
+ }
inc/functions/api.php CHANGED
@@ -264,28 +264,30 @@ function imagify_translate_api_message( $message ) {
264
 
265
  $messages = array(
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
  // API messages.
270
- 'Authentification not provided' => __( 'Authentication not provided.', 'imagify' ),
271
- 'Cannot create client token' => __( 'Cannot create client token.', 'imagify' ),
272
- 'Confirm your account to continue optimizing image' => __( 'Confirm your account to continue optimizing images.', 'imagify' ),
273
- 'Coupon doesn\'t exist' => __( 'Coupon does not exist.', 'imagify' ),
274
- 'Email field shouldn\'t be empty' => __( 'Email field should not be empty.', 'imagify' ),
275
- 'Email or Password field shouldn\'t be empty' => __( 'This account already exists.', 'imagify' ),
276
- 'Error uploading to data Storage' => __( 'Error uploading to Data Storage.', 'imagify' ),
277
- 'Not able to connect to Data Storage API to get the token' => __( 'Unable to connect to Data Storage API to get the token.', 'imagify' ),
278
- 'Not able to connect to Data Storage API' => __( 'Unable to connect to Data Storage API.', 'imagify' ),
279
- 'Not able to retrieve the token from DataStorage API' => __( 'Unable to retrieve the token from Data Storage API.', 'imagify' ),
280
- 'This email is already registered, you should try another email' => __( 'This email is already registered, you should try another email.', 'imagify' ),
281
- 'This user doesn\'t exit' => __( 'This user does not exist.', 'imagify' ),
282
- 'Too many request, be patient' => __( 'Too many requests, please be patient.', 'imagify' ),
283
- 'Unable to regenerate access token' => __( 'Unable to regenerate access token.', 'imagify' ),
284
- 'User not valid' => __( 'User not valid.', 'imagify' ),
285
- 'WELL DONE. This image is already compressed, no further compression required' => __( 'WELL DONE. This image is already optimized, no further optimization is required.', 'imagify' ),
286
- 'You are not authorized to perform this action' => __( 'You are not authorized to perform this action.', 'imagify' ),
287
- 'You\'ve consumed all your data. You have to upgrade your account to continue' => __( 'You have consumed all your data. You have to upgrade your account to continue.', 'imagify' ),
288
- 'Invalid token' => __( 'Invalid API key', 'imagify' ),
 
289
  );
290
 
291
  if ( isset( $messages[ $trim_message ] ) ) {
@@ -301,7 +303,13 @@ function imagify_translate_api_message( $message ) {
301
  // API message.
302
  if ( preg_match( '@^Custom one time plan starts from (\d+) MB$@', $trim_message, $matches ) ) {
303
  /* translators: %s is a formatted number, dont use %d. */
304
- return sprintf( __( 'Custom one time plan starts from %s&nbsp;MB.', 'imagify' ), number_format_i18n( (int) $matches[1] ) );
 
 
 
 
 
 
305
  }
306
 
307
  return $message;
264
 
265
  $messages = array(
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' ),
273
+ 'Confirm your account to continue optimizing image' => __( 'Confirm your account to continue optimizing images.', 'imagify' ),
274
+ 'Coupon doesn\'t exist' => __( 'Coupon does not exist.', 'imagify' ),
275
+ 'Email field shouldn\'t be empty' => __( 'Email field should not be empty.', 'imagify' ),
276
+ 'Email or Password field shouldn\'t be empty' => __( 'This account already exists.', 'imagify' ),
277
+ 'Error uploading to data Storage' => __( 'Error uploading to Data Storage.', 'imagify' ),
278
+ 'Not able to connect to Data Storage API to get the token' => __( 'Unable to connect to Data Storage API to get the token.', 'imagify' ),
279
+ 'Not able to connect to Data Storage API' => __( 'Unable to connect to Data Storage API.', 'imagify' ),
280
+ 'Not able to retrieve the token from DataStorage API' => __( 'Unable to retrieve the token from Data Storage API.', 'imagify' ),
281
+ 'This email is already registered, you should try another email' => __( 'This email is already registered, you should try another email.', 'imagify' ),
282
+ 'This user doesn\'t exit' => __( 'This user does not exist.', 'imagify' ),
283
+ 'Too many request, be patient' => __( 'Too many requests, please be patient.', 'imagify' ),
284
+ 'Unable to regenerate access token' => __( 'Unable to regenerate access token.', 'imagify' ),
285
+ 'User not valid' => __( 'User not valid.', 'imagify' ),
286
+ 'WELL DONE. This image is already compressed, no further compression required' => __( 'WELL DONE. This image is already optimized, no further optimization is required.', 'imagify' ),
287
+ 'You are not authorized to perform this action' => __( 'You are not authorized to perform this action.', 'imagify' ),
288
+ 'You\'ve consumed all your data. You have to upgrade your account to continue' => __( 'You have consumed all your data. You have to upgrade your account to continue.', 'imagify' ),
289
+ 'Invalid token' => __( 'Invalid API key', 'imagify' ),
290
+ 'Upload a valid image. The file you uploaded was either not an image or a corrupted image' => __( 'Invalid or corrupted file.', 'imagify' ),
291
  );
292
 
293
  if ( isset( $messages[ $trim_message ] ) ) {
303
  // API message.
304
  if ( preg_match( '@^Custom one time plan starts from (\d+) MB$@', $trim_message, $matches ) ) {
305
  /* translators: %s is a formatted number, dont use %d. */
306
+ return sprintf( __( 'Custom One Time plan starts from %s MB.', 'imagify' ), number_format_i18n( (int) $matches[1] ) );
307
+ }
308
+
309
+ // API message.
310
+ if ( preg_match( '@^(.*) is not a valid extension$@', $trim_message, $matches ) ) {
311
+ /* translators: %s is a file extension. */
312
+ return sprintf( __( '%s is not a valid extension.', 'imagify' ), sanitize_text_field( $matches[1] ) );
313
  }
314
 
315
  return $message;
inc/functions/attachments.php CHANGED
@@ -2,53 +2,18 @@
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
  /**
5
- * Get all mime type which could be optimized by Imagify.
6
  *
7
- * @since 1.3
8
  *
9
- * @return array $mime_type The mime type.
10
  */
11
- function get_imagify_mime_type() {
12
  return array(
13
- 'image/jpeg',
14
- 'image/png',
15
- 'image/gif',
16
- );
17
- }
18
-
19
- /**
20
- * Get a file mime type.
21
- *
22
- * @since 1.6.9
23
- * @author Grégory Viguier
24
- *
25
- * @param string $file_path A file path (prefered) or a filename.
26
- * @return string|bool A mime type. False on failure: the last test is limited to mime types supported by Imagify.
27
- */
28
- function imagify_get_mime_type_from_file( $file_path ) {
29
- if ( function_exists( 'exif_imagetype' ) ) {
30
- $image_type = @exif_imagetype( $file_path );
31
-
32
- if ( false !== $image_type ) {
33
- return image_type_to_mime_type( $image_type );
34
- }
35
- }
36
-
37
- if ( function_exists( 'getimagesize' ) ) {
38
- $image_type = @getimagesize( $file_path );
39
-
40
- if ( isset( $image_type[2] ) ) {
41
- return image_type_to_mime_type( $image_type[2] );
42
- }
43
- }
44
-
45
- $image_type = wp_check_filetype( $file_path, array(
46
  'jpg|jpeg|jpe' => 'image/jpeg',
47
  'png' => 'image/png',
48
  'gif' => 'image/gif',
49
- ) );
50
-
51
- return $image_type['type'];
52
  }
53
 
54
  /**
@@ -71,7 +36,7 @@ function imagify_is_attachment_mime_type_supported( $attachment_id ) {
71
  return $is[ $attachment_id ];
72
  }
73
 
74
- $mime_types = get_imagify_mime_type();
75
  $mime_types = array_flip( $mime_types );
76
  $mime_type = (string) get_post_mime_type( $attachment_id );
77
 
@@ -80,17 +45,99 @@ function imagify_is_attachment_mime_type_supported( $attachment_id ) {
80
  return $is[ $attachment_id ];
81
  }
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  /**
84
  * Tell if the attachment has the required WP metadata.
85
  *
86
  * @since 1.6.12
 
87
  * @author Grégory Viguier
88
  *
89
  * @param int $attachment_id The attachment ID.
90
  * @return bool
91
  */
92
  function imagify_attachment_has_required_metadata( $attachment_id ) {
93
- return get_attached_file( $attachment_id, true ) && wp_get_attachment_metadata( $attachment_id, true );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  }
95
 
96
  /**
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
  /**
5
+ * Get all mime types which could be optimized by Imagify.
6
  *
7
+ * @since 1.7
8
  *
9
+ * @return array The mime types.
10
  */
11
+ function imagify_get_mime_types() {
12
  return array(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  'jpg|jpeg|jpe' => 'image/jpeg',
14
  'png' => 'image/png',
15
  'gif' => 'image/gif',
16
+ );
 
 
17
  }
18
 
19
  /**
36
  return $is[ $attachment_id ];
37
  }
38
 
39
+ $mime_types = imagify_get_mime_types();
40
  $mime_types = array_flip( $mime_types );
41
  $mime_type = (string) get_post_mime_type( $attachment_id );
42
 
45
  return $is[ $attachment_id ];
46
  }
47
 
48
+ /**
49
+ * Get post statuses related to attachments.
50
+ *
51
+ * @since 1.7
52
+ * @author Grégory Viguier
53
+ *
54
+ * @return array
55
+ */
56
+ function imagify_get_post_statuses() {
57
+ static $statuses;
58
+
59
+ if ( isset( $statuses ) ) {
60
+ return $statuses;
61
+ }
62
+
63
+ $statuses = array(
64
+ 'inherit' => 'inherit',
65
+ 'private' => 'private',
66
+ );
67
+
68
+ $custom_statuses = get_post_stati( array( 'public' => true ) );
69
+ unset( $custom_statuses['publish'] );
70
+
71
+ if ( $custom_statuses ) {
72
+ $statuses = array_merge( $statuses, $custom_statuses );
73
+ }
74
+
75
+ /**
76
+ * Filter the post statuses Imagify is allowed to optimize.
77
+ *
78
+ * @since 1.7
79
+ * @author Grégory Viguier
80
+ *
81
+ * @param array $statuses An array of post statuses. Kays and values are set.
82
+ */
83
+ $statuses = apply_filters( 'imagify_post_statuses', $statuses );
84
+
85
+ return $statuses;
86
+ }
87
+
88
  /**
89
  * Tell if the attachment has the required WP metadata.
90
  *
91
  * @since 1.6.12
92
+ * @since 1.7 Also checks that the '_wp_attached_file' meta is valid (not a URL or anything funny).
93
  * @author Grégory Viguier
94
  *
95
  * @param int $attachment_id The attachment ID.
96
  * @return bool
97
  */
98
  function imagify_attachment_has_required_metadata( $attachment_id ) {
99
+ $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
100
+
101
+ if ( ! $file || preg_match( '@://@', $file ) || preg_match( '@^.:\\\@', $file ) ) {
102
+ return false;
103
+ }
104
+
105
+ return (bool) wp_get_attachment_metadata( $attachment_id, true );
106
+ }
107
+
108
+ /**
109
+ * Tell if the site has attachments (only the ones Imagify would optimize) without the required WP metadata.
110
+ *
111
+ * @since 1.7
112
+ * @author Grégory Viguier
113
+ *
114
+ * @return bool
115
+ */
116
+ function imagify_has_attachments_without_required_metadata() {
117
+ global $wpdb;
118
+ static $has;
119
+
120
+ if ( isset( $has ) ) {
121
+ return $has;
122
+ }
123
+
124
+ $mime_types = Imagify_DB::get_mime_types();
125
+ $statuses = Imagify_DB::get_post_statuses();
126
+ $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause( 'p.ID', false, false );
127
+ $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( array(), false, false );
128
+ $has = (bool) $wpdb->get_var( // WPCS: unprepared SQL ok.
129
+ "
130
+ SELECT p.ID
131
+ FROM $wpdb->posts AS p
132
+ $nodata_join
133
+ WHERE p.post_mime_type IN ( $mime_types )
134
+ AND p.post_type = 'attachment'
135
+ AND p.post_status IN ( $statuses )
136
+ $nodata_where
137
+ LIMIT 1"
138
+ );
139
+
140
+ return $has;
141
  }
142
 
143
  /**
inc/functions/common.php CHANGED
@@ -8,7 +8,7 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
8
  * @since 1.6.11 Uses a string as describer for the first argument.
9
  * @author Grégory Viguier
10
  *
11
- * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', and 'auto-optimize'.
12
  * @return string
13
  */
14
  function imagify_get_capacity( $describer = 'manage' ) {
@@ -27,6 +27,9 @@ function imagify_get_capacity( $describer = 'manage' ) {
27
  case 'manage':
28
  $capacity = imagify_is_active_for_network() ? 'manage_network_options' : 'manage_options';
29
  break;
 
 
 
30
  case 'bulk-optimize':
31
  $capacity = 'manage_options';
32
  break;
@@ -58,7 +61,7 @@ function imagify_get_capacity( $describer = 'manage' ) {
58
  * @since 1.6.11 Replaced $force_mono by $describer.
59
  *
60
  * @param string $capacity The user capacity.
61
- * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', and 'auto-optimize'.
62
  */
63
  return apply_filters( 'imagify_capacity', $capacity, $describer );
64
  }
@@ -67,9 +70,10 @@ function imagify_get_capacity( $describer = 'manage' ) {
67
  * Tell if the current user has the required ability to operate Imagify.
68
  *
69
  * @since 1.6.11
 
70
  * @author Grégory Viguier
71
  *
72
- * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', and 'auto-optimize'.
73
  * @param int $post_id A post ID (a gallery ID for NGG).
74
  * @return bool
75
  */
@@ -80,8 +84,8 @@ function imagify_current_user_can( $describer = 'manage', $post_id = null ) {
80
  $capacity = imagify_get_capacity( $describer );
81
  $user_can = false;
82
 
83
- if ( 'manage' !== $describer && 'bulk-optimize' !== $describer ) {
84
- // Describers that are not 'manage' and 'bulk-optimize' need an additional test for 'upload_files'.
85
  if ( ! isset( $can_upload ) ) {
86
  $can_upload = current_user_can( 'upload_files' );
87
  }
@@ -105,12 +109,43 @@ function imagify_current_user_can( $describer = 'manage', $post_id = null ) {
105
  *
106
  * @param bool $user_can Tell if the current user has the required ability to operate Imagify.
107
  * @param string $capacity The user capacity.
108
- * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', and 'auto-optimize'.
109
  * @param int $post_id A post ID (a gallery ID for NGG).
110
  */
111
  return apply_filters( 'imagify_current_user_can', $user_can, $capacity, $describer, $post_id );
112
  }
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  /**
115
  * Sanitize an optimization context.
116
  *
@@ -136,15 +171,33 @@ function imagify_sanitize_context( $context ) {
136
  function imagify_autoload( $class ) {
137
  // Generic classes.
138
  $classes = array(
139
- 'Imagify_Abstract_Attachment' => 1,
140
- 'Imagify_Abstract_DB' => 1,
141
- 'Imagify_Admin_Ajax_Post' => 1,
142
- 'Imagify_Assets' => 1,
143
- 'Imagify_Attachment' => 1,
144
- 'Imagify_DB' => 1,
145
- 'Imagify_Notices' => 1,
146
- 'Imagify_User' => 1,
147
- 'Imagify' => 1,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  );
149
 
150
  if ( isset( $classes[ $class ] ) ) {
@@ -305,3 +358,64 @@ function imagify_get_locale() {
305
  */
306
  return apply_filters( 'imagify_locale', $locale );
307
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  * @since 1.6.11 Uses a string as describer for the first argument.
9
  * @author Grégory Viguier
10
  *
11
+ * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize', and 'optimize-file'.
12
  * @return string
13
  */
14
  function imagify_get_capacity( $describer = 'manage' ) {
27
  case 'manage':
28
  $capacity = imagify_is_active_for_network() ? 'manage_network_options' : 'manage_options';
29
  break;
30
+ case 'optimize-file':
31
+ $capacity = is_multisite() ? 'manage_network_options' : 'manage_options';
32
+ break;
33
  case 'bulk-optimize':
34
  $capacity = 'manage_options';
35
  break;
61
  * @since 1.6.11 Replaced $force_mono by $describer.
62
  *
63
  * @param string $capacity The user capacity.
64
+ * @param string $describer Capacity describer. Possible values are 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize', and 'optimize-file'.
65
  */
66
  return apply_filters( 'imagify_capacity', $capacity, $describer );
67
  }
70
  * Tell if the current user has the required ability to operate Imagify.
71
  *
72
  * @since 1.6.11
73
+ * @see imagify_get_capacity()
74
  * @author Grégory Viguier
75
  *
76
+ * @param string $describer Capacity describer. See imagify_get_capacity() for possible values. Can also be a "real" user capacity.
77
  * @param int $post_id A post ID (a gallery ID for NGG).
78
  * @return bool
79
  */
84
  $capacity = imagify_get_capacity( $describer );
85
  $user_can = false;
86
 
87
+ if ( 'manage' !== $describer && 'bulk-optimize' !== $describer && 'optimize-file' !== $describer ) {
88
+ // Describers that are not 'manage', 'bulk-optimize', and 'optimize-file' need an additional test for 'upload_files'.
89
  if ( ! isset( $can_upload ) ) {
90
  $can_upload = current_user_can( 'upload_files' );
91
  }
109
  *
110
  * @param bool $user_can Tell if the current user has the required ability to operate Imagify.
111
  * @param string $capacity The user capacity.
112
+ * @param string $describer Capacity describer. See imagify_get_capacity() for possible values. Can also be a "real" user capacity.
113
  * @param int $post_id A post ID (a gallery ID for NGG).
114
  */
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
+ *
121
+ * @since 1.7
122
+ * @author Grégory Viguier
123
+ *
124
+ * @return bool
125
+ */
126
+ function imagify_can_optimize_custom_folders() {
127
+ static $can;
128
+
129
+ if ( isset( $can ) ) {
130
+ return $can;
131
+ }
132
+
133
+ // Check if the DB tables are ready.
134
+ if ( ! Imagify_Folders_DB::get_instance()->can_operate() || ! Imagify_Files_DB::get_instance()->can_operate() ) {
135
+ $can = false;
136
+ return $can;
137
+ }
138
+
139
+ // Check for user capacity.
140
+ if ( ! imagify_current_user_can( 'optimize-file' ) ) {
141
+ $can = false;
142
+ return $can;
143
+ }
144
+
145
+ $can = true;
146
+ return $can;
147
+ }
148
+
149
  /**
150
  * Sanitize an optimization context.
151
  *
171
  function imagify_autoload( $class ) {
172
  // Generic classes.
173
  $classes = array(
174
+ 'Imagify_Abstract_Attachment' => 1,
175
+ 'Imagify_Abstract_Cron' => 1,
176
+ 'Imagify_Abstract_DB' => 1,
177
+ 'Imagify_Abstract_Options' => 1,
178
+ 'Imagify_Admin_Ajax_Post' => 1,
179
+ 'Imagify_Assets' => 1,
180
+ 'Imagify_Attachment' => 1,
181
+ 'Imagify_Cron_Library_Size' => 1,
182
+ 'Imagify_Cron_Rating' => 1,
183
+ 'Imagify_Cron_Sync_Files' => 1,
184
+ 'Imagify_Custom_Folders' => 1,
185
+ 'Imagify_Data' => 1,
186
+ 'Imagify_DB' => 1,
187
+ 'Imagify_File_Attachment' => 1,
188
+ 'Imagify_Files_DB' => 1,
189
+ 'Imagify_Files_Iterator' => 1,
190
+ 'Imagify_Files_List_Table' => 1,
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,
200
+ 'Imagify' => 1,
201
  );
202
 
203
  if ( isset( $classes[ $class ] ) ) {
358
  */
359
  return apply_filters( 'imagify_locale', $locale );
360
  }
361
+
362
+ /**
363
+ * Get the label corresponding to the given optimization label.
364
+ *
365
+ * @since 1.7
366
+ * @author Grégory Viguier
367
+ *
368
+ * @param int|bool $level Optimization level (between 0 and 2). False if no level.
369
+ * @param string $format Format to display the label. Use %ICON% for the icon and %s for the label.
370
+ * @return string The label.
371
+ */
372
+ function imagify_get_optimization_level_label( $level, $format = '%s' ) {
373
+ if ( ! is_numeric( $level ) ) {
374
+ return '';
375
+ }
376
+
377
+ if ( strpos( $format, '%ICON%' ) !== false ) {
378
+ $icon = '<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g fill="#40B1D0" fill-rule="evenodd">';
379
+
380
+ switch ( $level ) {
381
+ case 2:
382
+ $icon .= '<polygon points="11.6054688 11.6054688 8.7890625 11.6054688 8.7890625 0.39453125 11.6054688 0.39453125"/><polygon points="7.39453125 11.6054688 4.60546875 11.6054688 4.60546875 3.89453125 7.39453125 3.89453125"/><polygon points="3.2109375 11.6054688 0.39453125 11.6054688 0.39453125 6 3.2109375 6"/>';
383
+ break;
384
+ case 1:
385
+ $icon .= '<polygon fill="#CCD1D6" points="11.6054688 11.6054688 8.7890625 11.6054688 8.7890625 0.39453125 11.6054688 0.39453125"/><polygon points="7.39453125 11.6054688 4.60546875 11.6054688 4.60546875 3.89453125 7.39453125 3.89453125"/><polygon points="3.2109375 11.6054688 0.39453125 11.6054688 0.39453125 6 3.2109375 6"/>';
386
+ break;
387
+ case 0:
388
+ $icon .= '<polygon fill="#CCD1D6" points="11.6054688 11.6054688 8.7890625 11.6054688 8.7890625 0.39453125 11.6054688 0.39453125"/><polygon fill="#CCD1D6" points="7.39453125 11.6054688 4.60546875 11.6054688 4.60546875 3.89453125 7.39453125 3.89453125"/><polygon points="3.2109375 11.6054688 0.39453125 11.6054688 0.39453125 6 3.2109375 6"/>';
389
+ }
390
+
391
+ $icon .= '</g></svg>';
392
+
393
+ $format = str_replace( '%ICON%', $icon, $format );
394
+ }
395
+
396
+ switch ( $level ) {
397
+ case 2:
398
+ return sprintf( $format, __( 'Ultra', 'imagify' ) );
399
+ case 1:
400
+ return sprintf( $format, __( 'Aggressive', 'imagify' ) );
401
+ case 0:
402
+ return sprintf( $format, __( 'Normal', 'imagify' ) );
403
+ }
404
+
405
+ return '';
406
+ }
407
+
408
+ /**
409
+ * `array_merge()` + `array_intersect_key()`.
410
+ *
411
+ * @since 1.7
412
+ * @author Grégory Viguier
413
+ *
414
+ * @param array $values The array we're interested in.
415
+ * @param array $default The array we use as boundaries.
416
+ * @return array
417
+ */
418
+ function imagify_merge_intersect( $values, $default ) {
419
+ $values = array_merge( $default, (array) $values );
420
+ return array_intersect_key( $values, $default );
421
+ }
inc/functions/compat-spl-autoload.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Polyfill for SPL autoload feature. This file is separate to prevent compiler notices
4
+ * on the deprecated __autoload() function.
5
+ *
6
+ * See https://core.trac.wordpress.org/ticket/41134
7
+ */
8
+
9
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
10
+
11
+ if ( ! function_exists( 'spl_autoload_register' ) ) :
12
+ $_wp_spl_autoloaders = array();
13
+
14
+ /**
15
+ * Autoloader compatibility callback.
16
+ *
17
+ * @since 1.6.12
18
+ * @since WP 4.6.0
19
+ * @source WordPress
20
+ *
21
+ * @param string $classname Class to attempt autoloading.
22
+ */
23
+ function __autoload( $classname ) {
24
+ global $_wp_spl_autoloaders;
25
+ foreach ( $_wp_spl_autoloaders as $autoloader ) {
26
+ if ( ! is_callable( $autoloader ) ) {
27
+ // Avoid the extra warning if the autoloader isn't callable.
28
+ continue;
29
+ }
30
+
31
+ call_user_func( $autoloader, $classname );
32
+
33
+ // If it has been autoloaded, stop processing.
34
+ if ( class_exists( $classname, false ) ) {
35
+ return;
36
+ }
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Registers a function to be autoloaded.
42
+ *
43
+ * @since 1.6.12
44
+ * @since WP 4.6.0
45
+ * @source WordPress
46
+ *
47
+ * @throws Exception If the function to register is not callable.
48
+ *
49
+ * @param callable $autoload_function The function to register.
50
+ * @param bool $throw Optional. Whether the function should throw an exception
51
+ * if the function isn't callable. Default true.
52
+ * @param bool $prepend Whether the function should be prepended to the stack.
53
+ * Default false.
54
+ */
55
+ function spl_autoload_register( $autoload_function, $throw = true, $prepend = false ) {
56
+ if ( $throw && ! is_callable( $autoload_function ) ) {
57
+ // String not translated to match PHP core.
58
+ throw new Exception( 'Function not callable' );
59
+ }
60
+
61
+ global $_wp_spl_autoloaders;
62
+
63
+ // Don't allow multiple registration.
64
+ if ( in_array( $autoload_function, $_wp_spl_autoloaders, true ) ) {
65
+ return;
66
+ }
67
+
68
+ if ( $prepend ) {
69
+ array_unshift( $_wp_spl_autoloaders, $autoload_function );
70
+ } else {
71
+ $_wp_spl_autoloaders[] = $autoload_function;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Unregisters an autoloader function.
77
+ *
78
+ * @since 1.6.12
79
+ * @since WP 4.6.0
80
+ * @source WordPress
81
+ *
82
+ * @param callable $function The function to unregister.
83
+ * @return bool True if the function was unregistered, false if it could not be.
84
+ */
85
+ function spl_autoload_unregister( $function ) {
86
+ global $_wp_spl_autoloaders;
87
+ foreach ( $_wp_spl_autoloaders as &$autoloader ) {
88
+ if ( $autoloader === $function ) {
89
+ unset( $autoloader );
90
+ return true;
91
+ }
92
+ }
93
+
94
+ return false;
95
+ }
96
+
97
+ /**
98
+ * Retrieves the registered autoloader functions.
99
+ *
100
+ * @since 1.6.12
101
+ * @since WP 4.6.0
102
+ * @source WordPress
103
+ *
104
+ * @return array List of autoloader functions.
105
+ */
106
+ function spl_autoload_functions() {
107
+ return $GLOBALS['_wp_spl_autoloaders'];
108
+ }
109
+ endif;
inc/functions/compat.php CHANGED
@@ -57,105 +57,43 @@ if ( ! function_exists( 'array_replace' ) ) :
57
  }
58
  endif;
59
 
60
- // SPL can be disabled on PHP 5.2.
61
- if ( ! function_exists( 'spl_autoload_register' ) ) :
62
- $_wp_spl_autoloaders = array();
63
-
64
  /**
65
- * Autoloader compatibility callback.
66
  *
67
- * @since 1.6.12
68
- * @since WP 4.6.0
69
- * @source WordPress
70
  *
71
- * @param string $classname Class to attempt autoloading.
72
- */
73
- function __autoload( $classname ) {
74
- global $_wp_spl_autoloaders;
75
- foreach ( $_wp_spl_autoloaders as $autoloader ) {
76
- if ( ! is_callable( $autoloader ) ) {
77
- // Avoid the extra warning if the autoloader isn't callable.
78
- continue;
79
- }
80
-
81
- call_user_func( $autoloader, $classname );
82
-
83
- // If it has been autoloaded, stop processing.
84
- if ( class_exists( $classname, false ) ) {
85
- return;
86
- }
87
- }
88
- }
89
-
90
- /**
91
- * Registers a function to be autoloaded.
92
  *
93
- * @since 1.6.12
94
- * @since WP 4.6.0
95
- * @source WordPress
96
  *
97
- * @throws Exception If the function to register is not callable.
 
 
98
  *
99
- * @param callable $autoload_function The function to register.
100
- * @param bool $throw Optional. Whether the function should throw an exception
101
- * if the function isn't callable. Default true.
102
- * @param bool $prepend Whether the function should be prepended to the stack.
103
- * Default false.
104
  */
105
- function spl_autoload_register( $autoload_function, $throw = true, $prepend = false ) {
106
- if ( $throw && ! is_callable( $autoload_function ) ) {
107
- // String not translated to match PHP core.
108
- throw new Exception( 'Function not callable' );
109
- }
110
-
111
- global $_wp_spl_autoloaders;
112
-
113
- // Don't allow multiple registration.
114
- if ( in_array( $autoload_function, $_wp_spl_autoloaders, true ) ) {
115
- return;
116
- }
117
-
118
- if ( $prepend ) {
119
- array_unshift( $_wp_spl_autoloaders, $autoload_function );
120
- } else {
121
- $_wp_spl_autoloaders[] = $autoload_function;
122
  }
123
- }
124
 
125
- /**
126
- * Unregisters an autoloader function.
127
- *
128
- * @since 1.6.12
129
- * @since WP 4.6.0
130
- * @source WordPress
131
- *
132
- * @param callable $function The function to unregister.
133
- * @return bool True if the function was unregistered, false if it could not be.
134
- */
135
- function spl_autoload_unregister( $function ) {
136
- global $_wp_spl_autoloaders;
137
- foreach ( $_wp_spl_autoloaders as &$autoloader ) {
138
- if ( $autoloader === $function ) {
139
- unset( $autoloader );
140
- return true;
141
- }
142
  }
143
 
144
- return false;
145
  }
 
146
 
147
- /**
148
- * Retrieves the registered autoloader functions.
149
- *
150
- * @since 1.6.12
151
- * @since WP 4.6.0
152
- * @source WordPress
153
- *
154
- * @return array List of autoloader functions.
155
- */
156
- function spl_autoload_functions() {
157
- return $GLOBALS['_wp_spl_autoloaders'];
158
- }
159
  endif;
160
 
161
  /** --------------------------------------------------------------------------------------------- */
@@ -589,3 +527,121 @@ if ( ! function_exists( 'wp_scripts' ) ) :
589
  return $wp_scripts;
590
  }
591
  endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
  endif;
59
 
60
+ if ( ! function_exists( 'hash_equals' ) ) :
 
 
 
61
  /**
62
+ * Timing attack safe string comparison
63
  *
64
+ * Compares two strings using the same time whether they're equal or not.
 
 
65
  *
66
+ * This function was added in PHP 5.6.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  *
68
+ * Note: It can leak the length of a string when arguments of differing length are supplied.
 
 
69
  *
70
+ * @since 1.7
71
+ * @since PHP 5.6.0
72
+ * @since WP 3.9.2
73
  *
74
+ * @param string $a Expected string.
75
+ * @param string $b Actual, user supplied, string.
76
+ * @return bool Whether strings are equal.
 
 
77
  */
78
+ function hash_equals( $a, $b ) {
79
+ $a_length = strlen( $a );
80
+ if ( strlen( $b ) !== $a_length ) {
81
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
+ $result = 0;
84
 
85
+ // Do not attempt to "optimize" this.
86
+ for ( $i = 0; $i < $a_length; $i++ ) {
87
+ $result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
+ return 0 === $result;
91
  }
92
+ endif;
93
 
94
+ // SPL can be disabled on PHP 5.2.
95
+ if ( ! function_exists( 'spl_autoload_register' ) ) :
96
+ require_once IMAGIFY_FUNCTIONS_PATH . 'compat-spl-autoload.php';
 
 
 
 
 
 
 
 
 
97
  endif;
98
 
99
  /** --------------------------------------------------------------------------------------------- */
527
  return $wp_scripts;
528
  }
529
  endif;
530
+
531
+ if ( ! function_exists( 'wp_doing_ajax' ) ) :
532
+ /**
533
+ * Determines whether the current request is a WordPress Ajax request.
534
+ *
535
+ * @since 1.7
536
+ * @since WP 4.7.0
537
+ *
538
+ * @return bool True if it's a WordPress Ajax request, false otherwise.
539
+ */
540
+ function wp_doing_ajax() {
541
+ /**
542
+ * Filters whether the current request is a WordPress Ajax request.
543
+ *
544
+ * @since 1.7
545
+ * @since WP 4.7.0
546
+ *
547
+ * @param bool $wp_doing_ajax Whether the current request is a WordPress Ajax request.
548
+ */
549
+ return apply_filters( 'wp_doing_ajax', defined( 'DOING_AJAX' ) && DOING_AJAX );
550
+ }
551
+ endif;
552
+
553
+ if ( ! function_exists( '_deprecated_hook' ) ) :
554
+ /**
555
+ * Marks a deprecated action or filter hook as deprecated and throws a notice.
556
+ *
557
+ * Use the {@see 'deprecated_hook_run'} action to get the backtrace describing where
558
+ * the deprecated hook was called.
559
+ *
560
+ * Default behavior is to trigger a user error if `WP_DEBUG` is true.
561
+ *
562
+ * This function is called by the do_action_deprecated() and apply_filters_deprecated()
563
+ * functions, and so generally does not need to be called directly.
564
+ *
565
+ * @since 1.7
566
+ * @since WP 4.6.0
567
+ * @access private
568
+ *
569
+ * @param string $hook The hook that was used.
570
+ * @param string $version The version of WordPress that deprecated the hook.
571
+ * @param string $replacement Optional. The hook that should have been used.
572
+ * @param string $message Optional. A message regarding the change.
573
+ */
574
+ function _deprecated_hook( $hook, $version, $replacement = null, $message = null ) {
575
+ /**
576
+ * Fires when a deprecated hook is called.
577
+ *
578
+ * @since 1.7
579
+ * @since WP 4.6.0
580
+ *
581
+ * @param string $hook The hook that was called.
582
+ * @param string $replacement The hook that should be used as a replacement.
583
+ * @param string $version The version of WordPress that deprecated the argument used.
584
+ * @param string $message A message regarding the change.
585
+ */
586
+ do_action( 'deprecated_hook_run', $hook, $replacement, $version, $message );
587
+
588
+ /**
589
+ * Filters whether to trigger deprecated hook errors.
590
+ *
591
+ * @since 1.7
592
+ * @since WP 4.6.0
593
+ *
594
+ * @param bool $trigger Whether to trigger deprecated hook errors. Requires
595
+ * `WP_DEBUG` to be defined true.
596
+ */
597
+ if ( WP_DEBUG && apply_filters( 'deprecated_hook_trigger_error', true ) ) {
598
+ $message = empty( $message ) ? '' : ' ' . $message;
599
+ if ( ! is_null( $replacement ) ) {
600
+ /* translators: 1: WordPress hook name, 2: version number, 3: alternative hook name */
601
+ trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.', 'imagify' ), $hook, $version, $replacement ) . $message );
602
+ } else {
603
+ /* translators: 1: WordPress hook name, 2: version number */
604
+ trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.', 'imagify' ), $hook, $version ) . $message );
605
+ }
606
+ }
607
+ }
608
+ endif;
609
+
610
+ if ( ! function_exists( 'apply_filters_deprecated' ) ) :
611
+ /**
612
+ * Fires functions attached to a deprecated filter hook.
613
+ *
614
+ * When a filter hook is deprecated, the apply_filters() call is replaced with
615
+ * apply_filters_deprecated(), which triggers a deprecation notice and then fires
616
+ * the original filter hook.
617
+ *
618
+ * Note: the value and extra arguments passed to the original apply_filters() call
619
+ * must be passed here to `$args` as an array. For example:
620
+ *
621
+ * // Old filter.
622
+ * return apply_filters( 'wpdocs_filter', $value, $extra_arg );
623
+ *
624
+ * // Deprecated.
625
+ * return apply_filters_deprecated( 'wpdocs_filter', array( $value, $extra_arg ), '4.9', 'wpdocs_new_filter' );
626
+ *
627
+ * @since 1.7
628
+ * @since WP 4.6.0
629
+ *
630
+ * @see _deprecated_hook()
631
+ *
632
+ * @param string $tag The name of the filter hook.
633
+ * @param array $args Array of additional function arguments to be passed to apply_filters().
634
+ * @param string $version The version of WordPress that deprecated the hook.
635
+ * @param string $replacement Optional. The hook that should have been used. Default false.
636
+ * @param string $message Optional. A message regarding the change. Default null.
637
+ */
638
+ function apply_filters_deprecated( $tag, $args, $version, $replacement = false, $message = null ) {
639
+ if ( ! has_filter( $tag ) ) {
640
+ return $args[0];
641
+ }
642
+
643
+ _deprecated_hook( $tag, $version, $replacement, $message );
644
+
645
+ return apply_filters_ref_array( $tag, $args );
646
+ }
647
+ endif;
inc/functions/deprecated.php CHANGED
@@ -2,9 +2,10 @@
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
  /**
5
- * Deprecated imagify.io API for WordPress.
6
  *
7
- * @since 1.6.5
 
8
  * @deprecated
9
  */
10
  class Imagify_Deprecated {
@@ -36,7 +37,7 @@ class Imagify_Deprecated {
36
  * @return object Main instance.
37
  */
38
  public static function instance() {
39
- _deprecated_function( get_class( $this ) . '::' . __FUNCTION__ . '()', '1.6.5', 'Imagify::get_instance()' );
40
  return Imagify::get_instance();
41
  }
42
 
@@ -163,7 +164,7 @@ class Imagify_Deprecated {
163
  }
164
 
165
  /**
166
- * Get prices for packs (one time).
167
  *
168
  * @since 1.6.5 Deprecated
169
  * @deprecated
@@ -231,6 +232,140 @@ class Imagify_Deprecated {
231
  }
232
  }
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  if ( class_exists( 'WpeCommon' ) ) :
235
 
236
  /**
@@ -415,57 +550,182 @@ if ( class_exists( 'C_NextGEN_Bootstrap' ) && class_exists( 'Mixin' ) && get_sit
415
  }
416
 
417
  /**
418
- * Combine two arrays with some specific keys.
419
- * We use this function to combine the result of 2 SQL queries.
420
  *
421
- * @since 1.4.5
422
- * @since 1.6.7 Added the $keep_keys_order parameter.
423
- * @since 1.6.13 Deprecated.
424
  * @deprecated
425
- *
426
- * @param array $keys An array of keys.
427
- * @param array $values An array of arrays like array( 'id' => id, 'value' => value ).
428
- * @param int $keep_keys_order Set to true to return an array ordered like $keys instead of $values.
429
- * @return array The combined arrays.
430
  */
431
- function imagify_query_results_combine( $keys, $values, $keep_keys_order = false ) {
432
- _deprecated_function( __FUNCTION__ . '()', '1.6.13', 'Imagify_DB::combine_query_results( $keys, $values, $keep_keys_order )' );
433
 
434
- return Imagify_DB::combine_query_results( $keys, $values, $keep_keys_order );
435
  }
436
 
437
  /**
438
- * A helper to retrieve all values from one or several post metas, given a list of post IDs.
439
- * The $wpdb cache is flushed to save memory.
440
  *
441
- * @since 1.6.7
442
- * @since 1.6.13 Deprecated.
443
- * @author Grégory Viguier
444
  * @deprecated
445
- *
446
- * @param array $metas An array of meta names like:
447
- * array(
448
- * 'key1' => 'meta_name_1',
449
- * 'key2' => 'meta_name_2',
450
- * 'key3' => 'meta_name_3',
451
- * )
452
- * If a key contains 'data', the results will be unserialized.
453
- * @param array $ids An array of post IDs.
454
- * @return array An array of arrays of results like:
455
- * array(
456
- * 'key1' => array( post_id_1 => 'result_1', post_id_2 => 'result_2', post_id_3 => 'result_3' ),
457
- * 'key2' => array( post_id_1 => 'result_4', post_id_3 => 'result_5' ),
458
- * 'key3' => array( post_id_1 => 'result_6', post_id_2 => 'result_7' ),
459
- * )
460
  */
461
- function imagify_get_wpdb_metas( $metas, $ids ) {
462
- _deprecated_function( __FUNCTION__ . '()', '1.6.13', 'Imagify_DB::get_metas( $metas, $ids )' );
463
 
464
- return Imagify_DB::get_metas( $metas, $ids );
 
 
 
 
 
 
 
 
 
465
  }
466
 
467
  endif;
468
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  if ( is_admin() ) :
470
 
471
  /**
@@ -531,7 +791,7 @@ if ( is_admin() ) :
531
  $data = method_exists( $notices, $callback ) ? call_user_func( array( $notices, $callback ) ) : false;
532
 
533
  if ( $data ) {
534
- $this->render_view( str_replace( '_', '-', $notice_id ), $data );
535
  }
536
  }
537
 
@@ -992,4 +1252,249 @@ if ( is_admin() ) :
992
  Imagify_Admin_Ajax_Post::get_instance()->imagify_update_estimate_sizes_callback();
993
  }
994
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
995
  endif;
2
  defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
 
4
  /**
5
+ * Class for deprecated methods from Imagify.
6
  *
7
+ * @since 1.6.5
8
+ * @author Grégory Viguier
9
  * @deprecated
10
  */
11
  class Imagify_Deprecated {
37
  * @return object Main instance.
38
  */
39
  public static function instance() {
40
+ _deprecated_function( 'Imagify::instance()', '1.6.5', 'Imagify::get_instance()' );
41
  return Imagify::get_instance();
42
  }
43
 
164
  }
165
 
166
  /**
167
+ * Get prices for packs (One Time).
168
  *
169
  * @since 1.6.5 Deprecated
170
  * @deprecated
232
  }
233
  }
234
 
235
+ /**
236
+ * Class for deprecated methods from Imagify_Abstract_DB.
237
+ *
238
+ * @since 1.7
239
+ * @author Grégory Viguier
240
+ * @deprecated
241
+ */
242
+ class Imagify_Abstract_DB_Deprecated {
243
+
244
+ /**
245
+ * Check if the given table exists.
246
+ *
247
+ * @since 1.5 In Imagify_Abstract_DB.
248
+ * @since 1.7 Deprecated.
249
+ * @access public
250
+ * @deprecated
251
+ *
252
+ * @param string $table The table name.
253
+ * @return bool True if the table name exists.
254
+ */
255
+ public function table_exists( $table ) {
256
+ _deprecated_function( get_class( $this ) . '::' . __FUNCTION__ . '()', '1.7.0', 'Imagify_DB::table_exists( $table )' );
257
+
258
+ return Imagify_DB::table_exists( $table );
259
+ }
260
+
261
+ /**
262
+ * Main Instance.
263
+ * Ensures only one instance of class is loaded or can be loaded.
264
+ * Well, actually it ensures nothing since it's not a full singleton pattern.
265
+ *
266
+ * @since 1.5 In Imagify_NGG_DB.
267
+ * @since 1.7 Deprecated.
268
+ * @access public
269
+ * @author Jonathan Buttigieg
270
+ * @deprecated
271
+ *
272
+ * @return object Main instance.
273
+ */
274
+ public static function instance() {
275
+ _deprecated_function( 'Imagify_Abstract_DB::instance()', '1.6.5', 'Imagify_Abstract_DB::get_instance()' );
276
+
277
+ return self::get_instance();
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Class for deprecated methods from Imagify_Abstract_Attachment.
283
+ *
284
+ * @since 1.7
285
+ * @author Grégory Viguier
286
+ * @deprecated
287
+ */
288
+ class Imagify_Abstract_Attachment_Deprecated {
289
+
290
+ /**
291
+ * Maybe backup a file.
292
+ *
293
+ * @since 1.6.6 In Imagify_AS3CF_Attachment.
294
+ * @since 1.6.8 Deprecated.
295
+ * @author Grégory Viguier
296
+ * @deprecated
297
+ *
298
+ * @param string $attachment_path The file path.
299
+ * @return bool|null True on success. False on failure. Null if backup is not needed.
300
+ */
301
+ protected function maybe_backup( $attachment_path ) {
302
+ _deprecated_function( get_class( $this ) . '::' . __FUNCTION__ . '()', '1.6.8', 'imagify_backup_file()' );
303
+
304
+ $result = imagify_backup_file( $attachment_path );
305
+
306
+ if ( false === $result ) {
307
+ return null;
308
+ }
309
+
310
+ return ! is_wp_error( $result );
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Class for deprecated methods from Imagify_AS3CF.
316
+ *
317
+ * @since 1.7
318
+ * @author Grégory Viguier
319
+ * @deprecated
320
+ */
321
+ class Imagify_AS3CF_Deprecated {
322
+
323
+ /**
324
+ * Tell if the attachment has a supported mime type.
325
+ *
326
+ * @since 1.6.6 In Imagify_AS3CF.
327
+ * @since 1.6.8 Deprecated.
328
+ * @see imagify_is_attachment_mime_type_supported()
329
+ * @author Grégory Viguier
330
+ * @deprecated
331
+ *
332
+ * @param int $post_id The attachment ID.
333
+ * @return bool
334
+ */
335
+ public function is_mime_type_supported( $post_id ) {
336
+ _deprecated_function( get_class( $this ) . '::' . __FUNCTION__ . '()', '1.6.8', 'imagify_is_attachment_mime_type_supported( $post_id )' );
337
+
338
+ return imagify_is_attachment_mime_type_supported( $post_id );
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Class for deprecated methods from Imagify_Notices.
344
+ *
345
+ * @since 1.7
346
+ * @author Grégory Viguier
347
+ * @deprecated
348
+ */
349
+ class Imagify_Notices_Deprecated {
350
+
351
+ /**
352
+ * Include the view file.
353
+ *
354
+ * @since 1.6.10 In Imagify_Notices
355
+ * @since 1.7 Deprecated
356
+ * @author Grégory Viguier
357
+ * @deprecated
358
+ *
359
+ * @param string $view The view ID.
360
+ * @param mixed $data Some data to pass to the view.
361
+ */
362
+ public function render_view( $view, $data = array() ) {
363
+ _deprecated_function( get_class( $this ) . '::' . __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->print_template( \'notice-\' . $view, $data )' );
364
+
365
+ Imagify_Views::get_instance()->print_template( 'notice-' . $view, $data );
366
+ }
367
+ }
368
+
369
  if ( class_exists( 'WpeCommon' ) ) :
370
 
371
  /**
550
  }
551
 
552
  /**
553
+ * Create the Imagify table needed for NGG compatibility.
 
554
  *
555
+ * @since 1.5
556
+ * @since 1.7 Deprecated.
557
+ * @author Jonathan Buttigieg
558
  * @deprecated
 
 
 
 
 
559
  */
560
+ function _imagify_create_ngg_table() {
561
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_NGG_DB::get_instance()->maybe_upgrade_table()' );
562
 
563
+ Imagify_NGG_DB::get_instance()->maybe_upgrade_table();
564
  }
565
 
566
  /**
567
+ * Update all Imagify stats for NGG Bulk Optimization.
 
568
  *
569
+ * @since 1.5
570
+ * @since 1.7 Deprecated.
571
+ * @author Jonathan Buttigieg
572
  * @deprecated
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  */
574
+ function _imagify_ngg_update_bulk_stats() {
575
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'imagify_ngg_bulk_page_data()' );
576
 
577
+ if ( empty( $_GET['page'] ) || imagify_get_ngg_bulk_screen_slug() !== $_GET['page'] ) { // WPCS: CSRF ok.
578
+ return;
579
+ }
580
+
581
+ add_filter( 'imagify_count_attachments' , 'imagify_ngg_count_attachments' );
582
+ add_filter( 'imagify_count_optimized_attachments' , 'imagify_ngg_count_optimized_attachments' );
583
+ add_filter( 'imagify_count_error_attachments' , 'imagify_ngg_count_error_attachments' );
584
+ add_filter( 'imagify_count_unoptimized_attachments' , 'imagify_ngg_count_unoptimized_attachments' );
585
+ add_filter( 'imagify_percent_optimized_attachments' , 'imagify_ngg_percent_optimized_attachments' );
586
+ add_filter( 'imagify_count_saving_data' , 'imagify_ngg_count_saving_data', 8 );
587
  }
588
 
589
  endif;
590
 
591
+ /**
592
+ * Combine two arrays with some specific keys.
593
+ * We use this function to combine the result of 2 SQL queries.
594
+ *
595
+ * @since 1.4.5
596
+ * @since 1.6.7 Added the $keep_keys_order parameter.
597
+ * @since 1.6.13 Deprecated.
598
+ * @deprecated
599
+ *
600
+ * @param array $keys An array of keys.
601
+ * @param array $values An array of arrays like array( 'id' => id, 'value' => value ).
602
+ * @param int $keep_keys_order Set to true to return an array ordered like $keys instead of $values.
603
+ * @return array The combined arrays.
604
+ */
605
+ function imagify_query_results_combine( $keys, $values, $keep_keys_order = false ) {
606
+ _deprecated_function( __FUNCTION__ . '()', '1.6.13', 'Imagify_DB::combine_query_results( $keys, $values, $keep_keys_order )' );
607
+
608
+ return Imagify_DB::combine_query_results( $keys, $values, $keep_keys_order );
609
+ }
610
+
611
+ /**
612
+ * A helper to retrieve all values from one or several post metas, given a list of post IDs.
613
+ * The $wpdb cache is flushed to save memory.
614
+ *
615
+ * @since 1.6.7
616
+ * @since 1.6.13 Deprecated.
617
+ * @author Grégory Viguier
618
+ * @deprecated
619
+ *
620
+ * @param array $metas An array of meta names like:
621
+ * array(
622
+ * 'key1' => 'meta_name_1',
623
+ * 'key2' => 'meta_name_2',
624
+ * 'key3' => 'meta_name_3',
625
+ * )
626
+ * If a key contains 'data', the results will be unserialized.
627
+ * @param array $ids An array of post IDs.
628
+ * @return array An array of arrays of results like:
629
+ * array(
630
+ * 'key1' => array( post_id_1 => 'result_1', post_id_2 => 'result_2', post_id_3 => 'result_3' ),
631
+ * 'key2' => array( post_id_1 => 'result_4', post_id_3 => 'result_5' ),
632
+ * 'key3' => array( post_id_1 => 'result_6', post_id_2 => 'result_7' ),
633
+ * )
634
+ */
635
+ function imagify_get_wpdb_metas( $metas, $ids ) {
636
+ _deprecated_function( __FUNCTION__ . '()', '1.6.13', 'Imagify_DB::get_metas( $metas, $ids )' );
637
+
638
+ return Imagify_DB::get_metas( $metas, $ids );
639
+ }
640
+
641
+ /**
642
+ * Get all mime types which could be optimized by Imagify.
643
+ *
644
+ * @since 1.3
645
+ * @since 1.7 Deprecated.
646
+ * @deprecated
647
+ *
648
+ * @return array $mime_type The mime type.
649
+ */
650
+ function get_imagify_mime_type() {
651
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'imagify_get_mime_types()' );
652
+
653
+ return imagify_get_mime_types();
654
+ }
655
+
656
+ /**
657
+ * Planning cron.
658
+ * If the task is not programmed, it is automatically triggered.
659
+ *
660
+ * @since 1.4.2
661
+ * @since 1.7 Deprecated.
662
+ * @deprecated
663
+ */
664
+ function _imagify_rating_scheduled() {
665
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Cron_Rating::get_instance()->schedule_event()' );
666
+
667
+ Imagify_Cron_Rating::get_instance()->schedule_event();
668
+ }
669
+
670
+ /**
671
+ * Save the user images count to display it later in a notice message to ask him to rate Imagify on WordPress.org.
672
+ *
673
+ * @since 1.4.2
674
+ * @since 1.7 Deprecated.
675
+ * @deprecated
676
+ */
677
+ function _do_imagify_rating_cron() {
678
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Cron_Rating::get_instance()->do_event()' );
679
+
680
+ Imagify_Cron_Rating::get_instance()->do_event();
681
+ }
682
+
683
+ /**
684
+ * Adds weekly interval for cron jobs.
685
+ *
686
+ * @since 1.6
687
+ * @since 1.7 Deprecated.
688
+ * @author Remy Perona
689
+ * @deprecated
690
+ *
691
+ * @param Array $schedules An array of intervals used by cron jobs.
692
+ * @return Array Updated array of intervals.
693
+ */
694
+ function imagify_purge_cron_schedule( $schedules ) {
695
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Cron_Library_Size::get_instance()->maybe_add_recurrence( $schedules )' );
696
+
697
+ return Imagify_Cron_Library_Size::get_instance()->do_event( $schedules );
698
+ }
699
+
700
+ /**
701
+ * Planning cron task to update weekly the size of the images and the size of images uploaded by month.
702
+ * If the task is not programmed, it is automatically triggered.
703
+ *
704
+ * @since 1.6
705
+ * @since 1.7 Deprecated.
706
+ * @author Remy Perona
707
+ * @deprecated
708
+ */
709
+ function _imagify_update_library_size_calculations_scheduled() {
710
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Cron_Library_Size::get_instance()->schedule_event()' );
711
+
712
+ Imagify_Cron_Library_Size::get_instance()->schedule_event();
713
+ }
714
+
715
+ /**
716
+ * Cron task to update weekly the size of the images and the size of images uploaded by month.
717
+ *
718
+ * @since 1.6
719
+ * @since 1.7 Deprecated.
720
+ * @author Remy Perona
721
+ * @deprecated
722
+ */
723
+ function _do_imagify_update_library_size_calculations() {
724
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Cron_Library_Size::get_instance()->do_event()' );
725
+
726
+ Imagify_Cron_Library_Size::get_instance()->do_event();
727
+ }
728
+
729
  if ( is_admin() ) :
730
 
731
  /**
791
  $data = method_exists( $notices, $callback ) ? call_user_func( array( $notices, $callback ) ) : false;
792
 
793
  if ( $data ) {
794
+ Imagify_Views::get_instance()->print_template( 'notice-' . $notice_id, $data );
795
  }
796
  }
797
 
1252
  Imagify_Admin_Ajax_Post::get_instance()->imagify_update_estimate_sizes_callback();
1253
  }
1254
 
1255
+ /**
1256
+ * Fix the capability for our capacity filter hook
1257
+ *
1258
+ * @since 1.0
1259
+ * @since 1.7 Deprecated.
1260
+ * @author Jonathan
1261
+ * @deprecated
1262
+ */
1263
+ function _imagify_correct_capability_for_options_page() {
1264
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Settings::get_instance()->get_capability()' );
1265
+
1266
+ return Imagify_Settings::get_instance()->get_capability();
1267
+ }
1268
+
1269
+ /**
1270
+ * Tell to WordPress to be confident with our setting, we are clean!
1271
+ *
1272
+ * @since 1.0
1273
+ * @since 1.7 Deprecated.
1274
+ * @author Jonathan
1275
+ * @deprecated
1276
+ */
1277
+ function _imagify_register_setting() {
1278
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Settings::get_instance()->register()' );
1279
+
1280
+ Imagify_Settings::get_instance()->register();
1281
+ }
1282
+
1283
+ /**
1284
+ * Filter specific options before its value is (maybe) serialized and updated.
1285
+ *
1286
+ * @since 1.0
1287
+ * @since 1.7 Deprecated.
1288
+ * @author Jonathan
1289
+ * @deprecated
1290
+ *
1291
+ * @param mixed $value The new option value.
1292
+ * @param mixed $old_value The old option value.
1293
+ * @return array The new option value.
1294
+ */
1295
+ function _imagify_pre_update_option( $value, $old_value ) {
1296
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Settings::get_instance()->sanitize_and_validate( $value )' );
1297
+
1298
+ return Imagify_Settings::get_instance()->sanitize_and_validate( $value );
1299
+ }
1300
+
1301
+ /**
1302
+ * If the user clicked the "Save & Go to Bulk Optimizer" button, set a redirection to the bulk optimizer.
1303
+ * We use this hook because it can be triggered even if the option value hasn't changed.
1304
+ *
1305
+ * @since 1.6.8
1306
+ * @since 1.7 Deprecated.
1307
+ * @author Grégory Viguier
1308
+ * @deprecated
1309
+ *
1310
+ * @param mixed $value The new, unserialized option value.
1311
+ * @param mixed $old_value The old option value.
1312
+ * @return mixed The option value.
1313
+ */
1314
+ function _imagify_maybe_set_redirection_before_save_options( $value, $old_value ) {
1315
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Settings::get_instance()->maybe_set_redirection( $value, $old_value )' );
1316
+
1317
+ return Imagify_Settings::get_instance()->maybe_set_redirection( $value, $old_value );
1318
+ }
1319
+
1320
+ /**
1321
+ * Used to launch some actions after saving the network options.
1322
+ *
1323
+ * @since 1.6.5
1324
+ * @since 1.7 Deprecated.
1325
+ * @author Grégory Viguier
1326
+ * @deprecated
1327
+ *
1328
+ * @param string $option Name of the network option.
1329
+ * @param mixed $value Current value of the network option.
1330
+ * @param mixed $old_value Old value of the network option.
1331
+ */
1332
+ function _imagify_after_save_network_options( $option, $value, $old_value ) {
1333
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Settings::get_instance()->after_save_network_options( $option, $value, $old_value )' );
1334
+
1335
+ Imagify_Settings::get_instance()->after_save_network_options( $option, $value, $old_value );
1336
+ }
1337
+
1338
+ /**
1339
+ * Used to launch some actions after saving the options.
1340
+ *
1341
+ * @since 1.0
1342
+ * @since 1.5 Used to redirect user to Bulk Optimizer (if requested).
1343
+ * @since 1.6.8 Not used to redirect user to Bulk Optimizer anymore: see _imagify_maybe_set_redirection_before_save_options().
1344
+ * @since 1.7 Deprecated.
1345
+ * @author Jonathan
1346
+ * @deprecated
1347
+ *
1348
+ * @param mixed $old_value The old option value.
1349
+ * @param mixed $value The new option value.
1350
+ */
1351
+ function _imagify_after_save_options( $old_value, $value ) {
1352
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Settings::get_instance()->after_save_options( $old_value, $value )' );
1353
+
1354
+ Imagify_Settings::get_instance()->after_save_options( $old_value, $value );
1355
+ }
1356
+
1357
+ /**
1358
+ * `options.php` do not handle site options. Let's use `admin-post.php` for multisite installations.
1359
+ *
1360
+ * @since 1.0
1361
+ * @since 1.7 Deprecated.
1362
+ * @deprecated
1363
+ */
1364
+ function _imagify_update_site_option_on_network() {
1365
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Settings::get_instance()->update_site_option_on_network()' );
1366
+
1367
+ Imagify_Settings::get_instance()->update_site_option_on_network();
1368
+ }
1369
+
1370
+ /**
1371
+ * Display the plan chooser section.
1372
+ *
1373
+ * @since 1.6
1374
+ * @since 1.7 Deprecated.
1375
+ * @author Geoffrey
1376
+ * @deprecated
1377
+ *
1378
+ * @return string HTML.
1379
+ */
1380
+ function get_imagify_new_to_imagify() {
1381
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'imagify_get_template( \'part-new-to-imagify\' )' );
1382
+
1383
+ return imagify_get_template( 'part-new-to-imagify' );
1384
+ }
1385
+
1386
+ /**
1387
+ * Get the payment modal HTML.
1388
+ *
1389
+ * @since 1.6
1390
+ * @since 1.6.3 Include discount banners.
1391
+ * @since 1.7 Deprecated.
1392
+ * @author Geoffrey
1393
+ * @deprecated
1394
+ */
1395
+ function imagify_payment_modal() {
1396
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->print_template( \'modal-payment\' )' );
1397
+
1398
+ Imagify_Views::get_instance()->print_template( 'modal-payment' );
1399
+ }
1400
+
1401
+ /**
1402
+ * Print the discount banner used inside Payment Modal.
1403
+ *
1404
+ * @since 1.6.3
1405
+ * @since 1.7 Deprecated.
1406
+ * @author Geoffrey Crofte
1407
+ * @deprecated
1408
+ */
1409
+ function imagify_print_discount_banner() {
1410
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->print_template( \'part-discount-banner\' )' );
1411
+
1412
+ Imagify_Views::get_instance()->print_template( 'part-discount-banner' );
1413
+ }
1414
+
1415
+ /**
1416
+ * Return the formatted price present in pricing tables.
1417
+ *
1418
+ * @since 1.6
1419
+ * @since 1.7 Deprecated.
1420
+ * @author Geoffrey
1421
+ * @deprecated
1422
+ *
1423
+ * @param float $value The price value.
1424
+ * @return string The markuped price.
1425
+ */
1426
+ function get_imagify_price_table_format( $value ) {
1427
+ _deprecated_function( __FUNCTION__ . '()', '1.7' );
1428
+
1429
+ $v = explode( '.', (string) $value );
1430
+
1431
+ return '<span class="imagify-price-big">' . $v[0] . '</span> <span class="imagify-price-mini">.' . ( strlen( $v[1] ) === 1 ? $v[1] . '0' : $v[1] ) . '</span>';
1432
+ }
1433
+
1434
+ /**
1435
+ * Add submenu in menu "Settings".
1436
+ *
1437
+ * @since 1.0
1438
+ * @since 1.7 Deprecated.
1439
+ * @deprecated
1440
+ */
1441
+ function _imagify_settings_menu() {
1442
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->add_network_menus()' );
1443
+
1444
+ Imagify_Views::get_instance()->add_network_menus();
1445
+ }
1446
+
1447
+ /**
1448
+ * Add submenu in menu "Media".
1449
+ *
1450
+ * @since 1.0
1451
+ * @since 1.7 Deprecated.
1452
+ * @deprecated
1453
+ */
1454
+ function _imagify_bulk_optimization_menu() {
1455
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->add_site_menus()' );
1456
+
1457
+ Imagify_Views::get_instance()->add_site_menus();
1458
+ }
1459
+
1460
+ /**
1461
+ * The main settings page.
1462
+ *
1463
+ * @since 1.0
1464
+ * @since 1.7 Deprecated.
1465
+ * @deprecated
1466
+ */
1467
+ function _imagify_display_options_page() {
1468
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->display_settings_page()' );
1469
+
1470
+ Imagify_Views::get_instance()->display_settings_page();
1471
+ }
1472
+
1473
+ /**
1474
+ * The bulk optimization page.
1475
+ *
1476
+ * @since 1.0
1477
+ * @since 1.7 Deprecated.
1478
+ * @deprecated
1479
+ */
1480
+ function _imagify_display_bulk_page() {
1481
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->display_bulk_page()' );
1482
+
1483
+ Imagify_Views::get_instance()->display_bulk_page();
1484
+ }
1485
+
1486
+ /**
1487
+ * Add link to the plugin configuration pages.
1488
+ *
1489
+ * @since 1.0
1490
+ *
1491
+ * @param array $actions An array of action links.
1492
+ * @return array
1493
+ */
1494
+ function _imagify_plugin_action_links( $actions ) {
1495
+ _deprecated_function( __FUNCTION__ . '()', '1.7', 'Imagify_Views::get_instance()->plugin_action_links( $actions )' );
1496
+
1497
+ return Imagify_Views::get_instance()->plugin_action_links( $actions );
1498
+ }
1499
+
1500
  endif;
inc/functions/files.php CHANGED
@@ -39,11 +39,60 @@ function imagify_get_filesystem() {
39
  * @since 1.2
40
  * @since 1.6.5 Use WP Filesystem.
41
  *
42
- * @param string $file The path to file.
43
  * @return bool
44
  */
45
- function imagify_chmod_file( $file ) {
46
- return imagify_get_filesystem()->chmod( $file, FS_CHMOD_FILE );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
 
@@ -95,25 +144,28 @@ function imagify_get_abspath() {
95
  * Also works for files from registered symlinked plugins.
96
  *
97
  * @since 1.6.10
 
98
  * @author Grégory Viguier
99
  *
100
  * @param string $file_path An absolute path.
 
101
  * @return string|bool A relative path. Can return the absolute path or false in case of a failure.
102
  */
103
- function imagify_make_file_path_relative( $file_path ) {
104
  static $abspath;
105
  global $wp_plugin_paths;
106
 
107
- if ( ! isset( $abspath ) ) {
108
- $abspath = wp_normalize_path( ABSPATH );
109
- }
110
-
111
  if ( ! $file_path ) {
112
  return false;
113
  }
114
 
 
 
 
 
115
  $file_path = wp_normalize_path( $file_path );
116
- $pos = strpos( $file_path, $abspath );
 
117
 
118
  if ( false === $pos && $wp_plugin_paths && is_array( $wp_plugin_paths ) ) {
119
  // The file is probably part of a symlinked plugin.
@@ -125,7 +177,7 @@ function imagify_make_file_path_relative( $file_path ) {
125
  }
126
  }
127
 
128
- $pos = strpos( $file_path, $abspath );
129
  }
130
 
131
  if ( false === $pos ) {
@@ -133,5 +185,49 @@ function imagify_make_file_path_relative( $file_path ) {
133
  return $file_path;
134
  }
135
 
136
- return substr_replace( $file_path, '', 0, $pos + strlen( $abspath ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  }
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
 
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.
177
  }
178
  }
179
 
180
+ $pos = strpos( $file_path, $base );
181
  }
182
 
183
  if ( false === $pos ) {
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/formatting.php CHANGED
@@ -33,6 +33,22 @@ function imagify_round_half_five( $number ) {
33
  return floatval( $number );
34
  }
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  /**
37
  * Get the Imagify attachment class name depending to a context.
38
  *
33
  return floatval( $number );
34
  }
35
 
36
+ /**
37
+ * Convert number of bytes largest unit bytes will fit into.
38
+ * This is a clone of size_format(), but with a non-breaking space.
39
+ *
40
+ * @since 1.7
41
+ * @author Grégory Viguier
42
+ *
43
+ * @param int|string $bytes Number of bytes. Note max integer size for integers.
44
+ * @param int $decimals Optional. Precision of number of decimal places. Default 0.
45
+ * @return string|false False on failure. Number string on success.
46
+ */
47
+ function imagify_size_format( $bytes, $decimals = 0 ) {
48
+ $bytes = @size_format( $bytes, $decimals );
49
+ return str_replace( ' ', ' ', $bytes );
50
+ }
51
+
52
  /**
53
  * Get the Imagify attachment class name depending to a context.
54
  *
inc/functions/i18n.php CHANGED
@@ -53,13 +53,22 @@ function get_imagify_localize_script_translations( $context ) {
53
 
54
  case 'options':
55
  return array(
56
- 'labels' => array(
 
57
  'ValidApiKeyText' => __( 'Your API key is valid.', 'imagify' ),
58
  'waitApiKeyCheckText' => __( 'Check in progress...', 'imagify' ),
59
  'ApiKeyCheckSuccessTitle' => __( 'Congratulations!', 'imagify' ),
60
  'ApiKeyCheckSuccessText' => __( 'Your API key is valid. You can now configure the Imagify settings to optimize your images.', 'imagify' ),
61
  'noBackupTitle' => __( 'Don\'t Need a Parachute?', 'imagify' ),
62
  'noBackupText' => __( 'If you keep this option deactivated, you won\'t be able to re-optimize your images to another compression level and restore your original images in case of need.', 'imagify' ),
 
 
 
 
 
 
 
 
63
  ),
64
  );
65
 
@@ -99,7 +108,7 @@ function get_imagify_localize_script_translations( $context ) {
99
 
100
  case 'library':
101
  return array(
102
- 'backupOption' => (int) get_imagify_option( 'backup' ),
103
  'labels' => array(
104
  'bulkActionsOptimize' => __( 'Optimize', 'imagify' ),
105
  'bulkActionsOptimizeMissingSizes' => __( 'Optimize Missing Sizes', 'imagify' ),
@@ -109,15 +118,24 @@ function get_imagify_localize_script_translations( $context ) {
109
 
110
  case 'bulk':
111
  $translations = array(
112
- 'totalUnoptimizedAttachments' => imagify_count_unoptimized_attachments(),
113
- 'totalOptimizedAttachments' => imagify_count_optimized_attachments(),
114
- 'totalErrorsAttachments' => imagify_count_error_attachments(),
115
- 'heartbeatId' => 'update_bulk_data',
116
- 'waitImageUrl' => IMAGIFY_ASSETS_IMG_URL . 'popin-loader.svg',
117
- 'ajaxAction' => 'imagify_get_unoptimized_attachment_ids',
118
- 'ajaxContext' => 'wp',
119
- 'bufferSize' => get_imagify_bulk_buffer_size(),
120
- 'labels' => array(
 
 
 
 
 
 
 
 
 
121
  'overviewChartLabels' => array(
122
  'unoptimized' => __( 'Unoptimized', 'imagify' ),
123
  'optimized' => __( 'Optimized', 'imagify' ),
@@ -127,13 +145,14 @@ function get_imagify_localize_script_translations( $context ) {
127
  'waitTitle' => __( 'Please wait...', 'imagify' ),
128
  'waitText' => __( 'We are trying to get your unoptimized images, it may take time depending on the number of images.', 'imagify' ),
129
  'invalidAPIKeyTitle' => __( 'Your API key isn\'t valid!', 'imagify' ),
130
- 'overQuotaTitle' => __( 'Oops, It\'s Over!', 'imagify' ),
131
- 'overQuotaText' => '',
132
  'noAttachmentToOptimizeTitle' => __( 'Hold on!', 'imagify' ),
133
  'noAttachmentToOptimizeText' => __( 'All your images have been optimized by Imagify. Congratulations!', 'imagify' ),
134
  'optimizing' => __( 'Optimizing', 'imagify' ),
135
  'error' => __( 'Error', 'imagify' ),
 
136
  'complete' => _x( 'Complete', 'adjective', 'imagify' ),
 
137
  /* translators: %s is a number. Don't use %d. */
138
  'nbrFiles' => __( '%s file(s)', 'imagify' ),
139
  'notice' => _x( 'Notice', 'noun', 'imagify' ),
@@ -144,49 +163,59 @@ function get_imagify_localize_script_translations( $context ) {
144
  'twitterShareURL' => imagify_get_external_url( 'share-twitter' ),
145
  'getUnoptimizedImagesErrorTitle' => __( 'Oops, There is something wrong!', 'imagify' ),
146
  'getUnoptimizedImagesErrorText' => __( 'An unknown error occurred when we tried to get all your unoptimized images. Try again and if the issue still persists, please contact us!', 'imagify' ),
 
 
 
 
 
 
 
 
147
  ),
148
  );
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  /**
151
  * Filter the number of parallel queries during the Bulk Optimization.
152
  *
153
- * @since 1.5.4
 
154
  *
155
- * @param int $buffer_size Number of parallel queries.
156
  */
157
- $translations['bufferSize'] = apply_filters( 'imagify_bulk_buffer_size', $translations['bufferSize'] );
158
-
159
- if ( ! imagify_valid_key() ) {
160
- return $translations;
161
- }
162
-
163
- $user = get_imagify_user();
164
-
165
- if ( is_wp_error( $user ) ) {
166
- $translations['labels']['overQuotaText'] = sprintf(
167
- /* translators: 1 is a link tag start, 2 is the link tag end. */
168
- __( 'To continue to optimize your images, log in to your Imagify account to %1$sbuy a pack or subscribe to a plan%2$s.', 'imagify' ),
169
- '<a target="_blank" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '">',
170
- '</a>'
171
- );
172
- } else {
173
- $translations['labels']['overQuotaText'] = sprintf(
174
- /* translators: 1 is a data quota, 2 is a date. */
175
- __( 'You have consumed all your credit for this month. You will have <strong>%1$s back on %2$s</strong>.', 'imagify' ),
176
- size_format( $user->quota * 1048576 ),
177
- date_i18n( __( 'F j, Y' ), strtotime( $user->next_date_update ) )
178
- );
179
- $translations['labels']['overQuotaText'] .= '<br/><br/>';
180
- $translations['labels']['overQuotaText'] .= sprintf(
181
- /* translators: 1 is a link tag start, 2 is the link tag end. */
182
- __( 'To continue to optimize your images, log in to your Imagify account to %1$sbuy a pack or subscribe to a plan%2$s.', 'imagify' ),
183
- '<a target="_blank" href="' . esc_url( imagify_get_external_url( 'subscription' ) ) . '">',
184
- '</a>'
185
- );
186
- }
187
 
188
  return $translations;
189
 
 
 
 
 
 
 
 
 
 
190
  default:
191
  return array();
192
  } // End switch().
53
 
54
  case 'options':
55
  return array(
56
+ 'getFilesTree' => imagify_can_optimize_custom_folders() ? get_imagify_admin_url( 'get-files-tree' ) : false,
57
+ 'labels' => array(
58
  'ValidApiKeyText' => __( 'Your API key is valid.', 'imagify' ),
59
  'waitApiKeyCheckText' => __( 'Check in progress...', 'imagify' ),
60
  'ApiKeyCheckSuccessTitle' => __( 'Congratulations!', 'imagify' ),
61
  'ApiKeyCheckSuccessText' => __( 'Your API key is valid. You can now configure the Imagify settings to optimize your images.', 'imagify' ),
62
  'noBackupTitle' => __( 'Don\'t Need a Parachute?', 'imagify' ),
63
  'noBackupText' => __( 'If you keep this option deactivated, you won\'t be able to re-optimize your images to another compression level and restore your original images in case of need.', 'imagify' ),
64
+ 'removeFolder' => _x( 'Remove', 'custom folder', 'imagify' ),
65
+ 'filesTreeTitle' => __( 'Select Folders', 'imagify' ),
66
+ 'filesTreeSubTitle' => __( 'Select one or several folders to optimize.', 'imagify' ),
67
+ 'cleaningInfo' => __( 'Some folders that do not contain any images are hidden.', 'imagify' ),
68
+ 'confirmFilesTreeBtn' => __( 'Select Folders', 'imagify' ),
69
+ 'customFilesLegend' => __( 'Choose the folders to optimize', 'imagify' ),
70
+ 'error' => __( 'Error', 'imagify' ),
71
+ 'themesAdded' => __( 'Added! All Good!', 'imagify' ),
72
  ),
73
  );
74
 
108
 
109
  case 'library':
110
  return array(
111
+ 'backupOption' => get_imagify_option( 'backup' ),
112
  'labels' => array(
113
  'bulkActionsOptimize' => __( 'Optimize', 'imagify' ),
114
  'bulkActionsOptimizeMissingSizes' => __( 'Optimize Missing Sizes', 'imagify' ),
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',
129
+ 'customFoldersOptimize' => 'imagify_bulk_optimize_file',
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' ),
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' ),
152
  'error' => __( 'Error', 'imagify' ),
153
+ 'ajaxErrorText' => __( 'The operation failed.', 'imagify' ),
154
  'complete' => _x( 'Complete', 'adjective', 'imagify' ),
155
+ 'alreadyOptimized' => _x( 'Already Optimized', 'file', 'imagify' ),
156
  /* translators: %s is a number. Don't use %d. */
157
  'nbrFiles' => __( '%s file(s)', 'imagify' ),
158
  'notice' => _x( 'Notice', 'noun', 'imagify' ),
163
  'twitterShareURL' => imagify_get_external_url( 'share-twitter' ),
164
  'getUnoptimizedImagesErrorTitle' => __( 'Oops, There is something wrong!', 'imagify' ),
165
  'getUnoptimizedImagesErrorText' => __( 'An unknown error occurred when we tried to get all your unoptimized images. Try again and if the issue still persists, please contact us!', 'imagify' ),
166
+ 'waitingOtimizationsText' => __( 'Waiting other optimizations to finish.', 'imagify' ),
167
+ /* translators: %s is a formatted number, dont use %d. */
168
+ 'imagesOptimizedText' => __( '%s Image(s) Optimized', 'imagify' ),
169
+ /* translators: %s is a formatted number, dont use %d. */
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'] ) ) {
186
+ /**
187
+ * Filter the number of parallel queries during the Bulk Optimization (library).
188
+ *
189
+ * @since 1.5.4
190
+ * @since 1.7 Deprecated
191
+ * @deprecated
192
+ *
193
+ * @param int $buffer_size Number of parallel queries.
194
+ */
195
+ $translations['bufferSizes']['wp'] = apply_filters_deprecated( 'imagify_bulk_buffer_size', array( $translations['bufferSizes']['wp'] ), '1.7', 'imagify_bulk_buffer_sizes' );
196
+ }
197
+
198
  /**
199
  * Filter the number of parallel queries during the Bulk Optimization.
200
  *
201
+ * @since 1.7
202
+ * @author Grégory Viguier
203
  *
204
+ * @param array $buffer_sizes An array of number of parallel queries, keyed by context.
205
  */
206
+ $translations['bufferSizes'] = apply_filters( 'imagify_bulk_buffer_sizes', $translations['bufferSizes'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  return $translations;
209
 
210
+ case 'files-list':
211
+ return array(
212
+ 'backupOption' => get_imagify_option( 'backup' ),
213
+ 'labels' => array(
214
+ 'bulkActionsOptimize' => __( 'Optimize', 'imagify' ),
215
+ 'bulkActionsRestore' => __( 'Restore Original', 'imagify' ),
216
+ ),
217
+ );
218
+
219
  default:
220
  return array();
221
  } // End switch().
inc/functions/options.php CHANGED
@@ -5,47 +5,13 @@ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
5
  * A wrapper to easily get imagify option.
6
  *
7
  * @since 1.0
 
8
  *
9
- * @param string $option The option name.
10
- * @param bool $default The default value of option.
11
- * @return mixed The option value.
12
  */
13
- function get_imagify_option( $option, $default = false ) {
14
- static $basename;
15
- /**
16
- * Pre-filter any Imagify option before read.
17
- *
18
- * @since 1.0
19
- *
20
- * @param mixed $value Value to return instead of the option value. Default null to skip it.
21
- * @param mixed $default The default value. Default false.
22
- */
23
- $value = apply_filters( 'pre_get_imagify_option_' . $option, null, $default );
24
-
25
- if ( isset( $value ) ) {
26
- return $value;
27
- }
28
-
29
- if ( ! isset( $basename ) ) {
30
- $basename = plugin_basename( IMAGIFY_FILE );
31
- }
32
-
33
- $options = imagify_is_active_for_network() ? get_site_option( IMAGIFY_SETTINGS_SLUG ) : get_option( IMAGIFY_SETTINGS_SLUG );
34
- $value = isset( $options[ $option ] ) ? $options[ $option ] : $default;
35
-
36
- if ( 'api_key' === $option && defined( 'IMAGIFY_API_KEY' ) && IMAGIFY_API_KEY ) {
37
- $value = IMAGIFY_API_KEY;
38
- }
39
-
40
- /**
41
- * Filter any Imagify option after read.
42
- *
43
- * @since 1.0
44
- *
45
- * @param mixed $value Value of the option.
46
- * @param mixed $default The default value. Default false.
47
- */
48
- return apply_filters( 'get_imagify_option_' . $option, $value, $default );
49
  }
50
 
51
  /**
@@ -54,17 +20,11 @@ function get_imagify_option( $option, $default = false ) {
54
  * @since 1.6
55
  * @author Remy Perona
56
  *
57
- * @param string $key The option name.
58
- * @param string $value The value of the option.
59
- * @return void
60
  */
61
  function update_imagify_option( $key, $value ) {
62
- $options = get_option( IMAGIFY_SETTINGS_SLUG );
63
- $options = is_array( $options ) ? $options : array();
64
-
65
- $options[ $key ] = $value;
66
-
67
- update_option( IMAGIFY_SETTINGS_SLUG, $options );
68
  }
69
 
70
  /**
@@ -81,7 +41,7 @@ function imagify_valid_key() {
81
  return $is_valid;
82
  }
83
 
84
- if ( ! get_imagify_option( 'api_key' ) ) {
85
  $is_valid = false;
86
  return $is_valid;
87
  }
@@ -101,3 +61,74 @@ function imagify_valid_key() {
101
 
102
  return $is_valid;
103
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  * A wrapper to easily get imagify option.
6
  *
7
  * @since 1.0
8
+ * @since 1.7 The $default parameter was removed.
9
  *
10
+ * @param string $key The option name.
11
+ * @return mixed The option value.
 
12
  */
13
+ function get_imagify_option( $key ) {
14
+ return Imagify_Options::get_instance()->get( $key );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
 
17
  /**
20
  * @since 1.6
21
  * @author Remy Perona
22
  *
23
+ * @param string $key The option name.
24
+ * @param mixed $value The value of the option.
 
25
  */
26
  function update_imagify_option( $key, $value ) {
27
+ Imagify_Options::get_instance()->set( $key, $value );
 
 
 
 
 
28
  }
29
 
30
  /**
41
  return $is_valid;
42
  }
43
 
44
+ if ( ! Imagify_Options::get_instance()->get( 'api_key' ) ) {
45
  $is_valid = false;
46
  return $is_valid;
47
  }
61
 
62
  return $is_valid;
63
  }
64
+
65
+ /**
66
+ * Autoload network options and put them in cache.
67
+ *
68
+ * @since 1.7
69
+ * @author Grégory Viguier
70
+ * @source SecuPress
71
+ *
72
+ * @param array $option_names A list of option names to cache.
73
+ * @param string|array $prefixes A prefix for the option names. Handy for transients for example (`_site_transient_` and `_site_transient_timeout_`).
74
+ */
75
+ function imagify_load_network_options( $option_names, $prefixes = '' ) {
76
+ global $wpdb;
77
+
78
+ $prefixes = (array) $prefixes;
79
+
80
+ if ( ! $option_names || count( $option_names ) * count( $prefixes ) === 1 ) {
81
+ return;
82
+ }
83
+
84
+ // Get values.
85
+ $not_exist = array();
86
+ $option_names = array_flip( array_flip( $option_names ) );
87
+ $options = '';
88
+
89
+ foreach ( $prefixes as $prefix ) {
90
+ $options .= "'$prefix" . implode( "','$prefix", esc_sql( $option_names ) ) . "',";
91
+ }
92
+
93
+ $options = rtrim( $options, ',' );
94
+
95
+ if ( is_multisite() ) {
96
+ $network_id = function_exists( 'get_current_network_id' ) ? get_current_network_id() : (int) $wpdb->siteid;
97
+ $cache_prefix = "$network_id:";
98
+ $notoptions_key = "$network_id:notoptions";
99
+ $cache_group = 'site-options';
100
+ $results = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key as name, meta_value as value FROM $wpdb->sitemeta WHERE meta_key IN ( $options ) AND site_id = %d", $network_id ), OBJECT_K ); // WPCS: unprepared SQL ok.
101
+ } else {
102
+ $cache_prefix = '';
103
+ $notoptions_key = 'notoptions';
104
+ $cache_group = 'options';
105
+ $results = $wpdb->get_results( "SELECT option_name as name, option_value as value FROM $wpdb->options WHERE option_name IN ( $options )", OBJECT_K ); // WPCS: unprepared SQL ok.
106
+ }
107
+
108
+ foreach ( $prefixes as $prefix ) {
109
+ foreach ( $option_names as $option_name ) {
110
+ $option_name = $prefix . $option_name;
111
+
112
+ if ( isset( $results[ $option_name ] ) ) {
113
+ // Cache the value.
114
+ $value = $results[ $option_name ]->value;
115
+ $value = maybe_unserialize( $value );
116
+ wp_cache_set( "$cache_prefix$option_name", $value, $cache_group );
117
+ } else {
118
+ // No value.
119
+ $not_exist[ $option_name ] = true;
120
+ }
121
+ }
122
+ }
123
+
124
+ if ( ! $not_exist ) {
125
+ return;
126
+ }
127
+
128
+ // Cache the options that don't exist in the DB.
129
+ $notoptions = wp_cache_get( $notoptions_key, $cache_group );
130
+ $notoptions = is_array( $notoptions ) ? $notoptions : array();
131
+ $notoptions = array_merge( $notoptions, $not_exist );
132
+
133
+ wp_cache_set( $notoptions_key, $notoptions, $cache_group );
134
+ }
inc/functions/process.php CHANGED
@@ -17,7 +17,7 @@ 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', 1 ),
21
  'keep_exif' => get_imagify_option( 'exif' ),
22
  'context' => 'wp',
23
  'resized' => false,
@@ -113,7 +113,7 @@ function do_imagify( $file_path, $args = array() ) {
113
  }
114
 
115
  if ( ! function_exists( 'download_url' ) ) {
116
- require( ABSPATH . 'wp-admin/includes/file.php' );
117
  }
118
 
119
  $temp_file = download_url( $response->image );
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' ),
22
  'context' => 'wp',
23
  'resized' => false,
113
  }
114
 
115
  if ( ! function_exists( 'download_url' ) ) {
116
+ require_once ABSPATH . 'wp-admin/includes/file.php';
117
  }
118
 
119
  $temp_file = download_url( $response->image );
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.1
6
- Stable tag: 1.6.14.2
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
 
@@ -107,7 +107,7 @@ Absolutely. Most of the time, Imagify will still be able to optimize your images
107
 
108
  Normal compression is a "lossless" optimization. This means there is no loss of image quality. Aggressive and Ultra compression are more powerful, so the picture quality will be somewhat reduced. The weight of the image will be much less.
109
 
110
- = Is the EXIF data of images removes? =
111
 
112
  By default EXIF data is removed. It is however possible to keep it by enabling the option.
113
 
@@ -135,7 +135,19 @@ When the plugin is disabled, your existing images remain optimized. Backups of t
135
 
136
  3. Media Page
137
 
 
 
138
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
139
  = 1.6.14.2 - 2018/01/15 =
140
  * Improvement: force browsers not to use the old version of our script for the charts.
141
 
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
 
107
 
108
  Normal compression is a "lossless" optimization. This means there is no loss of image quality. Aggressive and Ultra compression are more powerful, so the picture quality will be somewhat reduced. The weight of the image will be much less.
109
 
110
+ = Is the EXIF data of images removed? =
111
 
112
  By default EXIF data is removed. It is however possible to keep it by enabling the option.
113
 
135
 
136
  3. Media Page
137
 
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.
144
+ * Improvement: don't start the bulk optimization process if cURL is not available.
145
+ * Bug Fix: image dimensions not being stored sometimes after it is resized.
146
+ * Bug Fix: the comparison tool could display multiple handles.
147
+ * Bug Fix: issue with php 7.2.
148
+ * Dev stuff: lots of internal changes, many things have been rewritten.
149
+ * Dev stuff: the default options can now be filtered.
150
+
151
  = 1.6.14.2 - 2018/01/15 =
152
  * Improvement: force browsers not to use the old version of our script for the charts.
153
 
uninstall.php CHANGED
@@ -5,17 +5,30 @@ global $wpdb;
5
 
6
  // Delete Imagify options.
7
  delete_site_option( 'imagify_settings' );
 
 
 
 
 
 
 
 
 
8
 
9
  // Delete all transients.
10
  delete_site_transient( 'imagify_check_licence_1' );
 
11
  delete_transient( 'imagify_bulk_optimization_level' );
 
12
  delete_transient( 'imagify_large_library' );
13
  delete_transient( 'imagify_max_image_size' );
 
14
 
15
  // Delete transients.
16
  $transients = implode( '" OR option_name LIKE "', array(
17
  '_transient_%imagify-async-in-progress-%',
18
  '_transient_%imagify-ngg-async-in-progress-%',
 
19
  '_transient_%imagify_rpc_%',
20
  ) );
21
  $wpdb->query( 'DELETE from ' . $wpdb->options . ' WHERE option_name LIKE "' . $transients . '"' ); // WPCS: unprepared SQL ok.
@@ -28,4 +41,6 @@ wp_clear_scheduled_hook( 'imagify_update_library_size_calculations_event' );
28
  delete_metadata( 'user', '', '_imagify_ignore_notices', '', true );
29
 
30
  // Drop the tables.
 
 
31
  $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'ngg_imagify_data' );
5
 
6
  // Delete Imagify options.
7
  delete_site_option( 'imagify_settings' );
8
+ delete_site_option( 'imagify_old_version' );
9
+ delete_site_option( 'imagify_files_db_version' );
10
+ delete_site_option( 'imagify_folders_db_version' );
11
+ delete_site_option( 'do_imagify_rating_cron' );
12
+ delete_site_option( 'imagify_seen_rating_notice' );
13
+ delete_site_option( 'imagify_user_images_count' );
14
+ delete_option( 'imagify_data' );
15
+ delete_option( 'ngg_imagify_data_db_version' );
16
+ delete_option( $wpdb->prefix . 'ngg_imagify_data_db_version' );
17
 
18
  // Delete all transients.
19
  delete_site_transient( 'imagify_check_licence_1' );
20
+ delete_site_transient( 'imagify_user' );
21
  delete_transient( 'imagify_bulk_optimization_level' );
22
+ delete_transient( 'imagify_bulk_optimization_infos' );
23
  delete_transient( 'imagify_large_library' );
24
  delete_transient( 'imagify_max_image_size' );
25
+ delete_transient( 'imagify_user' );
26
 
27
  // Delete transients.
28
  $transients = implode( '" OR option_name LIKE "', array(
29
  '_transient_%imagify-async-in-progress-%',
30
  '_transient_%imagify-ngg-async-in-progress-%',
31
+ '_site_transient_%imagify-file-async-in-progress-%',
32
  '_transient_%imagify_rpc_%',
33
  ) );
34
  $wpdb->query( 'DELETE from ' . $wpdb->options . ' WHERE option_name LIKE "' . $transients . '"' ); // WPCS: unprepared SQL ok.
41
  delete_metadata( 'user', '', '_imagify_ignore_notices', '', true );
42
 
43
  // Drop the tables.
44
+ $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->base_prefix . 'imagify_files' );
45
+ $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->base_prefix . 'imagify_folders' );
46
  $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'ngg_imagify_data' );
views/modal-payment.php ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
+
4
+ ?>
5
+ <div id="imagify-pricing-modal" class="imagify-modal imagify-payment-modal hide-if-no-js" aria-hidden="false" role="dialog">
6
+ <div class="imagify-modal-content">
7
+ <div class="imagify-modal-main">
8
+ <div class="imagify-modal-views imagify-pre-checkout-view" id="imagify-pre-checkout-view" aria-hidden="false">
9
+
10
+ <?php
11
+ $attachments_number = imagify_count_attachments() + Imagify_Files_Stats::count_all_files();
12
+ $total_size = Imagify_Data::get_instance()->get( 'total_size_images_library' );
13
+ $per_month = Imagify_Data::get_instance()->get( 'average_size_images_per_month' );
14
+ ?>
15
+
16
+ <div class="imagify-modal-section section-gray imagify-estimation-block<?php echo false === $total_size ? ' imagify-analyzing' : ''; ?>">
17
+ <p class="imagify-modal-title">
18
+ <span class="imagify-numbers-calc"><?php esc_html_e( 'We analysed your images', 'imagify' ); ?></span>
19
+ <span class="imagify-numbers-notcalc"><?php esc_html_e( 'We are analysing your images', 'imagify' ); ?></span>
20
+ </p>
21
+
22
+ <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>loader-balls.svg" width="77" height="48" alt="<?php esc_attr_e( 'Analyzing', 'imagify' ); ?>" class="imagify-loader">
23
+
24
+ <div class="imagify-modal-cols">
25
+ <div class="imagify-col">
26
+ <p>
27
+ <span class="imagify-border-styled">
28
+ <?php
29
+ printf(
30
+ /* translators: %s is a formatted number (don't use %d). */
31
+ _n( 'You have %s original image', 'You have %s original images', $attachments_number, 'imagify' ),
32
+ '</span><span class="imagify-big-number">' . number_format_i18n( $attachments_number ) . '</span><span class="imagify-border-styled">'
33
+ );
34
+ ?>
35
+ </span>
36
+ </p>
37
+ </div>
38
+ <div class="imagify-col">
39
+ <p class="imagify-iconed">
40
+ <i class="dashicons dashicons-images-alt2" aria-hidden="true"></i>
41
+ <?php
42
+ printf(
43
+ /* translators: %s is a formatted file size. */
44
+ esc_html__( 'You currently have %s of images in your library and folders.', 'imagify' ),
45
+ '<strong class="imagify-dark total-library-size">' . ( is_float( $total_size ) ? imagify_size_format( $total_size ) : '' ) . '</strong>'
46
+ );
47
+ ?>
48
+ </p>
49
+ <p class="imagify-iconed">
50
+ <i class="dashicons dashicons-cloud" aria-hidden="true"></i>
51
+ <?php
52
+ printf(
53
+ /* translators: %s is a formatted file size. */
54
+ esc_html__( 'You upload around %s of images per month.', 'imagify' ),
55
+ '<strong class="imagify-dark average-month-size">' . ( is_float( $per_month ) ? imagify_size_format( $per_month ) : '' ) . '</strong>'
56
+ );
57
+ ?>
58
+ </p>
59
+ </div>
60
+ </div>
61
+ </div><!-- .imagify-modal-section -->
62
+
63
+ <?php $this->print_template( 'part-discount-banner' ); ?>
64
+
65
+ <div class="imagify-modal-section imagify-pre-checkout-offers">
66
+ <p class="imagify-modal-title">
67
+ <span class="imagify-not-enough-title"><?php esc_html_e( 'We recommend you this plan', 'imagify' ); ?></span>
68
+ <span class="imagify-enough-title"><?php esc_html_e( 'The free plan is enough to optimize your images', 'imagify' ); ?></span>
69
+
70
+ </p>
71
+
72
+ <div class="imagify-offer-line imagify-offer-monthly imagify-offer-selected imagify-month-selected" data-offer='{"lite":{"id":3,"name":"Lite","data":1073741824,"dataf":"1 GB","imgs":5000,"prices":{"monthly":4.99,"yearly":4.16,"add":4}}}'>
73
+ <div class="imagify-offer-header">
74
+ <p class="imagify-offer-title imagify-switch-my">
75
+ <span aria-hidden="false" class="imagify-monthly"><?php esc_html_e( 'Subscribe a monthly plan', 'imagify' ); ?></span>
76
+ <span aria-hidden="true" class="imagify-yearly"><?php esc_html_e( 'Subscribe a yearly plan', 'imagify' ); ?></span>
77
+ </p>
78
+ <div class="imagify-inline-options imagify-radio-line">
79
+ <input id="imagify-subscription-monthly" type="radio" value="monthly" name="plan-subscription" checked="checked">
80
+ <label for="imagify-subscription-monthly"><?php esc_html_e( 'Monthly' , 'imagify' ); ?></label>
81
+
82
+ <input id="imagify-subscription-yearly" type="radio" value="yearly" name="plan-subscription">
83
+ <label for="imagify-subscription-yearly"><?php esc_html_e( 'Yearly' , 'imagify' ); ?><span class="imagify-2-free"><?php esc_html_e( '2 months free', 'imagify' ); ?></span></label>
84
+ </div><!-- .imagify-radio-line -->
85
+ </div><!-- .imagify-offer-header -->
86
+
87
+ <div class="imagify-offer-content imagify-flex-table">
88
+
89
+ <div class="imagify-col-checkbox">
90
+ <input type="checkbox" name="imagify-offer" id="imagify-offer-1gb" value="1Gb" checked="checked" class="imagify-checkbox medium">
91
+ <label for="imagify-offer-1gb">
92
+ <span class="imagify-the-offer">
93
+ <span class="imagify-offer-size">1 GB</span>
94
+ <span class="imagify-offer-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
95
+ </span>
96
+ <span class="imagify-approx">
97
+ <?php
98
+ printf(
99
+ /* translators: %s is a formatted number (don't use %d). */
100
+ esc_html__( 'approx: %s images', 'imagify' ),
101
+ '<span class="imagify-approx-nb">' . number_format_i18n( 5000 ) . '</span>'
102
+ );
103
+ ?>
104
+ </span>
105
+ </label>
106
+ </div>
107
+ <div class="imagify-col-price imagify-flex-table">
108
+ <span class="imagify-price-block">
109
+ <span class="imagify-dollars">$</span>
110
+ <span class="imagify-number-block">
111
+ <span class="imagify-switch-my">
112
+ <span class="imagify-monthly" aria-hidden="false">
113
+ <span class="imagify-price-big">3</span>
114
+ <span class="imagify-price-mini">.99</span>
115
+ </span>
116
+ <span class="imagify-yearly" aria-hidden="true">
117
+ <span class="imagify-price-big">3</span>
118
+ <span class="imagify-price-mini">.16</span>
119
+ </span>
120
+ </span>
121
+ <span class="imagify-price-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
122
+ </span>
123
+ </span>
124
+
125
+ <p class="imagify-price-complement">
126
+ <?php
127
+ printf(
128
+ /* translators: %s is a formatted price. */
129
+ __( '%s per<br>additionnal Gb', 'imagify' ),
130
+ '<span class="imagify-price-add-data"></span>'
131
+ );
132
+ ?>
133
+ </p>
134
+
135
+ </div>
136
+ <div class="imagify-col-other-actions">
137
+ <a href="#imagify-plans-selection-view" class="imagify-choose-another-plan" data-imagify-choose="plan"><?php esc_html_e( 'Choose another plan', 'imagify' ); ?></a>
138
+ </div>
139
+
140
+ </div><!-- .imagify-offer-content -->
141
+
142
+ </div><!-- .imagify-offer-line -->
143
+
144
+ <div class="imagify-offer-line imagify-offer-onetime" data-offer='{"recommended":{"id":999,"name":"Customized","data":3000001337,"dataf":"3 GB","imgs":54634,"price":28.98}}'>
145
+ <div class="imagify-offer-header">
146
+ <p class="imagify-offer-title">
147
+ <?php esc_html_e( 'Optimize the images you already have, buy a One Time plan', 'imagify' ); ?>
148
+ </p>
149
+ </div><!-- .imagify-offer-header -->
150
+
151
+ <div class="imagify-offer-content imagify-flex-table">
152
+
153
+ <div class="imagify-col-checkbox">
154
+ <input type="checkbox" name="imagify-offer" id="imagify-offer-custom" value="1Gb" checked="checked" class="imagify-checkbox medium">
155
+ <label for="imagify-offer-custom">
156
+ <span class="imagify-the-offer">
157
+ <span class="imagify-offer-size">3 GB</span>
158
+ </span>
159
+ <span class="imagify-approx">
160
+ <?php
161
+ printf(
162
+ /* translators: %s is a formatted number (don't use %d). */
163
+ esc_html__( 'approx: %s images', 'imagify' ),
164
+ '<span class="imagify-approx-nb">' . number_format_i18n( 54000 ) . '</span>'
165
+ );
166
+ ?>
167
+ </span>
168
+ </label>
169
+ </div>
170
+ <div class="imagify-col-price imagify-flex-table">
171
+ <span class="imagify-price-block">
172
+ <span class="imagify-dollars">$</span>
173
+ <span class="imagify-number-block">
174
+ <span class="imagify-price-big"></span>
175
+ <span class="imagify-price-mini"></span>
176
+ </span>
177
+ </span>
178
+ </div>
179
+ <div class="imagify-col-other-actions">
180
+ <a href="#imagify-plans-selection-view" class="imagify-choose-another-plan" data-imagify-choose="onetime"><?php esc_html_e( 'Choose another plan', 'imagify' ); ?></a>
181
+ </div>
182
+
183
+ </div><!-- .imagify-offer-content -->
184
+
185
+ </div><!-- .imagify-offer-line -->
186
+
187
+
188
+ <div class="imagify-submit-line">
189
+ <div class="imagify-coupon-section">
190
+ <p class="imagify-coupon-text">
191
+
192
+ <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>loader-balls.svg" width="60" height="36" alt="<?php esc_attr_e( 'Checking Coupon', 'imagify' ); ?>" class="imagify-coupon-loader">
193
+
194
+ <label for="imagify-coupon-code"><?php _e( 'If you have a <strong>coupon code</strong><br> use it there:', 'imagify' ); ?></label>
195
+ </p>
196
+ <p class="imagify-coupon-input">
197
+ <input type="text" class="imagify-coupon-code" name="imagify-coupon-code" id="imagify-coupon-code" value="" placeholder="<?php _e( 'Coupon Code', 'imagify' ); ?>" autocomplete="off">
198
+ <button type="button" class="button button-secondary imagify-button-secondary" id="imagify-coupon-validate"><?php _e( 'OK' ); ?></button>
199
+ </p>
200
+ </div>
201
+ <div class="imagify-submit-section">
202
+ <button type="button" class="button button-secondary imagify-button-secondary" id="imagify-modal-checkout-btn">
203
+ <i class="dashicons dashicons-cart" aria-hidden="true"></i>
204
+ <?php _e( 'Checkout', 'imagify' ); ?>
205
+ </button>
206
+ </div>
207
+ </div>
208
+
209
+ <p class="imagify-footer-lines"><?php esc_html_e( 'Monthly plans come with credits which is renewed every months. The billing happens automatically each month or year depending the billing period you choose.', 'imagify' ); ?></p>
210
+ </div>
211
+ </div><!-- .imagify-pre-checkout-view -->
212
+
213
+ <?php
214
+ /**
215
+ * SECOND MODAL VIEW.
216
+ */
217
+ ?>
218
+
219
+ <div class="imagify-modal-views imagify-plans-selection-view" id="imagify-plans-selection-view" aria-hidden="true">
220
+ <p class="imagify-modal-title"><?php _e( 'Choose a plan', 'imagify' ); ?></p>
221
+ <ul class="imagify-tabs" role="tablist">
222
+ <li class="imagify-tab imagify-current">
223
+ <a href="#imagify-pricing-tab-monthly" role="tab" aria-controls="imagify-pricing-tab-monthly" aria-selected="true">
224
+ <?php esc_html_e( 'Monthly Plans', 'imagify' ); ?>
225
+ </a>
226
+ </li>
227
+ <li class="imagify-tab">
228
+ <a href="#imagify-pricing-tab-onetime" role="tab" aria-controls="imagify-pricing-tab-onetime" aria-selected="false">
229
+ <?php esc_html_e( 'One Time Plans', 'imagify' ); ?>
230
+ </a>
231
+ </li>
232
+ </ul><!-- .imagify-tabs -->
233
+
234
+ <div class="imagify-tabs-contents">
235
+
236
+ <div class="imagify-tab-content imagify-current" id="imagify-pricing-tab-monthly" role="tabpanel">
237
+
238
+ <div class="imagify-modal-section section-gray">
239
+ <p><?php esc_html_e( 'Monthly plans come with credits which is renewed every months. The billing happens automatically each month or year depending the billing period you choose.', 'imagify' ); ?></p>
240
+ </div>
241
+
242
+ <?php $this->print_template( 'part-settings-discount-banner' ); ?>
243
+
244
+ <div class="imagify-inline-options imagify-small-options imagify-radio-line">
245
+ <input id="imagify-pricing-montly" type="radio" value="monthly" name="plan-pricing" checked="checked">
246
+ <label for="imagify-pricing-montly"><?php esc_html_e( 'Monthly' , 'imagify' ); ?></label>
247
+
248
+ <input id="imagify-pricing-yearly" type="radio" value="yearly" name="plan-pricing">
249
+ <label for="imagify-pricing-yearly"><?php esc_html_e( 'Yearly' , 'imagify' ); ?><span class="imagify-2-free imagify-b-right"><?php esc_html_e( '2 months free', 'imagify' ); ?></span></label>
250
+ </div><!-- .imagify-radio-line -->
251
+
252
+
253
+ <div class="imagify-pricing-table imagify-month-selected">
254
+
255
+ <script type="text/template" id="imagify-offer-monthly-template"><div class="imagify-offer-line imagify-offer-monthlies imagify-flex-table">
256
+ <div class="imagify-col-details">
257
+ <p class="imagify-label">
258
+ <span class="imagify-the-offer">
259
+ <span class="imagify-offer-size"></span>
260
+ <span class="imagify-offer-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
261
+ </span>
262
+ <span class="imagify-approx">
263
+ <?php
264
+ printf(
265
+ /* translators: %s is a formatted number (don't use %d). */
266
+ __( 'approx: %s images', 'imagify' ),
267
+ '<span class="imagify-approx-nb"></span>'
268
+ );
269
+ ?>
270
+ </span>
271
+ </p>
272
+ </div>
273
+ <div class="imagify-col-price imagify-flex-table">
274
+ <span class="imagify-price-block">
275
+ <span class="imagify-dollars">$</span>
276
+ <span class="imagify-number-block">
277
+ <span class="imagify-switch-my"></span>
278
+ <span class="imagify-price-by"><?php esc_html_e( '/month', 'imagify' ); ?></span>
279
+ </span>
280
+ </span>
281
+
282
+ <span class="imagify-recommend" aria-hidden="true"><?php esc_html_e( 'we recommend for you', 'imagify' ); ?></span>
283
+
284
+ <p class="imagify-price-complement">
285
+ <?php
286
+ printf(
287
+ /* translators: %s is a formatted price. */
288
+ __( '%s per<br>additionnal Gb', 'imagify' ),
289
+ '<span class="imagify-price-add-data"></span>'
290
+ );
291
+ ?>
292
+ </p>
293
+
294
+ </div><!-- .imagify-col-price -->
295
+
296
+ <div class="imagify-col-other-actions">
297
+ <button type="button" class="button imagify-button-secondary mini imagify-payment-btn-select-plan"><?php esc_html_e( 'Choose plan', 'imagify' ); ?></button>
298
+ </div>
299
+ </div><!-- .imagify-offer-line --></script>
300
+ </div><!-- .imagify-pricing-table -->
301
+
302
+ <div class="imagify-cols">
303
+ <div class="imagify-col imagify-txt-start">
304
+ <p class="imagify-special-needs">
305
+ <strong><?php esc_html_e( 'Need more?', 'imagify' ); ?></strong>
306
+ <span><?php esc_html_e( 'for special needs', 'imagify' ); ?></span>
307
+ </p>
308
+ </div>
309
+ <div class="imagify-col imagify-txt-end">
310
+ <p><a class="button imagify-button-ghost imagify-button-medium imagify-mt1 imagify-mb1 imagify-mr1" href="<?php echo esc_html( imagify_get_external_url( 'contact' ) ); ?>" target="_blank"><i class="dashicons dashicons-email" aria-hidden="true"></i>&nbsp;<?php esc_html_e( 'Contact Us', 'imagify' ); ?></a></p>
311
+ </div>
312
+ </div>
313
+
314
+ </div><!-- .imagify-tab-content -->
315
+ <div class="imagify-tab-content" id="imagify-pricing-tab-onetime" role="tabpanel">
316
+ <div class="imagify-modal-section section-gray">
317
+ <p><?php esc_html_e( 'One Time plans are useful if you have a lots of existing images which need to be optimized. You can use it for bulk optimizing all your past images. You will pay only once.', 'imagify' ); ?></p>
318
+ </div>
319
+
320
+ <div class="imagify-pricing-table imagify-month-selected">
321
+ <script type="text/template" id="imagify-offer-onetime-template"><div class="imagify-offer-line imagify-flex-table imagify-offer-onetimes">
322
+ <div class="imagify-col-details">
323
+ <p class="imagify-label">
324
+ <span class="imagify-the-offer">
325
+ <span class="imagify-offer-size"></span>
326
+ </span>
327
+ <span class="imagify-approx">
328
+ <?php
329
+ printf(
330
+ /* translators: %s is a formatted number (don't use %d). */
331
+ __( 'approx: %s images', 'imagify' ),
332
+ '<span class="imagify-approx-nb"></span>'
333
+ );
334
+ ?>
335
+ </span>
336
+ </p>
337
+ </div>
338
+ <div class="imagify-col-price">
339
+ <span class="imagify-price-block">
340
+ <span class="imagify-dollars">$</span>
341
+ <span class="imagify-number-block"></span>
342
+ </span>
343
+ <span class="imagify-recommend"><?php esc_html_e( 'we recommend for you', 'imagify' ); ?></span>
344
+ </div><!-- .imagify-col-price -->
345
+
346
+ <div class="imagify-col-other-actions">
347
+ <button type="button" class="button imagify-button-secondary mini imagify-payment-btn-select-plan"><?php esc_html_e( 'Choose plan', 'imagify' ); ?></button>
348
+ </div>
349
+ </div><!-- .imagify-offer-line --></script>
350
+ </div><!-- .imagify-pricing-table -->
351
+
352
+ </div><!-- .imagify-tab-content -->
353
+
354
+ </div><!-- .imagify-tabs-contents -->
355
+ </div><!-- .imagify-plans-selection-view -->
356
+
357
+
358
+ <?php
359
+ /**
360
+ * THIRD MODAL VIEW.
361
+ */
362
+ ?>
363
+
364
+ <div class="imagify-modal-views imagify-payment-process-view" id="imagify-payment-process-view" aria-hidden="true">
365
+
366
+ <iframe data-imagify-api="<?php echo esc_attr( get_imagify_option( 'api_key' ) ); ?>" id="imagify-payment-iframe" data-src="<?php echo esc_url( imagify_get_external_url( 'payment' ) ); ?>" name="imagify-payment-iframe"></iframe>
367
+
368
+ </div><!-- .imagify-modal-views -->
369
+
370
+ <?php
371
+ /**
372
+ * SUCCESS VIEW.
373
+ */
374
+ ?>
375
+
376
+ <div class="imagify-modal-views imagify-success-view" id="imagify-success-view" aria-hidden="true">
377
+ <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>big-blue-check.png" width="113" height="109" alt="">
378
+ <p><?php esc_html_e( 'Thank you for being awesome!', 'imagify' ); ?></p>
379
+ </div><!-- .imagify-modal-views -->
380
+
381
+ <button class="close-btn" type="button">
382
+ <i aria-hidden="true" class="dashicons dashicons-no-alt"></i>
383
+ <span class="screen-reader-text"><?php esc_html_e( 'Close', 'imagify' ); ?></span>
384
+ </button>
385
+
386
+ </div><!-- .imagify-modal-main -->
387
+
388
+ <div class="imagify-modal-sidebar">
389
+ <div class="imagify-modal-sidebar-content imagify-txt-start">
390
+ <p class="imagify-modal-sidebar-title"><?php esc_html_e( 'What do our user think about Imagify', 'imagify' ); ?></p>
391
+
392
+ <div class="imagify-modal-testimony">
393
+ <div class="imagify-modal-testimony-person">
394
+ <span class="imagify-modal-avatar">
395
+ <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>pic-srhdesign.jpg" alt="">
396
+ </span>
397
+ <p class="imagify-modal-identity">
398
+ <a href="https://twitter.com/SRHDesign" target="_blank">@SRHDesign</a>
399
+ <a href="https://twitter.com/SRHDesign/status/686486119249260544" target="_blank"><time datetime="2016-01-11">11 jan. 2016 @ 17:40</time></a>
400
+ </p>
401
+ </div>
402
+ <div class="imagify-modal-testimony-content">
403
+ <p>@imagify is an awesome tool that is powerful &amp; easy to use. It's fast, rivals and surpasses other established plugins/software. Awesome!</p>
404
+ </div>
405
+ </div>
406
+
407
+ <div class="imagify-modal-testimony">
408
+ <div class="imagify-modal-testimony-person">
409
+ <span class="imagify-modal-avatar">
410
+ <img src="<?php echo IMAGIFY_ASSETS_IMG_URL; ?>pic-ericwaltr.jpg" alt=