EWWW Image Optimizer - Version 3.5.1

Version Description

  • added: optional help beacons on bulk and settings pages
  • added: disable deferring of WP Offload S3 uploads with EWWW_IMAGE_OPTIMIZER_NO_DEFER_S3
  • added: override use of wp_add_inline_script with non-standard jQuery by defining EWWW_IMAGE_OPTIMIZER_WEBP_INLINE_FALLBACK
  • fixed: javascript for bulk optimizers in NextGEN, NextCellent and FlaGallery
Download this release

Release Info

Developer nosilver4u
Plugin Icon 128x128 EWWW Image Optimizer
Version 3.5.1
Comparing to
See all releases

Version 3.5.1

Files changed (70) hide show
  1. .travis.yml +48 -0
  2. aux-optimize.php +675 -0
  3. background.php +12 -0
  4. bin/install-wp-tests.sh +127 -0
  5. binaries/cwebp-fbsd +0 -0
  6. binaries/cwebp-linux +0 -0
  7. binaries/cwebp-mac12 +0 -0
  8. binaries/cwebp-mac9 +0 -0
  9. binaries/cwebp-sol +0 -0
  10. binaries/cwebp.exe +0 -0
  11. binaries/gifsicle-fbsd +0 -0
  12. binaries/gifsicle-linux +0 -0
  13. binaries/gifsicle-mac +0 -0
  14. binaries/gifsicle-sol +0 -0
  15. binaries/gifsicle.exe +0 -0
  16. binaries/jpegtran-fbsd +0 -0
  17. binaries/jpegtran-linux +0 -0
  18. binaries/jpegtran-mac +0 -0
  19. binaries/jpegtran-sol +0 -0
  20. binaries/jpegtran.exe +0 -0
  21. binaries/optipng-fbsd +0 -0
  22. binaries/optipng-linux +0 -0
  23. binaries/optipng-mac +0 -0
  24. binaries/optipng-sol +0 -0
  25. binaries/optipng.exe +0 -0
  26. binaries/pngquant-fbsd +0 -0
  27. binaries/pngquant-linux +0 -0
  28. binaries/pngquant-mac +0 -0
  29. binaries/pngquant-sol +0 -0
  30. binaries/pngquant.exe +0 -0
  31. bulk.php +1464 -0
  32. changelog.txt +1041 -0
  33. classes/class-ewww-flag.php +776 -0
  34. classes/class-ewww-image.php +1069 -0
  35. classes/class-ewww-nextcellent.php +774 -0
  36. classes/class-ewww-nextgen.php +909 -0
  37. classes/class-ewwwdb.php +104 -0
  38. classes/class-ewwwio-cli.php +500 -0
  39. classes/class-ewwwio-gd-editor.php +352 -0
  40. classes/class-ewwwio-gmagick-editor.php +84 -0
  41. classes/class-ewwwio-hs-beacon.php +110 -0
  42. classes/class-ewwwio-imagick-editor.php +346 -0
  43. classes/class-ewwwio-media-background-process.php +590 -0
  44. classes/class-ewwwio-tracking.php +347 -0
  45. common.php +8102 -0
  46. ewww-image-optimizer.php +131 -0
  47. images/sample.jpg +0 -0
  48. images/spinner.gif +0 -0
  49. images/test.png +0 -0
  50. images/test.png.webp +0 -0
  51. images/testorig.gif +0 -0
  52. images/testorig.jpg +0 -0
  53. images/testorig.png +0 -0
  54. images/wpspin.gif +0 -0
  55. includes/eio.js +604 -0
  56. includes/flag.js +87 -0
  57. includes/jquery-ui-1.10.1.custom.css +404 -0
  58. includes/load_webp.js +641 -0
  59. includes/load_webp.min.js +1 -0
  60. includes/media.js +91 -0
  61. includes/nextcellent.js +39 -0
  62. includes/nextgen.js +87 -0
  63. includes/resize_detection.js +35 -0
  64. includes/webp.js +55 -0
  65. languages/ewww-image-optimizer-he_IL.mo +0 -0
  66. languages/ewww-image-optimizer-he_IL.po +1281 -0
  67. languages/ewww-image-optimizer-id_ID.mo +0 -0
  68. languages/ewww-image-optimizer-id_ID.po +1281 -0
  69. languages/ewww-image-optimizer-vi.mo +0 -0
  70. languages/ewww-image-optimizer-vi.po +1158 -0
.travis.yml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ sudo: false
2
+
3
+ language: php
4
+
5
+ notifications:
6
+ email:
7
+ on_success: never
8
+ on_failure: change
9
+
10
+ branches:
11
+ only:
12
+ - master
13
+
14
+ php:
15
+ - 5.3
16
+ - 5.4
17
+ - 5.5
18
+ - 5.6
19
+ - 7.0
20
+ - 7.1
21
+
22
+ env:
23
+ - WP_VERSION=latest WP_MULTISITE=0
24
+ - WP_VERSION=4.4 WP_MULTISITE=0
25
+
26
+ matrix:
27
+ include:
28
+ - php: 5.3
29
+ env: WP_VERSION=latest WP_MULTISITE=1
30
+ - php: 7.0
31
+ env: WP_VERSION=latest WP_MULTISITE=1
32
+
33
+ before_script:
34
+ - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
35
+ - export PATH="$HOME/.composer/vendor/bin:$PATH"
36
+ - |
37
+ if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then
38
+ composer global require "phpunit/phpunit=5.7.*"
39
+ else
40
+ composer global require "phpunit/phpunit=4.8.*"
41
+ fi
42
+ - |
43
+ composer global require wp-coding-standards/wpcs
44
+ phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs
45
+
46
+ script:
47
+ - phpcs --standard=phpcs.ruleset.xml $(find . -name '*.php')
48
+ - phpunit
aux-optimize.php ADDED
@@ -0,0 +1,675 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Functions for dealing with auxiliary images
4
+ *
5
+ * This file contains functions for bulk optimizing images outside the Media
6
+ * Library, and AJAX hooks for handling the image status table on the bulk
7
+ * optimize page.
8
+ *
9
+ * @link https://ewww.io
10
+ * @package EWWW_Image_Optimizer
11
+ */
12
+
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Displays the lower portion of the Bulk Optimize page.
19
+ *
20
+ * Includes the table migration notice, and the framework for displaying the image status table.
21
+ *
22
+ * @global object $wpdb
23
+ */
24
+ function ewww_image_optimizer_aux_images() {
25
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
26
+ global $wpdb;
27
+ $output = '';
28
+ // Find out if the auxiliary image table has anything in it.
29
+ $already_optimized = ewww_image_optimizer_aux_images_table_count();
30
+ // See if the auxiliary image table needs converting from md5sums to image sizes.
31
+ $column = $wpdb->get_row( "SHOW COLUMNS FROM $wpdb->ewwwio_images LIKE 'image_md5'", ARRAY_N );
32
+ if ( ! empty( $column ) ) {
33
+ ewwwio_debug_message( 'image_md5 column exists, checking for image_md5 values' );
34
+ $db_convert = $wpdb->get_results( "SELECT image_md5 FROM $wpdb->ewwwio_images WHERE image_md5 <> ''", ARRAY_N );
35
+ }
36
+ $output .= '<div id="ewww-aux-forms">';
37
+ if ( ! empty( $db_convert ) ) {
38
+ $output .= '<p class="ewww-bulk-info">' . esc_html__( 'The database schema has changed, you need to convert to the new format.', 'ewww-image-optimizer' ) . '</p>';
39
+ $output .= '<form method="post" id="ewww-aux-convert" class="ewww-bulk-form" action="">';
40
+ $output .= wp_nonce_field( 'ewww-image-optimizer-aux-images-convert', 'ewww_wpnonce', true, false );
41
+ $output .= '<input type="hidden" name="ewww_convert" value="1">';
42
+ $output .= '<button id="ewww-table-convert" type="submit" class="button-secondary action">' . esc_html__( 'Convert Table', 'ewww-image-optimizer' ) . '</button>';
43
+ $output .= '</form>';
44
+ }
45
+ if ( empty( $already_optimized ) ) {
46
+ $display = ' style="display:none"';
47
+ } else {
48
+ $display = '';
49
+ }
50
+ /* translators: %d: number of images */
51
+ $output .= "<p id='ewww-table-info' class='ewww-bulk-info' $display>" . sprintf( esc_html__( 'The plugin keeps track of already optimized images to prevent re-optimization. There are %d images that have been optimized so far.', 'ewww-image-optimizer' ), $already_optimized ) . '</p>';
52
+ $output .= "<form id='ewww-show-table' class='ewww-bulk-form' method='post' action='' $display>";
53
+ $output .= '<button type="submit" class="button-secondary action">' . esc_html__( 'Show Optimized Images', 'ewww-image-optimizer' ) . '</button>';
54
+ $output .= '</form>';
55
+ $output .= '<div class="tablenav ewww-aux-table" style="display:none">' .
56
+ '<div class="tablenav-pages ewww-aux-table">' .
57
+ '<span class="displaying-num ewww-aux-table"></span>' . "\n" .
58
+ '<span id="paginator" class="pagination-links ewww-aux-table">' . "\n" .
59
+ '<a id="first-images" class="first-page" style="display:none">&laquo;</a>' . "\n" .
60
+ '<a id="prev-images" class="prev-page" style="display:none">&lsaquo;</a>' . "\n";
61
+ $output .= esc_html__( 'page', 'ewww-image-optimizer' ) . ' <span class="current-page"></span> ' . esc_html__( 'of', 'ewww-image-optimizer' ) . "\n";
62
+ $output .= '<span class="total-pages"></span>' . "\n" .
63
+ '<a id="next-images" class="next-page" style="display:none">&rsaquo;</a>' . "\n" .
64
+ '<a id="last-images" class="last-page" style="display:none">&raquo;</a>' .
65
+ '</span>' .
66
+ '</div>' .
67
+ '</div>' .
68
+ '<div id="ewww-bulk-table" class="ewww-aux-table"></div>' .
69
+ '<span id="ewww-pointer" style="display:none">0</span>' .
70
+ '</div>' .
71
+ '</div>';
72
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
73
+ ewww_image_optimizer_options( 'debug-silent' );
74
+ global $ewww_debug;
75
+ $output .= '<div id="ewww-debug-info" style="clear:both;background:#ffff99;margin-left:-20px;padding:10px">' . $ewww_debug . '</div>';
76
+ }
77
+ echo $output;
78
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help' ) ) {
79
+ $help_instructions = esc_html__( 'Please turn on the Debugging option. Then copy and paste the Debug Information from the bottom of the settings page. This will allow us to assist you more quickly.', 'ewww-image-optimizer' );
80
+ $current_user = wp_get_current_user();
81
+ $help_email = $current_user->user_email;
82
+ $hs_config = array(
83
+ 'color' => '#3eadc9',
84
+ 'icon' => 'buoy',
85
+ 'instructions' => $help_instructions,
86
+ 'poweredBy' => false,
87
+ 'showContactFields' => true,
88
+ 'showSubject' => true,
89
+ 'topArticles' => true,
90
+ 'zIndex' => 100000,
91
+ );
92
+ $hs_identify = array(
93
+ 'email' => $help_email,
94
+ 'debug_info' => $ewww_debug,
95
+ );
96
+ ?>
97
+ <script type='text/javascript'>
98
+ !function(e,o,n){window.HSCW=o,window.HS=n,n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={},t.readyQueue=[],t.config=function(e){this.userConfig=e},t.ready=function(e){this.readyQueue.push(e)},o.config={docs:{enabled:!0,baseUrl:"//ewwwio.helpscoutdocs.com/"},contact:{enabled:!0,formId:"af75cf17-310a-11e7-9841-0ab63ef01522"}};var r=e.getElementsByTagName("script")[0],c=e.createElement("script");c.type="text/javascript",c.async=!0,c.src="https://djtflbt20bdde.cloudfront.net/",r.parentNode.insertBefore(c,r)}(document,window.HSCW||{},window.HS||{});
99
+ HS.beacon.config(<?php echo json_encode( $hs_config ); ?>);
100
+ HS.beacon.ready(function() {
101
+ HS.beacon.identify(
102
+ <?php echo json_encode( $hs_identify ); ?>
103
+ );
104
+ });
105
+ </script>
106
+ <?php
107
+ }
108
+ ewwwio_memory( __FUNCTION__ );
109
+ }
110
+
111
+ /**
112
+ * Displays 50 records from the images table.
113
+ *
114
+ * Called via AJAX to find 50 records from the images table and display them
115
+ * with alternating row style.
116
+ *
117
+ * @global object $wpdb
118
+ */
119
+ function ewww_image_optimizer_aux_images_table() {
120
+ // Verify that an authorized user has called function.
121
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) ) {
122
+ wp_die( esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) );
123
+ }
124
+ global $wpdb;
125
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
126
+ ewww_image_optimizer_db_init();
127
+ global $ewwwdb;
128
+ } else {
129
+ $ewwwdb = $wpdb;
130
+ }
131
+ $offset = 50 * (int) $_POST['ewww_offset'];
132
+ $query = "SELECT path,orig_size,image_size,id,backup,updated FROM $ewwwdb->ewwwio_images WHERE pending=0 AND image_size > 0 ORDER BY id DESC LIMIT $offset,50";
133
+ $already_optimized = $ewwwdb->get_results( $query, ARRAY_A );
134
+ $upload_info = wp_upload_dir();
135
+ $upload_path = $upload_info['basedir'];
136
+ echo '<br /><table class="wp-list-table widefat media" cellspacing="0"><thead><tr><th>&nbsp;</th><th>' . esc_html__( 'Filename', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Image Type', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Image Optimizer', 'ewww-image-optimizer' ) . '</th></tr></thead>';
137
+ $alternate = true;
138
+ foreach ( $already_optimized as $optimized_image ) {
139
+ $image_name = str_replace( ABSPATH, '', ewww_image_optimizer_relative_path_replace( $optimized_image['path'] ) );
140
+ $image_url = esc_url( trailingslashit( get_site_url() ) . $image_name );
141
+ $savings = esc_html( ewww_image_optimizer_image_results( $optimized_image['orig_size'], $optimized_image['image_size'] ) );
142
+ $updated_time = strtotime( $optimized_image['updated'] );
143
+ if ( DAY_IN_SECONDS * 30 + $updated_time < time() ) {
144
+ $optimized_image['backup'] = '';
145
+ }
146
+ if ( strpos( $optimized_image['path'], 's3' ) === 0 ) {
147
+ // Retrieve the mimetype of the attachment.
148
+ $type = esc_html__( 'Amazon S3 image', 'ewww-image-optimizer' );
149
+ $file_size = ewww_image_optimizer_size_format( $optimized_image['image_size'] );
150
+ /* translators: %s: human-readable filesize */
151
+ $size_string = sprintf( esc_html__( 'Image Size: %s', 'ewww-image-optimizer' ), $file_size );
152
+ ?> <tr<?php if ( $alternate ) { echo " class='alternate'"; } ?> id="ewww-image-<?php echo $optimized_image['id']; ?>">
153
+ <td style='width:80px' class='column-icon'>&nbsp;</td>
154
+ <td class='title'><?php echo $image_name; ?></td>
155
+ <td><?php echo $type; ?></td>
156
+ <td>
157
+ <?php echo "$savings <br>$size_string"; ?><br>
158
+ <a class="removeimage" onclick="ewwwRemoveImage( <?php echo $optimized_image['id']; ?> )"><?php esc_html_e( 'Remove from table', 'ewww-image-optimizer' ); ?></a>
159
+ <?php if ( $optimized_image['backup'] ) { ?>
160
+ <br><a class="restoreimage" onclick="ewwwRestoreImage( <?php echo $optimized_image['id']; ?> )"><?php esc_html_e( 'Restore original', 'ewww-image-optimizer' ); ?></a>
161
+ <?php } ?>
162
+ </td>
163
+ </tr>
164
+ <?php $alternate = ! $alternate;
165
+ } elseif ( file_exists( $optimized_image['path'] ) ) {
166
+ // Retrieve the mimetype of the attachment.
167
+ $type = ewww_image_optimizer_mimetype( $optimized_image['path'], 'i' );
168
+ // Get a human readable filesize.
169
+ $file_size = ewww_image_optimizer_size_format( $optimized_image['image_size'] );
170
+ /* translators: %s: human-readable filesize */
171
+ $size_string = sprintf( esc_html__( 'Image Size: %s', 'ewww-image-optimizer' ), $file_size );
172
+ ?> <tr<?php if ( $alternate ) { echo " class='alternate'"; } ?> id="ewww-image-<?php echo $optimized_image['id']; ?>">
173
+ <td style='width:80px' class='column-icon'><img width='50' height='50' src="<?php echo $image_url; ?>" /></td>
174
+ <td class='title'>...<?php echo $image_name; ?></td>
175
+ <td><?php echo $type; ?></td>
176
+ <td>
177
+ <?php echo "$savings <br>$size_string"; ?><br>
178
+ <a class="removeimage" onclick="ewwwRemoveImage( <?php echo $optimized_image['id']; ?> )"><?php esc_html_e( 'Remove from table', 'ewww-image-optimizer' ); ?></a>
179
+ <?php if ( $optimized_image['backup'] ) { ?>
180
+ <br><a class="restoreimage" onclick="ewwwRestoreImage( <?php echo $optimized_image['id']; ?> )"><?php esc_html_e( 'Restore original', 'ewww-image-optimizer' ); ?></a>
181
+ <?php } ?>
182
+ </td>
183
+ </tr>
184
+ <?php $alternate = ! $alternate;
185
+ } // End if().
186
+ } // End foreach().
187
+ echo '</table>';
188
+ ewwwio_memory( __FUNCTION__ );
189
+ ewww_image_optimizer_debug_log();
190
+ die();
191
+ }
192
+
193
+ /**
194
+ * Removes an image from the auxiliary images table.
195
+ *
196
+ * Called via AJAX, this function will remove the record in provided by the
197
+ * POST variable 'ewww_image_id' and return a '1' if successful.
198
+ *
199
+ * @global object $wpdb
200
+ */
201
+ function ewww_image_optimizer_aux_images_remove() {
202
+ // Verify that an authorized user has called function.
203
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) ) {
204
+ wp_die( esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) );
205
+ }
206
+ global $wpdb;
207
+ if ( $wpdb->delete(
208
+ $wpdb->ewwwio_images,
209
+ array(
210
+ 'id' => $_POST['ewww_image_id'],
211
+ )
212
+ ) ) {
213
+ echo '1';
214
+ }
215
+ ewwwio_memory( __FUNCTION__ );
216
+ die();
217
+ }
218
+
219
+ /**
220
+ * Find the number of optimized images in the ewwwio_images table.
221
+ *
222
+ * @global object $wpdb
223
+ * @return int The total number of records in the images table that are not pending and have a
224
+ * valid file-size.
225
+ */
226
+ function ewww_image_optimizer_aux_images_table_count() {
227
+ global $wpdb;
228
+ $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->ewwwio_images WHERE pending=0 AND image_size > 0" );
229
+ if ( ! empty( $_REQUEST['ewww_inline'] ) ) {
230
+ echo $count;
231
+ ewwwio_memory( __FUNCTION__ );
232
+ die();
233
+ }
234
+ ewwwio_memory( __FUNCTION__ );
235
+ return $count;
236
+ }
237
+
238
+ /**
239
+ * Find the number of un-optimized images in the ewwwio_images table.
240
+ *
241
+ * @global object $wpdb
242
+ * @return int Number of pending images in queue.
243
+ */
244
+ function ewww_image_optimizer_aux_images_table_count_pending() {
245
+ global $wpdb;
246
+ $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->ewwwio_images WHERE pending=1" );
247
+ return $count;
248
+ }
249
+
250
+ /**
251
+ * Remove all un-optimized images from the ewwwio_images table.
252
+ *
253
+ * @global object $wpdb
254
+ */
255
+ function ewww_image_optimizer_delete_pending() {
256
+ global $wpdb;
257
+ $wpdb->query( "DELETE from $wpdb->ewwwio_images WHERE pending=1 AND (image_size IS NULL OR image_size = 0)" );
258
+ $wpdb->update( $wpdb->ewwwio_images,
259
+ array(
260
+ 'pending' => 0,
261
+ ),
262
+ array(
263
+ 'pending' => 1,
264
+ )
265
+ );
266
+ }
267
+
268
+ /**
269
+ * Searches for images to optimize in a specific folder.
270
+ *
271
+ * Scan a folder for images and mark unoptimized images in the database
272
+ * (inserts new records as necessary).
273
+ *
274
+ * @global object $wpdb
275
+ * @global array|string $optimized_list An associative array containing information from the images
276
+ * table, or 'low_memory', 'large_list', 'small_scan'.
277
+ *
278
+ * @param string $dir The absolute path of the folder to be scanned for unoptimized images.
279
+ * @param int $started Optional. The number of seconds since the overall scanning process started. Default 0.
280
+ */
281
+ function ewww_image_optimizer_image_scan( $dir, $started = 0 ) {
282
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
283
+ $folders_completed = get_option( 'ewww_image_optimizer_aux_folders_completed' );
284
+ if ( ! is_array( $folders_completed ) ) {
285
+ $folders_completed = array();
286
+ }
287
+ if ( in_array( $dir, $folders_completed ) ) {
288
+ return;
289
+ }
290
+ global $wpdb;
291
+ global $optimized_list;
292
+ $images = array();
293
+ $reset_images = array();
294
+ if ( ! is_dir( $dir ) ) {
295
+ return;
296
+ }
297
+ ewwwio_debug_message( "scanning folder for images: $dir" );
298
+ $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ), RecursiveIteratorIterator::CHILD_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
299
+ $start = microtime( true );
300
+ if ( empty( $optimized_list ) || ! is_array( $optimized_list ) ) {
301
+ ewww_image_optimizer_optimized_list();
302
+ }
303
+ $file_counter = 0; // Used to track total files overall.
304
+ $image_count = 0; // Used to track number of files since last queue update.
305
+ if ( ewww_image_optimizer_stl_check() ) {
306
+ set_time_limit( 0 );
307
+ }
308
+ $enabled_types = array();
309
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) ) {
310
+ $enabled_types[] = 'image/jpeg';
311
+ }
312
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) ) {
313
+ $enabled_types[] = 'image/png';
314
+ }
315
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) ) {
316
+ $enabled_types[] = 'image/gif';
317
+ }
318
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) ) {
319
+ $enabled_types[] = 'application/pdf';
320
+ }
321
+ foreach ( $iterator as $file ) {
322
+ if ( get_transient( 'ewww_image_optimizer_aux_iterator' ) && get_transient( 'ewww_image_optimizer_aux_iterator' ) > $file_counter ) {
323
+ continue;
324
+ }
325
+ if ( $started && ! empty( $_REQUEST['ewww_scan'] ) && 0 === $file_counter % 100 && microtime( true ) - $started > apply_filters( 'ewww_image_optimizer_timeout', 15 ) ) {
326
+ if ( ! empty( $reset_images ) ) {
327
+ array_walk( $reset_images, 'intval' );
328
+ $wpdb->query( "UPDATE $wpdb->ewwwio_images SET pending = 1 WHERE id IN (" . implode( ',', $reset_images ) . ')' ); // WPCS: unprepared SQL ok.
329
+ }
330
+ if ( ! empty( $images ) ) {
331
+ ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, array( '%s', '%d', '%d' ) );
332
+ }
333
+ set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
334
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
335
+ die( json_encode( array(
336
+ 'remaining' => '<p>' . esc_html__( 'Stage 2, please wait.', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>",
337
+ 'notice' => '',
338
+ ) ) );
339
+ }
340
+ // TODO: can we tailor this for scheduled opt also?
341
+ if ( ! empty( $_REQUEST['ewww_scan'] ) && 0 === $file_counter % 100 && ! ewwwio_check_memory_available( 2097000 ) ) {
342
+ if ( $file_counter < 100 ) {
343
+ die( json_encode( array(
344
+ 'error' => esc_html__( 'Stage 2 unable to complete due to memory restrictions. Please increase the memory_limit setting for PHP and try again.', 'ewww-image-optimizer' ),
345
+ ) ) );
346
+ }
347
+ if ( ! empty( $reset_images ) ) {
348
+ array_walk( $reset_images, 'intval' );
349
+ $wpdb->query( "UPDATE $wpdb->ewwwio_images SET pending = 1 WHERE id IN (" . implode( ',', $reset_images ) . ')' ); // WPCS: unprepared SQL ok.
350
+ }
351
+ if ( ! empty( $images ) ) {
352
+ ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, array( '%s', '%d', '%d' ) );
353
+ }
354
+ set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
355
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
356
+ die( json_encode( array(
357
+ 'remaining' => '<p>' . esc_html__( 'Stage 2, please wait.', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>",
358
+ 'notice' => '',
359
+ ) ) );
360
+ }
361
+ $file_counter++;
362
+ if ( $file->isFile() ) {
363
+ $path = $file->getPathname();
364
+ if ( preg_match( '/(\/|\\\\)\./', $path ) && apply_filters( 'ewww_image_optimizer_ignore_hidden_files', true ) ) {
365
+ continue;
366
+ }
367
+ $mime = ewww_image_optimizer_quick_mimetype( $path );
368
+ if ( ! in_array( $mime, $enabled_types ) ) {
369
+ continue;
370
+ }
371
+ if ( apply_filters( 'ewww_image_optimizer_bypass', false, $path ) === true ) {
372
+ ewwwio_debug_message( "skipping $path as instructed" );
373
+ continue;
374
+ }
375
+
376
+ $already_optimized = false;
377
+ if ( is_string( $optimized_list ) ) {
378
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $path );
379
+ }
380
+
381
+ if ( $already_optimized || isset( $optimized_list[ $path ] ) ) {
382
+ if ( ! $already_optimized ) {
383
+ $already_optimized = $optimized_list[ $path ];
384
+ }
385
+ if ( ! empty( $already_optimized['pending'] ) ) {
386
+ ewwwio_debug_message( "pending record for $path" );
387
+ continue;
388
+ }
389
+ $image_size = $file->getSize();
390
+ if ( $image_size < ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) ) {
391
+ ewwwio_debug_message( "file skipped due to filesize: $path" );
392
+ continue;
393
+ }
394
+ if ( 'image/png' == $mime && ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) && $image_size > ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) ) {
395
+ ewwwio_debug_message( "file skipped due to PNG filesize: $path" );
396
+ continue;
397
+ }
398
+ if ( $already_optimized['image_size'] == $image_size && empty( $_REQUEST['ewww_force'] ) ) {
399
+ ewwwio_debug_message( "match found for $path" );
400
+ continue;
401
+ } else {
402
+ $reset_images[] = (int) $already_optimized['id'];
403
+ ewwwio_debug_message( "mismatch found for $path, db says " . $already_optimized['image_size'] . " vs. current $image_size" );
404
+ }
405
+ } else {
406
+ $image_size = $file->getSize();
407
+ if ( $image_size < ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) ) {
408
+ ewwwio_debug_message( "file skipped due to filesize: $path" );
409
+ continue;
410
+ }
411
+ if ( 'image/png' == $mime && ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) && $image_size > ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) ) {
412
+ ewwwio_debug_message( "file skipped due to PNG filesize: $path" );
413
+ continue;
414
+ }
415
+ ewwwio_debug_message( "queuing $path" );
416
+ $path = ewww_image_optimizer_relative_path_remove( $path );
417
+ if ( seems_utf8( $path ) ) {
418
+ $utf8_file_path = $path;
419
+ } else {
420
+ $utf8_file_path = utf8_encode( $path );
421
+ }
422
+ $images[] = array(
423
+ 'path' => $utf8_file_path,
424
+ 'orig_size' => $image_size,
425
+ 'pending' => 1,
426
+ );
427
+ $image_count++;
428
+ } // End if().
429
+ if ( $image_count > 1000 ) {
430
+ // Let's dump what we have so far to the db.
431
+ $image_count = 0;
432
+ ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, array( '%s', '%d', '%d' ) );
433
+ $images = array();
434
+ }
435
+ } // End if().
436
+ } // End foreach().
437
+ if ( ! empty( $images ) ) {
438
+ ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, array( '%s', '%d', '%d' ) );
439
+ }
440
+ if ( ! empty( $reset_images ) ) {
441
+ array_walk( $reset_images, 'intval' );
442
+ $wpdb->query( "UPDATE $wpdb->ewwwio_images SET pending = 1 WHERE id IN (" . implode( ',', $reset_images ) . ')' ); // WPCS: unprepared SQL ok.
443
+ }
444
+ delete_transient( 'ewww_image_optimizer_aux_iterator' );
445
+ $end = microtime( true ) - $start;
446
+ ewwwio_debug_message( "query time for $file_counter files (seconds): $end" );
447
+ clearstatcache();
448
+ ewwwio_memory( __FUNCTION__ );
449
+ $folders_completed[] = $dir;
450
+ update_option( 'ewww_image_optimizer_aux_folders_completed', $folders_completed, false );
451
+ }
452
+
453
+ /**
454
+ * Convert all records in table to use filesize rather than md5sum.
455
+ *
456
+ * @global object $wpdb
457
+ */
458
+ function ewww_image_optimizer_aux_images_convert() {
459
+ global $wpdb;
460
+ $old_records = $wpdb->get_results( "SELECT id,path,image_md5 FROM $wpdb->ewwwio_images", ARRAY_A );
461
+ foreach ( $old_records as $record ) {
462
+ if ( empty( $record['image_md5'] ) ) {
463
+ continue;
464
+ }
465
+ $record['path'] = ewww_image_optimizer_relative_path_replace( $record['path'] );
466
+ $image_md5 = md5_file( $record['path'] );
467
+ if ( $image_md5 === $record['image_md5'] ) {
468
+ $filesize = filesize( $record['path'] );
469
+ $wpdb->update( $wpdb->ewwwio_images,
470
+ array(
471
+ 'image_md5' => null,
472
+ 'image_size' => $filesize,
473
+ ),
474
+ array(
475
+ 'id' => $record['id'],
476
+ )
477
+ );
478
+ } else {
479
+ $wpdb->delete( $wpdb->ewwwio_images,
480
+ array(
481
+ 'id' => $record['id'],
482
+ )
483
+ );
484
+ }
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Searches for images to optimize.
490
+ *
491
+ * Scans all auxiliary folders, including some predefined ones, and those configured by the user.
492
+ * Used for the main bulk tool, and the scheduled optimization.
493
+ *
494
+ * @param string $hook Optional. Indicates if scheduled optimization is running.
495
+ * @global object $wpdb
496
+ * @return int Number of images ready to optimize.
497
+ */
498
+ function ewww_image_optimizer_aux_images_script( $hook = '' ) {
499
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
500
+ // Make sure we are being called from the proper page.
501
+ if ( wp_doing_ajax() && empty( $_REQUEST['ewww_scan'] ) ) {
502
+ return;
503
+ }
504
+ session_write_close();
505
+ if ( ! empty( $_REQUEST['ewww_force'] ) ) {
506
+ ewwwio_debug_message( 'forcing re-optimize: true' );
507
+ }
508
+ // Retrieve the time when the scan starts.
509
+ $started = microtime( true );
510
+ if ( ! get_transient( 'ewww_image_optimizer_skip_aux' ) ) {
511
+ update_option( 'ewww_image_optimizer_aux_resume', 'scanning' );
512
+ ewwwio_debug_message( 'getting fresh list of files to optimize' );
513
+ // Collect a list of images from the current theme (and parent theme if applicable).
514
+ $child_path = get_stylesheet_directory();
515
+ $parent_path = get_template_directory();
516
+ ewww_image_optimizer_image_scan( $child_path, $started );
517
+ if ( $child_path !== $parent_path ) {
518
+ ewww_image_optimizer_image_scan( $parent_path, $started );
519
+ }
520
+ if ( ! function_exists( 'is_plugin_active' ) ) {
521
+ // Need to include the plugin library for the is_plugin_active function.
522
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
523
+ }
524
+ if ( is_plugin_active( 'buddypress/bp-loader.php' ) || is_plugin_active_for_network( 'buddypress/bp-loader.php' ) ) {
525
+ $upload_dir = wp_upload_dir();
526
+ ewww_image_optimizer_image_scan( $upload_dir['basedir'] . '/avatars', $started );
527
+ ewww_image_optimizer_image_scan( $upload_dir['basedir'] . '/group-avatars', $started );
528
+ }
529
+ if ( is_plugin_active( 'buddypress-activity-plus/bpfb.php' ) || is_plugin_active_for_network( 'buddypress-activity-plus/bpfb.php' ) ) {
530
+ $upload_dir = wp_upload_dir();
531
+ ewww_image_optimizer_image_scan( $upload_dir['basedir'] . '/bpfb', $started );
532
+ }
533
+ if ( is_plugin_active( 'grand-media/grand-media.php' ) || is_plugin_active_for_network( 'grand-media/grand-media.php' ) ) {
534
+ // Scan the grand media folder for images.
535
+ ewww_image_optimizer_image_scan( WP_CONTENT_DIR . '/grand-media', $started );
536
+ }
537
+ if ( is_plugin_active( 'wp-symposium/wp-symposium.php' ) || is_plugin_active_for_network( 'wp-symposium/wp-symposium.php' ) ) {
538
+ ewww_image_optimizer_image_scan( get_option( 'symposium_img_path' ), $started );
539
+ }
540
+ if ( defined( 'WPS_CORE_PLUGINS' ) ) {
541
+ ewww_image_optimizer_image_scan( WP_CONTENT_DIR . '/wps-pro-content', $started );
542
+ }
543
+ if ( is_plugin_active( 'ml-slider/ml-slider.php' ) || is_plugin_active_for_network( 'ml-slider/ml-slider.php' ) ) {
544
+ global $wpdb;
545
+ $slide_paths = array();
546
+ $slides = $wpdb->get_col(
547
+ "
548
+ SELECT wpposts.ID
549
+ FROM $wpdb->posts wpposts
550
+ INNER JOIN $wpdb->term_relationships term_relationships
551
+ ON wpposts.ID = term_relationships.object_id
552
+ INNER JOIN $wpdb->terms wpterms
553
+ ON term_relationships.term_taxonomy_id = wpterms.term_id
554
+ INNER JOIN $wpdb->term_taxonomy term_taxonomy
555
+ ON wpterms.term_id = term_taxonomy.term_id
556
+ WHERE term_taxonomy.taxonomy = 'ml-slider'
557
+ AND wpposts.post_type = 'attachment'
558
+ "
559
+ );
560
+ if ( ewww_image_optimizer_iterable( $slides ) ) {
561
+ foreach ( $slides as $slide ) {
562
+ $type = get_post_meta( $slide, 'ml-slider_type', true );
563
+ $type = $type ? $type : 'image'; // For backwards compatibility, fall back to 'image'.
564
+ if ( 'image' != $type ) {
565
+ continue;
566
+ }
567
+ $backup_sizes = get_post_meta( $slide, '_wp_attachment_backup_sizes', true );
568
+ if ( ewww_image_optimizer_iterable( $backup_sizes ) ) {
569
+ foreach ( $backup_sizes as $backup_size => $meta ) {
570
+ if ( preg_match( '/resized-/', $backup_size ) ) {
571
+ $path = $meta['path'];
572
+ $image_size = ewww_image_optimizer_filesize( $path );
573
+ if ( ! $image_size ) {
574
+ continue;
575
+ }
576
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $path );
577
+ // A pending record already present.
578
+ if ( ! empty( $already_optimized ) && empty( $already_optimized['image_size'] ) ) {
579
+ continue;
580
+ }
581
+ $mimetype = ewww_image_optimizer_mimetype( $path, 'i' );
582
+ // This is a brand new image.
583
+ if ( preg_match( '/^image\/(jpeg|png|gif)/', $mimetype ) && empty( $already_optimized ) ) {
584
+ $slide_paths[] = array(
585
+ 'path' => ewww_image_optimizer_relative_path_remove( $path ),
586
+ 'orig_size' => $image_size,
587
+ );
588
+ // This is a changed image.
589
+ } elseif ( preg_match( '/^image\/(jpeg|png|gif)/', $mimetype ) && ! empty( $already_optimized ) && $already_optimized['image_size'] != $image_size ) {
590
+ $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->ewwwio_images SET pending = 1 WHERE id = %d", $already_optimized['id'] ) );
591
+ }
592
+ }
593
+ }
594
+ }
595
+ }
596
+ } // End if().
597
+ if ( ! empty( $slide_paths ) ) {
598
+ ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $slide_paths, array( '%s', '%d' ) );
599
+ }
600
+ } // End if().
601
+ // Collect a list of images in auxiliary folders provided by user.
602
+ $aux_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_aux_paths' );
603
+ if ( $aux_paths ) {
604
+ if ( ewww_image_optimizer_iterable( $aux_paths ) ) {
605
+ foreach ( $aux_paths as $aux_path ) {
606
+ ewww_image_optimizer_image_scan( $aux_path, $started );
607
+ }
608
+ }
609
+ }
610
+ // Scan images in two most recent media library folders if the option is enabled, and this is a scheduled optimization.
611
+ if ( 'ewww-image-optimizer-auto' == $hook && ewww_image_optimizer_get_option( 'ewww_image_optimizer_include_media_paths' ) ) {
612
+ // Retrieve the location of the wordpress upload folder.
613
+ $upload_dir = wp_upload_dir();
614
+ // Retrieve the path of the upload folder.
615
+ $upload_path = $upload_dir['basedir'];
616
+ $this_month = date( 'm' );
617
+ $this_year = date( 'Y' );
618
+ ewww_image_optimizer_image_scan( "$upload_path/$this_year/$this_month/", $started );
619
+ if ( class_exists( 'DateTime' ) ) {
620
+ $date = new DateTime();
621
+ $date->sub( new DateInterval( 'P1M' ) );
622
+ $last_year = $date->format( 'Y' );
623
+ $last_month = $date->format( 'm' );
624
+ ewww_image_optimizer_image_scan( "$upload_path/$last_year/$last_month/", $started );
625
+ }
626
+ }
627
+ } // End if().
628
+ $image_count = ewww_image_optimizer_aux_images_table_count_pending();
629
+ ewwwio_debug_message( "found $image_count images to optimize while scanning" );
630
+ update_option( 'ewww_image_optimizer_aux_folders_completed', array(), false );
631
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
632
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
633
+ ewww_image_optimizer_debug_log();
634
+ if ( wp_doing_ajax() ) {
635
+ ewwwio_memory( __FUNCTION__ );
636
+ /* translators: %d: number of images */
637
+ $ready_msg = sprintf( esc_html( _n( 'There is %d image ready to optimize.', 'There are %d images ready to optimize.', $image_count, 'ewww-image-optimizer' ) ), $image_count )
638
+ . ' <a href="http://docs.ewww.io/article/20-why-do-i-have-so-many-images-on-my-site" target="_blank" data-beacon-article="58598744c697912ffd6c3eb4">' . esc_html__( 'Why are there so many images?', 'ewww-image-optimizer' ) . '</a>';
639
+ die( json_encode( array(
640
+ 'ready' => $image_count,
641
+ 'message' => $ready_msg,
642
+ ) ) );
643
+ }
644
+ ewwwio_memory( __FUNCTION__ );
645
+ return $image_count;
646
+ }
647
+
648
+ /**
649
+ * Called by scheduled optimization to cleanup after ourselves.
650
+ *
651
+ * @param bool $auto Indicates whether or not the function is called from scheduled (auto) optimization mode.
652
+ */
653
+ function ewww_image_optimizer_aux_images_cleanup( $auto = false ) {
654
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
655
+ // Verify that an authorized user has started the optimizer.
656
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
657
+ if ( ! $auto && ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) ) {
658
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
659
+ }
660
+ $stored_last = get_option( 'ewww_image_optimizer_aux_last' );
661
+ update_option( 'ewww_image_optimizer_aux_last', array( time(), $stored_last[1] ) );
662
+ // All done, so we can update the bulk options with empty values.
663
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
664
+ if ( ! $auto ) {
665
+ // And let the user know we are done.
666
+ echo '<p><b>' . esc_html__( 'Finished', 'ewww-image-optimizer' ) . '</b></p>';
667
+ ewwwio_memory( __FUNCTION__ );
668
+ die();
669
+ }
670
+ }
671
+
672
+ add_action( 'wp_ajax_bulk_aux_images_table', 'ewww_image_optimizer_aux_images_table' );
673
+ add_action( 'wp_ajax_bulk_aux_images_table_count', 'ewww_image_optimizer_aux_images_table_count' );
674
+ add_action( 'wp_ajax_bulk_aux_images_remove', 'ewww_image_optimizer_aux_images_remove' );
675
+ ?>
background.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Include all the background/async classes.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EWWW_Image_Optimizer
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewwwio-media-background-process.php' );
bin/install-wp-tests.sh ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ if [ $# -lt 3 ]; then
4
+ echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
5
+ exit 1
6
+ fi
7
+
8
+ DB_NAME=$1
9
+ DB_USER=$2
10
+ DB_PASS=$3
11
+ DB_HOST=${4-localhost}
12
+ WP_VERSION=${5-latest}
13
+ SKIP_DB_CREATE=${6-false}
14
+
15
+ WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
16
+ WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}
17
+
18
+ download() {
19
+ if [ `which curl` ]; then
20
+ curl -s "$1" > "$2";
21
+ elif [ `which wget` ]; then
22
+ wget -nv -O "$2" "$1"
23
+ fi
24
+ }
25
+
26
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then
27
+ WP_TESTS_TAG="tags/$WP_VERSION"
28
+ elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
29
+ WP_TESTS_TAG="trunk"
30
+ else
31
+ # http serves a single offer, whereas https serves multiple. we only want one
32
+ download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
33
+ grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
34
+ LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
35
+ if [[ -z "$LATEST_VERSION" ]]; then
36
+ echo "Latest WordPress version could not be found"
37
+ exit 1
38
+ fi
39
+ WP_TESTS_TAG="tags/$LATEST_VERSION"
40
+ fi
41
+
42
+ set -ex
43
+
44
+ install_wp() {
45
+
46
+ if [ -d $WP_CORE_DIR ]; then
47
+ return;
48
+ fi
49
+
50
+ mkdir -p $WP_CORE_DIR
51
+
52
+ if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
53
+ mkdir -p /tmp/wordpress-nightly
54
+ download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip
55
+ unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/
56
+ mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR
57
+ else
58
+ if [ $WP_VERSION == 'latest' ]; then
59
+ local ARCHIVE_NAME='latest'
60
+ else
61
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
62
+ fi
63
+ download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz
64
+ tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
65
+ fi
66
+
67
+ download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
68
+ }
69
+
70
+ install_test_suite() {
71
+ # portable in-place argument for both GNU sed and Mac OSX sed
72
+ if [[ $(uname -s) == 'Darwin' ]]; then
73
+ local ioption='-i .bak'
74
+ else
75
+ local ioption='-i'
76
+ fi
77
+
78
+ # set up testing suite if it doesn't yet exist
79
+ if [ ! -d $WP_TESTS_DIR ]; then
80
+ # set up testing suite
81
+ mkdir -p $WP_TESTS_DIR
82
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
83
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
84
+ fi
85
+
86
+ if [ ! -f wp-tests-config.php ]; then
87
+ download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
88
+ # remove all forward slashes in the end
89
+ WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
90
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
91
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
92
+ sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
93
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
94
+ sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
95
+ fi
96
+
97
+ }
98
+
99
+ install_db() {
100
+
101
+ if [ ${SKIP_DB_CREATE} = "true" ]; then
102
+ return 0
103
+ fi
104
+
105
+ # parse DB_HOST for port or socket references
106
+ local PARTS=(${DB_HOST//\:/ })
107
+ local DB_HOSTNAME=${PARTS[0]};
108
+ local DB_SOCK_OR_PORT=${PARTS[1]};
109
+ local EXTRA=""
110
+
111
+ if ! [ -z $DB_HOSTNAME ] ; then
112
+ if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
113
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
114
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
115
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
116
+ elif ! [ -z $DB_HOSTNAME ] ; then
117
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
118
+ fi
119
+ fi
120
+
121
+ # create database
122
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
123
+ }
124
+
125
+ install_wp
126
+ install_test_suite
127
+ install_db
binaries/cwebp-fbsd ADDED
Binary file
binaries/cwebp-linux ADDED
Binary file
binaries/cwebp-mac12 ADDED
Binary file
binaries/cwebp-mac9 ADDED
Binary file
binaries/cwebp-sol ADDED
Binary file
binaries/cwebp.exe ADDED
Binary file
binaries/gifsicle-fbsd ADDED
Binary file
binaries/gifsicle-linux ADDED
Binary file
binaries/gifsicle-mac ADDED
Binary file
binaries/gifsicle-sol ADDED
Binary file
binaries/gifsicle.exe ADDED
Binary file
binaries/jpegtran-fbsd ADDED
Binary file
binaries/jpegtran-linux ADDED
Binary file
binaries/jpegtran-mac ADDED
Binary file
binaries/jpegtran-sol ADDED
Binary file
binaries/jpegtran.exe ADDED
Binary file
binaries/optipng-fbsd ADDED
Binary file
binaries/optipng-linux ADDED
Binary file
binaries/optipng-mac ADDED
Binary file
binaries/optipng-sol ADDED
Binary file
binaries/optipng.exe ADDED
Binary file
binaries/pngquant-fbsd ADDED
Binary file
binaries/pngquant-linux ADDED
Binary file
binaries/pngquant-mac ADDED
Binary file
binaries/pngquant-sol ADDED
Binary file
binaries/pngquant.exe ADDED
Binary file
bulk.php ADDED
@@ -0,0 +1,1464 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Functions for performing Bulk Optimizations
4
+ * This file contains functions for the main bulk optimize page.
5
+ *
6
+ * @link https://ewww.io
7
+ * @package EWWW_Image_Optimizer
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Presents the bulk optimize form and optimization results table.
16
+ */
17
+ function ewww_image_optimizer_bulk_preview() {
18
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
19
+ // Retrieve the attachment IDs that were pre-loaded in the database.
20
+ echo '<div class="wrap"><h1>' . esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ) . '</h1>';
21
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_auto' ) ) {
22
+ echo '<div class="error"><p>';
23
+ esc_html_e( 'Please disable Scheduled optimization before continuing.', 'ewww-image-optimizer' );
24
+ echo '</p></div></div>';
25
+ return;
26
+ }
27
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
28
+ echo '<span><a id="ewww-bulk-credits-available" target="_blank" class="page-title-action" style="float:right;" href="https://ewww.io/my-account/">' . esc_html__( 'Image credits available:', 'ewww-image-optimizer' ) . ' ' . ewww_image_optimizer_cloud_quota() . '</a></span>';
29
+ }
30
+ // Retrieve the value of the 'bulk resume' option and set the button text for the form to use.
31
+ $resume = get_option( 'ewww_image_optimizer_bulk_resume' );
32
+ if ( empty( $resume ) ) {
33
+ $fullsize_count = ewww_image_optimizer_count_optimized( 'media' );
34
+ $button_text = esc_attr__( 'Start optimizing', 'ewww-image-optimizer' );
35
+ } elseif ( 'scanning' == $resume ) {
36
+ $fullsize_count = ewww_image_optimizer_count_optimized( 'media' );
37
+ $button_text = esc_attr__( 'Start optimizing', 'ewww-image-optimizer' );
38
+ } else {
39
+ $fullsize_count = ewww_image_optimizer_aux_images_table_count_pending();
40
+ $button_text = esc_attr__( 'Resume previous optimization', 'ewww-image-optimizer' );
41
+ }
42
+ // Create the html for the bulk optimize form and status divs.
43
+ ewww_image_optimizer_bulk_head_output();
44
+ echo '<div id="ewww-bulk-forms">';
45
+ if ( $fullsize_count < 1 ) {
46
+ echo '<p>' . esc_html__( 'You do not appear to have uploaded any images yet.', 'ewww-image-optimizer' ) . '</p>';
47
+ } else {
48
+ if ( 'true' == $resume ) {
49
+ /* translators: %d: number of images */
50
+ echo '<p class="ewww-media-info ewww-bulk-info">' . sprintf( esc_html( _n( 'There is %d image ready to optimize.', 'There are %d images ready to optimize.', $fullsize_count, 'ewww-image-optimizer' ) ), $fullsize_count ) . '</p>';
51
+ } else {
52
+ /* translators: %d: number of images */
53
+ echo '<p class="ewww-media-info ewww-bulk-info">' . sprintf( esc_html( _n( '%1$d image in the Media Library has been selected.', '%1$d images in the Media Library have been selected.', $fullsize_count, 'ewww-image-optimizer' ) ), $fullsize_count ) . '<br />' .
54
+ esc_html__( 'The active theme, BuddyPress, WP Symposium, and folders that you have configured will also be scanned for unoptimized images.', 'ewww-image-optimizer' ) . '</p>';
55
+ }
56
+ ewww_image_optimizer_bulk_action_output( $button_text, $fullsize_count, $resume );
57
+ }
58
+ // If the 'bulk resume' option was not empty, offer to reset it so the user can start back from the beginning.
59
+ if ( 'true' == $resume ) {
60
+ ewww_image_optimizer_bulk_reset_form_output();
61
+ }
62
+ echo '</div>';
63
+ ewwwio_memory( __FUNCTION__ );
64
+ ewww_image_optimizer_aux_images();
65
+ }
66
+
67
+ /**
68
+ * Outputs the status area and delay/force controls for the Bulk optimize page.
69
+ */
70
+ function ewww_image_optimizer_bulk_head_output() {
71
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
72
+ $delay = ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) ? (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) : 0;
73
+ ?>
74
+ <div id="ewww-bulk-loading">
75
+ <p id="ewww-loading" class="ewww-bulk-info" style="display:none"><?php esc_html_e( 'Importing', 'ewww-image-optimizer' ); ?>&nbsp;<img src='<?php echo $loading_image; ?>' /></p>
76
+ </div>
77
+ <div id="ewww-bulk-progressbar"></div>
78
+ <div id="ewww-bulk-timer" style="float:right;"></div>
79
+ <div id="ewww-bulk-counter"></div>
80
+ <form id="ewww-bulk-stop" style="display:none;" method="post" action="">
81
+ <br /><input type="submit" class="button-secondary action" value="<?php esc_attr_e( 'Stop Optimizing', 'ewww-image-optimizer' ); ?>" />
82
+ </form>
83
+ <div id="ewww-bulk-widgets" class="metabox-holder" style="display:none">
84
+ <div class="meta-box-sortables">
85
+ <div id="ewww-bulk-last" class="postbox">
86
+ <button type="button" class="handlediv button-link" aria-expanded="true">
87
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ); ?></span>
88
+ <span class="toggle-indicator" aria-hidden="true"></span>
89
+ </button>
90
+ <h2 class="hndle"><span><?php esc_html_e( 'Last Batch Optimized', 'ewww-image-optimizer' ); ?></span></h2>
91
+ <div class="inside"></div>
92
+ </div>
93
+ </div>
94
+ <div class="meta-box-sortables">
95
+ <div id="ewww-bulk-status" class="postbox">
96
+ <button type="button" class="handlediv button-link" aria-expanded="true">
97
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ); ?></span>
98
+ <span class="toggle-indicator" aria-hidden="true"></span>
99
+ </button>
100
+ <h2 class="hndle"><span><?php esc_html_e( 'Optimization Log', 'ewww-image-optimizer' ); ?></span></h2>
101
+ <div class="inside"></div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ <form class="ewww-bulk-form">
106
+ <p><?php esc_html_e( 'Previously optimized images will be skipped by default.', 'ewww-image-optimizer' ); ?></p>
107
+ <p><label for="ewww-force" style="font-weight: bold"><?php esc_html_e( 'Force re-optimize', 'ewww-image-optimizer' ); ?></label>&emsp;<input type="checkbox" id="ewww-force" name="ewww-force"></p>
108
+ <p><label for="ewww-delay" style="font-weight: bold"><?php esc_html_e( 'Choose how long to pause between images (in seconds, 0 = disabled)', 'ewww-image-optimizer' ); ?></label>&emsp;<input type="text" id="ewww-delay" name="ewww-delay" value="<?php echo $delay; ?>"></p>
109
+ <div id="ewww-delay-slider" style="width:50%"></div>
110
+ </form>
111
+ <?php
112
+ }
113
+
114
+ /**
115
+ * Outputs the buttons and scanner status html for the Bulk optimize page.
116
+ *
117
+ * @param string $button_text Value for the button that starts the optimization (after scanning).
118
+ * @param int $fullsize_count The total number of images that need to be scanned.
119
+ * @param string $resume Optional. If a bulk operation was interrupted, indicates in which phase it
120
+ * was operating. Accepts 'true', 'scanning', or ''.
121
+ */
122
+ function ewww_image_optimizer_bulk_action_output( $button_text, $fullsize_count, $resume = '' ) {
123
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
124
+ /* translators: %d: number of images */
125
+ $scanning_starter_message = sprintf( esc_html__( 'Stage 1, %d images left to scan.', 'ewww-image-optimizer' ), $fullsize_count );
126
+ ?>
127
+ <p id="ewww-nothing" class="ewww-bulk-info" style="display:none"><?php echo esc_html_e( 'There are no images to optimize.', 'ewww-image-optimizer' ); ?></p>
128
+ <p id="ewww-scanning" class="ewww-bulk-info" style="display:none"><?php echo $scanning_starter_message; ?>&nbsp;<img src='<?php echo $loading_image; ?>' alt='loading'/></p>
129
+ <form id="ewww-aux-start" class="ewww-bulk-form" <?php if ( 'true' == $resume ) { ?>style="display:none" <?php } ?>method="post" action="">
130
+ <input id="ewww-aux-first" type="submit" class="button-primary action" value="<?php esc_attr_e( 'Scan for unoptimized images', 'ewww-image-optimizer' ); ?>" />
131
+ <input id="ewww-aux-again" type="submit" class="button-secondary action" style="display:none" value="<?php esc_attr_e( 'Scan Again', 'ewww-image-optimizer' ); ?>" />
132
+ </form>
133
+ <form id="ewww-bulk-start" class="ewww-bulk-form" <?php if ( 'true' != $resume ) { ?>style="display:none" <?php } ?>method="post" action="">
134
+ <input id="ewww-aux-first" type="submit" class="button-primary action" value="<?php echo $button_text; ?>" />
135
+ </form>
136
+ <?php
137
+ }
138
+
139
+ /**
140
+ * Outputs the Reset form on the Bulk optimize page.
141
+ */
142
+ function ewww_image_optimizer_bulk_reset_form_output() {
143
+ ?>
144
+ <p class="ewww-media-info ewww-bulk-info"><?php esc_html_e( 'If you would like to start over again, press the Reset Status button to reset the bulk operation status.', 'ewww-image-optimizer' ); ?></p>
145
+ <form class="ewww-bulk-form" method="post" action="">
146
+ <?php wp_nonce_field( 'ewww-image-optimizer-bulk-reset', 'ewww_wpnonce' ); ?>
147
+ <input type="hidden" name="ewww_reset" value="1">
148
+ <button id="ewww-bulk-reset" type="submit" class="button-secondary action"><?php esc_html_e( 'Reset Status', 'ewww-image-optimizer' ); ?></button>
149
+ </form>
150
+ <?php
151
+ }
152
+
153
+ /**
154
+ * Detect the current memory limit and reduce the query limit appropriately.
155
+ *
156
+ * @param int $max_query The default number of records to query in large batches.
157
+ * @return int The adjusted level based on the memory limit
158
+ */
159
+ function ewww_image_optimizer_reduce_query_count( $max_query ) {
160
+ $memory_limit = ewwwio_memory_limit();
161
+ if ( $memory_limit <= 33560000 ) {
162
+ return 500;
163
+ } elseif ( $memory_limit <= 67120000 ) {
164
+ return 1000;
165
+ } elseif ( $memory_limit <= 134300000 ) {
166
+ return 1500;
167
+ } elseif ( $memory_limit <= 268500000 ) {
168
+ return 3000;
169
+ }
170
+ return $max_query;
171
+ }
172
+
173
+ /**
174
+ * Retrieve image counts for the bulk process.
175
+ *
176
+ * For the media library, returns a simple count of the number of attachments. For other galleries,
177
+ * counts the number of thumbnails/resizes along with how many of each need to be optimized. Uses
178
+ * attachment "metadata" to calculate the counts, which will not be accurate for long.
179
+ *
180
+ * @param string $gallery Bulk page that is calling the function. Accepts 'media', 'ngg', and 'flag'.
181
+ * @return int|array {
182
+ * The image count(s) found during the search.
183
+ *
184
+ * @int int $full_count The number of original uploads found.
185
+ * @int int $unoptimized_full The number of original uploads that have not been optimized.
186
+ * @int int $resize_count The number of thumbnails/resizes found.
187
+ * @int int $unoptimized_re The number of resizes that are not optimized.
188
+ * }
189
+ */
190
+ function ewww_image_optimizer_count_optimized( $gallery ) {
191
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
192
+ global $wpdb;
193
+ $full_count = 0;
194
+ $unoptimized_full = 0;
195
+ $unoptimized_re = 0;
196
+ $resize_count = 0;
197
+ $attachment_query = '';
198
+ ewwwio_debug_message( "scanning for $gallery" );
199
+ // Retrieve the time when the counting starts.
200
+ $started = microtime( true );
201
+ $max_query = apply_filters( 'ewww_image_optimizer_count_optimized_queries', 4000 );
202
+ $max_query = (int) $max_query;
203
+ $attachment_query_count = 0;
204
+ switch ( $gallery ) {
205
+ case 'media':
206
+ $ids = array();
207
+ $resume = get_option( 'ewww_image_optimizer_bulk_resume' );
208
+ // See if we were given attachment IDs to work with via GET/POST.
209
+ if ( ! empty( $_REQUEST['ids'] ) || $resume ) {
210
+ ewwwio_debug_message( 'we have received attachment ids via $_REQUEST' );
211
+ // Retrieve the attachment IDs that were pre-loaded in the database.
212
+ if ( 'scanning' == $resume ) {
213
+ $finished = (array) get_option( 'ewww_image_optimizer_bulk_attachments' );
214
+ $remaining = (array) get_option( 'ewww_image_optimizer_scanning_attachments' );
215
+ $attachment_ids = array_merge( $finished, $remaining );
216
+ } elseif ( $resume ) {
217
+ // This shouldn't ever happen, but doesn't hurt to account for the use case, just in case something changes in the future.
218
+ $attachment_ids = get_option( 'ewww_image_optimizer_bulk_attachments' );
219
+ } else {
220
+ $attachment_ids = get_option( 'ewww_image_optimizer_scanning_attachments' );
221
+ }
222
+ if ( ! empty( $attachment_ids ) ) {
223
+ $full_count = count( $attachment_ids );
224
+ }
225
+ } else {
226
+ $full_count = $wpdb->get_var( "SELECT COUNT(ID) FROM $wpdb->posts WHERE (post_type = 'attachment' OR post_type = 'ims_image') AND (post_mime_type LIKE '%%image%%' OR post_mime_type LIKE '%%pdf%%')" );
227
+ }
228
+ return $full_count;
229
+ break;
230
+ case 'ngg':
231
+ // See if we were given attachment IDs to work with via GET/POST.
232
+ if ( ! empty( $_REQUEST['ewww_inline'] ) || get_option( 'ewww_image_optimizer_bulk_ngg_resume' ) ) {
233
+ // Retrieve the attachment IDs that were pre-loaded in the database.
234
+ $attachment_ids = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
235
+ array_walk( $attachment_ids, 'intval' );
236
+ while ( $attachment_ids && $attachment_query_count < $max_query ) {
237
+ $attachment_query .= "'" . array_pop( $attachment_ids ) . "',";
238
+ $attachment_query_count++;
239
+ }
240
+ $attachment_query = 'WHERE pid IN (' . substr( $attachment_query, 0, -1 ) . ')';
241
+ }
242
+ // Creating the 'registry' object for working with nextgen.
243
+ $registry = C_Component_Registry::get_instance();
244
+ // Creating a database storage object from the 'registry' object.
245
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
246
+ // Get an array of sizes available for the $image.
247
+ $sizes = $storage->get_image_sizes();
248
+ $offset = 0;
249
+ while ( $attachments = $wpdb->get_col( "SELECT meta_data FROM $wpdb->nggpictures $attachment_query LIMIT $offset, $max_query" ) ) { // WPCS: unprepared SQL ok.
250
+ foreach ( $attachments as $attachment ) {
251
+ if ( class_exists( 'Ngg_Serializable' ) ) {
252
+ $serializer = new Ngg_Serializable();
253
+ $meta = $serializer->unserialize( $attachment );
254
+ } else {
255
+ $meta = unserialize( $attachment );
256
+ }
257
+ if ( ! is_array( $meta ) ) {
258
+ continue;
259
+ }
260
+ if ( empty( $meta['ewww_image_optimizer'] ) ) {
261
+ $unoptimized_full++;
262
+ }
263
+ if ( ewww_image_optimizer_iterable( $sizes ) ) {
264
+ foreach ( $sizes as $size ) {
265
+ if ( 'full' !== $size ) {
266
+ $resize_count++;
267
+ if ( empty( $meta[ $size ]['ewww_image_optimizer'] ) ) {
268
+ $unoptimized_re++;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+ $full_count += count( $attachments );
275
+ $offset += $max_query;
276
+ if ( ! empty( $attachment_ids ) ) {
277
+ $attachment_query = '';
278
+ $attachment_query_count = 0;
279
+ $offset = 0;
280
+ while ( $attachment_ids && $attachment_query_count < $max_query ) {
281
+ $attachment_query .= "'" . array_pop( $attachment_ids ) . "',";
282
+ $attachment_query_count++;
283
+ }
284
+ $attachment_query = 'WHERE pid IN (' . substr( $attachment_query, 0, -1 ) . ')';
285
+ }
286
+ } // End while().
287
+ break;
288
+ case 'flag':
289
+ if ( ! empty( $_REQUEST['doaction'] ) || get_option( 'ewww_image_optimizer_bulk_flag_resume' ) ) {
290
+ // Retrieve the attachment IDs that were pre-loaded in the database.
291
+ $attachment_ids = get_option( 'ewww_image_optimizer_bulk_flag_attachments' );
292
+ array_walk( $attachment_ids, 'intval' );
293
+ while ( $attachment_ids && $attachment_query_count < $max_query ) {
294
+ $attachment_query .= "'" . array_pop( $attachment_ids ) . "',";
295
+ $attachment_query_count++;
296
+ }
297
+ $attachment_query = 'WHERE pid IN (' . substr( $attachment_query, 0, -1 ) . ')';
298
+ }
299
+ $offset = 0;
300
+ while ( $attachments = $wpdb->get_col( "SELECT meta_data FROM $wpdb->flagpictures $attachment_query LIMIT $offset, $max_query" ) ) { // WPCS: unprepared SQL ok.
301
+ foreach ( $attachments as $attachment ) {
302
+ $meta = unserialize( $attachment );
303
+ if ( ! is_array( $meta ) ) {
304
+ continue;
305
+ }
306
+ if ( empty( $meta['ewww_image_optimizer'] ) ) {
307
+ $unoptimized_full++;
308
+ }
309
+ if ( ! empty( $meta['webview'] ) ) {
310
+ $resize_count++;
311
+ if ( empty( $meta['webview']['ewww_image_optimizer'] ) ) {
312
+ $unoptimized_re++;
313
+ }
314
+ }
315
+ if ( ! empty( $meta['thumbnail'] ) ) {
316
+ $resize_count++;
317
+ if ( empty( $meta['thumbnail']['ewww_image_optimizer'] ) ) {
318
+ $unoptimized_re++;
319
+ }
320
+ }
321
+ }
322
+ $full_count += count( $attachments );
323
+ $offset += $max_query;
324
+ if ( ! empty( $attachment_ids ) ) {
325
+ $attachment_query = '';
326
+ $attachment_query_count = 0;
327
+ $offset = 0;
328
+ while ( $attachment_ids && $attachment_query_count < $max_query ) {
329
+ $attachment_query .= "'" . array_pop( $attachment_ids ) . "',";
330
+ $attachment_query_count++;
331
+ }
332
+ $attachment_query = 'WHERE pid IN (' . substr( $attachment_query, 0, -1 ) . ')';
333
+ }
334
+ }
335
+ break;
336
+ } // End switch().
337
+ if ( empty( $full_count ) && ! empty( $attachment_ids ) ) {
338
+ ewwwio_debug_message( 'query appears to have failed, just counting total images instead' );
339
+ $full_count = count( $attachment_ids );
340
+ }
341
+ $elapsed = microtime( true ) - $started;
342
+ ewwwio_debug_message( "counting images took $elapsed seconds" );
343
+ ewwwio_debug_message( "found $full_count fullsize ($unoptimized_full unoptimized), and $resize_count resizes ($unoptimized_re unoptimized)" );
344
+ ewwwio_memory( __FUNCTION__ );
345
+ return array( $full_count, $unoptimized_full, $resize_count, $unoptimized_re );
346
+ }
347
+
348
+ /**
349
+ * Prepares the bulk operation and includes the javascript functions.
350
+ *
351
+ * Checks to see if a scan was in progress, or if attachment IDs were POSTed, and loads the
352
+ * appropriate attachments into the list to be scanned. Also sets up the js includes, and
353
+ * defines a few js variables needed for the bulk operation.
354
+ *
355
+ * @global object $wpdb
356
+ *
357
+ * @param string $hook An indicator if this was not called from AJAX, like WP-CLI.
358
+ */
359
+ function ewww_image_optimizer_bulk_script( $hook ) {
360
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
361
+ // Make sure we are being called from the bulk optimization page.
362
+ if ( 'media_page_ewww-image-optimizer-bulk' != $hook ) {
363
+ return;
364
+ }
365
+ // Initialize the $attachments variable.
366
+ $attachments = array();
367
+ // Check to see if we are supposed to reset the bulk operation and verify we are authorized to do so.
368
+ if ( ! empty( $_REQUEST['ewww_reset'] ) && wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk-reset' ) ) {
369
+ // Set the 'bulk resume' option to an empty string to reset the bulk operation.
370
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
371
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
372
+ update_option( 'ewww_image_optimizer_scanning_attachments', '', false );
373
+ update_option( 'ewww_image_optimizer_bulk_attachments', '', false );
374
+ ewww_image_optimizer_delete_pending();
375
+ }
376
+ global $wpdb;
377
+ // Check to see if we are supposed to reset the bulk operation and verify we are authorized to do so.
378
+ if ( ! empty( $_REQUEST['ewww_reset_aux'] ) && wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-aux-images-reset' ) ) {
379
+ // Set the 'aux resume' option to an empty string to reset the bulk operation.
380
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
381
+ $wpdb->query( "DELETE from $wpdb->ewwwio_images WHERE image_size IS NULL" );
382
+ }
383
+ // Check to see if we are supposed to convert the auxiliary images table and verify we are authorized to do so.
384
+ if ( ! empty( $_REQUEST['ewww_convert'] ) && wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-aux-images-convert' ) ) {
385
+ ewww_image_optimizer_aux_images_convert();
386
+ }
387
+ // Check the 'bulk resume' option.
388
+ $resume = get_option( 'ewww_image_optimizer_bulk_resume' );
389
+ $scanning = get_option( 'ewww_image_optimizer_aux_resume' );
390
+ if ( ! $resume && ! $scanning ) {
391
+ update_option( 'ewww_image_optimizer_scanning_attachments', '', false );
392
+ update_option( 'ewww_image_optimizer_bulk_attachments', '', false );
393
+ ewww_image_optimizer_delete_pending();
394
+ }
395
+ // See if we were given attachment IDs to work with via GET/POST.
396
+ $ids = array();
397
+ if ( ! empty( $_REQUEST['ids'] ) && ( preg_match( '/^[\d,]+$/', $_REQUEST['ids'], $request_ids ) || is_numeric( $_REQUEST['ids'] ) ) ) {
398
+ ewww_image_optimizer_delete_pending();
399
+ set_transient( 'ewww_image_optimizer_skip_aux', true, 3 * MINUTE_IN_SECONDS );
400
+ if ( is_numeric( $_REQUEST['ids'] ) ) {
401
+ $ids[] = (int) $_REQUEST['ids'];
402
+ } else {
403
+ $ids = explode( ',', $request_ids[0] );
404
+ array_walk( $ids, 'intval' );
405
+ }
406
+ $sample_post_type = get_post_type( $ids[0] );
407
+ // ewwwio_debug_message( "ids: " . $request_ids[0] ); // keeping just in case.
408
+ ewwwio_debug_message( "post type (checking for ims_gallery): $sample_post_type" );
409
+ if ( 'ims_gallery' == $sample_post_type ) {
410
+ $attachments = array();
411
+ foreach ( $ids as $gid ) {
412
+ ewwwio_debug_message( "gallery id: $gid" );
413
+ $ims_images = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'ims_image' AND post_mime_type LIKE '%%image%%' AND post_parent = %d ORDER BY ID DESC", $gid ) );
414
+ $attachments = array_merge( $attachments, $ims_images );
415
+ }
416
+ } else {
417
+ ewwwio_debug_message( "validating requested ids: {$request_ids[0]}" );
418
+ // Retrieve post IDs correlating to the IDs submitted to make sure they are all valid.
419
+ $attachments = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE (post_type = 'attachment' OR post_type = 'ims_image') AND (post_mime_type LIKE '%%image%%' OR post_mime_type LIKE '%%pdf%%') AND ID IN ({$request_ids[0]}) ORDER BY ID DESC" ); // WPCS: unprepared SQL ok.
420
+ }
421
+ // Unset the 'bulk resume' option since we were given specific IDs to optimize.
422
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
423
+ // Check if there is a previous bulk operation to resume.
424
+ } elseif ( 'scanning' == $resume ) {
425
+ // Retrieve the attachment IDs that have not been finished from the 'scanning attachments' option.
426
+ $attachments = get_option( 'ewww_image_optimizer_scanning_attachments' );
427
+ } elseif ( $scanning || $resume ) {
428
+ $attachments = array();
429
+ // Since we aren't resuming, and weren't given a list of IDs, we will optimize everything.
430
+ } elseif ( empty( $attachments ) ) {
431
+ delete_transient( 'ewww_image_optimizer_scan_aux' );
432
+ // Load up all the image attachments we can find.
433
+ $attachments = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE (post_type = 'attachment' OR post_type = 'ims_image') AND (post_mime_type LIKE '%%image%%' OR post_mime_type LIKE '%%pdf%%') ORDER BY ID DESC" );
434
+ } // End if().
435
+ // Store the attachment IDs we retrieved in the 'bulk_attachments' option so we can keep track of our progress in the database.
436
+ update_option( 'ewww_image_optimizer_scanning_attachments', $attachments, false );
437
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', __FILE__ ), array( 'jquery', 'jquery-ui-slider', 'jquery-ui-progressbar', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
438
+ // Number of images in the ewwwio_table (previously optimized images).
439
+ $image_count = ewww_image_optimizer_aux_images_table_count();
440
+ // Number of image attachments to be optimized.
441
+ $attachment_count = count( $attachments );
442
+ // Submit a couple variables for our javascript to work with.
443
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
444
+ wp_localize_script('ewwwbulkscript', 'ewww_vars', array(
445
+ '_wpnonce' => wp_create_nonce( 'ewww-image-optimizer-bulk' ),
446
+ 'attachments' => ewww_image_optimizer_aux_images_table_count_pending(),
447
+ 'image_count' => $image_count,
448
+ /* translators: %d: number of images */
449
+ 'count_string' => sprintf( esc_html__( '%d images', 'ewww-image-optimizer' ), $image_count ),
450
+ 'scan_fail' => esc_html__( 'Operation timed out, you may need to increase the max_execution_time for PHP', 'ewww-image-optimizer' ),
451
+ 'scan_incomplete' => esc_html__( 'Scan did not complete, will try again', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' />",
452
+ 'operation_stopped' => esc_html__( 'Optimization stopped, reload page to resume.', 'ewww-image-optimizer' ),
453
+ 'operation_interrupted' => esc_html__( 'Operation Interrupted', 'ewww-image-optimizer' ),
454
+ 'temporary_failure' => esc_html__( 'Temporary failure, seconds left to retry:', 'ewww-image-optimizer' ),
455
+ 'invalid_response' => esc_html__( 'Received an invalid response from your website, please check for errors in the Developer Tools console of your browser.', 'ewww-image-optimizer' ),
456
+ 'bad_attachment' => esc_html__( 'Previous failure due to broken/missing metadata, skipped resizes for attachment:', 'ewww-image-optimizer' ),
457
+ 'remove_failed' => esc_html__( 'Could not remove image from table.', 'ewww-image-optimizer' ),
458
+ /* translators: used for Bulk Optimize progress bar, like so: Optimized 32/346 */
459
+ 'optimized' => esc_html__( 'Optimized', 'ewww-image-optimizer' ),
460
+ 'last_image_header' => esc_html__( 'Last Image Optimized', 'ewww-image-optimizer' ),
461
+ 'time_remaining' => esc_html__( 'remaining', 'ewww-image-optimizer' ),
462
+ 'original_restored' => esc_html__( 'Original Restored', 'ewww-image-optimizer' ),
463
+ 'restoring' => '<p>' . esc_html__( 'Restoring', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>",
464
+ )
465
+ );
466
+ // Load the stylesheet for the jquery progressbar.
467
+ wp_enqueue_style( 'jquery-ui-progressbar', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', __FILE__ ) );
468
+ ewwwio_memory( __FUNCTION__ );
469
+ }
470
+
471
+ /**
472
+ * Loads the list of optimized images into memory.
473
+ *
474
+ * Pulls a list of all optimized images from the database, and stores it globally unless there is
475
+ * a memory constraint, or the list of images is too large to be efficient.
476
+ *
477
+ * @global string|array $optimized_list A list of all images that have been optimized, or a string
478
+ * indicating why that is not a good idea.
479
+ * @global object $wpdb
480
+ */
481
+ function ewww_image_optimizer_optimized_list() {
482
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
483
+ // Retrieve the time when the list building starts.
484
+ $started = microtime( true );
485
+ global $optimized_list;
486
+ global $wpdb;
487
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
488
+ ewww_image_optimizer_db_init();
489
+ global $ewwwdb;
490
+ } else {
491
+ $ewwwdb = $wpdb;
492
+ }
493
+ $offset = 0;
494
+ $max_query = (int) apply_filters( 'ewww_image_optimizer_count_optimized_queries', 4000 );
495
+ $optimized_list = array();
496
+ if ( get_transient( 'ewww_image_optimizer_low_memory_mode' ) ) {
497
+ $optimized_list = get_transient( 'ewww_image_optimizer_low_memory_mode' );
498
+ return;
499
+ }
500
+ $starting_memory_usage = memory_get_usage( true );
501
+ while ( $already_optimized = $ewwwdb->get_results( "SELECT id,path,image_size,pending,attachment_id,updated FROM $ewwwdb->ewwwio_images LIMIT $offset,$max_query", ARRAY_A ) ) {
502
+ $ewwwdb->flush();
503
+ foreach ( $already_optimized as $optimized ) {
504
+ $optimized_path = ewww_image_optimizer_relative_path_replace( $optimized['path'] );
505
+ // Check for duplicate records.
506
+ if ( ! empty( $optimized_list[ $optimized_path ] ) && ! empty( $optimized_list[ $optimized_path ]['id'] ) ) {
507
+ $optimized = ewww_image_optimizer_remove_duplicate_records( array( $optimized_list[ $optimized_path ]['id'], $optimized['id'] ) );
508
+ }
509
+ $optimized_list[ $optimized_path ]['image_size'] = $optimized['image_size'];
510
+ $optimized_list[ $optimized_path ]['id'] = $optimized['id'];
511
+ $optimized_list[ $optimized_path ]['pending'] = $optimized['pending'];
512
+ $optimized_list[ $optimized_path ]['attachment_id'] = $optimized['attachment_id'];
513
+ $optimized_list[ $optimized_path ]['updated'] = $optimized['updated'];
514
+ }
515
+ ewwwio_memory( 'removed original records' );
516
+ $offset += $max_query;
517
+ if ( empty( $estimated_batch_memory ) ) {
518
+ $estimated_batch_memory = memory_get_usage( true ) - $starting_memory_usage;
519
+ if ( ! $estimated_batch_memory ) { // If the memory did not appear to increase, set it to a safe default.
520
+ $estimated_batch_memory = 3146000;
521
+ }
522
+ ewwwio_debug_message( "estimated batch memory is $estimated_batch_memory" );
523
+ }
524
+ if ( ! ewwwio_check_memory_available( 3146000 + $estimated_batch_memory ) ) { // Initial batch storage used + 3MB.
525
+ $optimized_list = 'low_memory';
526
+ set_transient( 'ewww_image_optimizer_low_memory_mode', 'low_memory', 600 ); // Put it in low memory mode for at least 10 minutes so we don't abuse the db server with extra requests.
527
+ return;
528
+ }
529
+ $elapsed = microtime( true ) - $started;
530
+ ewwwio_debug_message( "loading optimized list took $elapsed seconds so far" );
531
+ if ( $elapsed > 9 ) {
532
+ ewwwio_debug_message( 'loading optimized list took too long' );
533
+ $optimized_list = 'large_list';
534
+ set_transient( 'ewww_image_optimizer_low_memory_mode', 'large_list', 600 ); // Use low memory mode so that we don't waste lots of time pulling a huge list of images repeatedly.
535
+ return;
536
+ }
537
+ } // End while().
538
+ }
539
+
540
+ /**
541
+ * Retrieves a selected set of attachment metadata from the postmeta table.
542
+ *
543
+ * @global object $wpdb
544
+ *
545
+ * @param string $attachments_in A comma-imploded array containing a list of attachment IDs.
546
+ * @return array Multi-dimensional array containing all the postmeta and mime-types for the IDs
547
+ * of $attachments_in.
548
+ */
549
+ function ewww_image_optimizer_fetch_metadata_batch( $attachments_in ) {
550
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
551
+ if ( ! preg_match( "/^[\d,']+$/", $attachments_in ) ) {
552
+ ewwwio_debug_message( 'invalid attachments string' );
553
+ return array();
554
+ }
555
+ global $wpdb;
556
+ // Retrieve image attachment metadata from the database (in batches).
557
+ $attachments = $wpdb->get_results( "SELECT metas.post_id,metas.meta_key,metas.meta_value,posts.post_mime_type FROM $wpdb->postmeta metas INNER JOIN $wpdb->posts posts ON posts.ID = metas.post_id WHERE (posts.post_mime_type LIKE '%%image%%' OR posts.post_mime_type LIKE '%%pdf%%') AND metas.post_id IN ($attachments_in)", ARRAY_A ); // WPCS: unprepared SQL ok.
558
+ ewwwio_debug_message( 'fetched ' . count( $attachments ) . ' attachment meta items' );
559
+ $wpdb->flush();
560
+ $attachment_meta = array();
561
+ foreach ( $attachments as $attachment ) {
562
+ if ( '_wp_attached_file' == $attachment['meta_key'] ) {
563
+ $attachment_meta[ $attachment['post_id'] ]['_wp_attached_file'] = $attachment['meta_value'];
564
+ if ( ! empty( $attachment['post_mime_type'] ) && empty( $attachment_meta[ $attachment['post_id'] ]['type'] ) ) {
565
+ $attachment_meta[ $attachment['post_id'] ]['type'] = $attachment['post_mime_type'];
566
+ }
567
+ continue;
568
+ } elseif ( '_wp_attachment_metadata' == $attachment['meta_key'] ) {
569
+ $attachment_meta[ $attachment['post_id'] ]['meta'] = $attachment['meta_value'];
570
+ if ( ! empty( $attachment['post_mime_type'] ) && empty( $attachment_meta[ $attachment['post_id'] ]['type'] ) ) {
571
+ $attachment_meta[ $attachment['post_id'] ]['type'] = $attachment['post_mime_type'];
572
+ }
573
+ continue;
574
+ }
575
+ if ( ! empty( $attachment['post_mime_type'] ) && empty( $attachment_meta[ $attachment['post_id'] ]['type'] ) ) {
576
+ $attachment_meta[ $attachment['post_id'] ]['type'] = $attachment['post_mime_type'];
577
+ }
578
+ }
579
+ unset( $attachments );
580
+ return $attachment_meta;
581
+ }
582
+
583
+ /**
584
+ * Scans the Media Library for images that need optimizing.
585
+ *
586
+ * Searches for images using the attachment metadata and stores them in the ewwwio_images table.
587
+ * Optionally restricted to specific attachments selected by the user. If Force Re-optimize is
588
+ * checked, marks existing records as pending also.
589
+ *
590
+ * @global object $wpdb
591
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
592
+ * @global string|array $optimized_list A list of all images that have been optimized, or a string
593
+ * indicating why that is not a good idea.
594
+ *
595
+ * @param string $hook An indicator if this was not called from AJAX, like WP-CLI.
596
+ */
597
+ function ewww_image_optimizer_media_scan( $hook = '' ) {
598
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
599
+
600
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
601
+ if ( 'ewww-image-optimizer-cli' !== $hook && empty( $_REQUEST['ewww_scan'] ) ) {
602
+ ewwwio_debug_message( 'bailing no cli' );
603
+ ewww_image_optimizer_debug_log();
604
+ die( json_encode( array(
605
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
606
+ ) ) );
607
+ }
608
+ if ( ! empty( $_REQUEST['ewww_scan'] ) && ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) ) {
609
+ ewwwio_debug_message( 'bailing no nonce' );
610
+ ewww_image_optimizer_debug_log();
611
+ die( json_encode( array(
612
+ 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ),j
613
+ ) ) );
614
+ }
615
+ global $wpdb;
616
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
617
+ ewww_image_optimizer_db_init();
618
+ global $ewwwdb;
619
+ } else {
620
+ $ewwwdb = $wpdb;
621
+ }
622
+ global $optimized_list;
623
+ $image_count = 0;
624
+ $reset_count = 0;
625
+ $attachments_processed = 0;
626
+ $attachment_query = '';
627
+ $images = array();
628
+ $attachment_images = array();
629
+ $reset_images = array();
630
+ $queued_ids = array();
631
+ $field_formats = array(
632
+ '%s', // path
633
+ '%s', // gallery
634
+ '%d', // orig_size
635
+ '%d', // attachment_id
636
+ '%s', // resize
637
+ '%d', // pending.
638
+ );
639
+ ewwwio_debug_message( 'scanning for media attachments' );
640
+ update_option( 'ewww_image_optimizer_bulk_resume', 'scanning' );
641
+ set_transient( 'ewww_image_optimizer_no_scheduled_optimization', true, 30 * MINUTE_IN_SECONDS );
642
+
643
+ // Retrieve the time when the scan starts.
644
+ $started = microtime( true );
645
+ $attachment_ids = get_option( 'ewww_image_optimizer_scanning_attachments' );
646
+
647
+ if ( ! empty( $attachment_ids ) && count( $attachment_ids ) > 300 ) {
648
+ ewww_image_optimizer_debug_log();
649
+ ewww_image_optimizer_optimized_list();
650
+ } elseif ( ! empty( $attachment_ids ) ) {
651
+ $optimized_list = 'small_scan';
652
+ }
653
+ ewww_image_optimizer_debug_log();
654
+
655
+ list( $bad_attachments, $bad_attachment ) = ewww_image_optimizer_get_bad_attachments();
656
+
657
+ $max_query = apply_filters( 'ewww_image_optimizer_count_optimized_queries', 4000 );
658
+ $max_query = (int) $max_query;
659
+
660
+ $attachment_ids = get_option( 'ewww_image_optimizer_scanning_attachments' );
661
+ if ( empty( $attachment_ids ) ) {
662
+ // When the media library is finished, run the aux script function to scan for additional images.
663
+ ewww_image_optimizer_aux_images_script();
664
+ }
665
+
666
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes_opt' );
667
+
668
+ $enabled_types = array();
669
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) ) {
670
+ $enabled_types[] = 'image/jpeg';
671
+ }
672
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) ) {
673
+ $enabled_types[] = 'image/png';
674
+ }
675
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) ) {
676
+ $enabled_types[] = 'image/gif';
677
+ }
678
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) ) {
679
+ $enabled_types[] = 'application/pdf';
680
+ }
681
+
682
+ ewww_image_optimizer_debug_log();
683
+ $starting_memory_usage = memory_get_usage( true );
684
+ while ( microtime( true ) - $started < apply_filters( 'ewww_image_optimizer_timeout', 22 ) && count( $attachment_ids ) ) {
685
+ ewww_image_optimizer_debug_log();
686
+ if ( ! empty( $estimated_batch_memory ) && ! ewwwio_check_memory_available( 3146000 + $estimated_batch_memory ) ) { // Initial batch storage used + 3MB.
687
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
688
+ if ( is_array( $optimized_list ) ) {
689
+ set_transient( 'ewww_image_optimizer_low_memory_mode', 'low_memory', 600 ); // Keep us in low memory mode for up to 10 minutes.
690
+ $optimized_list = 'low_memory';
691
+ }
692
+ } else {
693
+ break;
694
+ }
695
+ }
696
+ if ( ! empty( $attachment_ids ) && is_array( $attachment_ids ) ) {
697
+ $selected_ids = null;
698
+ ewwwio_debug_message( 'remaining items: ' . count( $attachment_ids ) );
699
+ // Retrieve the attachment IDs that were pre-loaded in the database.
700
+ $selected_ids = array_splice( $attachment_ids, 0, $max_query );
701
+ array_walk( $selected_ids, 'intval' );
702
+ ewwwio_debug_message( 'selected items: ' . count( $selected_ids ) );
703
+ $attachments_in = "'" . implode( "','", $selected_ids ) . "'";
704
+ } else {
705
+ ewwwio_debug_message( 'no array found' );
706
+ die( json_encode( array(
707
+ 'error' => esc_html__( 'List of attachment IDs not found.', 'ewww-image-optimizer' ),
708
+ ) ) );
709
+ }
710
+
711
+ $failsafe_selected_ids = $selected_ids;
712
+
713
+ $attachment_meta = ewww_image_optimizer_fetch_metadata_batch( $attachments_in );
714
+ $attachments_in = null;
715
+
716
+ // If we just completed the first batch, check how much the memory usage increased.
717
+ if ( empty( $estimated_batch_memory ) ) {
718
+ $estimated_batch_memory = memory_get_usage( true ) - $starting_memory_usage;
719
+ if ( ! $estimated_batch_memory ) { // If the memory did not appear to increase, set it to a safe default.
720
+ $estimated_batch_memory = 3146000;
721
+ }
722
+ ewwwio_debug_message( "estimated batch memory is $estimated_batch_memory" );
723
+ }
724
+
725
+ ewwwio_debug_message( 'validated ' . count( $attachment_meta ) . ' attachment meta items' );
726
+ ewwwio_debug_message( 'remaining items after selection: ' . count( $attachment_ids ) );
727
+ foreach ( $selected_ids as $selected_id ) {
728
+ $attachments_processed++;
729
+ if ( 0 == $attachments_processed % 5 && ( microtime( true ) - $started > apply_filters( 'ewww_image_optimizer_timeout', 22 ) || ! ewwwio_check_memory_available( 2194304 ) ) ) {
730
+ ewwwio_debug_message( 'time exceeded, or memory exceeded' );
731
+ ewww_image_optimizer_debug_log();
732
+ $attachment_ids = array_merge( $failsafe_selected_ids, $attachment_ids );
733
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
734
+ if ( is_array( $optimized_list ) ) {
735
+ set_transient( 'ewww_image_optimizer_low_memory_mode', 'low_memory', 600 ); // Keep us in low memory mode for up to 10 minutes.
736
+ $optimized_list = 'low_memory';
737
+ }
738
+ break;
739
+ } else {
740
+ break 2;
741
+ }
742
+ }
743
+ ewww_image_optimizer_debug_log();
744
+ array_shift( $failsafe_selected_ids );
745
+ clearstatcache();
746
+ $pending = false;
747
+ $remote_file = false;
748
+ if ( empty( $attachment_meta[ $selected_id ]['meta'] ) ) {
749
+ ewwwio_debug_message( "empty meta for $selected_id" );
750
+ $meta = array();
751
+ } else {
752
+ $meta = maybe_unserialize( $attachment_meta[ $selected_id ]['meta'] );
753
+ }
754
+ if ( ! empty( $attachment_meta[ $selected_id ]['type'] ) ) {
755
+ $mime = $attachment_meta[ $selected_id ]['type'];
756
+ ewwwio_debug_message( "got mime via db query: $mime" );
757
+ } elseif ( ! empty( $meta['file'] ) ) {
758
+ $mime = ewww_image_optimizer_quick_mimetype( $meta['file'] );
759
+ ewwwio_debug_message( "got quick mime via filename: $mime" );
760
+ } elseif ( ! empty( $selected_id ) ) {
761
+ $mime = get_post_mime_type( $selected_id );
762
+ ewwwio_debug_message( "checking mime via get_post_mime_type: $mime" );
763
+ }
764
+ if ( empty( $mime ) ) {
765
+ ewwwio_debug_message( "missing mime for $selected_id" );
766
+ }
767
+
768
+ ewww_image_optimizer_debug_log();
769
+ if ( 'application/pdf' != $mime // NOT a pdf...
770
+ && ! in_array( $selected_id, $bad_attachments ) // AND NOT a known broken attachment, which would mean we already tried this once before...
771
+ && ( // AND...
772
+ empty( $meta ) // metadata is empty...
773
+ || ( is_string( $meta ) && 'processing' == $meta ) // OR the string 'processing'...
774
+ || ( is_array( $meta ) && ! empty( $meta[0] ) && 'processing' == $meta[0] ) // OR array( 'processing' ).
775
+ )
776
+ ) {
777
+ // Attempt to rebuild the metadata.
778
+ ewwwio_debug_message( "attempting to rebuild attachment meta for $selected_id" );
779
+ set_transient( 'ewww_image_optimizer_rebuilding_attachment', $selected_id, 5 * MINUTE_IN_SECONDS );
780
+ ewww_image_optimizer_debug_log();
781
+ $new_meta = ewww_image_optimizer_rebuild_meta( $selected_id );
782
+ delete_transient( 'ewww_image_optimizer_rebuilding_attachment' );
783
+ if ( is_array( $new_meta ) ) {
784
+ $meta = $new_meta;
785
+ } else {
786
+ $meta = array();
787
+ }
788
+ }
789
+
790
+ if ( ! in_array( $mime, $enabled_types ) ) {
791
+ continue;
792
+ }
793
+ ewwwio_debug_message( "id: $selected_id and type: $mime" );
794
+ ewww_image_optimizer_debug_log();
795
+ $attached_file = ( ! empty( $attachment_meta[ $selected_id ]['_wp_attached_file'] ) ? $attachment_meta[ $selected_id ]['_wp_attached_file'] : '' );
796
+ list( $file_path, $upload_path ) = ewww_image_optimizer_attachment_path( $meta, $selected_id, $attached_file, false );
797
+ // Run a quick fix for as3cf files.
798
+ if ( class_exists( 'Amazon_S3_And_CloudFront' ) && strpos( $file_path, 's3' ) === 0 ) {
799
+ ewww_image_optimizer_check_table_as3cf( $meta, $selected_id, $file_path );
800
+ }
801
+ ewww_image_optimizer_debug_log();
802
+ if ( ( strpos( $file_path, 's3' ) === 0 || ! is_file( $file_path ) ) && ( class_exists( 'WindowsAzureStorageUtil' ) || class_exists( 'Amazon_S3_And_CloudFront' ) ) ) {
803
+ // Construct a $file_path and proceed IF a supported CDN plugin is installed.
804
+ ewwwio_debug_message( 'Azure or S3 detected and no local file found' );
805
+ $file_path = get_attached_file( $selected_id );
806
+ if ( strpos( $file_path, 's3' ) === 0 ) {
807
+ $file_path = get_attached_file( $selected_id, true );
808
+ }
809
+ ewwwio_debug_message( "remote file possible: $file_path" );
810
+ if ( ! $file_path ) {
811
+ ewwwio_debug_message( 'no file found on remote storage, bailing' );
812
+ continue;
813
+ }
814
+ $remote_file = true;
815
+ } elseif ( ! $file_path ) {
816
+ ewwwio_debug_message( "no file path for $selected_id" );
817
+ continue;
818
+ }
819
+ ewww_image_optimizer_debug_log();
820
+ $attachment_images['full'] = $file_path;
821
+ $retina_path = ewww_image_optimizer_hidpi_optimize( $file_path, true );
822
+ if ( $retina_path ) {
823
+ $attachment_images['full-retina'] = $retina_path;
824
+ }
825
+ ewww_image_optimizer_debug_log();
826
+ // Resized versions available, see what we can find.
827
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
828
+ // Meta sizes don't contain a full path, so we calculate one.
829
+ $base_ims_dir = trailingslashit( dirname( $file_path ) ) . '_resized/';
830
+ $base_dir = trailingslashit( dirname( $file_path ) );
831
+ // To keep track of the ones we have already processed.
832
+ $processed = array();
833
+ foreach ( $meta['sizes'] as $size => $data ) {
834
+ ewwwio_debug_message( "checking for size: $size" );
835
+ ewww_image_optimizer_debug_log();
836
+ if ( strpos( $size, 'webp' ) === 0 ) {
837
+ continue;
838
+ }
839
+ if ( ! empty( $disabled_sizes[ $size ] ) ) {
840
+ continue;
841
+ }
842
+ if ( ! empty( $disabled_sizes['pdf-full'] ) && 'full' == $size ) {
843
+ continue;
844
+ }
845
+ if ( empty( $data['file'] ) ) {
846
+ continue;
847
+ }
848
+
849
+ // Check to see if an IMS record exist from before a resize was moved to the IMS _resized folder.
850
+ $ims_path = $base_ims_dir . $data['file'];
851
+ if ( file_exists( $ims_path ) ) {
852
+ // We reset base_dir, because base_dir potentially gets overwritten with base_ims_dir.
853
+ $base_dir = trailingslashit( dirname( $file_path ) );
854
+ $ims_temp_path = $base_dir . $data['file'];
855
+ ewwwio_debug_message( "ims path: $ims_path" );
856
+ if ( $file_path != $ims_temp_path && is_array( $optimized_list ) && isset( $optimized_list[ $ims_temp_path ] ) ) {
857
+ $optimized_list[ $ims_path ] = $optimized_list[ $ims_temp_path ];
858
+ ewwwio_debug_message( "updating record {$optimized_list[ $ims_temp_path ]['id']} with $ims_path" );
859
+ // Update our records so that we have the correct path going forward.
860
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
861
+ array(
862
+ 'path' => ewww_image_optimizer_relative_path_remove( $ims_path ),
863
+ 'updated' => $optimized_list[ $ims_temp_path ]['updated'],
864
+ ),
865
+ array(
866
+ 'id' => $optimized_list[ $ims_temp_path ]['id'],
867
+ )
868
+ );
869
+ }
870
+ $base_dir = $base_ims_dir;
871
+ }
872
+
873
+ // Check through all the sizes we've processed so far.
874
+ foreach ( $processed as $proc => $scan ) {
875
+ // If a previous resize had identical dimensions...
876
+ if ( $scan['height'] == $data['height'] && $scan['width'] == $data['width'] ) {
877
+ // Found a duplicate size, get outta here!
878
+ continue( 2 );
879
+ }
880
+ }
881
+ $resize_path = $base_dir . $data['file'];
882
+ if ( ( $remote_file || is_file( $resize_path ) ) && 'application/pdf' == $mime && 'full' == $size ) {
883
+ $attachment_images[ 'pdf-' . $size ] = $resize_path;
884
+ } elseif ( $remote_file || is_file( $resize_path ) ) {
885
+ $attachment_images[ $size ] = $resize_path;
886
+ }
887
+ // Optimize retina image, if it exists.
888
+ if ( function_exists( 'wr2x_get_retina' ) ) {
889
+ $retina_path = wr2x_get_retina( $resize_path );
890
+ } else {
891
+ $retina_path = false;
892
+ }
893
+ if ( $retina_path && is_file( $retina_path ) ) {
894
+ ewwwio_debug_message( "found retina via wr2x_get_retina $retina_path" );
895
+ $attachment_images[ $size . '-retina' ] = $retina_path;
896
+ } else {
897
+ $retina_path = ewww_image_optimizer_hidpi_optimize( $resize_path, true );
898
+ if ( $retina_path ) {
899
+ ewwwio_debug_message( "found retina via hidpi_opt $retina_path" );
900
+ $attachment_images[ $size . '-retina' ] = $retina_path;
901
+ }
902
+ }
903
+ // Store info on the sizes we've processed, so we can check the list for duplicate sizes.
904
+ $processed[ $size ]['width'] = $data['width'];
905
+ $processed[ $size ]['height'] = $data['height'];
906
+ } // End foreach().
907
+ } // End if().
908
+
909
+ ewww_image_optimizer_debug_log();
910
+ // Queue sizes from a custom theme.
911
+ if ( isset( $meta['image_meta']['resized_images'] ) && ewww_image_optimizer_iterable( $meta['image_meta']['resized_images'] ) ) {
912
+ $imagemeta_resize_pathinfo = pathinfo( $file_path );
913
+ $imagemeta_resize_path = '';
914
+ foreach ( $meta['image_meta']['resized_images'] as $index => $imagemeta_resize ) {
915
+ $imagemeta_resize_path = $imagemeta_resize_pathinfo['dirname'] . '/' . $imagemeta_resize_pathinfo['filename'] . '-' . $imagemeta_resize . '.' . $imagemeta_resize_pathinfo['extension'];
916
+ if ( is_file( $imagemeta_resize_path ) ) {
917
+ $attachment_images[ 'resized-images-' . $index ] = $imagemeta_resize_path;
918
+ }
919
+ }
920
+ }
921
+
922
+ ewww_image_optimizer_debug_log();
923
+ // Queue size from another custom theme.
924
+ if ( isset( $meta['custom_sizes'] ) && ewww_image_optimizer_iterable( $meta['custom_sizes'] ) ) {
925
+ $custom_sizes_pathinfo = pathinfo( $file_path );
926
+ $custom_size_path = '';
927
+ foreach ( $meta['custom_sizes'] as $dimensions => $custom_size ) {
928
+ $custom_size_path = $custom_sizes_pathinfo['dirname'] . '/' . $custom_size['file'];
929
+ if ( is_file( $custom_size_path ) ) {
930
+ $attachment_images[ 'custom-size-' . $dimensions ] = $custom_size_path;
931
+ }
932
+ }
933
+ }
934
+
935
+ ewww_image_optimizer_debug_log();
936
+ // Check if the files are 'prev opt', pending, or brand new, and then queue the file as needed.
937
+ foreach ( $attachment_images as $size => $file_path ) {
938
+ ewwwio_debug_message( "here is a path $file_path" );
939
+ ewww_image_optimizer_debug_log();
940
+ if ( ! $remote_file && strpos( $file_path, 's3' ) !== 0 && ! defined( 'EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER' ) ) {
941
+ $file_path = realpath( $file_path );
942
+ }
943
+ if ( empty( $file_path ) ) {
944
+ continue;
945
+ }
946
+ if ( apply_filters( 'ewww_image_optimizer_bypass', false, $file_path ) === true ) {
947
+ ewwwio_debug_message( "skipping $file_path as instructed" );
948
+ ewww_image_optimizer_debug_log();
949
+ continue;
950
+ }
951
+ ewwwio_debug_message( "here is the real path $file_path" );
952
+ ewwwio_debug_message( 'memory used: ' . memory_get_usage( true ) );
953
+ ewww_image_optimizer_debug_log();
954
+ $already_optimized = false;
955
+ if ( ! is_array( $optimized_list ) && is_string( $optimized_list ) ) {
956
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $file_path );
957
+ } elseif ( is_array( $optimized_list ) && isset( $optimized_list[ $file_path ] ) ) {
958
+ $already_optimized = $optimized_list[ $file_path ];
959
+ }
960
+ if ( $already_optimized && ( ! $remote_file || ! empty( $_REQUEST['ewww_force'] ) ) ) {
961
+ ewwwio_debug_message( 'potential match found' );
962
+ ewww_image_optimizer_debug_log();
963
+ if ( ! empty( $already_optimized['pending'] ) ) {
964
+ $pending = true;
965
+ ewwwio_debug_message( "pending record for $file_path" );
966
+ ewww_image_optimizer_debug_log();
967
+ continue;
968
+ }
969
+ if ( $remote_file ) {
970
+ $image_size = $already_optimized['image_size'];
971
+ ewwwio_debug_message( "image size for remote file is $image_size" );
972
+ ewww_image_optimizer_debug_log();
973
+ } else {
974
+ $image_size = filesize( $file_path );
975
+ ewwwio_debug_message( "image size is $image_size" );
976
+ if ( ! $image_size ) {
977
+ continue;
978
+ }
979
+ ewww_image_optimizer_debug_log();
980
+ }
981
+ if ( $image_size < ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) ) {
982
+ ewwwio_debug_message( "file skipped due to filesize: $file_path" );
983
+ ewww_image_optimizer_debug_log();
984
+ continue;
985
+ }
986
+ if ( 'image/png' == $mime && ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) && $image_size > ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) ) {
987
+ ewwwio_debug_message( "file skipped due to PNG filesize: $file_path" );
988
+ ewww_image_optimizer_debug_log();
989
+ continue;
990
+ }
991
+ if ( $already_optimized['image_size'] == $image_size && empty( $_REQUEST['ewww_force'] ) ) {
992
+ ewwwio_debug_message( "match found for $file_path" );
993
+ ewww_image_optimizer_debug_log();
994
+ continue;
995
+ } else {
996
+ ewwwio_debug_message( "mismatch found for $file_path, db says " . $already_optimized['image_size'] . " vs. current $image_size" );
997
+ ewww_image_optimizer_debug_log();
998
+ $pending = true;
999
+ if ( empty( $already_optimized['attachment_id'] ) ) {
1000
+ ewwwio_debug_message( "updating record for $file_path, with id $selected_id and resize $size" );
1001
+ ewww_image_optimizer_debug_log();
1002
+ $ewwwdb->update(
1003
+ $ewwwdb->ewwwio_images,
1004
+ array(
1005
+ 'pending' => 1,
1006
+ 'attachment_id' => $selected_id,
1007
+ 'gallery' => 'media',
1008
+ 'resize' => $size,
1009
+ 'updated' => $already_optimized['updated'],
1010
+ ),
1011
+ array(
1012
+ 'id' => $already_optimized['id'],
1013
+ )
1014
+ );
1015
+ ewwwio_debug_message( 'updated record' );
1016
+ } else {
1017
+ ewwwio_debug_message( "adding $selected_id to reset queue" );
1018
+ ewww_image_optimizer_debug_log();
1019
+ $reset_images[] = (int) $already_optimized['id'];
1020
+ }
1021
+ }
1022
+ ewww_image_optimizer_debug_log();
1023
+ } else { // Looks like a new image.
1024
+ if ( ! empty( $images[ $file_path ] ) ) {
1025
+ continue;
1026
+ }
1027
+ $pending = true;
1028
+ ewwwio_debug_message( "queuing $file_path" );
1029
+ ewww_image_optimizer_debug_log();
1030
+ if ( $remote_file ) {
1031
+ $image_size = 0;
1032
+ ewwwio_debug_message( 'image size set to 0' );
1033
+ } else {
1034
+ $image_size = filesize( $file_path );
1035
+ ewwwio_debug_message( "image size is $image_size" );
1036
+ if ( ! $image_size ) {
1037
+ continue;
1038
+ }
1039
+ ewww_image_optimizer_debug_log();
1040
+ if ( $image_size < ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) ) {
1041
+ ewwwio_debug_message( "file skipped due to filesize: $file_path" );
1042
+ ewww_image_optimizer_debug_log();
1043
+ continue;
1044
+ }
1045
+ if ( 'image/png' == $mime && ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) && $image_size > ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) ) {
1046
+ ewwwio_debug_message( "file skipped due to PNG filesize: $file_path" );
1047
+ ewww_image_optimizer_debug_log();
1048
+ continue;
1049
+ }
1050
+ }
1051
+ if ( seems_utf8( $file_path ) ) {
1052
+ ewwwio_debug_message( 'file seems utf8' );
1053
+ $utf8_file_path = $file_path;
1054
+ } else {
1055
+ ewwwio_debug_message( 'file will become utf8' );
1056
+ $utf8_file_path = utf8_encode( $file_path );
1057
+ }
1058
+ ewww_image_optimizer_debug_log();
1059
+ $images[ $file_path ] = array(
1060
+ 'path' => ewww_image_optimizer_relative_path_remove( $utf8_file_path ),
1061
+ 'gallery' => 'media',
1062
+ 'orig_size' => $image_size,
1063
+ 'attachment_id' => $selected_id,
1064
+ 'resize' => $size,
1065
+ 'pending' => 1,
1066
+ );
1067
+ $image_count++;
1068
+ ewwwio_debug_message( 'image added to $images queue' );
1069
+ ewww_image_optimizer_debug_log();
1070
+ } // End if().
1071
+ if ( $image_count > 1000 || count( $reset_images ) > 1000 ) {
1072
+ ewwwio_debug_message( 'making a dump run' );
1073
+ ewww_image_optimizer_debug_log();
1074
+ // Let's dump what we have so far to the db.
1075
+ $image_count = 0;
1076
+ if ( ! empty( $images ) ) {
1077
+ ewwwio_debug_message( 'doing mass insert' );
1078
+ ewww_image_optimizer_debug_log();
1079
+ ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, $field_formats );
1080
+ }
1081
+ $images = array();
1082
+ if ( ! empty( $reset_images ) ) {
1083
+ ewwwio_debug_message( 'marking reset_images as pending' );
1084
+ ewww_image_optimizer_debug_log();
1085
+ $ewwwdb->query( "UPDATE $ewwwdb->ewwwio_images SET pending = 1, updated = updated WHERE id IN (" . implode( ',', $reset_images ) . ')' );
1086
+ }
1087
+ $reset_images = array();
1088
+ }
1089
+ } // End foreach().
1090
+ // End of loop checking all the attachment_images for selected_id to see if they are optimized already or pending already.
1091
+ if ( $pending ) {
1092
+ ewwwio_debug_message( "$selected_id added to queue" );
1093
+ ewww_image_optimizer_debug_log();
1094
+ $queued_ids[] = $selected_id;
1095
+ }
1096
+ $attachment_images = array();
1097
+ ewwwio_debug_message( 'checking for bad attachment' );
1098
+ ewww_image_optimizer_debug_log();
1099
+ if ( $selected_id == $bad_attachment ) {
1100
+ ewwwio_debug_message( 'found bad attachment, bailing to reset the counter' );
1101
+ ewww_image_optimizer_debug_log();
1102
+ if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
1103
+ $attachment_ids = array_merge( $failsafe_selected_ids, $attachment_ids );
1104
+ break 2;
1105
+ }
1106
+ }
1107
+ } // End foreach().
1108
+ // End of loop for the selected_id.
1109
+ ewwwio_debug_message( 'finished foreach, storing remaining attachments in scanning_attachments' );
1110
+ ewww_image_optimizer_debug_log();
1111
+ update_option( 'ewww_image_optimizer_scanning_attachments', $attachment_ids, false );
1112
+ $attachments_queued = get_option( 'ewww_image_optimizer_bulk_attachments' );
1113
+ if ( empty( $attachments_queued ) || ! is_array( $attachments_queued ) ) {
1114
+ ewwwio_debug_message( 'storing queued attachments in bulk_attachments' );
1115
+ ewww_image_optimizer_debug_log();
1116
+ update_option( 'ewww_image_optimizer_bulk_attachments', $queued_ids, false );
1117
+ } else {
1118
+ ewwwio_debug_message( 'storing queued attachments in bulk_attachments, merged with existing' );
1119
+ ewww_image_optimizer_debug_log();
1120
+ update_option( 'ewww_image_optimizer_bulk_attachments', array_merge( $attachments_queued, $queued_ids ), false );
1121
+ }
1122
+ $queued_ids = array();
1123
+ ewwwio_debug_message( 'finished a loop in the while, going back for more possibly' );
1124
+ ewww_image_optimizer_debug_log();
1125
+ } // End while().
1126
+ ewwwio_debug_message( 'done for a while, wrapping up' );
1127
+ ewww_image_optimizer_debug_log();
1128
+ if ( ! empty( $images ) ) {
1129
+ ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, $field_formats );
1130
+ }
1131
+ if ( ! empty( $reset_images ) ) {
1132
+ $ewwwdb->query( "UPDATE $ewwwdb->ewwwio_images SET pending = 1, updated = updated WHERE id IN (" . implode( ',', $reset_images ) . ')' );
1133
+ }
1134
+ ewwwio_debug_message( 'storing remaining attachments in scanning_attachments' );
1135
+ ewww_image_optimizer_debug_log();
1136
+ update_option( 'ewww_image_optimizer_scanning_attachments', $attachment_ids, false );
1137
+ if ( ! empty( $queued_ids ) ) {
1138
+ $attachments_queued = get_option( 'ewww_image_optimizer_bulk_attachments' );
1139
+ if ( empty( $attachments_queued ) || ! is_array( $attachments_queued ) ) {
1140
+ ewwwio_debug_message( 'storing queued attachments in bulk_attachments' );
1141
+ ewww_image_optimizer_debug_log();
1142
+ update_option( 'ewww_image_optimizer_bulk_attachments', $queued_ids, false );
1143
+ } else {
1144
+ ewwwio_debug_message( 'storing queued attachments in bulk_attachments, merged with existing' );
1145
+ ewww_image_optimizer_debug_log();
1146
+ update_option( 'ewww_image_optimizer_bulk_attachments', array_merge( $attachments_queued, $queued_ids ), false );
1147
+ }
1148
+ }
1149
+ $elapsed = microtime( true ) - $started;
1150
+ ewwwio_debug_message( "counting images took $elapsed seconds" );
1151
+ ewwwio_memory( __FUNCTION__ );
1152
+ ewww_image_optimizer_debug_log();
1153
+ if ( 'ewww-image-optimizer-cli' === $hook ) {
1154
+ return;
1155
+ }
1156
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
1157
+ $notice = ( 'low_memory' == get_transient( 'ewww_image_optimizer_low_memory_mode' ) ? esc_html__( "Increasing PHP's memory_limit setting will allow for faster scanning with fewer database queries. Please allow up to 10 minutes for changes to memory limit to be detected.", 'ewww-image-optimizer' ) : '' );
1158
+ if ( count( $attachment_ids ) ) {
1159
+ die( json_encode( array(
1160
+ /* translators: %d: number of images */
1161
+ 'remaining' => sprintf( esc_html__( 'Stage 1, %d images left to scan.', 'ewww-image-optimizer' ), count( $attachment_ids ) ) . "&nbsp;<img src='$loading_image' />",
1162
+ 'notice' => $notice,
1163
+ 'bad_attachment' => $bad_attachment,
1164
+ ) ) );
1165
+ } else {
1166
+ die( json_encode( array(
1167
+ 'remaining' => esc_html__( 'Stage 2, please wait.', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' />",
1168
+ 'notice' => $notice,
1169
+ 'bad_attachment' => $bad_attachment,
1170
+ ) ) );
1171
+ }
1172
+ }
1173
+
1174
+ /**
1175
+ * Called via AJAX to get an update on the API quota usage.
1176
+ */
1177
+ function ewww_image_optimizer_bulk_quota_update() {
1178
+ // Verify that an authorized user has made the request.
1179
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
1180
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
1181
+ die( esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) );
1182
+ }
1183
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
1184
+ echo esc_html__( 'Image credits available:', 'ewww-image-optimizer' ) . ' ' . ewww_image_optimizer_cloud_quota();
1185
+ }
1186
+ ewwwio_memory( __FUNCTION__ );
1187
+ die();
1188
+ }
1189
+
1190
+ /**
1191
+ * Called via AJAX to start the bulk operation and get the name of the first image in the queue.
1192
+ */
1193
+ function ewww_image_optimizer_bulk_initialize() {
1194
+ // Verify that an authorized user has made the request.
1195
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
1196
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
1197
+ die( json_encode( array(
1198
+ 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ),
1199
+ ) ) );
1200
+ }
1201
+ session_write_close();
1202
+ $output = array();
1203
+ // Update the 'bulk resume' option to show that an operation is in progress.
1204
+ update_option( 'ewww_image_optimizer_bulk_resume', 'true' );
1205
+ $attachments = get_option( 'ewww_image_optimizer_bulk_attachments' );
1206
+ if ( ! is_array( $attachments ) && ! empty( $attachments ) ) {
1207
+ $attachments = unserialize( $attachments );
1208
+ }
1209
+ if ( ! is_array( $attachments ) ) {
1210
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
1211
+ die( json_encode( array(
1212
+ 'error' => esc_html__( 'Error retrieving list of images', 'ewww-image-optimizer' ),
1213
+ 'data' => print_r( $attachments, true ),
1214
+ ) ) );
1215
+ } else {
1216
+ die( json_encode( array(
1217
+ 'error' => esc_html__( 'Error retrieving list of images', 'ewww-image-optimizer' ),
1218
+ 'data' => 'print_r disabled',
1219
+ ) ) );
1220
+ }
1221
+ }
1222
+ $attachment = (int) array_shift( $attachments );
1223
+ ewwwio_debug_message( "first image: $attachment" );
1224
+ $first_image = new EWWW_Image( $attachment, 'media' );
1225
+ $file = $first_image->file;
1226
+ // Generate the WP spinner image for display.
1227
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
1228
+ // Let the user know that we are beginning.
1229
+ if ( $file ) {
1230
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <b>$file</b>&nbsp;<img src='$loading_image' /></p>";
1231
+ } else {
1232
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>";
1233
+ }
1234
+ $output['start_time'] = time();
1235
+ ewwwio_memory( __FUNCTION__ );
1236
+ die( json_encode( $output ) );
1237
+ }
1238
+
1239
+ /**
1240
+ * Called by AJAX to process each image in the queue.
1241
+ *
1242
+ * @global object $wpdb
1243
+ * @global bool $ewww_defer Change to false so nothing is deferred.
1244
+ *
1245
+ * @param string $hook Optional. Lets us know if WP-CLI is running. Default empty.
1246
+ * @param int $delay Optional. Number of seconds to pause between images. Default 0.
1247
+ * @return bool When using WP-CLI, true keeps the process running, false indicates completion.
1248
+ */
1249
+ function ewww_image_optimizer_bulk_loop( $hook = '', $delay = 0 ) {
1250
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1251
+ global $ewww_defer;
1252
+ $ewww_defer = false;
1253
+ $output = array();
1254
+ $time_adjustment = 0;
1255
+ // Verify that an authorized user has started the optimizer.
1256
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
1257
+ if ( 'ewww-image-optimizer-cli' !== $hook && ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) ) {
1258
+ die( json_encode( array(
1259
+ 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ),
1260
+ ) ) );
1261
+ }
1262
+ session_write_close();
1263
+ // Retrieve the time when the optimizer starts.
1264
+ $started = microtime( true );
1265
+ // Prevent the scheduled optimizer from firing during a bulk optimization.
1266
+ set_transient( 'ewww_image_optimizer_no_scheduled_optimization', true, 5 * MINUTE_IN_SECONDS );
1267
+ // Find out if our nonce is on it's last leg/tick.
1268
+ if ( ! empty( $_REQUEST['ewww_wpnonce'] ) ) {
1269
+ $tick = wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' );
1270
+ if ( 2 === $tick ) {
1271
+ $output['new_nonce'] = wp_create_nonce( 'ewww-image-optimizer-bulk' );
1272
+ } else {
1273
+ $output['new_nonce'] = '';
1274
+ }
1275
+ }
1276
+ $batch_image_limit = ( empty( $_REQUEST['ewww_batch_limit'] ) ? 999 : 1 );
1277
+ // Get the 'bulk attachments' with a list of IDs remaining.
1278
+ $attachments = get_option( 'ewww_image_optimizer_bulk_attachments' );
1279
+ if ( ! empty( $attachments ) && is_array( $attachments ) ) {
1280
+ $attachment = (int) $attachments[0];
1281
+ } else {
1282
+ $attachment = 0;
1283
+ }
1284
+ $image = new EWWW_Image( $attachment, 'media' );
1285
+ if ( ! $image->file ) {
1286
+ die( json_encode( array(
1287
+ 'done' => 1,
1288
+ 'completed' => 0,
1289
+ ) ) );
1290
+ }
1291
+ $output['results'] = '';
1292
+ $output['completed'] = 0;
1293
+ while ( $output['completed'] < $batch_image_limit && $image->file && microtime( true ) - $started + $time_adjustment < apply_filters( 'ewww_image_optimizer_timeout', 15 ) ) {
1294
+ $output['completed']++;
1295
+ $meta = false;
1296
+ // See if the image needs fetching from a CDN.
1297
+ if ( ! is_file( $image->file ) ) {
1298
+ $meta = wp_get_attachment_metadata( $image->attachment_id );
1299
+ $file_path = ewww_image_optimizer_remote_fetch( $image->attachment_id, $meta );
1300
+ unset( $meta );
1301
+ if ( ! $file_path ) {
1302
+ ewwwio_debug_message( 'could not retrieve path' );
1303
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
1304
+ WP_CLI::line( __( 'Could not find image', 'ewww-image-optimizer' ) . ' ' . $image->file );
1305
+ } else {
1306
+ $output['results'] .= sprintf( '<p>' . esc_html__( 'Could not find image', 'ewww-image-optimizer' ) . ' <strong>%s</strong></p>', esc_html( $image->file ) );
1307
+ }
1308
+ }
1309
+ }
1310
+ // If a resize is missing, see if it should (and can) be regenerated.
1311
+ if ( $image->resize && 'full' != $image->resize && ! is_file( $image->file ) ) {
1312
+ // TODO: Make sure this is optional, because of CDN offloading: resized image does not exist, regenerate it.
1313
+ }
1314
+ if ( 'full' === $image->resize && ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_existing' ) && ! function_exists( 'imsanity_get_max_width_height' ) ) {
1315
+ if ( ! $meta || ! is_array( $meta ) ) {
1316
+ $meta = wp_get_attachment_metadata( $image->attachment_id );
1317
+ }
1318
+ $new_dimensions = ewww_image_optimizer_resize_upload( $image->file );
1319
+ if ( is_array( $new_dimensions ) ) {
1320
+ $meta['width'] = $new_dimensions[0];
1321
+ $meta['height'] = $new_dimensions[1];
1322
+ }
1323
+ }
1324
+ list( $file, $msg, $converted, $original ) = ewww_image_optimizer( $image->file, 1, false, false, 'full' == $image->resize );
1325
+ // Gotta make sure we don't delete a pending record if the license is exceeded, so the license check goes first.
1326
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
1327
+ if ( ! empty( $ewww_status ) && preg_match( '/exceeded/', $ewww_status ) ) {
1328
+ $output['error'] = esc_html__( 'License Exceeded', 'ewww-image-optimizer' );
1329
+ die( json_encode( $output ) );
1330
+ }
1331
+ // Delete a pending record if the optimization failed for whatever reason.
1332
+ if ( ! $file && $image->id ) {
1333
+ global $wpdb;
1334
+ $wpdb->delete(
1335
+ $wpdb->ewwwio_images,
1336
+ array(
1337
+ 'id' => $image->id,
1338
+ ),
1339
+ array( '%d' )
1340
+ );
1341
+ }
1342
+ // If this is a full size image and it was converted.
1343
+ if ( 'full' == $image->resize && ( false !== $image->increment || false !== $converted ) ) {
1344
+ if ( ! $meta || ! is_array( $meta ) ) {
1345
+ $meta = wp_get_attachment_metadata( $image->attachment_id );
1346
+ }
1347
+ if ( $converted ) {
1348
+ $image->increment = $converted;
1349
+ }
1350
+ $image->file = $file;
1351
+ $image->converted = $original;
1352
+ $meta['file'] = trailingslashit( dirname( $meta['file'] ) ) . basename( $file );
1353
+ $image->update_converted_attachment( $meta );
1354
+ $meta = $image->convert_sizes( $meta );
1355
+ }
1356
+
1357
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
1358
+ WP_CLI::line( __( 'Optimized', 'ewww-image-optimizer' ) . ' ' . $image->file );
1359
+ WP_CLI::line( str_replace( '&nbsp;', '', $msg ) );
1360
+ }
1361
+ $output['results'] .= sprintf( '<p>' . esc_html__( 'Optimized', 'ewww-image-optimizer' ) . ' <strong>%s</strong><br>', esc_html( $image->file ) );
1362
+ $output['results'] .= "$msg</p>";
1363
+
1364
+ // Do metadata update after full-size is processed, usually because of conversion or resizing.
1365
+ if ( 'full' == $image->resize && $image->attachment_id ) {
1366
+ if ( $meta && is_array( $meta ) ) {
1367
+ $meta_saved = wp_update_attachment_metadata( $image->attachment_id, $meta );
1368
+ if ( ! $meta_saved ) {
1369
+ ewwwio_debug_message( 'failed to save meta' );
1370
+ }
1371
+ }
1372
+ }
1373
+
1374
+ // Pull the next image.
1375
+ $next_image = new EWWW_Image( $attachment, 'media' );
1376
+
1377
+ // When we finish all the sizes, we want to fire off any filters for plugins that might need to take action when an image is updated.
1378
+ if ( $attachment && $attachment != $next_image->attachment_id ) {
1379
+ $meta = apply_filters( 'wp_update_attachment_metadata', wp_get_attachment_metadata( $image->attachment_id ), $image->attachment_id );
1380
+ }
1381
+ // When an image (attachment) is done, pull the next attachment ID off the stack.
1382
+ if ( ( 'full' == $next_image->resize || empty( $next_image->resize ) ) && ! empty( $attachment ) && $attachment != $next_image->attachment_id ) {
1383
+ $attachment = (int) array_shift( $attachments ); // Pull the last image off the stack first.
1384
+ if ( ! empty( $attachments ) && is_array( $attachments ) ) {
1385
+ $attachment = (int) $attachments[0]; // Then grab the next one (if any are left).
1386
+ } else {
1387
+ $attachment = 0;
1388
+ }
1389
+ $next_image = new EWWW_Image( $attachment, 'media' );
1390
+ }
1391
+ $image = $next_image;
1392
+ $time_adjustment = $image->time_estimate();
1393
+ } // End while().
1394
+
1395
+ // Calculate how much time has elapsed since we started.
1396
+ $elapsed = microtime( true ) - $started;
1397
+ // Output how much time has elapsed since we started.
1398
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
1399
+ /* translators: %s: number of seconds */
1400
+ WP_CLI::line( sprintf( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ), number_format_i18n( $elapsed ) ) );
1401
+ if ( ewww_image_optimizer_function_exists( 'sleep' ) ) {
1402
+ sleep( $delay );
1403
+ }
1404
+ }
1405
+ /* translators: %s: number of seconds */
1406
+ $output['results'] .= sprintf( '<p>' . esc_html( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ) ) . '</p>', number_format_i18n( $elapsed ) );
1407
+ // Store the updated list of attachment IDs back in the 'bulk_attachments' option.
1408
+ update_option( 'ewww_image_optimizer_bulk_attachments', $attachments, false );
1409
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
1410
+ global $ewww_debug;
1411
+ $output['results'] .= '<div style="background-color:#ffff99;">' . $ewww_debug . '</div>';
1412
+ }
1413
+ if ( ! empty( $next_image->file ) ) {
1414
+ $next_file = esc_html( $next_image->file );
1415
+ // Generate the WP spinner image for display.
1416
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
1417
+ if ( $next_file ) {
1418
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <b>$next_file</b>&nbsp;<img src='$loading_image' /></p>";
1419
+ } else {
1420
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>";
1421
+ }
1422
+ } else {
1423
+ $output['done'] = 1;
1424
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
1425
+ return false;
1426
+ }
1427
+ }
1428
+ ewww_image_optimizer_debug_log();
1429
+ ewwwio_memory( __FUNCTION__ );
1430
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
1431
+ return true;
1432
+ }
1433
+ $output['current_time'] = time();
1434
+ die( json_encode( $output ) );
1435
+ }
1436
+
1437
+ /**
1438
+ * Called by javascript to cleanup after ourselves after a bulk operation.
1439
+ */
1440
+ function ewww_image_optimizer_bulk_cleanup() {
1441
+ // Verify that an authorized user has started the optimizer.
1442
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
1443
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
1444
+ die( '<p><b>' . esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) . '</b></p>' );
1445
+ }
1446
+ // All done, so we can update the bulk options with empty values.
1447
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
1448
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
1449
+ update_option( 'ewww_image_optimizer_bulk_attachments', '', false );
1450
+ delete_transient( 'ewww_image_optimizer_skip_aux' );
1451
+ // Let the user know we are done.
1452
+ ewwwio_memory( __FUNCTION__ );
1453
+ die( '<p><b>' . esc_html__( 'Finished', 'ewww-image-optimizer' ) . '</b> - <a href="upload.php">' . esc_html__( 'Return to Media Library', 'ewww-image-optimizer' ) . '</a></p>' );
1454
+ }
1455
+
1456
+ add_action( 'admin_enqueue_scripts', 'ewww_image_optimizer_bulk_script' );
1457
+ add_action( 'wp_ajax_bulk_scan', 'ewww_image_optimizer_media_scan' );
1458
+ add_action( 'wp_ajax_bulk_init', 'ewww_image_optimizer_bulk_initialize' );
1459
+ add_action( 'wp_ajax_bulk_filename', 'ewww_image_optimizer_bulk_filename' );
1460
+ add_action( 'wp_ajax_bulk_loop', 'ewww_image_optimizer_bulk_loop' );
1461
+ add_action( 'wp_ajax_bulk_cleanup', 'ewww_image_optimizer_bulk_cleanup' );
1462
+ add_action( 'wp_ajax_bulk_quota_update', 'ewww_image_optimizer_bulk_quota_update' );
1463
+ add_filter( 'ewww_image_optimizer_count_optimized_queries', 'ewww_image_optimizer_reduce_query_count' );
1464
+ ?>
changelog.txt ADDED
@@ -0,0 +1,1041 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ = 3.4.0 =
2
+ * added: optional usage tracking
3
+ * added: close sessions even earlier in background/async handling to prevent lock-ups
4
+ * added: multisite option to network activate and allow individual site configuration
5
+ * changed: disabling resizes must be done on individual sites even when network activated
6
+ * changed: PNG files with empty alpha channels can be converted to JPG without setting a background/fill color
7
+ * fixed: webp migration script sending wrong nonce variable
8
+ * fixed: wp-cli help text was not being parsed properly
9
+ * updated: bundled cwebp to version 0.6.0
10
+ * updated: bundled pngquant to verision 2.9.1 (2.8.1 for Windows)
11
+ * deprecated: cwebp will not be updated for Mac OS X 10.9 past 0.5.1
12
+ * obsoleted: FreeBSD 9 and CentOS 5 are "End of Life" and will no longer be tested
13
+
14
+ = 3.3.1 =
15
+ * added: alt webp supports Jetpack Carousel for image galleries
16
+ * added: hard crop images during resizing using ewww_image_optimizer_crop_image filter
17
+ * changed: plugin status on settings revamped to rely less on javascript
18
+ * fixed: regression with scheduled optimizer scanning causing timeouts
19
+ * fixed: alt webp compatibility with Divi Builder in Visual mode
20
+
21
+ = 3.3.0 =
22
+ * added: optional image backups for API users, restore images from bulk optimize, or media library list view
23
+ * added: relative file location support, automatically enabled for Pantheon, use EWWW_IMAGE_OPTIMIZER_RELATIVE and EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER to enable elsewhere
24
+ * added: filename as second parameter to ewww_image_optimizer_resize_dimensions filter
25
+ * added: prevent accidental regeneration of an image resize with the built-in WP_Image_Editor, disable by defining EWWWIO_EDITOR_OVERWRITE
26
+ * changed: JPG quality setting applies to WebP generation also
27
+ * changed: retina images can be processed in background
28
+ * changed: prevent sleep() and print_r() from running when disabled
29
+ * changed: entire ewwwio_images table no longer loaded into memory when running bulk operation on small batches of images, or when the table is too large
30
+ * changed: when resize optimization is disabled, Include Media Folders is disabled to prevent optimization of disabled sizes
31
+ * changed: Swedish translation moved to wp.org
32
+ * changed: permissions check uses is_readable() and is_executable() instead of requiring 755 permissions
33
+ * changed: requires at least PHP 5.3
34
+ * fixed: WP_Image_Editor integration was not disabled when using Regenerate Thumbs plugin, resulting in disabled resizes being ignored, and optimization not being backgrounded properly
35
+ * fixed: Media Library Plus actions triggered optimization too early, preventing background optimization.
36
+ * fixed: settings page would not load on very large multisite installs (1,000+ blogs) because of too many queries for total savings achieved
37
+ * fixed: background optimization not working properly on multisite installs
38
+ * fixed: imported attachments queued multiple times when plugins like Facebook Events Importer use media_sideload_image()
39
+ * fixed: notice when clearing queues
40
+ * fixed: when a background process is running, queues repopulate even after clearing all items
41
+ * fixed: WP-CLI not dropping to low memory mode in constrained environments, causing incomplete scans
42
+ * fixed: nextgen not showing optimization stats
43
+ * fixed: proper i18n for strings that could contain singular and plural numbers
44
+ * fixed: bulk scanner could skip images that need optimization when in 'low memory' mode
45
+ * fixed: all JPG images down-sampled when only one of max height or max width is set
46
+ * fixed: permissions error on tool folder cause media grid to appear empty
47
+ * fixed: fatal error when both EWWW I.O. plugins are activated
48
+ * fixed: edited images show active and backup compression results in media library
49
+
50
+ = 3.2.7 =
51
+ * added: function to remove duplicate records from the ewwwio table when doing a bulk scan or re-optimizing an image
52
+ * changed: zero-byte files skipped during bulk scan instead of during optimization
53
+ * changed: exec() check rewritten, please report any errors right away
54
+ * fixed: plugin status shows All Clear even though exec disabled and warning is displayed
55
+
56
+ = 3.2.6 =
57
+ * changed: time elapsed test now runs every 10 attachments
58
+ * fixed: time elapsed test during bulk scan was not running every X number of images
59
+ * fixed: scan was not returning results directly after detecting a broken attachment
60
+ * fixed: maximum number of rows for ewwwio_images was not high enough, bumped to 4 billion
61
+ * fixed: db migration function was not linking records to attachments properly
62
+
63
+ = 3.2.5 =
64
+ * fixed: converting PNG to JPG with GD did not properly convert resizes
65
+ * fixed: broken attachment metadata could halt the bulk scanner
66
+ * fixed: background optimization running when sleep is disabled
67
+
68
+ = 3.2.4 =
69
+ * changed: when license has been exceeded, visiting the settings page flushes the license cache
70
+ * fixed: warnings for illegal string offsets
71
+ * fixed: regression with the dreaded duplicate key name
72
+ * fixed: scheduled optimization could run during bulk optimization, causing unexpected results
73
+
74
+ = 3.2.3 =
75
+ * added: image linker for media images optimized using scheduled optimizer or the old Scan and Optimize
76
+ * added: low memory mode for bulk scanner with notice to user
77
+ * added: ability to manually configure how much memory is available using EWWW_MEMORY_LIMIT constant
78
+ * added: variable query counts depending on available memory
79
+ * added: ability to view and remove debug.log from settings page
80
+ * added: ability to manually disable background optimization using EWWW_DISABLE_ASYNC constant
81
+ * changed: check every 100 images during scan to avoid timeouts and memory errors
82
+ * changed: additional folder scanner can stop & resume mid-folder
83
+ * fixed: bulk scanner updates timestamps when it should not
84
+ * fixed: special characters are mangled during database insert on some systems
85
+ * fixed: pending images that were already optimized were not cleared from queue
86
+ * fixed: images with invalid updated dates in database corrected
87
+ * fixed: images that should be excluded from optimization were still queued even though they would not be optimized
88
+ * fixed: results column was too short, causing bulk optimization to get stuck on an image that was previously optimized
89
+ * fixed: if two different attachments reference the same image, duplicate records could be inserted into database during media scan
90
+
91
+ = 3.2.2 =
92
+ * added: estimated time remaining on bulk optimize
93
+ * added: 'ewww_image_optimizer_image_resized' hook added right after resizing, before original is overwritten
94
+ * changed: image resizing is performed before any thumbnails are generated for reduced resource usage
95
+ * fixed: compatibility with Azure storage plugin
96
+ * fixed: bulk optimization not playing nice with WP Offload S3
97
+ * fixed: optimization results for resized original not displayed when using Imsanity
98
+ * fixed: bulk optimization not working for utf-8 filenames - credit to devsporadic on github
99
+ * fixed: retina paths not tested correctly in some odd cases
100
+ * notice: FreeBSD 9 is EOL, version 10.3 is now the currently supported version
101
+ * notice: RHEL 5 and CentOS 5 will be EOL at the end of March, at that point version 6 will be the lowest supported version
102
+ * removed: generating full-size retina image automatically when resizing images and WP Retina 2x Pro detected
103
+
104
+ = 3.2.1 =
105
+ * fixed: really old versions of PHP (less than 5.5) cannot cope with using empty() on a function return value
106
+ * fixed: queue of images not reset when reloading bulk page
107
+
108
+ = 3.2.0 =
109
+ * added: option to ignore folders when optimizing
110
+ * added: ability to disable optimization or creation for any or all previews of PDF files in WordPress 4.7
111
+ * added: optimization results detail for all resizes of an image in media library list view
112
+ * added: automatic metadata rebuilding for broken image attachments in media library during bulk scan
113
+ * changed: bulk optimizers for media library and everything else have been merged
114
+ * changed: bulk optimization processes images in batches for fewer AJAX requests to your server
115
+ * changed: tool locations saved for the duration of a request to improve optimization speed
116
+ * changed: optimization results no longer stored in attachment metadata
117
+ * changed: populating list of optimized images during scan uses less memory
118
+ * changed: obsolete options removed from database
119
+ * changed: if scan is interrupted, it will automatically retry
120
+ * changed: excessive re-optimization warning ignores theme and plugin images
121
+ * changed: if full-size image is converted, all resizes, custom sizes, and retina images will be converted
122
+ * changed: conversion will not inject extra numbers if possible
123
+ * changed: image results message generated on demand to avoid stale results
124
+ * removed: ability to use the ImageMagick 'convert' binary, use Imagick extension for PHP instead
125
+ * removed: unoptimized images page, bulk scanner is now able to accomplish the job more accurately
126
+ * fixed: parallel mode prevents successful conversion
127
+ * fixed: removing API key on multisite did not fallback to local mode properly
128
+ * fixed: pngout enabled after API key removed
129
+ * fixed: image paths with special characters stored incorrectly in database
130
+ * fixed: parallel optimization for retina and custom sizes was missing parameters
131
+ * fixed: bulk optimizing a single image was broken, but who does that anyway?
132
+ * fixed: notice when LIBXML_VERSION is undefined and alt webp is enabled
133
+ * fixed: invalid default value for timestamp in db records
134
+ * fixed: one-click optimization returns no error when running out of API credits
135
+ * fixed: background mode was not checked properly in nextgen and flagallery functions
136
+ * fixed: incorrect mimetype set after image conversion for PNG2JPG
137
+ * fixed: using getimagesize on pdf files
138
+
139
+ = 3.1.3 =
140
+ * added: settings which require validation display appropriate errors when validation fails
141
+ * added: filter to make sure test images in the ewww-image-optimizer folder never get optimized
142
+ * fixed: optimizing "other" images with wp-cli was broken
143
+
144
+ = 3.1.2 =
145
+ * added: ability to disable background optimization via ewww_image_optimizer_background_optimization filter
146
+ * changed: scan and optimize rewritten to store images in batches, with auto-retry for very large sites
147
+ * changed: folders to optimize validator will attempt to fix relative paths and urls
148
+ * changed: conversion operations are not run in background, override with ewww_image_optimizer_defer_conversion filter
149
+ * changed: reverted Alt WebP support for lazy load, as it does not work consistently
150
+ * changed: cache query results for excessive reoptimization up to an hour
151
+ * fixed: ensure disabled resizes are not optimized during Enable Media Replace uploader
152
+ * fixed: images were not optimized after editing with Post Thumbnail Editor
153
+ * fixed: bulk operation handles attachment ID as a string instead of an integer
154
+ * fixed: bulk optimizing a single image displays no results
155
+ * fixed: bulk optimizing images with corrupted metadata does not result in a repair operation
156
+ * fixed: image scanner skips optimized metaslider images even if they have changed
157
+ * fixed: scan and optimize includes file types that are disabled
158
+
159
+ = 3.1.1 =
160
+ * fixed: exec() notice surpressed when it should not be
161
+
162
+ = 3.1.0 =
163
+ * added: warning when excessive re-optimizations have been detected
164
+ * added: Alt WebP supports lazy loading in Hueman theme
165
+ * added: Alt WebP supports Lazy Load plugin and Cherry Lazy modifications
166
+ * added: Alt WebP supports BJ Lazy Load plugin
167
+ * added: Alt WebP supports Retina Lazysizes
168
+ * added: ability to defer resizing of uploaded image using ewww_image_optimizer_defer_resizing filter, "other" dimensions will apply regardless of upload method
169
+ * changed: wp_image_editor integration does not use background optimization, reverting to inline processing, holler if you want it back
170
+ * changed: all scripts have proper version numbers to avoid caching issues
171
+ * changed: inline webp script moved to head element to work better with lazy loading
172
+ * changed: optimized Alt WebP code for smaller size
173
+
174
+ = 3.0.2 =
175
+ * fixed: fatal error running empty() on a constant in PHP less than 5.5
176
+
177
+ = 3.0.0 =
178
+ * fixed: resizes not checked for existence before calling parallel/async optimization, causing the process to stall
179
+ * fixed: background optimization disabled when settings are saved
180
+ * fixed: regression in db upgrade function throws warning on plugin upgrade
181
+ * fixed: alt webp breaks Slider Revolution's lazyload when dummy.png.webp exists
182
+ * fixed: background optimization for nextcellent was incomplete
183
+ * fixed: notices under Manage Gallery for nextcellent when tool constants were not defined
184
+ * changed: one-click actions in Media Library don't require reload, now possible to optimize several images at the same time
185
+ * changed: API quota check no longer requires a verification on every attempt
186
+ * changed: webp settings moved to separate tab
187
+ * added: forced webp mode, to generate webp for every image, regardless of final filesize
188
+ * added: in forced webp mode, must specify allowed url patterns for rewriting
189
+
190
+ = 2.9.9 =
191
+ * fixed: broken uploads with W3TC CDN option enabled
192
+ * fixed: warning when scanning Meta Slider metadata for images
193
+ * fixed: should not check for 'nice' when exec() is disabled
194
+ * fixed: notices for 'nice' when exec() output is empty
195
+ * fixed: wp-cli command skipping pdf files
196
+ * added: ability to view API history at https://history.exactlywww.com/
197
+ * added: abiltiy to disable set_time_limit() function with EWWW_IMAGE_OPTIMIZER_DISABLE_STL constant
198
+ * added: plugin now on GitHub https://github.com/nosilver4u/ewww-image-optimizer
199
+ * changed: removed baseline JPG encoding trial, since progressive compression is almost always smaller, and is always more desirable from a UX perspective
200
+ * updated: cwebp version 0.5.1
201
+
202
+ = 2.9.8 =
203
+ * fixed: also disable parallel mode iternally if background testing is not successful
204
+ * fixed: fatal error when WP Retina 2x is enabled with EWWW's parallel mode
205
+ * fixed: parallel opt would hang if resizes were missing
206
+ * fixed: prevent background test from accidentally spawning more tests
207
+ * fixed: background test stuck in queue indefinitely if it didn't succeed
208
+
209
+ = 2.9.7 =
210
+ * fixed: cached value for multisite uploads directory incorrect on some sites
211
+ * fixed: retina/hidpi images required separate async task with parallel optimization
212
+ * fixed: retina function would try to run an async optimization even if the file didn't exist
213
+ * fixed: one-time convert links (like JPG2PNG) from Media Library not working when Parallel mode enabled
214
+ * fixed: images with transparency were being converted if PNG2JPG enabled regardless of JPG background setting when using API
215
+ * fixed: mime-type meta for resizes updated on conversion and restoration
216
+ * fixed: resizes were being checked, even if no filename was available
217
+ * added: thread limit for parallel optimization, set to 5, can be modified by filter
218
+ * added: filter to modify timeout for parallel optimization
219
+ * added: filter to disable (or modify) the suffix added to converted images
220
+ * added: debugging page to view and clear background optimization queues (must have EWWW's debug setting enabled) - under Media menu
221
+ * changed: parallel mode only enabled if using API or your images have more than 5 resizes each
222
+ * changed: background mode only enabled if background test succeeds (on plugin upgrade)
223
+ * changed: file types with disabled optimization no longer included in unoptimized image counts
224
+
225
+ = 2.9.6 =
226
+ * fixed: set_time_limit() was still being called in a couple spots even if set_time_limit() is disabled by PHP
227
+ * fixed: regression in scheduled optimization which allowed multiple processes to run
228
+ * fixed: total savings for multisite was incorrectly requerying site 1 for each blog
229
+ * fixed: optimization being attempted via API even if license exceeded
230
+ * added: ewwwio_images table is checked on settings page to make sure it exists
231
+ * added: run utf8_encode() on all filenames for Scheduled Optimize and Scan & Optimize to avoid database update issues, please report any new issues with Scan & Optimize right away
232
+
233
+ = 2.9.5 =
234
+ * fixed: wrong path pre-pended using parallel optimization and wp-content or uploads folder is not within the WP root
235
+ * fixed: absolute paths passed to async optimization are pre-pended with ABSPATH
236
+ * fixed: Bulk Optimize excluding images from count based on wrong option (disabled generation vs. disabled optimization)
237
+ * fixed: timeouts during Media optimize could corrupt metadata, added routine to rebuild the meta on re-optimization
238
+ * changed: running out of API credits puts the verification function to sleep for up to 5 minutes
239
+ * added: extra checks to make sure the Background/Async objects are properly initialized before using them
240
+
241
+ = 2.9.4 =
242
+ * fixed: permissions after optimization are different than what WP core uses and falls back to umask on unixy systems
243
+ * fixed: API server address not re-fetched properly when cache expires
244
+ * changed: Parallel Optimization no longer ON by default
245
+
246
+ = 2.9.3 =
247
+ * fixed: sorry, missed a session locking operation (manual optimize)
248
+
249
+ = 2.9.2 =
250
+ * changed: priority level of Alt WebP Rewriting so that pages do not get un-minified after Autoptimize runs
251
+ * fixed: async requests for parallel optimization had an empty user agent
252
+ * fixed: uploads broken because start_session() locks all async processes
253
+
254
+ = 2.9.1 =
255
+ * changed: full paths are not POSTed to avoid Local File Inclusion blocks put in place by various security plugins (Wordfence & Shield)
256
+ * fixed: reduced number of database queries during parallel optimization
257
+ * fixed: undefined methods for BFI thumb editor class
258
+ * added: detect Shield's Lock to Location feature and disable background/parallel operations
259
+
260
+ = 2.9.0 =
261
+ * added: parallel optimization for Media uploads (original and resizes are done concurrently), turn off under Advanced if it affects site performance
262
+ * added: allow resize dimensions to be filtered: https://ewww.io/2016/07/05/changing-the-dimensions-for-resizing-images/
263
+ * changed: deferred (background) optimization is now the normal mode of operation as it runs instantly, and no longer relies on wp_cron
264
+ * changed: scheduled optimization uses new background processing to allow it to run longer, and resume quicker
265
+ * changed: webp .htaccess rules removed when plugin is deleted
266
+ * changed: JPG quality setting applies to conversion AND image editing (but not regular optimization), so that you can override the WP default of 82
267
+ * changed: API license status check is faster, as results are cached while checking for updates in the background
268
+ * fixed: .htaccess rules for webp inserted properly for sub-directory installs
269
+ * fixed: .jpe files properly detected as image/jpeg when fetching from CDN or during folder-scanning operations
270
+ * fixed: images generated by NextGEN are properly optimized with latest version
271
+ * fixed: deprecated class constructors for NextGEN, Nextcellent, and FlaGallery classes (potential white screen with PHP 7)
272
+ * fixed: basic uploader for FlaGallery broken due to missing class
273
+ * fixed: images uploaded with WPML Media active are now resized, with better detection for newly uploaded images
274
+
275
+ = 2.8.5 =
276
+ * fixed: previous security hardening used boolval(), which is not present on PHP < 5.5
277
+
278
+ = 2.8.4 =
279
+ * security: remote command execution, please update immediately
280
+
281
+ = 2.8.3 =
282
+ * fixed: tool status not shown when tool could not be found, prevents pngout installation
283
+ * fixed: notice when checking nonce lifetime during scheduled optimization
284
+ * fixed: multi-site not saving cloud optimization levels
285
+ * fixed: settings page requiring a refresh to display properly after inserting/removing an API key
286
+
287
+ = 2.8.2 =
288
+ * added: ability to use ImageMagick's 'convert' tool to convert images on Windows
289
+ * fixed: WebP images regenerated during scheduled optimization when PNG optimization disabled
290
+ * fixed: Windows executable checks obey 'use system tools' option
291
+ * fixed: settings page checks for tools which have already been tested and known missing
292
+
293
+ = 2.8.1 =
294
+ * added: kudos to Cache Enabler plugin from KeyCDN for adding WebP rewrite support to work with images generated by EWWW I.O.
295
+ * fixed: untranslatable string for resize setting description
296
+ * fixed: Resize Media Images was not applying to the Media->Add New menu item
297
+ * fixed: Bulk Optimize counted webp images as valid resizes
298
+
299
+ = 2.8.0 =
300
+ * added: resizing for uploaded images, set max width and height and optionally resize all existing images
301
+ * added: retina derivative for resized original is generated if original was at least twice the size of the max dimensions (WP Retina 2x Pro only)
302
+ * fixed: warnings for file_exists in Alt WebP function when open_basedir restriction is in effect
303
+ * removed: disable automatic optimization, use deferred optimization instead
304
+ * removed: disable optipng (it still functions, just seeing if anyone actually needs that option anymore)
305
+ * changed: consolidated various settings into optimization levels for each file format, and removed Cloud tab
306
+
307
+ = 2.7.2 =
308
+ * fixed: retina images not obeying deferred and disabled auto-optimize options
309
+ * fixed: fatal error for wp-cli when trying to optimize Media Library
310
+ * fixed: pdf optimization was checking for gif option
311
+ * fixed: pdf could not use bulk optimization or deferred optimization due to empty metadata
312
+
313
+ = 2.7.1 =
314
+ * fixed: Bulk Optimization not including PDF files
315
+ * fixed: PDF files not being checked for prior optimization
316
+ * fixed: notice for undefined index when running scheduled optimization
317
+ * changed: Scan and Optimize changed from extension blacklist to smaller extension whitelist
318
+
319
+ = 2.7.0 =
320
+ * added: PDF Optimization, both lossless AND lossy
321
+
322
+ = 2.6.2 =
323
+ * fixed: url matching for Amazon S3 urls not working for region-specific protocol handlers
324
+ * fixed: discrepancy between number of images actually queued for bulk and number of images listed as selected
325
+ * fixed: S3 images not being fetched when doing local optimization and local images have been removed
326
+ * removed: optimize again for media library after bulk optimize is complete
327
+ * changed: fewer timeouts for long-running Bulk operations by re-issuing nonce values
328
+ * changed: previously optimized CDN images show Re-optimize instead of Optimize Now
329
+ * added: pre-emptive mime-type detection for Amazon S3 images since the AWS Stream Wrapper is not reliable
330
+
331
+ = 2.6.1 =
332
+ * fixed: disabled tools being tested during optimization
333
+ * fixed: slow loading of Media Library list view with Amazon S3 attachments
334
+ * fixed: Amazon S3 images could be re-optimized after upload without Force enabled
335
+ * fixed: Amazon S3 images not shown when pressing Show Optimized Images
336
+ * fixed: error when legacy image_md5 column did not exist
337
+ * changed: last optimized time set in db for all images, not just re-optimized ones
338
+ * changed: NextGEN bulk optimize requires admin permissions by default
339
+
340
+ = 2.6.0 =
341
+ * security: missing validate, sanitize, and escape for some user and database inputs
342
+ * security: bulk optimize uses a js sleep instead of php to help avoid timeouts and protect against DOS attacks
343
+ * security: protect from CSRF by adding nonce values to one-click optimize/re-optimize/convert links
344
+ * removed: support for legacy NextGEN 1.x, please use Nextcellent for continued integration with EWWW I.O.
345
+ * fixed: nextgen (nextcellent and 2.x) styling for ui when bulk optimizing galleries and images on the Manage Galleries page
346
+ * fixed: advanced settings not showing the medium_large size introduced in WP 4.4
347
+ * fixed: path to Image Store resizes not built properly
348
+ * fixed: notices when querying for MetaSlider images
349
+ * fixed: fatal error when NextGEN2 and EWWW are active with the Photocrati theme and you try to activate another plugin
350
+ * fixed: white screen when using NextGen2's Reset Options to Default
351
+ * fixed: not properly detecting if login session expires while running bulk optimization
352
+ * fixed: webp js attempting to load even if jQuery not present
353
+ * fixed: conflict with Alternative WebP Rewriting and Cornerstone editor from X-theme
354
+ * fixed: warning generated by trying to create ewww/ tool folder when wp-content is not writable
355
+ * fixed: blank settings page when wp-content/ folder was not writable
356
+ * fixed: arrow on Plugin Status was missing due to WP admin style updates
357
+ * fixed: bulk optimize will output a proper error message then the full-size image cannot be found
358
+ * added: compatibility with Alternative WebP Rewriting and infinite scroll from Avada theme, Animated Infinite Scroll plugin, and other functions that retrieve full-page content via AJAX
359
+ * added: full compatibility with Alternative WebP Rewriting and Revolution Slider from ThemePunch
360
+ * added: Alternative WebP Rewriting supports protocol-less urls
361
+ * added: Alternative WebP Rewriting works with Easy Social Share Buttons plugin (footer widget had extra spacing)
362
+ * added: debugging page for dynamic image (re)generation to help find problematic plugins
363
+ * added: bulk optimize displays image credits needed and used/remaining credits for API users
364
+ * added: better admin notices when the wp-content/ewww/ folder cannot be created or is not writable
365
+ * changed: bulk optimize combines ajax queries for greater efficiency and to avoid tripping request limits
366
+ * changed: bulk optimize shows last optimized image details and optimization log in movable and collapsible metaboxes
367
+ * changed: speed up Cloud optimization by removing redundant API verifications when optimizing image resizes
368
+ * changed: use sha256 algorithm instead of md5 for stronger binary verification
369
+ * changed: replaced get_posts with direct wpdb calls for less overhead and to avoid broken filters from other plugins
370
+ * changed: standard lossy JPG compression (via TinyJPG) now preserves copyright when Remove Metadata is unchecked
371
+ * changed: cwebp updated to 0.5.0 and linux binaries consolidated into one static binary for better compatibility
372
+ * changed: jpegtran updated to 9b and linux binaries consolidated into one static binary for better compatibility
373
+
374
+ = 2.5.9 =
375
+ * fixed: warnings when attempting to unlink (delete) a non-existent test file
376
+ * fixed: deep checking was not enabled for pngquant and cwebp (optional utilities)
377
+
378
+ = 2.5.8 =
379
+ * added: advanced checking for binaries using sample images when version output is suppressed
380
+ * fixed: CPU overload causing 503 errors related to WebP function and output buffering parameters
381
+ * fixed: call to old debug function in Image Store Optimize page
382
+ * fixed: notices if action2 is not specified from Media Library bulk action drop-down
383
+ * changed: streamlined binary checking to allow -custom and -alt binaries for all tools, including Windows
384
+
385
+ = 2.5.7 =
386
+ * fixed: MySQL column index too large when collation is utf8mb4 prevents table creation and throws warnings on upgrades
387
+ * fixed: cleanup of table upgrade function to avoid unnecessary queries
388
+ * fixed: Optimized string was undefined for flagallery and nextgen bulk optimization
389
+ * fixed: When activated network-wide, settings link on per-site Plugins page was incorrect
390
+
391
+ = 2.5.6 =
392
+ * fixed: avoid memory leaks from calls to ewwwio_debug_message() within ewww_image_optimizer_require() for multi-site users
393
+
394
+ = 2.5.5 =
395
+ * fixed: prevent duplicate scheduled optimizations from running concurrently
396
+ * fixed: removed redundant checks from scheduled optimization
397
+ * changed: files without extensions are skipped by the folder scanning function
398
+ * changed: hidden files are skipped by the folder scanning function (can be modified with a filter)
399
+ * changed: new installs will have the collation set properly for the ewwwio_images table
400
+ * changed: make require() and include() less fatal and use admin notices instead
401
+ * fixed: warnings when deferred optimization queue is empty
402
+
403
+ = 2.5.4 =
404
+ * changed: Remove metadata turned on by default, should not affect existing installations/upgrades
405
+ * changed: Português and Español moved to language packs
406
+ * fixed: notices from redefining constants
407
+ * updated: bundled pngquant to version 2.5.2
408
+ * updated: bundled cwebp to version 0.4.4
409
+ * deprecated: cwebp will not be updated for Mac OS X 10.8 past 0.4.2
410
+
411
+ = 2.5.3 =
412
+ * fixed: wpdb call causes error during scheduled optimization
413
+ * fixed: mismatched CN for SSL certs on cloud servers
414
+ * changed: French, Bulgarian, Romanian, German and Polish translations have been moved to language packs for auto-updating
415
+ * changed: allow 755 or greater permissions instead of only 755 for local binaries
416
+ * added: Alt WebP Rewriting supports new srcset and sizes attributes in WordPress 4.4
417
+
418
+ = 2.5.2 =
419
+ * new: all our installation videos have been re-done so that they are up-to-date and answer some common questions
420
+ * changed: much faster scanning for Scan & Optimize when ewwwio table is large
421
+ * fixed: check WP_CONTENT_DIR setting if wp_upload_dir() is reporting the wrong upload directory
422
+ * fixed: translations for fr_BE and uk (Ukrainian)
423
+ * fixed: .htaccess installer for webp rules
424
+ * fixed: alt webp rewriting gets stuck when <head> tag has a space: <head >
425
+ * fixed: notice thrown when trying to call unregister_setting before any settings were actually registered for EWWW
426
+
427
+ = 2.5.1 =
428
+ * added: Portuguese (Portugal) translation for pt_PT thanks to Celso Azevedo
429
+ * added: optimization for custom sizes for "Fraction" theme
430
+ * added: filter to override restrictions for Folders to Optimize
431
+ * added: automatic fallback for conversion options if a toolkit does not produce any output
432
+ * added: notice for WP Engine users to use Cloud version of EWWW Image Optimizer
433
+ * fixed: bulk delay was ignored when processing deferred images
434
+ * fixed: notices when scanning media library to load Bulk Optimize page
435
+ * fixed: tooltip text was not escaped properly for one-click conversion links
436
+ * fixed: warning when deferred optimization runs and there is nothing available to optimize
437
+ * fixed: error when bulk optimizing and w3_upload_info() function is missing
438
+ * fixed: error when passing empty value to json_encode()
439
+ * fixed: error on Unoptimized Images when bulk optimization resume flag is set, but no attachments are left
440
+ * fixed: Unoptimized Images will scan entire library when bulk optimization resume flag is set, instead of just remaining attachments
441
+
442
+ = 2.5.0 =
443
+ * deprecated: Disable Automatic Optimization and Include Media Folders options: will be removed from the UI in 2.6 but remain functional if enabled
444
+ * added: deferred optimization lets you upload images with no delays, and optimize later automatically
445
+ * added: wp_cron filter has additional parameter to allow setting scheduled & deferred optimization on different freqencies
446
+ * added: remote images on S3 can be fetched when using WP Offload S3 (Amazon S3 and Cloudfront)
447
+ * added: remote images on Azure Storage can be fetched when using Windows Azure Storage for WordPress
448
+ * added: (re)upload to Dreamspeed after optimization
449
+ * added: action hooks before and after optimization
450
+ * added: filter to modify the number of records queried when counting unoptimized images (default 3000)
451
+ * added: check for retina images generated without WP Retina 2x, with filter to modify @2x extension
452
+ * added: support for Imagick and Gmagick extensons when converting images (JPG2PNG and PNG2JPG)
453
+ * changed: nextcellent thumbs are optimized on creation, no need to manually optimize after upload
454
+ * changed: API keys are masked as password fields
455
+ * changed: debugging functions streamlined to reduce memory usage
456
+ * updated: translator credits - huge THANK YOU to all of them!
457
+ * fixed: errant tool warnings for cloud users in nextgen and flagallery
458
+ * fixed: catch extraction error for pngout during automatic install
459
+ * fixed: settings link in error notices for network-activated installs
460
+ * fixed: regression with alt webp rewriting introduced in 2.4.4 that caused duplicate <html> and <head> tags in some cases
461
+ * fixed: url replacement when restoring original for a converted image
462
+
463
+ = 2.4.7 =
464
+ * fixed: defer nextgen loading until 'init' to prevent activation/upgrade problems
465
+ * fixed: nextgen dynamic image generation fails if API subscription is out of image credits
466
+
467
+ = 2.4.6 =
468
+ * fixed: some admin pages were testing all tools regardless of the active settings (also improves admin load times)
469
+ * fixed: check that image exists in WP_Image_Editor extension
470
+ * fixed: load 'tool_init' earlier on Media Library to prevent errors with Enhanced Media Library plugin
471
+ * added: filter to modify/suppress output of thumbnail optimization message after image upload for Nextcellent (useful for things like Lightroom integration)
472
+ * updated: Italian (it_IT) translation
473
+
474
+ = 2.4.5 =
475
+ * fixed: warning on settings page for implode() function
476
+ * fixed: notice on admin pages with get_home_url() function
477
+ * updated: gifsicle works again on Windows XP and Server 2003
478
+ * added: filter to allow changing time period for scheduled optimization
479
+
480
+ = 2.4.4 =
481
+ * fixed: Alt WebP Rewriting unable to find images when WP url and Site url are different (subdirectory install)
482
+ * fixed: Alt WebP Rewriting mangles certain characters due to older versions of libxml
483
+ * fixed: Alt WebP Rewriting parses xml files when it should leave them alone - feeds and sitemaps
484
+ * fixed: issues with API license exceeded during bulk optimization
485
+ * fixed: pngout regression with .tmp and .tmp.png files preventing optimization
486
+ * updated: bundled Gifsicle updated to 1.87
487
+ * updated: bundled cwebp updated to 0.4.3 (0.4.2 for Mac OS 10.8)
488
+ * deprecated: pngout 20151319 does not work on CentOS 5, older versions available at http://static.jonof.id.au/dl/kenutils/
489
+ * deprecated: FreeBSD 8.4 support, moving to 9.3 64-bit only
490
+
491
+ = 2.4.3 =
492
+ * fixed: Alt WebP Rewriting breaks themes with <header> elements
493
+
494
+ = 2.4.2 =
495
+ * updated: pngout installer updated to release 20150319
496
+ * updated: set_time_limit() moved to core function for even better timeout avoidance, and threshold increased to 90
497
+ * fixed: Alt WebP Rewriting detects XHTML themes, and attempts to parse them as XML, but will still break if your theme does not pass validation.
498
+ * fixed: cleanup output of html entities when using wp-cli
499
+ * fixed: Scan & Optimize throws warnings when a directory is not detected properly
500
+ * fixed: --noprompt for wp-cli has no effect
501
+ * fixed: notices for exec() and Safe Mode not firing properly
502
+ * fixed: prevent tools from being checked if exec() is disabled or Safe Mode is on during optimization
503
+ * fixed: check to see if set_time_limit() is disabled before running it
504
+ * added: W3TC S3 CDN - update original image on S3 after optimization
505
+ * added: German (de_DE) translation
506
+ * added: French (fr_FR) translation
507
+ * added: call set_time_limit() to avoid timeouts loading the Bulk Optimize page
508
+
509
+ = 2.4.1 =
510
+ * fixed: Alt WebP Rewriting was slow due to an inefficient regexp
511
+ * fixed: Scan & Optimize fails when it encounters a permissions error
512
+
513
+ = 2.4.0 =
514
+ * added: advanced option to disable specific resizes or just exclude them from optimization
515
+ * added: Turkish and Swedish translations (with updates of most other translations)
516
+ * added: protection to prevent corruption of images in case of broken mimetype detection
517
+ * fixed: check to prevent issues with reloading nextgen2 support was only half-effective
518
+ * fixed: previous fix for wrong slash on Windows breaks savings settings for Network sites
519
+ * fixed: WP_Image_Editor init() check was not checking the right constant
520
+ * fixed: Alternative WebP Rewriting had a mismatched preg_replace causing broken <html> or <head> tags
521
+ * fixed: some NextGen bulk optimize functions were broken when using various translations
522
+
523
+ = 2.3.2 =
524
+ * fixed: sql error for duplicate key name during plugin upgrade
525
+ * fixed: is_plugin_active undefined during scheduled optimization
526
+ * fixed: webp rewriting strips </body> and </html>
527
+ * fixed: leftover javascript showing 0 for Total Savings
528
+ * changed: minify and load webp script inline
529
+ * changed: client-side webp detection for caching plugins
530
+ * changed: settings page ui refinements
531
+ * changed: prevent parsing the request with alternative webp rewriting unless it contains html
532
+ * changed: more extensions added to blacklist during Scan and Optimize to prevent memory errors
533
+ * changed: added checks to prevent redeclaring ewwwngg and ewwwflag classes
534
+
535
+ = 2.3.1 =
536
+ * fixed: load_webp.js was being inserted regardless of the associated Alternative WebP Rewrites option
537
+ * fixed: wrong slash in plugin path for Windows users with NextGEN and FlaGallery
538
+ * fixed: extra comma in table upgrade sql
539
+ * fixed: special characters malformed by alternate webp rewriting
540
+ * updated: translation for Spanish
541
+ * changed: progressbar color updated to match new colors in 4.2 for default theme
542
+
543
+ = 2.3.0 =
544
+ * fixed: bug in GIF processing rendered Gifsicle impotent (no savings possible), non Cloud users should re-optimize all their GIFs in Force mode
545
+ * added: WebP url rewriting for sites using CDNs, requires output buffering and libxml in PHP, and may require modifications for some themes
546
+ * added: option to include last two months of Media Library images in Scheduled Optimization (for those that have disabled Automatic Optimization)
547
+ * added: automatic optimization for dynamic resizes generated by NextGEN 2+, particularly useful for Plus/Pro users
548
+ * added: option to speed up lossy compression by using less compression
549
+ * added: compatibility with NextGEN Public Uploader and other NextGEN 2 plugins that use legacy uploads
550
+ * added: auto-optimization for MyArcade plugin
551
+ * added: delay uploading with W3TC CDN function until after optimization
552
+ * changed: resizes are not processed twice during upload. they were only optimized once previously, but this should give a small speed boost to uploads.
553
+ * changed: manual optimize/convert/restore links require editor role, bulk optimization requires admin role, can be changed via filters
554
+ * changed: disabling automatic optimization affects Nextgen, Nextcellent, and FlaGallery as well
555
+ * changed: lossy compression for EWWW I.O. Cloud users now uses TinyJPG and TinyPNG for superior compression
556
+ * changed: added index to ewwwio_images table and modified queries for substantial speed-up (and less load on database servers)
557
+ * changed: Total Savings calculation now uses a single SQL statement, please report any related errors right away
558
+ * changed: cleaned up flagallery and nextgen integration loading and made it folder-agnostic
559
+ * changed: suppress plugin warnings when running 'init' outside of admin pages
560
+ * fixed: Folders to Optimize was not being validated properly
561
+ * fixed: notice on Unoptimized Images page
562
+ * fixed: mysql error when attempting to query negative number of records on settings page
563
+ * fixed: disabling cloud api no longer sets optipng/pngout levels to max
564
+ * fixed: bug with image savings string in Spanish translation
565
+ * fixed: referencing object as an array when scanning for Meta Slider images causes Scan & Optimize to fail
566
+ * fixed: BIGINT errors when calculating savings
567
+ * fixed: warning with Nextgen2 when plugin init had not yet occurred
568
+ * fixed: Scan and Optimize consuming too much memory when checking mimetype of .po files
569
+ * fixed: wp retina detection queries referencing object as an array
570
+ * fixed: originals from converted resizes were not deleted during attachment removal
571
+ * fixed: WebP versions of retina 2x images were not renamed properly
572
+ * fixed: Unoptimized images displays an empty table for zero images to optimize
573
+ * updated: translations for Portuguese, Romanian, and Polish
574
+
575
+ = 2.2.2 =
576
+ * fixed: previous fix for deleting webp images was not working properly
577
+
578
+ = 2.2.1 =
579
+ * fixed: infinite loop on hosts where set_time_limit does not work
580
+
581
+ = 2.2.0 =
582
+ * added: wp-cli command to optimize via command-line, 'wp-cli help ewwwio optimize' for more details
583
+ * added: Unoptimized Images page to show ONLY images that have not been processed by EWWW (under Media Library)
584
+ * added: advanced option to preserve metadata for full-size originals
585
+ * added: disable automatic optimization on upload under advanced options if you prefer to manually optimize in batches, or by scheduled optimization
586
+ * changed: webp images are checked during deletion of images, though WP already removes any newer webp versions that are in the attachment metadata
587
+ * fixed: load text domain earlier so that admin menu items are properly translated
588
+ * fixed: Total Savings calculates properly on multi-site installs when network-activated
589
+ * fixed: Total Savings was double-counting the first 1000 image query
590
+ * FlaGallery 4.27 resolves the optimize on upload issue, and fixes problems with the new wp-cli functions
591
+
592
+ = 2.1.2 =
593
+ *fixed: post-processing call to Amazon S3 and Cloudfront was broken when upgrading it to .7 or higher, fixed to allow both .6 and .7 to work with EWWW IO
594
+
595
+ = 2.1.1 =
596
+ * broken: optimize on upload currently broken for flagallery
597
+ * deprecated: NextGEN legacy support will be removed in 2.2 unless I hear from anyone still using it, Nextcellent will continue to be supported
598
+ * changed: all image types are enabled when cloud API key is validated (but only if you do not choose individual options)
599
+ * changed: prefixed javascript/request variables to avoid potential conflicts
600
+ * fixed: undefined variable $log when uploading images
601
+ * fixed: undefined variable $force when running scheduled optimize
602
+ * fixed: undefined index JPG Support when GD is missing
603
+ * added: memory logging in memory.log when WP_DEBUG is turned on in wp-config.php
604
+ * fixed: bulk actions for Nextcellent were missing
605
+ * fixed: notices generated because webp versions do not have height and width when WP is scanning resizes
606
+ * fixed: notices generated due to no optimization status during bulk optimization for webp versions
607
+ * fixed: error when trying to unserialize an array for Image Store Optimize page
608
+ * changed: binary installation and checking only on specific admin pages instead of all admin pages, please report breakages ASAP
609
+ * added: Portuguese translation (pt_BR), props to Pedro Marcelo de Sá Alves
610
+
611
+ = 2.1.0 =
612
+ * security: ssl strengthened for cloud users, no more SSLv3 (thanks POODLE), and other additional encryption tweaks, please report related errors ASAP
613
+ * fixed: warning when scheduled scanner doesn't have any images to optimize
614
+ * added: option to skip PNG images over a certain size since PNG images are prone to timeouts
615
+ * added: compatibility with Animated Gif Resize plugin to preserve animation even in resizes
616
+ * added: compatibility with Hammy plugin to generate dynamic resize versions on demand (and any other plugin/theme that uses WPThumb)
617
+ * added: optimizing previously uploaded images (via bulk or otherwise) also uploads to Amazon S3 with the Amazon Cloudfront and S3 plugin
618
+ * added: webp images are tracked in attachment metadata to enable upload via AWS plugins, but webp images are not deleted when attachments are deleted from Media Library (yet)
619
+ * added: previously generated retina images (WP Retina 2x) are processed by standard Media Library routine, instead of via Folders to Optimize
620
+ * changed: streamlined wp_image_editor extensions to be more future-proof
621
+ * updated: all translations have been updated
622
+
623
+ = 2.0.2 =
624
+ * security: pngout error message properly sanitized to prevent XSS attack
625
+ * changed: changed priority for processing Media Library images to run before Amazon Cloudfront plugin, this could affect other plugins that hook on wp_generate_attachment_metadata
626
+ * fixed: cloud users seeing 'needs attention' incorrectly
627
+ * fixed: error counter for bulk not being reset when successfully resuming
628
+ * fixed: clarification about jpegmini and cmyk images
629
+ * fixed: debugging errors for optipng/pngout levels should not be displayed for cloud users
630
+ * fixed: pngout error was printing to screen prematurely
631
+ * fixed: Image Store resizes were being double-optimized due to filename changes
632
+
633
+ = 2.0.1 =
634
+ * fixed: naming conflict with webp when jpg/png files have identical names, read NOTE above
635
+ * fixed: folders to optimize are not retrieved properly on settings page
636
+ * fixed: undefined variable in permissions check for cwebp on Mac OSX
637
+ * fixed: prevent excess calls for cwebp
638
+ * fixed: wpdb->prepare should have two arguments
639
+ * updated: Spanish translation
640
+ * added: Russian translation
641
+ * changed: alternative binaries for jpegtran and cwebp use -alt suffix to avoid conflict with user-compiled binaries
642
+ * removed: deprecated import process from bulk optimize page
643
+ * removed: empty table option from bulk optimize page, use the Force checkbox instead
644
+ * changed: force re-optimize checkbox applies to Media Library AND the Scan and Optimize function
645
+ * changed: plugin status auto-collapses to save screen space, unless something needs your attention
646
+ * changed: settings tabs have been moved below the status section (directly above the settings area) to enhance usability
647
+
648
+ = 2.0.0 =
649
+ * NOTE: while this is a release with new features, it is not a rewrite, only the next number in the decimal system, just like the WP numbering scheme
650
+ * added: webp generation (wahooooooooo)
651
+ * added: jpegmini support (more wahooooo, but requires a cloud subscription)
652
+ * fixed: jpeg quality not being set properly for 4.0 on resizes
653
+ * changed: settings page, feel free to give me feedback on the new menubar
654
+ * fixed: some settings not being validated properly for multi-site
655
+ * added: up to 30 second retry when bulk optimize is interrupted
656
+ * changed: various code cleanup
657
+ * fixed: prevent excess warnings/notices when binaries can't be installed
658
+ * fixed: prevent binary installer from firing on unsupported operating systems
659
+ * changed: better verification when saving settings for multi-site
660
+ * changed: all cloud transactions are now secured (https)
661
+ * fixed: use nextgen2's unserialize function to query metadata during bulk optimize
662
+ * added: Polish translation
663
+ * updated: Dutch and Romanian translations
664
+ * updated: Tutorial videos on the Installation page have updated finally
665
+ * updated: new binaries for optipng, gifsicle, and pngquant
666
+ * updated: recompiled jpegtran binaries to be smaller
667
+ * fixed: import failed if nextgen classes aren't available during import
668
+
669
+ = 1.9.3 =
670
+ * added: fallback mode when totals for resizes and unoptimized images cannot be determined by the bulk optimize tool
671
+ * added: up to 30 second retry when import is interrupted on bulk optimize page
672
+ * fixed: suppress 'empty server response' messages for cloud users, instead correctly report No Savings
673
+
674
+ = 1.9.2 =
675
+ * fixed: memory limit exceeded when counting total savings on settings page
676
+ * fixed: application/octet-stream is accepted as valid output for mimetype check on executables
677
+ * added: PngOptimizerCL for even better optimization of PNG images on cloud service
678
+ * changed: cloud processing nodes upgraded for faster image processing
679
+ * changed: made queries for resuming bulk operations more efficient to avoid running into max query length problems
680
+ * fixed: images that were not processed (cloud or otherwise) can be optimized later (they are no longer stored in ewwwio_images table)
681
+ * changed: more efficient verification of cloud api keys
682
+
683
+ = 1.9.1 =
684
+ * fixed: escapeshellarg command breaks Windows filenames
685
+ * fixed: newer versions of pngquant not detected
686
+ * fixed: properly check paletted/indexed PNG files for transparency (requires GD)
687
+ * fixed: images smaller than imsanity resize limit trigger notice
688
+ * changed: exclude full-size from lossy optimization applies to lossy conversions too
689
+ * changed: no more caching of cloud key verification results, since verification is 300x faster, and only called when we absolutely need it
690
+ * added: status for pngquant on settings page when lossy optimization is enabled
691
+ * added: Optimized/webview sizes in FlaGallery are tracked properly, and optimized during bulk operations, and manual one-time optimizations.
692
+ * added: use nextgen2 hook for adding action link in gallery management pages
693
+
694
+ = 1.9.0 =
695
+ * changed: verification results for cloud optimization are still cached, but actual optimization requires pre-verification to maintain load-balancing
696
+ * added: NextCellent Gallery support - no future development will be done for NextGEN 1.9.13, all future development will be on NextCellent.
697
+ * updated translations for Romanian and Dutch
698
+ * fixed some warnings and notices
699
+ * added GMedia folder to Scan and Optimize function
700
+ * show cumulative savings in status section
701
+ * added: filter to bypass optimization for developer use
702
+ * added: option to bypass optimization for small images
703
+
704
+ = 1.8.5 =
705
+ * fixed: images with empty metadata count as unoptimized images on Bulk Optimize
706
+ * changed: Import process split into batches via AJAX to make it less likely to timeout and use less memory
707
+ * changed: Bulk Optimize page uses less memory and is quicker to load
708
+ * fixed: custom column in NextGEN galleries works again with NextGEN 2.0.50+
709
+ * changed: cloud api cache refreshes properly when visiting Settings page
710
+ * fixed: license exceeded messages do not stall Bulk Optimize incorrectly
711
+ * fixed: warning on Bulk Optimize for sites using UTC
712
+ * fixed: user-specified paths to optimize did not work if using multi-site WP with plugin activated per-site
713
+ * fixed: gifsicle sometimes generates slightly larger images (not anymore)
714
+
715
+ = 1.8.4 =
716
+ * fixed: Import process is much faster by about 50x
717
+
718
+ = 1.8.3 =
719
+ * fixed: tools cannot be found if there are spaces in the WP paths
720
+ * changed: API key validation is now cached to greatly reduce page load time, mostly on the admin side, but also for any sites that generate or allow uploading images on the front-end
721
+ * fixed: a few WP Retina @2x images were not being optimized, and none of them were stored in the ewwwio_images table properly
722
+ * new: better compression for cloud users via advpng
723
+ * new: lossy compression for PNG images via pngquant
724
+ * changed: Bulk Optimize loads much quicker (mostly noticable on sites with thousands of images)
725
+
726
+ = 1.8.2 =
727
+ * updated Romanian translation
728
+ * removed: potentially long-running query from upgrade
729
+ * fixed: cloud queries were using the wrong hostname, all cloud users must apply this update to avoid service degradation
730
+
731
+ = 1.8.1 =
732
+ * fixed: ewww_image_optimizer_aux_images_loop() undefined causes any calls to WP_Image_Editor to fail (breaks lots of stuff)
733
+
734
+ = 1.8.0 =
735
+ * fixed: debug output not working properly on bulk optimize
736
+ * changed: when cloud license has been exceeded, the optimizer will not attempt to upload images, and bulk operations will stop immediately
737
+ * fixed: unnecessary decimals will not be displayed for file-sizes in bytes
738
+ * added: button to stop bulk optimization process
739
+ * fixed: rewrote escapeshellarg() to avoid stripping accented characters from filenames
740
+ * fixed: problems with apostrophes in filenames
741
+ * changed: Optimize More and Bulk Optimize are now on the same page
742
+ * changed: After running Optimize More, you can Show Optimized Images and Empty Table without refreshing the page.
743
+ * fixed: blank page when resetting bulk status in flagallery
744
+ * change: already optimized images in Media Library will not be re-optimized by default via bulk tool
745
+ * fixed: FlaGallery version 4.0, optimize on upload now works with plupload
746
+ * fixed: proper validation that an image has been removed from the auxilliary images table
747
+ * move more code into admin_init to improve page load on front-end
748
+ * added: ability to specify number of seconds between images (throttling)
749
+ * added: nextgen and grand flagallery thumb optimization is now stored in database
750
+ * change: significant speed improvement, optimizer only checks for the tools it needs for the current image
751
+ * fixed: urls for converted resizes were not being updated in posts
752
+ * fixed: attempt to convert PNGs with empty alpha channels after optimization on first pass, instead of on re-optimization
753
+
754
+ = 1.7.6 =
755
+ * fixed: color of progressbar for 4 more admin themes in WP 3.8
756
+ * changed: metadata stripping now applies to PNG images, but only if using optipng 0.7.x
757
+ * added: ability to remove individual images from the Optimize More table
758
+ * fixed: Optimize More was using case-insensitive queries for matching paths
759
+ * fixed: Optimize More was unable to record image sizes over 8388607 bytes
760
+ * removed: obsolete jquery 1.9.1 file used for maintaining backwards compatiblity with really old versions of WP
761
+ * fixed: weirdness with paths preventing Windows servers from activating, and cleanup of plugin path code
762
+
763
+ = 1.7.5 =
764
+ * new version of gifsicle (1.78), for more detail, see http://www.lcdf.org/gifsicle/changes.html
765
+ * proper detection of Cloudinary images instead of error message
766
+ * plays nicer with Imsanity, detect when a newly uploaded image has been modified and optimized already (instead of re-optimizing)
767
+ * Dutch translation - nl_NL
768
+ * Romanian translation - ro_RO
769
+ * Spanish translation - es_ES
770
+ * Cloudinary integration: auto-upload after optimization when uploading to Media Library, must be enabled in settings
771
+ * debugging output for Media Library (let's you see resizes)
772
+ * visual tweaking for upcoming WP 3.8
773
+ * better checking for safe_mode
774
+
775
+ = 1.7.4 =
776
+ * fixed: some settings were set to incorrect defaults after enabling and disabling cloud features
777
+ * fixed: invalid status on some systems for 'tar' command
778
+ * new: SunOS support - OpenIndiana and Solaris
779
+ * fixed: resizes not properly checking for re-optimization prevention
780
+
781
+ = 1.7.3 =
782
+ * fixed: some security plugins disable Optimize More - use install_themes permission instead of edit_themes
783
+ * fixed: table schema changes not firing on upgrade
784
+ * changed: bulk_attachment variables are not autoloaded to improve performance
785
+
786
+ = 1.7.2 =
787
+ * added: internationalization - need volunteers to provide translations.
788
+ * fixed: Import button not shown on Optimize More in some cases
789
+ * fixed: Bulk Optimize for Nextgen was broken
790
+ * changed: file comparison from md5sum to filesize for Optimize More to improve load time
791
+ * added: quota information for cloud users on settings page
792
+ * fixed: sub-folders of uploads directory were not allowed if /uploads is outside of wp folder
793
+ * changed: increased cloud_verify timeout to avoid false results
794
+ * added: link to status page for cloud service on settings page
795
+ * fixed: debug log created if it does not exist already
796
+
797
+ = 1.7.1 =
798
+ * fixed: syntax error causing white screen of death for Nextgen v2
799
+
800
+ = 1.7.0 =
801
+ * added: ability to optimize specified folders within your wordpress install
802
+ * added: option to optimize on a schedule for images that cannot be automatically optimized on upload (buddypress, symposium, metaslider, user-specified folders)
803
+ * added: WP Symposium support via 'Optimize More' in Tools menu
804
+ * added: BuddyPress Activity Plus support via 'Optimize More'
805
+ * fixed: unnecessary check for 'file' field in attachment metadata
806
+ * fixed: network-level settings are not reset on deactivation and reactivation
807
+ * fixed: blog-level settings not displayed when activated at the blog-level on multi-site
808
+ * added: Any plugin that uses wp_image_editor (GD, Imagick, and Gmagick implementations) will be auto-optimized on upload
809
+ * fixed: Optimize More will crash if one of the standard folders does not exist (e.g.: buddypress avatar folders)
810
+ * fixed: filenames are escaped to prevent potential crashes and security risks
811
+ * fixed: temporary jpgs are checked to be sure they exist to avoid warnings
812
+ * fixed: prevent warnings on bulk optimize due to empty arrays
813
+ * fixed: don't check permissions until after we know file exists
814
+ * fixed: WP get_attached_file() doesn't always work, try other methods to get attachment path
815
+ * removed: deprecated setting to skip utility verification
816
+ * fixed: init not firing for plugins with front-end functionality
817
+ * fixed: suppress warnings if corrupt jpg crashes jpegtran
818
+ * added: screencasts on plugin Installation page
819
+
820
+ = 1.6.3 =
821
+ * plugin will failover gracefully if one of the cloud optimization servers is offline
822
+ * prevent excess database calls when optimizing theme images
823
+ * fixed plugin mangles metadata for Image Store plugin
824
+ * added optimization support for Image Store plugin
825
+ * verify md5 on buddypress optimization, so changed images will get re-optimized by the bulk tool
826
+ * cleaned up settings page (mostly) for cloud users
827
+
828
+ = 1.6.2 =
829
+ * added license exceeded status into status message so users know if they've gone over
830
+ * prevent tool checks and cloud verification from firing on every page load, yikes...
831
+
832
+ = 1.6.1 =
833
+ *fixed: temporary jpgs were not being deleted (leftovers from testing for last release)
834
+ *fixed: jpgs would not be converted to pngs if jpgs had already been optimized
835
+ *fixed: cloud service not converting gif to png
836
+
837
+ = 1.6.0 =
838
+ * Cloud Optimization option (BETA: get your free API key at http://www.exactlywww.com/cloud/)
839
+ * fixed if exec() is disabled or safe mode is on, don't bother testing local tools
840
+ * more tweaks for exec() detection, including suhosin extension
841
+
842
+ = 1.5.0 =
843
+ * BuddyPress integration to optimize avatars
844
+ * added function to optimize all images in currently active theme
845
+ * full compatibility with NextGEN 2.0.x
846
+ * thumbnails are now optimized automatically on upload with NextGEN 2.0.x
847
+ * fixed detection of disabled exec() function when exec is the first function in the list
848
+ * use internal wordpress functions for retrieving image path, displaying filesize, building redirect urls, and downloading pngout
849
+
850
+ = 1.4.4 =
851
+ * fixed bulk optimization functions for non-English users in NextGEN
852
+ * fixed bulk action conflict in NextGEN
853
+
854
+ = 1.4.3 =
855
+ * global configuration for multi-site/network installs
856
+ * prevent loading of bundled jquery on WP versions that don't need it to avoid conflicts with other plugins not doing the 'right thing'
857
+ * removed enqueueing of common.js to make things run quicker
858
+ * fixed hardcoded link for optimizing nextgen thumbs after upload
859
+ * added links in media library for one time conversion of images
860
+ * better error reporting for pngout auto-install
861
+ * no longer alert users of jpegtran update if they are using version 8
862
+
863
+ = 1.4.2 =
864
+ * fixed fatal errors when posix_getpwuid() is missing from server
865
+ * removed path restrictions, and fixed path detection for old blogs where upload path was modified
866
+
867
+ = 1.4.1 =
868
+ * FlaGallery and NextGEN Bulk functions are now using ajax functions with nicer progress bars and such
869
+ * NextGEN now has ability to optimize selected galleries, or selected images in bulk (FlaGallery already had it)
870
+ * NextGEN users can now click a button to optimize thumbnails after uploading new images
871
+ * use built-in php mimetype functions to check binaries, saving 'file' command for fallback
872
+ * added donation links, since several folks have expressed interest in contributing financially
873
+ * bundled jquery and jquery-ui for using bulk functions on older WP versions
874
+ * use 32-bit jpegtran binary on 'odd' 64-bit linux servers
875
+ * rewrote debugging functionality, available on bulk operations and settings page
876
+ * increased compatibility back to 2.8 - hope no one is actually using that, but just in case...
877
+
878
+ = 1.4.0 =
879
+ * fixed bug with missing 'nice' not detected properly
880
+ * added: Windows support, includes gifsicle, optipng, and jpegtran executables
881
+ * added: FreeBSD support, includes gifsicle, optipng, and jpegtran executables
882
+ * rewrote calls to jpegtran to avoid shell-redirection and work in Windows
883
+ * jpegtran is now bundled for all platforms
884
+ * updated gifsicle to 1.70
885
+ * pngout installer and version updated to February 20-21 2013
886
+ * removed use of shell_exec()
887
+ * fixed warning on ImageMagick version check
888
+ * revamped binary checking, should work on more hosts
889
+ * check permissions on jpegtran
890
+ * rewrote bulk optimizer to use ajax for better progress indication and error handling
891
+ * added: 64-bit jpegtran binary for linux servers missing compatibility libraries
892
+
893
+ = 1.3.8 =
894
+ * fixed: finfo library doesn't work on PHP versions below 5.3.0 due to missing constant
895
+ * fixed: resume button doesn't resume when the running the bulk action on groups of images
896
+ * shell_exec() and exec() detection is more robust
897
+ * added architecture information and warning if 'file' command is missing on settings page
898
+ * added finfo functionality to nextgen and flagallery
899
+
900
+ = 1.3.7 =
901
+ * re-compiled bundled optipng and gifsicle on CentOS 5 for wider compatibility
902
+
903
+ = 1.3.6 =
904
+ * fixed: servers with gzip still failed on bulk operations, forgot to delete a line I was testing for alternatives
905
+ * fixed: some servers with shell_exec() disabled were not detected due to whitespace issues
906
+ * fixed: shell_exec() was not used in PNGtoJPG conversion
907
+ * fixed: JPGs not optimized during PNGtoJPG conversion
908
+ * allow debug info to be shown via javascript link on settings page
909
+ * code cleanup
910
+
911
+ = 1.3.5 =
912
+ * fixed: resuming a bulk optimize on FlAGallery was broken
913
+ * added resume button when running the bulk optimize operation to make it easier to resume a bulk optimize
914
+
915
+ = 1.3.4 =
916
+ * fixed optipng check for older versions (0.6.x)
917
+ * look in system paths for pngout and pngout-static
918
+ * added option for ignoring bundled binaries and using binaries located in system paths instead
919
+ * added notices on options page for out-of-date binaries
920
+
921
+ = 1.3.3 =
922
+ * use finfo functions in PHP 5.3+ instead of deprecated mime_content_type
923
+ * use shell_exec() to make calls to jpegtran more secure and avoid output redirection
924
+ * added bulk action to optimize multiple galleries on the manage galleries page - FlAGallery
925
+ * added bulk action to optimize multiple images on the manage images page - FlAGallery
926
+
927
+ = 1.3.2 =
928
+ * fixed: forgot to apply gzip fix to NextGEN and FlAGallery
929
+
930
+ = 1.3.1 =
931
+ * fixed: turning off gzip for Apache broke bulk operations
932
+
933
+ = 1.3.0 =
934
+ * support for GRAND FlAGallery (flash album gallery)
935
+ * added ability to restore originals after a conversion (we were already storing the original paths in the database)
936
+ * fixed: resized converted images had the wrong original path stored
937
+ * fixed: tools get deleted after every upgrade (moved to wp-content/ewww)
938
+ * fixed: using activation hook incorrectly to fix permissions on upgrades (now we check when you visit the wordpress admin)
939
+ * removed deprecated path settings, custom-built binaries will be copied automatically to the wp-content/ewww folder
940
+ * better validation of tools, no longer using 'which'
941
+ * removed redundant path checks to avoid extra processing time
942
+ * moved NextGEN bulk optimize into NextGEN menu
943
+ * NextGEN and FlAGallery functions only run when the associated gallery plugin is active
944
+ * turn off page compression for bulk operations to avoid output buffering
945
+ * added status messages when attempting automatic installation of jpegtran or pngout
946
+ * NEW version of bundled gifsicle can produce better-optimized GIFs
947
+ * revamped settings page to combine version info, optimizer status, and installation options
948
+ * binaries for Mac OS X available: gifsicle, optipng, and pngout
949
+ * images are re-optimized when you use the WP Image Editor (but never converted)
950
+ * fixed: unsupported files have empty path stored in meta
951
+ * fixed: files with empty paths throw PHP notices in Media Library (DEBUG mode only)
952
+ * when a converted attachment is deleted from wordpress, original images are also cleaned up
953
+
954
+ = 1.2.2 =
955
+ * fixed: uninitialized variables
956
+ * update links in posts for converted images
957
+ * fixed: png2jpg sometimes fills with black instead of chosen color
958
+ * fixed: thumbnails for animated gifs were not allowed to convert to png
959
+ * added pngout version to debug
960
+
961
+ = 1.2.1 =
962
+ * fixed: wordpress plugin installer removes executable bit from bundled tools
963
+
964
+ = 1.2.0 =
965
+ * SECURITY: bundled optipng updated to 0.7.4
966
+ * deprecated manual path settings, please put binaries in the plugin folder instead
967
+ * new one-click install option for jpegtran
968
+ * one-click for pngout is more efficient (doesn't redownload tarball) if it exists
969
+ * optipng and gifsicle now bundled with the plugin
970
+ * new *optional* conversion routines check for smallest file format
971
+ * added gif2png
972
+ * added jpg2png
973
+ * added png2jpg
974
+ * reorganized settings page (it was getting ugly) and cleaned up debug area
975
+ * added poll for feedback
976
+ * thumbnails are now optimized in NextGEN during a manual optimize (but not on initial upload)
977
+ * utilities have a 'niceness' value of 10 added to give them lower priority
978
+
979
+ = 1.1.1 =
980
+ * fixed not returning results of resized version of image
981
+
982
+ = 1.1.0 =
983
+ * added pngout functionality for even better PNG optimization (disabled by default)
984
+ * added options to disable/bypass each tool
985
+ * pre-compiled binaries are now available via links on the settings page - try them out and let me know if there are problems
986
+
987
+ = 1.0.11 =
988
+ * path validation was broken for nextgen in previous version, now fixed
989
+
990
+ = 1.0.10 =
991
+ * added the ability to resume a bulk optimization that doesn't complete
992
+ * changed path validation for images from wordpress folder to wordpress uploads folder to accomodate users who have located this elsewhere
993
+ * minor code cleanup
994
+
995
+ = 1.0.9 =
996
+ * fixed parse error due to php short tags (old habits die hard)
997
+
998
+ = 1.0.8 =
999
+ * added extra progress and time indicators on Bulk Optimize
1000
+ * allow each image in Bulk Optimize 50 seconds to help prevent timeouts (doesn't work if PHP's Safe Mode is turned on)
1001
+ * added check for safe mode (because we can't function that way)
1002
+ * changed default PNG optimization to level 2 (8 trials) to improve performance
1003
+ * restored calls to flush output buffers for php 5.3
1004
+
1005
+ = 1.0.7 =
1006
+ * added bulk optimize to Tools menu and re-optimize for individual images with NextGEN
1007
+ * fixed optimizer function to skip images where the utilities are missing
1008
+ * added check to ensure user doesn't pass arguments in utility paths
1009
+ * added check to prevent utilities from being located in web root
1010
+ * changed optipng level setting from text entry to drop-down to prevent arbitrary script execution
1011
+ * more code cleanup
1012
+
1013
+ = 1.0.6 =
1014
+ * ported basic NextGEN integration from WP Smush.it (no bulk or re-optimize... yet)
1015
+ * added extra output for bulk operations
1016
+ * if the jpeg optimization produces an empty file, it will be discarded (instead of overwriting your originals)
1017
+ * output filesize in custom column for Media Library
1018
+ * fixed various PHP notices/warnings
1019
+
1020
+ = 1.0.5 =
1021
+ * missed documentation updates in 1.0.4 - sorry
1022
+
1023
+ = 1.0.4 =
1024
+ * Added trial with -progressive switch for JPGs (jpegtran), thanks to Alex Vojacek for noticing something was missing. We still check to make sure the progressive option is better, just in case.
1025
+ * tested against 3.4-RC3
1026
+
1027
+ = 1.0.3 =
1028
+ * Allow user to specify PNG optimization level
1029
+ * Code and screenshot cleanup
1030
+ * Settings page beautification (if you can think of further improvements, feel free to use the support link)
1031
+ * Bulk Optimize action drop-down on Media Library - ported from Regenerate Thumbnails plugin
1032
+
1033
+ = 1.0.2 =
1034
+ * Forgot to add Settings link to warning message when tools are missing
1035
+
1036
+ = 1.0.1 =
1037
+ * Fixed optimization level for optipng (-o3)
1038
+ * Added Installation and Support links to Settings page, and a link to Settings from the Plugin page.
1039
+
1040
+ = 1.0.0 =
1041
+ * First release (forked from CW Image Optimizer)
classes/class-ewww-flag.php ADDED
@@ -0,0 +1,776 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class and methods to integrate EWWW IO and GRAND FlaGallery.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EWWW_Image_Optimizer
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ // TODO: may be able to make things more efficient by not getting a new flagMeta, but get a new flagImage instead, but still probably need it to migrate data to ewwwio_images.
14
+ if ( ! class_exists( 'EWWW_Flag' ) ) {
15
+ /**
16
+ * Allows EWWW to integrate with the GRAND FlaGallery plugin.
17
+ *
18
+ * Adds automatic optimization on upload, a bulk optimizer, and compression details when
19
+ * managing galleries.
20
+ */
21
+ class EWWW_Flag {
22
+ /**
23
+ * Initializes the flagallery integration.
24
+ */
25
+ function __construct() {
26
+ add_filter( 'flag_manage_images_columns', array( $this, 'ewww_manage_images_columns' ) );
27
+ add_action( 'flag_manage_gallery_custom_column', array( $this, 'ewww_manage_image_custom_column_wrapper' ), 10, 2 );
28
+ add_action( 'admin_enqueue_scripts', array( $this, 'ewww_flag_manual_actions_script' ), 21 );
29
+ if ( current_user_can( apply_filters( 'ewww_image_optimizer_bulk_permissions', '' ) ) ) {
30
+ add_action( 'flag_manage_images_bulkaction', array( $this, 'ewww_manage_images_bulkaction' ) );
31
+ add_action( 'flag_manage_galleries_bulkaction', array( $this, 'ewww_manage_galleries_bulkaction' ) );
32
+ add_action( 'flag_manage_post_processor_images', array( $this, 'ewww_flag_bulk' ) );
33
+ add_action( 'flag_manage_post_processor_galleries', array( $this, 'ewww_flag_bulk' ) );
34
+ }
35
+ if ( ewww_image_optimizer_test_background_opt() ) {
36
+ add_action( 'flag_image_optimized', array( $this, 'queue_new_image' ) );
37
+ add_action( 'flag_image_resized', array( $this, 'queue_new_image' ) );
38
+ } else {
39
+ add_action( 'flag_image_optimized', array( $this, 'ewww_added_new_image_slow' ) );
40
+ add_action( 'flag_image_resized', array( $this, 'ewww_added_new_image_slow' ) );
41
+ }
42
+ // To prevent webview from being prematurely optimized.
43
+ add_action( 'flag_thumbnail_created', array( $this, 'ewww_remove_image_editor' ) );
44
+ add_action( 'wp_ajax_ewww_flag_manual', array( $this, 'ewww_flag_manual' ) );
45
+ add_action( 'wp_ajax_ewww_flag_cloud_restore', array( $this, 'ewww_flag_cloud_restore' ) );
46
+ add_action( 'admin_action_ewww_flag_manual', array( $this, 'ewww_flag_manual' ) );
47
+ add_action( 'admin_menu', array( $this, 'ewww_flag_bulk_menu' ) );
48
+ add_action( 'admin_enqueue_scripts', array( $this, 'ewww_flag_bulk_script' ) );
49
+ add_action( 'wp_ajax_bulk_flag_init', array( $this, 'ewww_flag_bulk_init' ) );
50
+ add_action( 'wp_ajax_bulk_flag_filename', array( $this, 'ewww_flag_bulk_filename' ) );
51
+ add_action( 'wp_ajax_bulk_flag_loop', array( $this, 'ewww_flag_bulk_loop' ) );
52
+ add_action( 'wp_ajax_bulk_flag_cleanup', array( $this, 'ewww_flag_bulk_cleanup' ) );
53
+ }
54
+
55
+ /**
56
+ * Adds the Bulk Optimize page to the menu.
57
+ */
58
+ function ewww_flag_bulk_menu() {
59
+ add_submenu_page( 'flag-overview', esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), 'FlAG Manage gallery', 'flag-bulk-optimize', array( &$this, 'ewww_flag_bulk' ) );
60
+ }
61
+
62
+ /**
63
+ * Add bulk optimize action to image management page.
64
+ */
65
+ function ewww_manage_images_bulkaction() {
66
+ echo '<option value="bulk_optimize_images">' . esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ) . '</option>';
67
+ }
68
+
69
+ /**
70
+ * Add bulk optimize action to gallery management page.
71
+ */
72
+ function ewww_manage_galleries_bulkaction() {
73
+ echo '<option value="bulk_optimize_galleries">' . esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ) . '</option>';
74
+ }
75
+
76
+ /**
77
+ * Displays the bulk optimiizer html output.
78
+ */
79
+ function ewww_flag_bulk() {
80
+ // If there is POST data, make sure bulkaction and doaction are the values we want.
81
+ if ( ! empty( $_POST ) && empty( $_REQUEST['ewww_reset'] ) ) {
82
+ // If there is no requested bulk action, do nothing.
83
+ if ( empty( $_REQUEST['bulkaction'] ) ) {
84
+ return;
85
+ }
86
+ // If there is no media to optimize, do nothing.
87
+ if ( empty( $_REQUEST['doaction'] ) || ! is_array( $_REQUEST['doaction'] ) ) {
88
+ return;
89
+ }
90
+ if ( ! preg_match( '/^bulk_optimize/', $_REQUEST['bulkaction'] ) ) {
91
+ return;
92
+ }
93
+ }
94
+ list( $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) = ewww_image_optimizer_count_optimized( 'flag' );
95
+ // Bail-out if there aren't any images to optimize.
96
+ if ( $fullsize_count < 1 ) {
97
+ echo '<p>' . esc_html__( 'You do not appear to have uploaded any images yet.', 'ewww-image-optimizer' ) . '</p>';
98
+ return;
99
+ }
100
+ ?>
101
+ <div class="wrap"><h1>GRAND FlAGallery <?php esc_html_e( 'Bulk Optimize', 'ewww-image-optimizer' ); ?></h1><?php
102
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
103
+ ewww_image_optimizer_cloud_verify();
104
+ echo '<a id="ewww-bulk-credits-available" target="_blank" class="page-title-action" style="float:right;" href="https://ewww.io/my-account/">' . esc_html__( 'Image credits available:', 'ewww-image-optimizer' ) . ' ' . ewww_image_optimizer_cloud_quota() . '</a>';
105
+ }
106
+ // Retrieve the value of the 'bulk resume' option and set the button text for the form to use.
107
+ $resume = get_option( 'ewww_image_optimizer_bulk_flag_resume' );
108
+ if ( empty( $resume ) ) {
109
+ $button_text = esc_attr__( 'Start optimizing', 'ewww-image-optimizer' );
110
+ } else {
111
+ $button_text = esc_attr__( 'Resume previous bulk operation', 'ewww-image-optimizer' );
112
+ }
113
+ $delay = ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) ? ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) : 0;
114
+ /* translators: 1-4: number(s) of images */
115
+ $selected_images_text = sprintf( esc_html__( '%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized).', 'ewww-image-optimizer' ), $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count );
116
+ ?>
117
+ <div id="ewww-bulk-loading"></div>
118
+ <div id="ewww-bulk-progressbar"></div>
119
+ <div id="ewww-bulk-counter"></div>
120
+ <form id="ewww-bulk-stop" style="display:none;" method="post" action="">
121
+ <br /><input type="submit" class="button-secondary action" value="<?php esc_attr_e( 'Stop Optimizing', 'ewww-image-optimizer' ); ?>" />
122
+ </form>
123
+ <div id="ewww-bulk-widgets" class="metabox-holder" style="display:none">
124
+ <div class="meta-box-sortables">
125
+ <div id="ewww-bulk-last" class="postbox">
126
+ <button type="button" class="handlediv button-link" aria-expanded="true">
127
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ) ?></span>
128
+ <span class="toggle-indicator" aria-hidden="true"></span>
129
+ </button>
130
+ <h2 class="hndle"><span><?php esc_html_e( 'Last Image Optimized', 'ewww-image-optimizer' ) ?></span></h2>
131
+ <div class="inside"></div>
132
+ </div>
133
+ </div>
134
+ <div class="meta-box-sortables">
135
+ <div id="ewww-bulk-status" class="postbox">
136
+ <button type="button" class="handlediv button-link" aria-expanded="true">
137
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ) ?></span>
138
+ <span class="toggle-indicator" aria-hidden="true"></span>
139
+ </button>
140
+ <h2 class="hndle"><span><?php esc_html_e( 'Optimization Log', 'ewww-image-optimizer' ) ?></span></h2>
141
+ <div class="inside"></div>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ <form class="ewww-bulk-form">
146
+ <p><label for="ewww-force" style="font-weight: bold"><?php esc_html_e( 'Force re-optimize', 'ewww-image-optimizer' ); ?></label>&emsp;<input type="checkbox" id="ewww-force" name="ewww-force"></p>
147
+ <p><label for="ewww-delay" style="font-weight: bold"><?php esc_html_e( 'Choose how long to pause between images (in seconds, 0 = disabled)', 'ewww-image-optimizer' ); ?></label>&emsp;<input type="text" id="ewww-delay" name="ewww-delay" value="<?php echo $delay; ?>"></p>
148
+ <div id="ewww-delay-slider" style="width:50%"></div>
149
+ </form>
150
+ <div id="ewww-bulk-forms">
151
+ <p class="ewww-bulk-info"><?php echo $selected_images_text; ?><br />
152
+ <?php esc_html_e( 'Previously optimized images will be skipped by default.', 'ewww-image-optimizer' ); ?></p>
153
+ <form id="ewww-bulk-start" class="ewww-bulk-form" method="post" action="">
154
+ <input type="submit" class="button-secondary action" value="<?php echo $button_text; ?>" />
155
+ </form>
156
+ <?php
157
+ // If there was a previous operation, offer the option to reset the option in the db.
158
+ if ( ! empty( $resume ) ) :
159
+ ?>
160
+ <p class="ewww-bulk-info"><?php esc_html_e( 'If you would like to start over again, press the Reset Status button to reset the bulk operation status.', 'ewww-image-optimizer' ); ?></p>
161
+ <form method="post" class="ewww-bulk-form" action="">
162
+ <?php wp_nonce_field( 'ewww-image-optimizer-bulk', 'ewww_wpnonce' ); ?>
163
+ <input type="hidden" name="ewww_reset" value="1">
164
+ <button id="bulk-reset" type="submit" class="button-secondary action"><?php esc_html_e( 'Reset Status', 'ewww-image-optimizer' ); ?></button>
165
+ </form>
166
+ <?php
167
+ endif;
168
+ echo '</div></div>';
169
+ }
170
+
171
+ /**
172
+ * Prepares the bulk operation and includes the necessary javascript files.
173
+ *
174
+ * @global object $flagdb
175
+ * @global object $wpdb
176
+ *
177
+ * @param string $hook The hook value for the current page.
178
+ */
179
+ function ewww_flag_bulk_script( $hook ) {
180
+ // Make sure we are being hooked from a valid location.
181
+ if ( 'flagallery_page_flag-bulk-optimize' != $hook && 'flagallery_page_flag-manage-gallery' != $hook ) {
182
+ return;
183
+ }
184
+ // If there is no requested bulk action, do nothing.
185
+ if ( 'flagallery_page_flag-manage-gallery' == $hook && ( empty( $_REQUEST['bulkaction'] ) || ! preg_match( '/^bulk_optimize/', $_REQUEST['bulkaction'] )) ) {
186
+ return;
187
+ }
188
+ // If there is no media to optimize, do nothing.
189
+ if ( 'flagallery_page_flag-manage-gallery' == $hook && ( empty( $_REQUEST['doaction'] ) || ! is_array( $_REQUEST['doaction'] )) ) {
190
+ return;
191
+ }
192
+ $ids = null;
193
+ // Reset the resume flag if the user requested it.
194
+ if ( ! empty( $_REQUEST['ewww_reset'] ) ) {
195
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
196
+ }
197
+ // Get the resume flag from the db.
198
+ $resume = get_option( 'ewww_image_optimizer_bulk_flag_resume' );
199
+ // Check if we are being asked to optimize galleries or images rather than a full bulk optimize.
200
+ if ( ! empty( $_REQUEST['doaction'] ) ) {
201
+ // See if the bulk operation requested is from the manage images page.
202
+ if ( 'manage-images' == $_REQUEST['page'] && 'bulk_optimize_images' == $_REQUEST['bulkaction'] ) {
203
+ // Check the referring page and nonce.
204
+ check_admin_referer( 'flag_updategallery' );
205
+ // We don't allow previous operations to resume if the user is asking to optimize specific images.
206
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
207
+ // Retrieve the image IDs from POST.
208
+ $ids = array_map( 'intval', $_REQUEST['doaction'] );
209
+ }
210
+ // See if the bulk operation requested is from the manage galleries page.
211
+ if ( 'manage-galleries' == $_REQUEST['page'] && 'bulk_optimize_galleries' == $_REQUEST['bulkaction'] ) {
212
+ // Check the referring page and nonce.
213
+ check_admin_referer( 'flag_bulkgallery' );
214
+ global $flagdb;
215
+ // We don't allow previous operations to resume if the user is asking to optimize specific galleries.
216
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
217
+ $ids = array();
218
+ $gids = array_map( 'intval', $_REQUEST['doaction'] );
219
+ // For each gallery ID, retrieve the image IDs within.
220
+ foreach ( $gids as $gid ) {
221
+ $gallery_list = $flagdb->get_gallery( $gid );
222
+ // For each image ID found, put it onto the $ids array.
223
+ foreach ( $gallery_list as $image ) {
224
+ $ids[] = $image->pid;
225
+ }
226
+ }
227
+ }
228
+ } elseif ( ! empty( $resume ) ) {
229
+ // If there is an operation to resume, get those IDs from the db.
230
+ $ids = get_option( 'ewww_image_optimizer_bulk_flag_attachments' );
231
+ } elseif ( 'flagallery_page_flag-bulk-optimize' == $hook ) {
232
+ // Otherwise, if we are on the main bulk optimize page, just get all the IDs available.
233
+ global $wpdb;
234
+ $ids = $wpdb->get_col( "SELECT pid FROM $wpdb->flagpictures ORDER BY sortorder ASC" );
235
+ } // End if().
236
+ // Store the IDs to optimize in the options table of the db.
237
+ update_option( 'ewww_image_optimizer_bulk_flag_attachments', $ids );
238
+ // Add the EWWW IO javascript.
239
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
240
+ // Add the styling for the progressbar.
241
+ wp_enqueue_style( 'jquery-ui-progressbar', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) );
242
+ // Prepare a few variables to be used by the javascript code.
243
+ wp_localize_script('ewwwbulkscript', 'ewww_vars', array(
244
+ '_wpnonce' => wp_create_nonce( 'ewww-image-optimizer-bulk' ),
245
+ 'gallery' => 'flag',
246
+ 'attachments' => count( $ids ),
247
+ 'scan_fail' => esc_html__( 'Operation timed out, you may need to increase the max_execution_time for PHP', 'ewww-image-optimizer' ),
248
+ 'operation_stopped' => esc_html__( 'Optimization stopped, reload page to resume.', 'ewww-image-optimizer' ),
249
+ 'operation_interrupted' => esc_html__( 'Operation Interrupted', 'ewww-image-optimizer' ),
250
+ 'temporary_failure' => esc_html__( 'Temporary failure, seconds left to retry:', 'ewww-image-optimizer' ),
251
+ 'remove_failed' => esc_html__( 'Could not remove image from table.', 'ewww-image-optimizer' ),
252
+ 'optimized' => esc_html__( 'Optimized', 'ewww-image-optimizer' ),
253
+ )
254
+ );
255
+ }
256
+
257
+ /**
258
+ * Adds a newly uploaded image to the background queue.
259
+ *
260
+ * @param object $image A Flag_Image object for the new upload.
261
+ */
262
+ function queue_new_image( $image ) {
263
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
264
+ $image_id = $image->pid;
265
+ global $ewwwio_flag_background;
266
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
267
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
268
+ }
269
+ if ( ! is_object( $ewwwio_flag_background ) ) {
270
+ $ewwwio_flag_background = new EWWWIO_Flag_Background_Process();
271
+ }
272
+ ewwwio_debug_message( "optimization (flagallery) queued for $image_id" );
273
+ $ewwwio_flag_background->push_to_queue( array(
274
+ 'id' => $image_id,
275
+ ) );
276
+ $ewwwio_flag_background->save()->dispatch();
277
+ set_transient( 'ewwwio-background-in-progress-flag-' . $image_id, true, 24 * HOUR_IN_SECONDS );
278
+ ewww_image_optimizer_debug_log();
279
+ }
280
+
281
+ /**
282
+ * Optimizes newly uploaded images from the queue.
283
+ *
284
+ * @param int $id The ID number for the new image.
285
+ * @param object $image A Flag_Image object for the new upload.
286
+ */
287
+ function ewww_added_new_image( $id, $image ) {
288
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
289
+ global $ewww_defer;
290
+ global $ewww_image;
291
+ // Make sure the image path is set.
292
+ if ( ! isset( $image->image->imagePath ) ) {
293
+ return;
294
+ }
295
+ $ewww_image = new EWWW_Image( $id, 'flag', $image->image->imagePath );
296
+ $ewww_image->resize = 'full';
297
+ // Optimize the full size.
298
+ $res = ewww_image_optimizer( $image->image->imagePath, 3, false, false, true );
299
+ $ewww_image = new EWWW_Image( $id, 'flag', $image->image->webimagePath );
300
+ $ewww_image->resize = 'webview';
301
+ // Optimize the web optimized version.
302
+ $wres = ewww_image_optimizer( $image->image->webimagePath, 3, false, true );
303
+ $ewww_image = new EWWW_Image( $id, 'flag', $image->image->thumbPath );
304
+ $ewww_image->resize = 'thumbnail';
305
+ // Optimize the thumbnail.
306
+ $tres = ewww_image_optimizer( $image->image->thumbPath, 3, false, true );
307
+
308
+ ewww_image_optimizer_debug_log();
309
+ }
310
+
311
+ /**
312
+ * Optimizes newly uploaded images immediately on upload (no background opt).
313
+ *
314
+ * @param object $image A Flag_Image object for the new upload.
315
+ */
316
+ function ewww_added_new_image_slow( $image ) {
317
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
318
+ // Make sure the image path is set.
319
+ if ( isset( $image->imagePath ) ) {
320
+ global $ewww_image;
321
+ $ewww_image = new EWWW_Image( $image->pid, 'flag', $image->imagePath );
322
+ $ewww_image->resize = 'full';
323
+ // Optimize the full size.
324
+ $res = ewww_image_optimizer( $image->imagePath, 3, false, false, true );
325
+ $ewww_image = new EWWW_Image( $image->pid, 'flag', $image->webimagePath );
326
+ $ewww_image->resize = 'webview';
327
+ // Optimize the web optimized version.
328
+ $wres = ewww_image_optimizer( $image->webimagePath, 3, false, true );
329
+ $ewww_image = new EWWW_Image( $image->pid, 'flag', $image->thumbPath );
330
+ $ewww_image->resize = 'thumbnail';
331
+ // Optimize the thumbnail.
332
+ $tres = ewww_image_optimizer( $image->thumbPath, 3, false, true );
333
+ if ( ! class_exists( 'flagMeta' ) ) {
334
+ require_once( FLAG_ABSPATH . 'lib/meta.php' );
335
+ }
336
+ // Retrieve the metadata for the image ID.
337
+ $pid = $image->pid;
338
+ $meta = new flagMeta( $pid );
339
+ }
340
+ ewww_image_optimizer_debug_log();
341
+ }
342
+
343
+ /**
344
+ * Remove the image editor filter during upload, and add a new filter that will restore it later.
345
+ */
346
+ function ewww_remove_image_editor() {
347
+ remove_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
348
+ add_action( 'flag_image_optimized', 'ewww_image_optimizer_restore_editor_hooks' );
349
+ }
350
+
351
+ /**
352
+ * Manually process an image from the gallery.
353
+ */
354
+ function ewww_flag_manual() {
355
+ // Make sure the current user has appropriate permissions.
356
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
357
+ if ( false === current_user_can( $permissions ) ) {
358
+ if ( ! wp_doing_ajax() ) {
359
+ wp_die( esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ) );
360
+ }
361
+ wp_die( json_encode( array(
362
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
363
+ ) ) );
364
+ }
365
+ // Make sure we have an attachment ID.
366
+ if ( empty( $_REQUEST['ewww_attachment_ID'] ) ) {
367
+ if ( ! wp_doing_ajax() ) {
368
+ wp_die( esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ) );
369
+ }
370
+ wp_die( json_encode( array(
371
+ 'error' => esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ),
372
+ ) ) );
373
+ }
374
+ $id = intval( $_REQUEST['ewww_attachment_ID'] );
375
+ if ( empty( $_REQUEST['ewww_manual_nonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_manual_nonce'], "ewww-manual-$id" ) ) {
376
+ if ( ! wp_doing_ajax() ) {
377
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
378
+ }
379
+ wp_die( json_encode( array(
380
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
381
+ ) ) );
382
+ }
383
+ global $ewww_image;
384
+ if ( ! class_exists( 'flagMeta' ) ) {
385
+ require_once( FLAG_ABSPATH . 'lib/meta.php' );
386
+ }
387
+ // Retrieve the metadata for the image ID.
388
+ $meta = new flagMeta( $id );
389
+ // Determine the path of the image.
390
+ $file_path = $meta->image->imagePath;
391
+ $ewww_image = new EWWW_Image( $id, 'flag', $file_path );
392
+ $ewww_image->resize = 'full';
393
+ // Optimize the full size.
394
+ $res = ewww_image_optimizer( $file_path, 3, false, false, true );
395
+ if ( ! empty( $meta->image->meta_data['webview'] ) ) {
396
+ // Determine path of the webview.
397
+ $web_path = $meta->image->webimagePath;
398
+ $ewww_image = new EWWW_Image( $id, 'flag', $web_path );
399
+ $ewww_image->resize = 'webview';
400
+ $wres = ewww_image_optimizer( $web_path, 3, false, true );
401
+ }
402
+ // Determine the path of the thumbnail.
403
+ $thumb_path = $meta->image->thumbPath;
404
+ $ewww_image = new EWWW_Image( $id, 'flag', $thumb_path );
405
+ $ewww_image->resize = 'thumbnail';
406
+ // Optimize the thumbnail.
407
+ $tres = ewww_image_optimizer( $thumb_path, 3, false, true );
408
+ if ( ! wp_doing_ajax() ) {
409
+ // Get the referring page...
410
+ $sendback = wp_get_referer();
411
+ // and clean it up a bit.
412
+ $sendback = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback );
413
+ // Send the user back where they came from.
414
+ wp_redirect( $sendback );
415
+ die;
416
+ }
417
+ $success = $this->ewww_manage_image_custom_column( $id );
418
+ wp_die( json_encode( array(
419
+ 'success' => $success,
420
+ ) ) );
421
+ }
422
+
423
+ /**
424
+ * Restore an image from the API.
425
+ */
426
+ function ewww_flag_cloud_restore() {
427
+ // Check permission of current user.
428
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
429
+ if ( false === current_user_can( $permissions ) ) {
430
+ if ( ! wp_doing_ajax() ) {
431
+ wp_die( esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ) );
432
+ }
433
+ wp_die( json_encode( array(
434
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
435
+ ) ) );
436
+ }
437
+ // Make sure function wasn't called without an attachment to work with.
438
+ if ( false === isset( $_REQUEST['ewww_attachment_ID'] ) ) {
439
+ if ( ! wp_doing_ajax() ) {
440
+ wp_die( esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ) );
441
+ }
442
+ wp_die( json_encode( array(
443
+ 'error' => esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ),
444
+ ) ) );
445
+ }
446
+ // Store the attachment $id.
447
+ $id = intval( $_REQUEST['ewww_attachment_ID'] );
448
+ if ( empty( $_REQUEST['ewww_manual_nonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_manual_nonce'], "ewww-manual-$id" ) ) {
449
+ if ( ! wp_doing_ajax() ) {
450
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
451
+ }
452
+ wp_die( json_encode( array(
453
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
454
+ ) ) );
455
+ }
456
+ if ( ! class_exists( 'flagMeta' ) ) {
457
+ require_once( FLAG_ABSPATH . 'lib/meta.php' );
458
+ }
459
+ ewww_image_optimizer_cloud_restore_from_meta_data( $id, 'flag' );
460
+ $success = $this->ewww_manage_image_custom_column( $id );
461
+ die( json_encode( array(
462
+ 'success' => $success,
463
+ ) ) );
464
+ }
465
+
466
+ /**
467
+ * Initialize the bulk operation.
468
+ */
469
+ function ewww_flag_bulk_init() {
470
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
471
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
472
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
473
+ }
474
+ $output = array();
475
+ // Set the resume flag to indicate the bulk operation is in progress.
476
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', 'true' );
477
+ // Retrieve the list of attachments left to work on.
478
+ $attachments = get_option( 'ewww_image_optimizer_bulk_flag_attachments' );
479
+ if ( ! is_array( $attachments ) && ! empty( $attachments ) ) {
480
+ $attachments = unserialize( $attachments );
481
+ }
482
+ if ( ! is_array( $attachments ) ) {
483
+ $output['error'] = esc_html__( 'Error retrieving list of images' );
484
+ echo json_encode( $output );
485
+ die();
486
+ }
487
+ $id = array_shift( $attachments );
488
+ $file_name = $this->ewww_flag_bulk_filename( $id );
489
+ $loading_image = plugins_url( '/images/wpspin.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
490
+ // Output the initial message letting the user know we are starting.
491
+ if ( empty( $file_name ) ) {
492
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' alt='loading'/></p>";
493
+ } else {
494
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . ' <b>' . $file_name . "</b>&nbsp;<img src='$loading_image' alt='loading'/></p>";
495
+ }
496
+ echo json_encode( $output );
497
+ die();
498
+ }
499
+
500
+ /**
501
+ * Get the filename of the currently optimizing image.
502
+ *
503
+ * @param int $id The ID number for the image.
504
+ * @return string|bool The name of the first image in the queue, or false.
505
+ */
506
+ function ewww_flag_bulk_filename( $id ) {
507
+ // Need this file to work with flag meta.
508
+ require_once( WP_CONTENT_DIR . '/plugins/flash-album-gallery/lib/meta.php' );
509
+ // Retrieve the meta for the current ID.
510
+ $meta = new flagMeta( $id );
511
+ // Retrieve the filename for the current image ID.
512
+ $file_name = esc_html( $meta->image->filename );
513
+ if ( ! empty( $file_name ) ) {
514
+ return $file_name;
515
+ } else {
516
+ return false;
517
+ }
518
+ }
519
+
520
+ /**
521
+ * Process each image during the bulk operation.
522
+ */
523
+ function ewww_flag_bulk_loop() {
524
+ global $ewww_defer;
525
+ $ewww_defer = false;
526
+ $output = array();
527
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
528
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
529
+ $output['error'] = esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' );
530
+ echo json_encode( $output );
531
+ die();
532
+ }
533
+ session_write_close();
534
+ // Find out if our nonce is on it's last leg/tick.
535
+ $tick = wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' );
536
+ if ( 2 === $tick ) {
537
+ $output['new_nonce'] = wp_create_nonce( 'ewww-image-optimizer-bulk' );
538
+ } else {
539
+ $output['new_nonce'] = '';
540
+ }
541
+ global $ewww_image;
542
+ // Need this file to work with flag meta.
543
+ require_once( WP_CONTENT_DIR . '/plugins/flash-album-gallery/lib/meta.php' );
544
+ // Record the starting time for the current image (in microseconds).
545
+ $started = microtime( true );
546
+ // Retrieve the list of attachments left to work on.
547
+ $attachments = get_option( 'ewww_image_optimizer_bulk_flag_attachments' );
548
+ $id = array_shift( $attachments );
549
+ // Get the image meta for the current ID.
550
+ $meta = new flagMeta( $id );
551
+ $file_path = $meta->image->imagePath;
552
+ $ewww_image = new EWWW_Image( $id, 'flag', $file_path );
553
+ $ewww_image->resize = 'full';
554
+ // Optimize the full-size version.
555
+ $fres = ewww_image_optimizer( $file_path, 3, false, false, true );
556
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
557
+ if ( ! empty( $ewww_status ) && preg_match( '/exceeded/', $ewww_status ) ) {
558
+ $output['error'] = esc_html__( 'License Exceeded', 'ewww-image-optimizer' );
559
+ echo json_encode( $output );
560
+ die();
561
+ }
562
+ // Let the user know what happened.
563
+ $output['results'] = sprintf( '<p>' . esc_html__( 'Optimized image:', 'ewww-image-optimizer' ) . ' <strong>%s</strong><br>', esc_html( $meta->image->filename ) );
564
+ /* Translators: %s: The compression results/savings */
565
+ $output['results'] .= sprintf( esc_html__( 'Full size – %s', 'ewww-image-optimizer' ) . '<br>', esc_html( $fres[1] ) );
566
+ if ( ! empty( $meta->image->meta_data['webview'] ) ) {
567
+ // Determine path of the webview.
568
+ $web_path = $meta->image->webimagePath;
569
+ $ewww_image = new EWWW_Image( $id, 'flag', $web_path );
570
+ $ewww_image->resize = 'webview';
571
+ $wres = ewww_image_optimizer( $web_path, 3, false, true );
572
+ /* Translators: %s: The compression results/savings */
573
+ $output['results'] .= sprintf( esc_html__( 'Optimized size – %s', 'ewww-image-optimizer' ) . '<br>', esc_html( $wres[1] ) );
574
+ }
575
+ $thumb_path = $meta->image->thumbPath;
576
+ $ewww_image = new EWWW_Image( $id, 'flag', $thumb_path );
577
+ $ewww_image->resize = 'thumbnail';
578
+ // Optimize the thumbnail.
579
+ $tres = ewww_image_optimizer( $thumb_path, 3, false, true );
580
+ // And let the user know the results.
581
+ /* Translators: %s: The compression results/savings */
582
+ $output['results'] .= sprintf( esc_html__( 'Thumbnail – %s', 'ewww-image-optimizer' ) . '<br>', esc_html( $tres[1] ) );
583
+ // Determine how much time the image took to process.
584
+ $elapsed = microtime( true ) - $started;
585
+ // And output it to the user.
586
+ /* Translators: %s: number of seconds, localized */
587
+ $output['results'] .= sprintf( esc_html( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ) ) . '</p>', number_format_i18n( $elapsed ) );
588
+ $output['completed'] = 1;
589
+ // Send the list back to the db.
590
+ update_option( 'ewww_image_optimizer_bulk_flag_attachments', $attachments, false );
591
+ if ( ! empty( $attachments ) ) {
592
+ $next_attachment = array_shift( $attachments );
593
+ $next_file = $this->ewww_flag_bulk_filename( $next_attachment );
594
+ $loading_image = plugins_url( '/images/wpspin.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
595
+ if ( $next_file ) {
596
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <b>$next_file</b>&nbsp;<img src='$loading_image' alt='loading'/></p>";
597
+ } else {
598
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' alt='loading'/></p>";
599
+ }
600
+ } else {
601
+ $output['done'] = 1;
602
+ }
603
+ die( json_encode( $output ) );
604
+ }
605
+
606
+ /**
607
+ * Finish the bulk operation, and clear out the bulk_flag options.
608
+ */
609
+ function ewww_flag_bulk_cleanup() {
610
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
611
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
612
+ wp_die( esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) );
613
+ }
614
+ // Reset the bulk flags in the db.
615
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
616
+ update_option( 'ewww_image_optimizer_bulk_flag_attachments', '', false );
617
+ // And let the user know we are done.
618
+ echo '<p><b>' . esc_html__( 'Finished Optimization!', 'ewww-image-optimizer' ) . '</b></p>';
619
+ die();
620
+ }
621
+
622
+ /**
623
+ * Prepare javascript for one-click actions on manage gallery page.
624
+ *
625
+ * @param string $hook The hook value for the current page.
626
+ */
627
+ function ewww_flag_manual_actions_script( $hook ) {
628
+ if ( 'flagallery_page_flag-manage-gallery' != $hook ) {
629
+ return;
630
+ }
631
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
632
+ return;
633
+ }
634
+ add_thickbox();
635
+ wp_enqueue_script( 'ewwwflagscript', plugins_url( '/includes/flag.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery' ), EWWW_IMAGE_OPTIMIZER_VERSION );
636
+ wp_enqueue_style( 'jquery-ui-tooltip-custom', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array(), EWWW_IMAGE_OPTIMIZER_VERSION );
637
+ // Submit a couple variables needed for javascript functions.
638
+ $loading_image = plugins_url( '/images/spinner.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
639
+ wp_localize_script(
640
+ 'ewwwflagscript',
641
+ 'ewww_vars',
642
+ array(
643
+ 'optimizing' => '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <img src='$loading_image' /></p>",
644
+ 'restoring' => '<p>' . esc_html__( 'Restoring', 'ewww-image-optimizer' ) . " <img src='$loading_image' /></p>",
645
+ )
646
+ );
647
+ }
648
+
649
+ /**
650
+ * Add a column on the gallery display.
651
+ *
652
+ * @param array $columns A list of columns displayed on the manage gallery page.
653
+ * @return array The list of columns, with EWWW's custom column added.
654
+ */
655
+ function ewww_manage_images_columns( $columns ) {
656
+ $columns['ewww_image_optimizer'] = esc_html__( 'Image Optimizer', 'ewww-image-optimizer' );
657
+ return $columns;
658
+ }
659
+
660
+ /**
661
+ * Output the EWWW IO information on the gallery display.
662
+ *
663
+ * @param int $id The ID number of the image being displayed.
664
+ */
665
+ function ewww_manage_image_custom_column( $id ) {
666
+ $output = "<div id='ewww-flag-status-$id'>";
667
+ // Get the metadata.
668
+ $meta = new flagMeta( $id );
669
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) && ewww_image_optimizer_function_exists( 'print_r' ) ) {
670
+ $print_meta = print_r( $meta->image->meta_data, true );
671
+ $print_meta = preg_replace( array( '/ /', '/\n+/' ), array( '&nbsp;', '<br />' ), esc_html( $print_meta ) );
672
+ echo '<div style="background-color:#ffff99;font-size: 10px;padding: 10px;margin:-10px -10px 10px;line-height: 1.1em">' . $print_meta . '</div>';
673
+ }
674
+ // Get the image path from the meta.
675
+ $file_path = $meta->image->imagePath;
676
+ // Grab the image status from the meta.
677
+ if ( empty( $meta->image->meta_data['ewww_image_optimizer'] ) ) {
678
+ $status = '';
679
+ } else {
680
+ // Run db import here.
681
+ if ( $file_path ) {
682
+ ewww_image_optimizer_update_file_from_meta( $file_path, 'flag', $id, 'full' );
683
+ }
684
+ if ( $meta->image->webimagePath ) {
685
+ ewww_image_optimizer_update_file_from_meta( $meta->image->webimagePath, 'flag', $id, 'webview' );
686
+ }
687
+ if ( $meta->image->thumbPath ) {
688
+ ewww_image_optimizer_update_file_from_meta( $meta->image->thumbPath, 'flag', $id, 'thumbnail' );
689
+ }
690
+ }
691
+ $msg = '';
692
+ // Get the mimetype.
693
+ $type = ewww_image_optimizer_mimetype( $file_path, 'i' );
694
+ $valid = true;
695
+ // If we don't have a valid tool for the image type, output the appropriate message.
696
+ $skip = ewww_image_optimizer_skip_tools();
697
+ switch ( $type ) {
698
+ case 'image/jpeg':
699
+ if ( ! EWWW_IMAGE_OPTIMIZER_JPEGTRAN && ! $skip['jpegtran'] ) {
700
+ /* translators: %s: name of a tool like jpegtran */
701
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>jpegtran</em>' ) . '</div>';
702
+ }
703
+ break;
704
+ case 'image/png':
705
+ if ( ! EWWW_IMAGE_OPTIMIZER_PNGOUT && ! EWWW_IMAGE_OPTIMIZER_OPTIPNG && ! $skip['optipng'] && ! $skip['pngout'] ) {
706
+ /* translators: %s: name of a tool like jpegtran */
707
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>optipng/pngout</em>' ) . '</div>';
708
+ }
709
+ break;
710
+ case 'image/gif':
711
+ if ( ! EWWW_IMAGE_OPTIMIZER_GIFSICLE && ! $skip['gifsicle'] ) {
712
+ /* translators: %s: name of a tool like jpegtran */
713
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>gifsicle</em>' ) . '</div>';
714
+ }
715
+ break;
716
+ default:
717
+ $msg = '<div>' . esc_html__( 'Unsupported file type', 'ewww-image-optimizer' ) . '</div>';
718
+ }
719
+ // Let user know if the file type is unsupported.
720
+ if ( $msg ) {
721
+ return $msg;
722
+ }
723
+ $backup_available = false;
724
+ global $wpdb;
725
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'flag' AND image_size <> 0 ORDER BY orig_size DESC", $id ), ARRAY_A );
726
+ $ewww_manual_nonce = wp_create_nonce( 'ewww-manual-' . $id );
727
+ if ( ! empty( $optimized_images ) ) {
728
+ list( $detail_output, $converted, $backup_available ) = ewww_image_optimizer_custom_column_results( $id, $optimized_images );
729
+ $output .= $detail_output;
730
+ if ( current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
731
+ $output .= sprintf( '<a class="ewww-manual-optimize" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_flag_manual&amp;ewww_manual_nonce=%2$s&amp;ewww_force=1&amp;ewww_attachment_ID=%1$d">%3$s</a>',
732
+ $id,
733
+ $ewww_manual_nonce,
734
+ esc_html__( 'Re-optimize', 'ewww-image-optimizer' )
735
+ );
736
+ if ( $backup_available ) {
737
+ $output .= sprintf( '<br><a class="ewww-manual-cloud-restore" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_flag_cloud_restore&amp;ewww_manual_nonce=%2$s&amp;ewww_attachment_ID=%1$d">%3$s</a>',
738
+ $id,
739
+ $ewww_manual_nonce,
740
+ esc_html__( 'Restore original', 'ewww-image-optimizer' )
741
+ );
742
+ }
743
+ }
744
+ } elseif ( get_transient( 'ewwwio-background-in-progress-flag-' . $id ) ) {
745
+ $output .= esc_html__( 'In Progress', 'ewww-image-optimizer' );
746
+ // Otherwise, tell the user that they can optimize the image now.
747
+ } else {
748
+ if ( current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
749
+ $output .= sprintf( '<a class="ewww-manual-optimize" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_flag_manual&amp;ewww_manual_nonce=%2$s&amp;ewww_attachment_ID=%1$d">%3$s</a>',
750
+ $id,
751
+ $ewww_manual_nonce,
752
+ esc_html__( 'Optimize now!', 'ewww-image-optimizer' )
753
+ );
754
+ }
755
+ }
756
+ $output .= '</div>';
757
+ return $output;
758
+ }
759
+
760
+ /**
761
+ * Wrapper around the custom column display when being called normally (no AJAX).
762
+ *
763
+ * @param string $column_name The name of the current column.
764
+ * @param int $id The ID number of the image to display.
765
+ */
766
+ function ewww_manage_image_custom_column_wrapper( $column_name, $id ) {
767
+ // Check to make sure we're outputing our custom column.
768
+ if ( 'ewww_image_optimizer' == $column_name ) {
769
+ echo $this->ewww_manage_image_custom_column( $id );
770
+ }
771
+ }
772
+ }
773
+
774
+ global $ewwwflag;
775
+ $ewwwflag = new EWWW_Flag();
776
+ } // End if().
classes/class-ewww-image.php ADDED
@@ -0,0 +1,1069 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class file for EWWW_Image
4
+ *
5
+ * EWWW_Image contains methods for retrieving records from the ewwwio_images table.
6
+ *
7
+ * @link https://ewww.io
8
+ * @package EWWW_Image_Optimizer
9
+ */
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Retrieves image records from the database.
17
+ *
18
+ * Usually used to retrieve pending records, provides functions to process conversion of resizes
19
+ * and utility functions during bulk operations. Can also create an object for a new image to
20
+ * ensure proper record-keeping when new images are insterted to the database.
21
+ */
22
+ class EWWW_Image {
23
+
24
+ /**
25
+ * The id number in the database.
26
+ *
27
+ * @var int $id
28
+ */
29
+ public $id = 0;
30
+ /**
31
+ * The id number of the related attachment.
32
+ *
33
+ * @var int $attachment_id
34
+ */
35
+ public $attachment_id = null;
36
+ /**
37
+ * The path to the image.
38
+ *
39
+ * @var string $file
40
+ */
41
+ public $file = '';
42
+ /**
43
+ * The name of the original file if the image was converted. False if not converted.
44
+ *
45
+ * @var string|bool $converted
46
+ */
47
+ public $converted = false;
48
+ /**
49
+ * The original size of the image.
50
+ *
51
+ * @var int $orig_size
52
+ */
53
+ public $orig_size = 0;
54
+ /**
55
+ * The optimized size of the image.
56
+ *
57
+ * @var int $opt_size
58
+ */
59
+ public $opt_size = 0;
60
+ /**
61
+ * The size/type of the image, like 'thumbnail', 'medium', 'large'.
62
+ *
63
+ * @var string $resize
64
+ */
65
+ public $resize = null;
66
+ /**
67
+ * The gallery of the image, if applicable. Accepts 'media', 'nextgen', etc.
68
+ *
69
+ * @var string $gallery
70
+ */
71
+ public $gallery = '';
72
+ /**
73
+ * To be appended to converted files if necessary.
74
+ *
75
+ * @var int|bool $increment
76
+ */
77
+ public $increment = false;
78
+ /**
79
+ * The url to the image.
80
+ *
81
+ * @var string $url
82
+ */
83
+ public $url = '';
84
+ /**
85
+ * Compression level as an integer.
86
+ *
87
+ * @var int $level
88
+ */
89
+ public $level = 0;
90
+
91
+ /**
92
+ * Creates an image record, either from a pending record in the database, or from a file path.
93
+ *
94
+ * @global object $wpdb
95
+ * @global object $ewwwdb A new database connection with super powers.
96
+ *
97
+ * @param int $id Optional. The attachment ID to search for.
98
+ * @param string $gallery Optional. The type of image to work with. Accepts 'media', 'nextgen', 'flag', or 'nextcellent'.
99
+ * @param string $path Optional. The absolute path to an image.
100
+ */
101
+ function __construct( $id = 0, $gallery = '', $path = '' ) {
102
+ if ( ! is_numeric( $id ) ) {
103
+ $id = 0;
104
+ }
105
+ if ( ! is_string( $path ) ) {
106
+ $path = '';
107
+ }
108
+ if ( ! is_string( $gallery ) ) {
109
+ $gallery = '';
110
+ }
111
+ global $wpdb;
112
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
113
+ ewww_image_optimizer_db_init();
114
+ global $ewwwdb;
115
+ } else {
116
+ $ewwwdb = $wpdb;
117
+ }
118
+ $ewwwdb->flush();
119
+ if ( $path && is_file( $path ) ) {
120
+ ewwwio_debug_message( "creating EWWW_Image with $path" );
121
+ $new_image = ewww_image_optimizer_find_already_optimized( $path );
122
+ if ( ! $new_image ) {
123
+ $this->file = $path;
124
+ $this->orig_size = filesize( $path );
125
+ $this->gallery = $gallery;
126
+ if ( $id ) {
127
+ $this->attachment_id = $id;
128
+ }
129
+ return;
130
+ } elseif ( is_array( $new_image ) ) {
131
+ if ( $id && empty( $new_image['attachment_id'] ) ) {
132
+ $new_image['attachment_id'] = $id;
133
+ }
134
+ if ( $gallery && empty( $new_image['gallery'] ) ) {
135
+ $new_image['gallery'] = $gallery;
136
+ }
137
+ }
138
+ } elseif ( $path ) { // If $path is supplied but is not a file, then bail.
139
+ ewwwio_debug_message( "could not create EWWW_Image with $path, not a file" );
140
+ return;
141
+ } elseif ( $id && $gallery ) {
142
+ ewwwio_debug_message( "looking for $gallery image $id" );
143
+ // Matches $id, $gallery, is 'full', and pending.
144
+ $new_image = $ewwwdb->get_row( "SELECT * FROM $ewwwdb->ewwwio_images WHERE attachment_id = $id AND gallery = '$gallery' AND resize = 'full' AND pending = 1 LIMIT 1", ARRAY_A );
145
+ if ( empty( $new_image ) ) {
146
+ // Matches $id, $gallery and pending.
147
+ $new_image = $ewwwdb->get_row( "SELECT * FROM $ewwwdb->ewwwio_images WHERE attachment_id = $id AND gallery = '$gallery' AND pending = 1 LIMIT 1", ARRAY_A );
148
+ }
149
+ if ( empty( $new_image ) ) {
150
+ // Matches $gallery, is 'full' and pending.
151
+ $new_image = $ewwwdb->get_row( "SELECT * FROM $ewwwdb->ewwwio_images WHERE gallery = '$gallery' AND resize = 'full' AND pending = 1 LIMIT 1", ARRAY_A );
152
+ }
153
+ if ( empty( $new_image ) ) {
154
+ // Pull a random image.
155
+ $new_image = $ewwwdb->get_row( "SELECT * FROM $ewwwdb->ewwwio_images WHERE pending = 1 LIMIT 1", ARRAY_A );
156
+ }
157
+ } else {
158
+ ewwwio_debug_message( 'no id or path, just pulling next image' );
159
+ $new_image = $ewwwdb->get_row( "SELECT * FROM $ewwwdb->ewwwio_images WHERE pending = 1 LIMIT 1", ARRAY_A );
160
+ } // End if().
161
+
162
+ if ( empty( $new_image ) ) {
163
+ ewwwio_debug_message( 'failed to find a pending image with the parameters supplied' );
164
+ return;
165
+ }
166
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
167
+ ewwwio_debug_message( print_r( $new_image, true ) );
168
+ }
169
+ $this->id = $new_image['id'];
170
+ $this->file = ewww_image_optimizer_relative_path_replace( $new_image['path'] );
171
+ $this->attachment_id = $new_image['attachment_id'];
172
+ $this->opt_size = $new_image['image_size'];
173
+ $this->orig_size = $new_image['orig_size'];
174
+ $this->resize = $new_image['resize'];
175
+ $this->converted = ewww_image_optimizer_relative_path_replace( $new_image['converted'] );
176
+ $this->gallery = ( empty( $gallery ) ? $new_image['gallery'] : $gallery );
177
+ $this->backup = $new_image['backup'];
178
+ }
179
+
180
+ /**
181
+ * Updates the post mime type field for an attachment after successful conversion.
182
+ *
183
+ * @param array $meta The attachment metadata.
184
+ */
185
+ public function update_converted_attachment( $meta ) {
186
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
187
+ $this->url = wp_get_attachment_url( $this->attachment_id );
188
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
189
+ ewwwio_debug_message( print_r( $this, true ) );
190
+ }
191
+ // Update the file location in the post metadata based on the new path stored in the attachment metadata.
192
+ update_attached_file( $this->attachment_id, $meta['file'] );
193
+ $this->replace_url();
194
+
195
+ // If the new image is a JPG.
196
+ if ( preg_match( '/.jpg$/i', $meta['file'] ) ) {
197
+ // Set the mimetype to JPG.
198
+ $mime = 'image/jpeg';
199
+ }
200
+ // If the new image is a PNG.
201
+ if ( preg_match( '/.png$/i', $meta['file'] ) ) {
202
+ // Set the mimetype to PNG.
203
+ $mime = 'image/png';
204
+ }
205
+ if ( preg_match( '/.gif$/i', $meta['file'] ) ) {
206
+ // Set the mimetype to GIF.
207
+ $mime = 'image/gif';
208
+ }
209
+ // Update the attachment post with the new mimetype and id.
210
+ wp_update_post( array(
211
+ 'ID' => $this->attachment_id,
212
+ 'post_mime_type' => $mime,
213
+ )
214
+ );
215
+ }
216
+
217
+ /**
218
+ * Converts all the 'resizes' after a successful conversion of the original image.
219
+ *
220
+ * @global object $wpdb
221
+ * @global object $ewwwdb A new database connection with super powers.
222
+ *
223
+ * @param array $meta The attachment metadata.
224
+ * @return array $meta The updated attachment metadata.
225
+ */
226
+ public function convert_sizes( $meta ) {
227
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
228
+
229
+ global $wpdb;
230
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
231
+ ewww_image_optimizer_db_init();
232
+ global $ewwwdb;
233
+ } else {
234
+ $ewwwdb = $wpdb;
235
+ }
236
+ $sizes_queried = $ewwwdb->get_results( "SELECT * FROM $ewwwdb->ewwwio_images WHERE attachment_id = $this->attachment_id AND resize <> 'full' AND resize <> ''", ARRAY_A );
237
+ /* ewwwio_debug_message( 'found some images in the db: ' . count( $sizes_queried ) ); */
238
+ $sizes = array();
239
+ if ( 'ims_image' == get_post_type( $this->attachment_id ) ) {
240
+ $base_dir = trailingslashit( dirname( $this->file ) ) . '_resized/';
241
+ } else {
242
+ $base_dir = trailingslashit( dirname( $this->file ) );
243
+ }
244
+ /* ewwwio_debug_message( 'about to process db results' ); */
245
+ foreach ( $sizes_queried as $size_queried ) {
246
+ $size_queried['path'] = ewww_image_optimizer_relative_path_replace( $size_queried['path'] );
247
+ $sizes[ $size_queried['resize'] ] = $size_queried;
248
+ // Convert here.
249
+ $new_name = $this->convert( $size_queried['path'] );
250
+ if ( $new_name ) {
251
+ $this->convert_retina( $size_queried['path'] );
252
+ $this->convert_db_path( $size_queried['path'], $new_name, $size_queried['id'] );
253
+ /* ewwwio_debug_message( print_r( $meta['sizes'], true ) ); */
254
+
255
+ /* ewwwio_debug_message( print_r( $size_queried, true ) ); */
256
+ if ( ewww_image_optimizer_iterable( $meta['sizes'] ) && is_array( $meta['sizes'][ $size_queried['resize'] ] ) ) {
257
+ ewwwio_debug_message( 'updating regular size' );
258
+ $meta['sizes'][ $size_queried['resize'] ]['file'] = basename( $new_name );
259
+ $meta['sizes'][ $size_queried['resize'] ]['mime-type'] = ewww_image_optimizer_quick_mimetype( $new_name );
260
+ } elseif ( ewww_image_optimizer_iterable( $meta['custom_sizes'] ) ) {
261
+ $dimensions = str_replace( 'custom-size-', '', $size_queried['resize'] );
262
+ if ( is_array( $meta['custom_sizes'][ $dimensions ] ) ) {
263
+ ewwwio_debug_message( 'updating custom size' );
264
+ $meta['custom_sizes'][ $dimensions ]['file'] = basename( $new_name );
265
+ }
266
+ }
267
+ }
268
+ ewwwio_debug_message( "converted {$size_queried['resize']} from db query" );
269
+ /* ewwwio_debug_message( print_r( $meta, true ) ); */
270
+ }
271
+ /* ewwwio_debug_message( print_r( $meta, true ) ); */
272
+
273
+ /* ewwwio_debug_message( 'next up for conversion search: meta' ); */
274
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
275
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes_opt' );
276
+ foreach ( $meta['sizes'] as $size => $data ) {
277
+ /* ewwwio_debug_message( "checking to see if we should convert $size" ); */
278
+ if ( strpos( $size, 'webp' ) === 0 ) {
279
+ /* ewwwio_debug_message( 'skipping webp' ); */
280
+ continue;
281
+ }
282
+ // Skip sizes that were already in ewwwio_images.
283
+ if ( isset( $sizes[ $size ] ) ) {
284
+ /* ewwwio_debug_message( 'skipping size that was in db results' ); */
285
+ continue;
286
+ }
287
+ if ( ! empty( $disabled_sizes[ $size ] ) ) {
288
+ /* ewwwio_debug_message( 'skipping disabled size' ); */
289
+ continue;
290
+ }
291
+ if ( empty( $data['file'] ) ) {
292
+ /* ewwwio_debug_message( 'skipping size with missing filename' ); */
293
+ continue;
294
+ }
295
+ foreach ( $sizes as $done ) {
296
+ if ( empty( $done['height'] ) || empty( $done['width'] ) ) {
297
+ continue;
298
+ }
299
+ if ( $data['height'] == $done['height'] && $data['width'] == $done['width'] ) {
300
+ continue( 2 );
301
+ }
302
+ }
303
+ $sizes[ $size ] = $data;
304
+ // Convert here.
305
+ $new_name = $this->convert( $base_dir . $data['file'] );
306
+ if ( $new_name ) {
307
+ $this->convert_retina( $base_dir . $data['file'] );
308
+ $this->convert_db_path( $base_dir . $data['file'], $new_name );
309
+ $meta['sizes'][ $size ]['file'] = basename( $new_name );
310
+ $meta['sizes'][ $size ]['mime-type'] = ewww_image_optimizer_quick_mimetype( $new_name );
311
+ }
312
+ ewwwio_debug_message( "converted $size from meta" );
313
+ } // End foreach().
314
+ } // End if().
315
+ /* ewwwio_debug_message( 'next up for conversion search: image_meta resizes' ); */
316
+
317
+ // Convert sizes from a custom theme.
318
+ if ( isset( $meta['image_meta']['resized_images'] ) && ewww_image_optimizer_iterable( $meta['image_meta']['resized_images'] ) ) {
319
+ $imagemeta_resize_pathinfo = pathinfo( $this->file );
320
+ $imagemeta_resize_path = '';
321
+ foreach ( $meta['image_meta']['resized_images'] as $index => $imagemeta_resize ) {
322
+ if ( isset( $sizes[ 'resized-images-' . $index ] ) ) {
323
+ continue;
324
+ }
325
+ $imagemeta_resize_path = $imagemeta_resize_pathinfo['dirname'] . '/' . $imagemeta_resize_pathinfo['filename'] . '-' . $imagemeta_resize . '.' . $imagemeta_resize_pathinfo['extension'];
326
+ $new_name = $this->convert( $imagemeta_resize_path );
327
+ if ( $new_name ) {
328
+ $this->convert_retina( $imagemeta_resize_path );
329
+ $this->convert_db_path( $imagemeta_resize_path, $new_name );
330
+ }
331
+ }
332
+ }
333
+
334
+ /* ewwwio_debug_message( 'next up for conversion search: custom_sizes' ); */
335
+
336
+ // and another custom theme.
337
+ if ( isset( $meta['custom_sizes'] ) && ewww_image_optimizer_iterable( $meta['custom_sizes'] ) ) {
338
+ $custom_sizes_pathinfo = pathinfo( $file_path );
339
+ $custom_size_path = '';
340
+ foreach ( $meta['custom_sizes'] as $dimensions => $custom_size ) {
341
+ if ( isset( $sizes[ 'custom-size-' . $dimensions ] ) ) {
342
+ continue;
343
+ }
344
+ $custom_size_path = $custom_sizes_pathinfo['dirname'] . '/' . $custom_size['file'];
345
+ $new_name = $this->convert( $custom_size_path );
346
+ if ( $new_name ) {
347
+ $this->convert_retina( $custom_size_path );
348
+ $this->convert_db_path( $custom_size_path, $new_name );
349
+ $meta['custom_sizes'][ $dimensions ]['file'] = basename( $new_name );
350
+ }
351
+ }
352
+ }
353
+ /* ewwwio_debug_message( print_r( $meta, true ) ); */
354
+
355
+ /* ewwwio_debug_message( 'all done converting sizes' ); */
356
+ return $meta;
357
+ }
358
+
359
+ /**
360
+ * Restore a converted image using the metadata.
361
+ *
362
+ * @param array $meta The attachment metadata.
363
+ * @return array The updated attachment metadata.
364
+ */
365
+ public function restore_with_meta( $meta ) {
366
+ if ( empty( $meta ) || ! is_array( $meta ) ) {
367
+ ewwwio_debug_message( 'invalid meta for restoration' );
368
+ return $meta;
369
+ }
370
+ if ( ! $this->file || ! is_file( $this->file ) || ! $this->converted || ! is_file( $this->converted ) ) {
371
+ ewwwio_debug_message( 'one of the files was not set for restoration (or did not exist)' );
372
+ return $meta;
373
+ }
374
+ $this->restore_db_path( $this->file, $this->converted, $this->id );
375
+ $converted_path = $this->file;
376
+ unlink( $this->file );
377
+ $this->file = $this->converted;
378
+ $this->converted = $converted_path;
379
+ $meta['file'] = trailingslashit( dirname( $meta['file'] ) ) . basename( $this->file );
380
+ $this->update_converted_attachment( $meta );
381
+ $meta = $this->restore_sizes( $meta );
382
+ return $meta;
383
+ }
384
+
385
+ /**
386
+ * Restores all the 'resizes' of a converted image.
387
+ *
388
+ * @global object $wpdb
389
+ * @global object $ewwwdb A new database connection with super powers.
390
+ *
391
+ * @param array $meta The attachment metadata.
392
+ * @return array $meta The updated attachment metadata.
393
+ */
394
+ private function restore_sizes( $meta ) {
395
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
396
+
397
+ global $wpdb;
398
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
399
+ ewww_image_optimizer_db_init();
400
+ global $ewwwdb;
401
+ } else {
402
+ $ewwwdb = $wpdb;
403
+ }
404
+ $sizes_queried = $ewwwdb->get_results( "SELECT id,path,converted,resize FROM $ewwwdb->ewwwio_images WHERE attachment_id = $this->attachment_id AND resize <> 'full'", ARRAY_A );
405
+ ewwwio_debug_message( 'found some images in the db: ' . count( $sizes_queried ) );
406
+
407
+ foreach ( $sizes_queried as $size_queried ) {
408
+ // Restore here.
409
+ if ( empty( $size_queried['converted'] ) ) {
410
+ continue;
411
+ }
412
+ $size_queried['path'] = ewww_image_optimizer_relative_path_replace( $size_queried['path'] );
413
+ $size_queried['converted'] = ewww_image_optimizer_relative_path_replace( $size_queried['converted'] );
414
+ $new_name = ( empty( $size_queried['converted'] ) ? '' : $size_queried['converted'] );
415
+ if ( $new_name && is_file( $size_queried['path'] ) && is_file( $new_name ) ) {
416
+ $this->restore_db_path( $size_queried['path'], $new_name, $size_queried['id'] );
417
+ $this->replace_url( $new_name, $size_queried['path'] );
418
+ if ( ewww_image_optimizer_iterable( $meta['sizes'] ) && is_array( $meta['sizes'][ $size_queried['resize'] ] ) ) {
419
+ ewwwio_debug_message( 'updating regular size' );
420
+ $meta['sizes'][ $size_queried['resize'] ]['file'] = basename( $new_name );
421
+ $meta['sizes'][ $size_queried['resize'] ]['mime-type'] = ewww_image_optimizer_quick_mimetype( $new_name );
422
+ } elseif ( ewww_image_optimizer_iterable( $meta['custom_sizes'] ) ) {
423
+ $dimensions = str_replace( 'custom-size-', '', $size_queried['resize'] );
424
+ if ( is_array( $meta['custom_sizes'][ $dimensions ] ) ) {
425
+ ewwwio_debug_message( 'updating custom size' );
426
+ $meta['custom_sizes'][ $dimensions ]['file'] = basename( $new_name );
427
+ }
428
+ }
429
+ unlink( $size_queried['path'] );
430
+ // Look for any 'duplicate' sizes that have the same dimensions as the current queried size.
431
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
432
+ foreach ( $meta['sizes'] as $size => $data ) {
433
+ if ( $meta['sizes'][ $size_queried['resize'] ]['height'] == $data['height'] && $meta['sizes'][ $size_queried['resize'] ]['width'] == $data['width'] ) {
434
+ $meta['sizes'][ $size ]['file'] = $meta['sizes'][ $size_queried['resize'] ]['file'];
435
+ $meta['sizes'][ $size ]['mime-type'] = $meta['sizes'][ $size_queried['resize'] ]['mime-type'];
436
+ }
437
+ }
438
+ }
439
+ }
440
+ ewwwio_debug_message( "restored {$size_queried['resize']} from db query" );
441
+ /* ewwwio_debug_message( print_r( $meta, true ) ); */
442
+ } // End foreach().
443
+
444
+ return $meta;
445
+ }
446
+
447
+ /**
448
+ * Looks for retina images to convert.
449
+ *
450
+ * @param string $file The name of the non-retina file.
451
+ */
452
+ private function convert_retina( $file ) {
453
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
454
+ $retina_path = ewww_image_optimizer_hidpi_optimize( $file, true );
455
+ if ( ! $retina_path ) {
456
+ return;
457
+ }
458
+ $new_name = $this->convert( $retina_path );
459
+ if ( $new_name ) {
460
+ $this->convert_db_path( $retina_path, $new_name );
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Converts a file using built-in PHP functions.
466
+ *
467
+ * @access private
468
+ *
469
+ * @param string $file The name of the file to convert.
470
+ * @return string The name of the new file.
471
+ */
472
+ private function convert( $file ) {
473
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
474
+ if ( empty( $file ) ) {
475
+ ewwwio_debug_message( 'no file provided to convert' );
476
+ return false;
477
+ }
478
+ if ( false === is_file( $file ) ) {
479
+ ewwwio_debug_message( "$file is not a file, cannot convert" );
480
+ return false;
481
+ }
482
+ if ( false === is_writable( $file ) ) {
483
+ ewwwio_debug_message( "$file is not writable, cannot convert" );
484
+ return false;
485
+ }
486
+ $type = ewww_image_optimizer_mimetype( $file, 'i' );
487
+ if ( ! $type ) {
488
+ ewwwio_debug_message( 'could not find any functions for mimetype detection' );
489
+ return false;
490
+ }
491
+ if ( strpos( $type, 'image' ) === false ) {
492
+ ewwwio_debug_message( "cannot convert mimetype: $type" );
493
+ return false;
494
+ }
495
+
496
+ // Just in case, run through the constants and utility checks, someday to be replaced with a proper object (or transient) that we can reference.
497
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) || ! EWWW_IMAGE_OPTIMIZER_CLOUD ) {
498
+ ewww_image_optimizer_define_noexec();
499
+ if ( EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
500
+ $nice = '';
501
+ } else {
502
+ // Check to see if 'nice' exists.
503
+ $nice = ewww_image_optimizer_find_nix_binary( 'nice', 'n' );
504
+ }
505
+ }
506
+ $skip = ewww_image_optimizer_skip_tools();
507
+ // If the user has disabled the utility checks.
508
+ if ( EWWW_IMAGE_OPTIMIZER_CLOUD ) {
509
+ $skip['jpegtran'] = true;
510
+ $skip['optipng'] = true;
511
+ $skip['gifsicle'] = true;
512
+ $skip['pngout'] = true;
513
+ $skip['pngquant'] = true;
514
+ $skip['webp'] = true;
515
+ }
516
+ switch ( $type ) {
517
+ case 'image/jpeg':
518
+ $png_size = 0;
519
+ $newfile = $this->unique_filename( $file, '.png' );
520
+ ewwwio_debug_message( "attempting to convert JPG to PNG: $newfile" );
521
+ // Convert the JPG to PNG.
522
+ if ( ewww_image_optimizer_gmagick_support() ) {
523
+ try {
524
+ $gmagick = new Gmagick( $file );
525
+ $gmagick->stripimage();
526
+ $gmagick->setimageformat( 'PNG' );
527
+ $gmagick->writeimage( $newfile );
528
+ } catch ( Exception $gmagick_error ) {
529
+ ewwwio_debug_message( $gmagick_error->getMessage() );
530
+ }
531
+ $png_size = ewww_image_optimizer_filesize( $newfile );
532
+ }
533
+ if ( ! $png_size && ewww_image_optimizer_imagick_support() ) {
534
+ try {
535
+ $imagick = new Imagick( $file );
536
+ $imagick->stripImage();
537
+ $imagick->setImageFormat( 'PNG' );
538
+ $imagick->writeImage( $newfile );
539
+ } catch ( Exception $imagick_error ) {
540
+ ewwwio_debug_message( $imagick_error->getMessage() );
541
+ }
542
+ $png_size = ewww_image_optimizer_filesize( $newfile );
543
+ }
544
+ if ( ! $png_size && ewww_image_optimizer_gd_support() ) {
545
+ ewwwio_debug_message( 'converting with GD' );
546
+ imagepng( imagecreatefromjpeg( $file ), $newfile );
547
+ $png_size = ewww_image_optimizer_filesize( $newfile );
548
+ }
549
+ ewwwio_debug_message( "converted PNG size: $png_size" );
550
+ // If the PNG exists, and we didn't end up with an empty file.
551
+ if ( $png_size && is_file( $newfile ) && ewww_image_optimizer_mimetype( $newfile, 'i' ) == 'image/png' ) {
552
+ ewwwio_debug_message( 'JPG to PNG successful' );
553
+ // Check to see if the user wants the originals deleted.
554
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
555
+ // Delete the original JPG.
556
+ unlink( $file );
557
+ }
558
+ } else {
559
+ ewwwio_debug_message( 'converted PNG is no good' );
560
+ if ( is_file( $newfile ) ) {
561
+ unlink( $newfile );
562
+ }
563
+ return false;
564
+ }
565
+ break;
566
+ case 'image/png':
567
+ $jpg_size = 0;
568
+ $newfile = $this->unique_filename( $file, '.jpg' );
569
+ ewwwio_debug_message( "attempting to convert PNG to JPG: $newfile" );
570
+ // If the user set a fill background for transparency.
571
+ $background = ewww_image_optimizer_jpg_background();
572
+ if ( $background ) {
573
+ // Set background color for GD.
574
+ $r = hexdec( '0x' . strtoupper( substr( $background, 0, 2 ) ) );
575
+ $g = hexdec( '0x' . strtoupper( substr( $background, 2, 2 ) ) );
576
+ $b = hexdec( '0x' . strtoupper( substr( $background, 4, 2 ) ) );
577
+ } else {
578
+ $r = '';
579
+ $g = '';
580
+ $b = '';
581
+ }
582
+ // If the user manually set the JPG quality.
583
+ $quality = ewww_image_optimizer_jpg_quality();
584
+ if ( empty( $quality ) ) {
585
+ $quality = '92';
586
+ }
587
+ $magick_background = ewww_image_optimizer_jpg_background();
588
+ if ( empty( $magick_background ) ) {
589
+ $magick_background = '000000';
590
+ }
591
+ // Convert the PNG to a JPG with all the proper options.
592
+ if ( ewww_image_optimizer_gmagick_support() ) {
593
+ try {
594
+ if ( ewww_image_optimizer_png_alpha( $file ) ) {
595
+ $gmagick_overlay = new Gmagick( $file );
596
+ $gmagick = new Gmagick();
597
+ $gmagick->newimage( $gmagick_overlay->getimagewidth(), $gmagick_overlay->getimageheight(), '#' . $magick_background );
598
+ $gmagick->compositeimage( $gmagick_overlay, 1, 0, 0 );
599
+ } else {
600
+ $gmagick = new Gmagick( $file );
601
+ }
602
+ $gmagick->setimageformat( 'JPG' );
603
+ $gmagick->setcompressionquality( $quality );
604
+ $gmagick->writeimage( $newfile );
605
+ } catch ( Exception $gmagick_error ) {
606
+ ewwwio_debug_message( $gmagick_error->getMessage() );
607
+ }
608
+ $jpg_size = ewww_image_optimizer_filesize( $newfile );
609
+ }
610
+ if ( ! $jpg_size && ewww_image_optimizer_imagick_support() ) {
611
+ try {
612
+ $imagick = new Imagick( $file );
613
+ if ( ewww_image_optimizer_png_alpha( $file ) ) {
614
+ $imagick->setImageBackgroundColor( new ImagickPixel( '#' . $magick_background ) );
615
+ $imagick->setImageAlphaChannel( 11 );
616
+ }
617
+ $imagick->setImageFormat( 'JPG' );
618
+ $imagick->setCompressionQuality( $quality );
619
+ $imagick->writeImage( $newfile );
620
+ } catch ( Exception $imagick_error ) {
621
+ ewwwio_debug_message( $imagick_error->getMessage() );
622
+ }
623
+ $jpg_size = ewww_image_optimizer_filesize( $newfile );
624
+ }
625
+ if ( ! $jpg_size && ewww_image_optimizer_gd_support() ) {
626
+ ewwwio_debug_message( 'converting with GD' );
627
+ // Retrieve the data from the PNG.
628
+ $input = imagecreatefrompng( $file );
629
+ // Retrieve the dimensions of the PNG.
630
+ list( $width, $height ) = getimagesize( $file );
631
+ // Create a new image with those dimensions.
632
+ $output = imagecreatetruecolor( $width, $height );
633
+ if ( '' === $r ) {
634
+ $r = 255;
635
+ $g = 255;
636
+ $b = 255;
637
+ }
638
+ // Allocate the background color.
639
+ $rgb = imagecolorallocate( $output, $r, $g, $b );
640
+ // Fill the new image with the background color.
641
+ imagefilledrectangle( $output, 0, 0, $width, $height, $rgb );
642
+ // Copy the original image to the new image.
643
+ imagecopy( $output, $input, 0, 0, 0, 0, $width, $height );
644
+ // Output the JPG with the quality setting.
645
+ imagejpeg( $output, $newfile, $quality );
646
+ $jpg_size = ewww_image_optimizer_filesize( $newfile );
647
+ }
648
+ ewwwio_debug_message( "converted JPG size: $jpg_size" );
649
+ // If the new JPG is smaller than the original PNG.
650
+ if ( $jpg_size && is_file( $newfile ) && ewww_image_optimizer_mimetype( $newfile, 'i' ) == 'image/jpeg' ) {
651
+ ewwwio_debug_message( 'JPG to PNG successful' );
652
+ // If the user wants originals delted after a conversion.
653
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
654
+ // Delete the original PNG.
655
+ unlink( $file );
656
+ }
657
+ } else {
658
+ if ( is_file( $newfile ) ) {
659
+ // Otherwise delete the new JPG.
660
+ unlink( $newfile );
661
+ }
662
+ return false;
663
+ }
664
+ break;
665
+ case 'image/gif':
666
+ $png_size = 0;
667
+ $newfile = $this->unique_filename( $file, '.png' );
668
+ ewwwio_debug_message( "attempting to convert GIF to PNG: $newfile" );
669
+ // Convert the GIF to PNG.
670
+ if ( ewww_image_optimizer_gmagick_support() ) {
671
+ try {
672
+ $gmagick = new Gmagick( $file );
673
+ $gmagick->stripimage();
674
+ $gmagick->setimageformat( 'PNG' );
675
+ $gmagick->writeimage( $newfile );
676
+ } catch ( Exception $gmagick_error ) {
677
+ ewwwio_debug_message( $gmagick_error->getMessage() );
678
+ }
679
+ $png_size = ewww_image_optimizer_filesize( $newfile );
680
+ }
681
+ if ( ! $png_size && ewww_image_optimizer_imagick_support() ) {
682
+ try {
683
+ $imagick = new Imagick( $file );
684
+ $imagick->stripImage();
685
+ $imagick->setImageFormat( 'PNG' );
686
+ $imagick->writeImage( $newfile );
687
+ } catch ( Exception $imagick_error ) {
688
+ ewwwio_debug_message( $imagick_error->getMessage() );
689
+ }
690
+ $png_size = ewww_image_optimizer_filesize( $newfile );
691
+ }
692
+ if ( ! $png_size && ewww_image_optimizer_gd_support() ) {
693
+ ewwwio_debug_message( 'converting with GD' );
694
+ imagepng( imagecreatefromgif( $file ), $newfile );
695
+ $png_size = ewww_image_optimizer_filesize( $newfile );
696
+ }
697
+ ewwwio_debug_message( "converted PNG size: $png_size" );
698
+ // If the PNG exists, and we didn't end up with an empty file.
699
+ if ( $png_size && is_file( $newfile ) && ewww_image_optimizer_mimetype( $newfile, 'i' ) == 'image/png' ) {
700
+ ewwwio_debug_message( 'GIF to PNG successful' );
701
+ // Check to see if the user wants the originals deleted.
702
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
703
+ // Delete the original JPG.
704
+ unlink( $file );
705
+ }
706
+ } else {
707
+ ewwwio_debug_message( 'converted PNG is no good' );
708
+ if ( is_file( $newfile ) ) {
709
+ unlink( $newfile );
710
+ }
711
+ return false;
712
+ }
713
+ break;
714
+ default:
715
+ return false;
716
+ } // End switch().
717
+ $this->replace_url( $newfile, $file );
718
+ return $newfile;
719
+ }
720
+
721
+ /**
722
+ * Generate a unique filename for a converted image.
723
+ *
724
+ * @param string $file The original name of the file.
725
+ * @param string $fileext The extension of the new file.
726
+ * @return string The new filename.
727
+ */
728
+ public function unique_filename( $file, $fileext ) {
729
+ // Strip the file extension.
730
+ $filename = preg_replace( '/\.\w+$/', '', $file );
731
+ if ( ! is_file( $filename . $fileext ) ) {
732
+ return $filename . $fileext;
733
+ }
734
+ // Set the increment to 1 ( but allow the user to override it ).
735
+ $filenum = apply_filters( 'ewww_image_optimizer_converted_filename_suffix', $this->increment );
736
+ // But it must be only letters, numbers, or underscores.
737
+ $filenum = ( preg_match( '/^[\w\d]*$/', $filenum ) ? $filenum : 1 );
738
+ $suffix = ( ! empty( $filenum ) ? '-' . $filenum : '' );
739
+ $dimensions = '';
740
+ $default_hidpi_suffix = apply_filters( 'ewww_image_optimizer_hidpi_suffix', '@2x' );
741
+ $hidpi_suffix = '';
742
+ // See if this is a retina image, and strip the suffix.
743
+ if ( preg_match( "/$default_hidpi_suffix$/", $filename ) ) {
744
+ // Strip the dimensions.
745
+ $filename = str_replace( $default_hidpi_suffix, '', $filename );
746
+ $hidpi_suffix = $default_hidpi_suffix;
747
+ }
748
+ // See if this is a resize, and strip the dimensions.
749
+ if ( preg_match( '/-\d+x\d+(-\d+)*$/', $filename, $fileresize ) ) {
750
+ // Strip the dimensions.
751
+ $filename = str_replace( $fileresize[0], '', $filename );
752
+ $dimensions = $fileresize[0];
753
+ }
754
+ // While a file exists with the current increment.
755
+ while ( file_exists( $filename . $suffix . $dimensions . $hidpi_suffix . $fileext ) ) {
756
+ // Increment the increment...
757
+ $filenum++;
758
+ $suffix = '-' . $filenum;
759
+ }
760
+ // All done, let's reconstruct the filename.
761
+ ewwwio_memory( __FUNCTION__ );
762
+ $this->increment = $filenum;
763
+ return $filename . $suffix . $dimensions . $hidpi_suffix . $fileext;
764
+ }
765
+
766
+ /**
767
+ * Update urls in the WP database.
768
+ *
769
+ * @global object $wpdb
770
+ *
771
+ * @param string $new_path Optional. The url to the newly converted image.
772
+ * @param string $old_path Optional. The url to the old version of the image.
773
+ */
774
+ public function replace_url( $new_path = '', $old_path = '' ) {
775
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
776
+
777
+ $new = ( empty( $new_path ) ? $this->file : $new_path );
778
+ $old = ( empty( $old_path ) ? $this->converted : $old_path );
779
+ if ( empty( $new ) || empty( $old ) ) {
780
+ return;
781
+ }
782
+ if ( empty( $new_path ) && empty( $old_path ) ) {
783
+ $old_guid = $this->url;
784
+ } else {
785
+ $old_guid = trailingslashit( dirname( $this->url ) ) . basename( $old );
786
+ }
787
+ $guid = trailingslashit( dirname( $this->url ) ) . basename( $new );
788
+ // Construct the new guid based on the filename from the attachment metadata.
789
+ ewwwio_debug_message( "old guid: $old_guid" );
790
+ ewwwio_debug_message( "new guid: $guid" );
791
+ if ( substr( $old_guid, -1 ) == '/' || substr( $guid, -1 ) == '/' ) {
792
+ ewwwio_debug_message( 'could not obtain full url for current and previous image, bailing' );
793
+ return;
794
+ }
795
+
796
+ global $wpdb;
797
+ // Retrieve any posts that link the image.
798
+ $esql = $wpdb->prepare( "SELECT ID, post_content FROM $wpdb->posts WHERE post_content LIKE '%%%s%%'", $old_guid );
799
+ ewwwio_debug_message( "using query: $esql" );
800
+ $rows = $wpdb->get_results( $esql, ARRAY_A ); // WPCS: unprepared SQL ok.
801
+ if ( ewww_image_optimizer_iterable( $rows ) ) {
802
+ // While there are posts to process.
803
+ foreach ( $rows as $row ) {
804
+ // Replace all occurences of the old guid with the new guid.
805
+ $post_content = str_replace( $old_guid, $guid, $row['post_content'] );
806
+ ewwwio_debug_message( "replacing $old_guid with $guid in post " . $row['ID'] );
807
+ // Send the updated content back to the database.
808
+ $wpdb->update(
809
+ $wpdb->posts,
810
+ array(
811
+ 'post_content' => $post_content,
812
+ ),
813
+ array(
814
+ 'ID' => $row['ID'],
815
+ )
816
+ );
817
+ }
818
+ }
819
+ }
820
+
821
+ /**
822
+ * Updates records in the ewwwio_images table after conversion.
823
+ *
824
+ * @global object $wpdb
825
+ * @global object $ewwwdb A new database connection with super powers.
826
+ *
827
+ * @param string $path The old path to search for.
828
+ * @param string $new_path The new path to update.
829
+ * @param int $id Optional. Database record id for the original image.
830
+ */
831
+ private function convert_db_path( $path, $new_path, $id = false ) {
832
+ if ( empty( $path ) || empty( $new_path ) ) {
833
+ return;
834
+ }
835
+ global $wpdb;
836
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
837
+ ewww_image_optimizer_db_init();
838
+ global $ewwwdb;
839
+ } else {
840
+ $ewwwdb = $wpdb;
841
+ }
842
+ if ( ! $id ) {
843
+ $image_record = ewww_image_optimizer_find_already_optimized( $path );
844
+ if ( ! empty( $image_record ) && is_array( $image_record ) && ! empty( $image_record['id'] ) ) {
845
+ $id = $image_record['id'];
846
+ } else { // Insert a new record.
847
+ $ewwwdb->insert( $ewwwdb->ewwwio_images, array(
848
+ 'path' => ewww_image_optimizer_relative_path_remove( $new_path ),
849
+ 'converted' => ewww_image_optimizer_relative_path_remove( $path ),
850
+ 'orig_size' => filesize( $new_path ),
851
+ 'attachment_id' => $this->attachment_id,
852
+ 'results' => __( 'No savings', 'ewww-image-optimizer' ),
853
+ 'updated' => date( 'Y-m-d H:i:s' ),
854
+ 'updates' => 0,
855
+ ) );
856
+ return;
857
+ }
858
+ }
859
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
860
+ array(
861
+ 'path' => ewww_image_optimizer_relative_path_remove( $new_path ),
862
+ 'converted' => ewww_image_optimizer_relative_path_remove( $path ),
863
+ 'results' => ewww_image_optimizer_image_results( $image_record['orig_size'], filesize( $new_path ) ),
864
+ 'updates' => 0,
865
+ 'trace' => '',
866
+ ),
867
+ array(
868
+ 'id' => $id,
869
+ )
870
+ );
871
+ }
872
+
873
+ /**
874
+ * Updates records in the ewwwio_images table after the original image is restored.
875
+ *
876
+ * @global object $wpdb
877
+ * @global object $ewwwdb A new database connection with super powers.
878
+ *
879
+ * @param string $path The old path to search for.
880
+ * @param string $new_path The new path to update.
881
+ * @param int $id Optional. Database record id for the original image.
882
+ */
883
+ private function restore_db_path( $path, $new_path, $id = false ) {
884
+ if ( empty( $path ) || empty( $new_path ) ) {
885
+ return;
886
+ }
887
+ global $wpdb;
888
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
889
+ ewww_image_optimizer_db_init();
890
+ global $ewwwdb;
891
+ } else {
892
+ $ewwwdb = $wpdb;
893
+ }
894
+ if ( ! $id ) {
895
+ $image_record = ewww_image_optimizer_find_already_optimized( $path );
896
+ if ( ! empty( $image_record ) && is_array( $image_record ) && ! empty( $image_record['id'] ) ) {
897
+ $id = $image_record['id'];
898
+ } else {
899
+ return false;
900
+ }
901
+ }
902
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
903
+ array(
904
+ 'path' => ewww_image_optimizer_relative_path_remove( $new_path ),
905
+ 'converted' => '',
906
+ 'image_size' => 0,
907
+ 'results' => __( 'Original Restored', 'ewww-image-optimizer' ),
908
+ 'updates' => 0,
909
+ 'trace' => '',
910
+ 'level' => null,
911
+ ),
912
+ array(
913
+ 'id' => $id,
914
+ )
915
+ );
916
+ }
917
+
918
+ /**
919
+ * Perform an estimate of the time required to optimize an image.
920
+ *
921
+ * Estimates are based on the image type, file size, and optimization level using averages from API logs.
922
+ *
923
+ * @return int The number of seconds expected to compress the current image.
924
+ */
925
+ public function time_estimate() {
926
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
927
+ $time = 0;
928
+ $type = ewww_image_optimizer_quick_mimetype( $this->file );
929
+ $image_size = ( empty( $this->opt_size ) ? $this->orig_size : $this->opt_size );
930
+ if ( empty( $image_size ) ) {
931
+ $this->orig_size = filesize( $this->file );
932
+ $image_size = $this->orig_size;
933
+ }
934
+ switch ( $type ) {
935
+ case 'image/jpeg':
936
+ if ( $image_size > 10000000 ) { // greater than 10MB.
937
+ $time += 20;
938
+ } elseif ( $image_size > 5000000 ) { // greater than 5MB.
939
+ $time += 10;
940
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 40 ) {
941
+ $time += 25;
942
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 30 ) {
943
+ $time += 7;
944
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 20 ) {
945
+ $time += 2;
946
+ }
947
+ } elseif ( $image_size > 1000000 ) { // greater than 1MB.
948
+ $time += 5;
949
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 40 ) {
950
+ if ( $image_size > 2000000 ) { // greater than 2MB.
951
+ $time += 15;
952
+ } else {
953
+ $time += 11;
954
+ }
955
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 30 ) {
956
+ $time += 6;
957
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 20 ) {
958
+ $time += 2;
959
+ }
960
+ } else {
961
+ $time++;
962
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 40 ) {
963
+ if ( $image_size > 200000 ) { // greater than 200k.
964
+ $time += 11;
965
+ } else {
966
+ $time += 5;
967
+ }
968
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 30 ) {
969
+ $time += 3;
970
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 20 ) {
971
+ $time += 3;
972
+ }
973
+ } // End if().
974
+ break;
975
+ case 'image/png':
976
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
977
+ $time++;
978
+ }
979
+ if ( $image_size > 2500000 ) { // greater than 2.5MB.
980
+ $time += 35;
981
+ } elseif ( $image_size > 1000000 ) { // greater than 1MB.
982
+ $time += 15;
983
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 50 ) {
984
+ $time += 8;
985
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 ) {
986
+ /* $time++; */
987
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 30 ) {
988
+ $time += 10;
989
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 20 ) {
990
+ $time++;
991
+ }
992
+ } elseif ( $image_size > 500000 ) { // greater than 500kb.
993
+ $time += 7;
994
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 50 ) {
995
+ $time += 5;
996
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 ) {
997
+ /* $time++; */
998
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 30 ) {
999
+ $time += 8;
1000
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 20 ) {
1001
+ $time++;
1002
+ }
1003
+ } elseif ( $image_size > 100000 ) { // greater than 100kb.
1004
+ $time += 4;
1005
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 50 ) {
1006
+ $time += 5;
1007
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 ) {
1008
+ /* $time++; */
1009
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 30 ) {
1010
+ $time += 9;
1011
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 20 ) {
1012
+ $time++;
1013
+ }
1014
+ } else {
1015
+ $time++;
1016
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 50 ) {
1017
+ $time += 2;
1018
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 ) {
1019
+ $time ++;
1020
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 30 ) {
1021
+ $time += 3;
1022
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 20 ) {
1023
+ $time++;
1024
+ }
1025
+ } // End if().
1026
+ break;
1027
+ case 'image/gif':
1028
+ $time++;
1029
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) == 10 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
1030
+ $time++;
1031
+ }
1032
+ if ( $image_size > 1000000 ) { // greater than 1MB.
1033
+ $time += 5;
1034
+ }
1035
+ break;
1036
+ case 'application/pdf':
1037
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) ) {
1038
+ $time += 2;
1039
+ }
1040
+ if ( $image_size > 25000000 ) { // greater than 25MB.
1041
+ $time += 20;
1042
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) == 20 ) {
1043
+ $time += 16;
1044
+ }
1045
+ } elseif ( $image_size > 10000000 ) { // greater than 10MB.
1046
+ $time += 10;
1047
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) == 20 ) {
1048
+ $time += 20;
1049
+ }
1050
+ } elseif ( $image_size > 4000000 ) { // greater than 4MB.
1051
+ $time += 3;
1052
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) == 20 ) {
1053
+ $time += 12;
1054
+ }
1055
+ } elseif ( $image_size > 1000000 ) { // greater than 1MB.
1056
+ $time++;
1057
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) == 20 ) {
1058
+ $time += 10;
1059
+ }
1060
+ }
1061
+ break;
1062
+ default:
1063
+ $time = 30;
1064
+ } // End switch().
1065
+ ewwwio_debug_message( "estimated time for this image is $time" );
1066
+ return $time;
1067
+ }
1068
+
1069
+ }
classes/class-ewww-nextcellent.php ADDED
@@ -0,0 +1,774 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class and methods to integrate EWWW IO and Nextcellent Gallery.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EWWW_Image_Optimizer
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ if ( ! class_exists( 'EWWW_Nextcellent' ) ) {
13
+ /**
14
+ * Allows EWWW to integrate with the Nextcellent Gallery plugin.
15
+ *
16
+ * Adds automatic optimization on upload, a bulk optimizer, and compression details when
17
+ * managing galleries.
18
+ */
19
+ class EWWW_Nextcellent {
20
+ /**
21
+ * Initializes the nextcellent integration functions.
22
+ */
23
+ function __construct() {
24
+ add_filter( 'ngg_manage_images_columns', array( $this, 'ewww_manage_images_columns' ) );
25
+ add_action( 'ngg_manage_image_custom_column', array( $this, 'ewww_manage_image_custom_column' ), 10, 2 );
26
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_background_optimization' ) ) {
27
+ add_action( 'ngg_after_new_images_added', array( $this, 'dispatch_new_images' ), 10, 2 );
28
+ } else {
29
+ add_action( 'ngg_added_new_image', array( $this, 'ewww_added_new_image_slow' ) );
30
+ }
31
+ add_action( 'admin_enqueue_scripts', array( $this, 'ewww_ngg_manual_actions_script' ) );
32
+ add_action( 'wp_ajax_ewww_ngg_manual', array( $this, 'ewww_ngg_manual' ) );
33
+ add_action( 'wp_ajax_ewww_ngg_cloud_restore', array( $this, 'ewww_ngg_cloud_restore' ) );
34
+ add_action( 'admin_action_ewww_ngg_manual', array( $this, 'ewww_ngg_manual' ) );
35
+ add_action( 'admin_menu', array( $this, 'ewww_ngg_bulk_menu' ) );
36
+ add_action( 'admin_head-galleries_page_nggallery-manage-gallery', array( $this, 'ewww_ngg_bulk_actions_script' ) );
37
+ add_action( 'admin_enqueue_scripts', array( $this, 'ewww_ngg_bulk_script' ), 9 );
38
+ add_action( 'wp_ajax_bulk_ngg_preview', array( $this, 'ewww_ngg_bulk_preview' ) );
39
+ add_action( 'wp_ajax_bulk_ngg_init', array( $this, 'ewww_ngg_bulk_init' ) );
40
+ add_action( 'wp_ajax_bulk_ngg_filename', array( $this, 'ewww_ngg_bulk_filename' ) );
41
+ add_action( 'wp_ajax_bulk_ngg_loop', array( $this, 'ewww_ngg_bulk_loop' ) );
42
+ add_action( 'wp_ajax_bulk_ngg_cleanup', array( $this, 'ewww_ngg_bulk_cleanup' ) );
43
+ add_action( 'ngg_ajax_image_save', array( $this, 'ewww_ngg_image_save' ) );
44
+ }
45
+
46
+ /**
47
+ * Adds the Bulk Optimize page to the tools menu.
48
+ */
49
+ function ewww_ngg_bulk_menu() {
50
+ add_submenu_page( NGGFOLDER, esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), 'NextGEN Manage gallery', 'ewww-ngg-bulk', array( &$this, 'ewww_ngg_bulk_preview' ) );
51
+ }
52
+
53
+ /**
54
+ * Adds a newly uploaded image to the optimization queue.
55
+ *
56
+ * @param int $gallery The gallery ID number (I think).
57
+ * @param array $images The list of new images.
58
+ */
59
+ function dispatch_new_images( $gallery, $images ) {
60
+ global $ewwwio_ngg_background;
61
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
62
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
63
+ }
64
+ if ( ! is_object( $ewwwio_ngg_background ) ) {
65
+ $ewwwio_ngg_background = new EWWWIO_Ngg_Background_Process();
66
+ }
67
+ foreach ( $images as $id ) {
68
+ $ewwwio_ngg_background->push_to_queue( array(
69
+ 'id' => $id,
70
+ ) );
71
+ set_transient( 'ewwwio-background-in-progress-ngg-' . $id, true, 24 * HOUR_IN_SECONDS );
72
+ ewwwio_debug_message( "optimization (nextcellent) queued for $id" );
73
+ }
74
+ $ewwwio_ngg_background->save()->dispatch();
75
+ ewww_image_optimizer_debug_log();
76
+ }
77
+
78
+ /**
79
+ * Optimizes a new image from the queue.
80
+ *
81
+ * @global object $ewww_image Contains more information about the image currently being processed.
82
+ * @global object $nggdb
83
+ *
84
+ * @param int $id The ID number of the image.
85
+ * @param array $meta The image metadata.
86
+ */
87
+ function ewww_added_new_image( $id, $meta ) {
88
+ global $ewww_image;
89
+ // Retrieve the image path.
90
+ $file_path = $meta->image->imagePath;
91
+ $ewww_image = new EWWW_Image( $id, 'nextcell', $file_path );
92
+ $ewww_image->resize = 'full';
93
+ // Run the optimizer on the current image.
94
+ $fres = ewww_image_optimizer( $file_path, 2, false, false, true );
95
+ // Update the metadata for the optimized image.
96
+ global $nggdb;
97
+ $nggdb->update_image_meta( $id, array(
98
+ 'ewww_image_optimizer' => $fres[1],
99
+ ) );
100
+ }
101
+
102
+ /**
103
+ * Optimizes a new image in foreground mode.
104
+ *
105
+ * @global bool $ewww_defer Set to false to avoid deferring image optimization.
106
+ * @global object $wpdb
107
+ * @global object $ewww_image Contains more information about the image currently being processed.
108
+ *
109
+ * @param array $image The new image and all the related data.
110
+ */
111
+ function ewww_added_new_image_slow( $image ) {
112
+ // Query the filesystem path of the gallery from the database.
113
+ global $ewww_defer;
114
+ global $wpdb;
115
+ global $ewww_image;
116
+ $gallery_path = $wpdb->get_var( $wpdb->prepare( "SELECT path FROM {$wpdb->prefix}ngg_gallery WHERE gid = %d LIMIT 1", $image['galleryID'] ) );
117
+ // If we have a path to work with.
118
+ if ( $gallery_path ) {
119
+ // Construct the absolute path of the current image.
120
+ $file_path = trailingslashit( $gallery_path ) . $image['filename'];
121
+ $ewww_image = new EWWW_Image( $image['id'], 'nextcell', $file_path );
122
+ $ewww_image->resize = 'full';
123
+ // Run the optimizer on the current image.
124
+ $res = ewww_image_optimizer( ABSPATH . $file_path, 2, false, false, true );
125
+ // Update the metadata for the optimized image.
126
+ nggdb::update_image_meta( $image['id'], array(
127
+ 'ewww_image_optimizer' => $res[1],
128
+ ) );
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Optimizes the thumbnail generated for a new upload.
134
+ *
135
+ * @global bool $ewww_defer Set to false to avoid deferring image optimization.
136
+ * @global object $ewww_image Contains more information about the image currently being processed.
137
+ *
138
+ * @param string $filename The name of the file generated.
139
+ */
140
+ function ewww_ngg_image_save( $filename ) {
141
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
142
+ global $ewww_defer;
143
+ global $ewww_image;
144
+ if ( file_exists( $filename ) ) {
145
+ if ( ! empty( $_POST['id'] ) ) {
146
+ $id = (int) $_POST['id'];
147
+ } elseif ( ! empty( $_POST['image'] ) && is_numeric( $_POST['image'] ) ) {
148
+ $id = (int) $_POST['image'];
149
+ }
150
+ $ewww_image = new EWWW_Image( $id, 'nextcell', $filename );
151
+ $ewww_image->resize = 'thumbnail';
152
+ ewww_image_optimizer( $filename );
153
+ }
154
+ ewww_image_optimizer_debug_log();
155
+ ewwwio_memory( __FUNCTION__ );
156
+ }
157
+
158
+ /**
159
+ * Manually process an image from the NextGEN Gallery.
160
+ */
161
+ function ewww_ngg_manual() {
162
+ // Check permission of current user.
163
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
164
+ if ( false === current_user_can( $permissions ) ) {
165
+ if ( ! wp_doing_ajax() ) {
166
+ wp_die( esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ) );
167
+ }
168
+ wp_die( json_encode( array(
169
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
170
+ ) ) );
171
+ }
172
+ // Make sure function wasn't called without an attachment to work with.
173
+ if ( empty( $_REQUEST['ewww_attachment_ID'] ) ) {
174
+ if ( ! wp_doing_ajax() ) {
175
+ wp_die( esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ) );
176
+ }
177
+ wp_die( json_encode( array(
178
+ 'error' => esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ),
179
+ ) ) );
180
+ }
181
+ // Store the attachment $id.
182
+ $id = intval( $_REQUEST['ewww_attachment_ID'] );
183
+ if ( empty( $_REQUEST['ewww_manual_nonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_manual_nonce'], "ewww-manual-$id" ) ) {
184
+ if ( ! wp_doing_ajax() ) {
185
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
186
+ }
187
+ wp_die( json_encode( array(
188
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
189
+ ) ) );
190
+ }
191
+ $this->ewww_ngg_optimize( $id );
192
+ $success = $this->ewww_manage_image_custom_column( 'ewww_image_optimizer', $id, true );
193
+ ewww_image_optimizer_debug_log();
194
+ if ( ! wp_doing_ajax() ) {
195
+ // Get the referring page, and send the user back there.
196
+ $sendback = wp_get_referer();
197
+ $sendback = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback );
198
+ wp_redirect( $sendback );
199
+ die;
200
+ }
201
+ die( json_encode( array(
202
+ 'success' => $success,
203
+ ) ) );
204
+ }
205
+
206
+ /**
207
+ * Restore an image from the NextGEN Gallery.
208
+ */
209
+ function ewww_ngg_cloud_restore() {
210
+ // Check permission of current user.
211
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
212
+ if ( false === current_user_can( $permissions ) ) {
213
+ if ( ! wp_doing_ajax() ) {
214
+ wp_die( esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ) );
215
+ }
216
+ wp_die( json_encode( array(
217
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
218
+ ) ) );
219
+ }
220
+ // Make sure function wasn't called without an attachment to work with.
221
+ if ( false === isset( $_REQUEST['ewww_attachment_ID'] ) ) {
222
+ if ( ! wp_doing_ajax() ) {
223
+ wp_die( esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ) );
224
+ }
225
+ wp_die( json_encode( array(
226
+ 'error' => esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ),
227
+ ) ) );
228
+ }
229
+ // Sanitize the attachment $id.
230
+ $id = intval( $_REQUEST['ewww_attachment_ID'] );
231
+ if ( empty( $_REQUEST['ewww_manual_nonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_manual_nonce'], "ewww-manual-$id" ) ) {
232
+ if ( ! wp_doing_ajax() ) {
233
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
234
+ }
235
+ wp_die( json_encode( array(
236
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
237
+ ) ) );
238
+ }
239
+ ewww_image_optimizer_cloud_restore_from_meta_data( $id, 'nextcell' );
240
+ $success = $this->ewww_manage_image_custom_column( 'ewww_image_optimizer', $id, true );
241
+ die( json_encode( array(
242
+ 'success' => $success,
243
+ ) ) );
244
+ }
245
+
246
+ /**
247
+ * Optimize a nextcellent image by ID.
248
+ *
249
+ * @global object $ewww_image Contains more information about the image currently being processed.
250
+ * @global object nggdb
251
+ *
252
+ * @param int $id The ID number of the image.
253
+ * @return array {
254
+ * The optimization results for the image.
255
+ *
256
+ * @type array $fres The optimization results for the full-size image.
257
+ * @type array $tres The optimization results for the thumbnail.
258
+ * }
259
+ */
260
+ function ewww_ngg_optimize( $id ) {
261
+ global $ewww_image;
262
+ // Need this file to work with metadata.
263
+ require_once( WP_CONTENT_DIR . '/plugins/nextcellent-gallery-nextgen-legacy/lib/meta.php' );
264
+ // Retrieve the metadata for the image.
265
+ $meta = new nggMeta( $id );
266
+ // Retrieve the image path.
267
+ $file_path = $meta->image->imagePath;
268
+ $ewww_image = new EWWW_Image( $id, 'nextcell', $file_path );
269
+ $ewww_image->resize = 'full';
270
+ // Run the optimizer on the current image.
271
+ $fres = ewww_image_optimizer( $file_path, 2, false, false, true );
272
+ // Update the metadata for the optimized image.
273
+ global $nggdb;
274
+ $nggdb->update_image_meta( $id, array(
275
+ 'ewww_image_optimizer' => $fres[1],
276
+ ) );
277
+ // Get the filepath of the thumbnail image.
278
+ $thumb_path = $meta->image->thumbPath;
279
+ $ewww_image = new EWWW_Image( $id, 'nextcell', $thumb_path );
280
+ $ewww_image->resize = 'thumbnail';
281
+ // Run the optimization on the thumbnail.
282
+ $tres = ewww_image_optimizer( $thumb_path, 2, false, true );
283
+ return array( $fres, $tres );
284
+ }
285
+
286
+ /**
287
+ * Adds the Image Optimizer column via the ngg_manage_images_columns hook.
288
+ *
289
+ * @param array $columns A list of columns to display in the images table.
290
+ * @return array The updated list of columns.
291
+ */
292
+ function ewww_manage_images_columns( $columns ) {
293
+ $columns['ewww_image_optimizer'] = esc_html__( 'Image Optimizer', 'ewww-image-optimizer' );
294
+ return $columns;
295
+ }
296
+
297
+ /**
298
+ * Displays the Image Optimizer data via the ngg_manage_image_custom_column hook.
299
+ *
300
+ * @param string $column_name The name of the current column.
301
+ * @param int $id The ID number of the current image.
302
+ * @param bool $return Return the output instead of sending it straight to the screen.
303
+ * @return string The output when $return is true.
304
+ */
305
+ function ewww_manage_image_custom_column( $column_name, $id, $return = false ) {
306
+ // Once we've found our custom column.
307
+ if ( 'ewww_image_optimizer' == $column_name ) {
308
+ // Need this file to work with metadata.
309
+ require_once( WP_CONTENT_DIR . '/plugins/nextcellent-gallery-nextgen-legacy/lib/meta.php' );
310
+ // Get the metadata for the image.
311
+ $meta = new nggMeta( $id );
312
+ // Get the optimization status for the image.
313
+ $status = $meta->get_META( 'ewww_image_optimizer' );
314
+ $output = "<div id='ewww-nextcellent-status-$id'>";
315
+ $msg = '';
316
+ // Get the file path of the image.
317
+ $file_path = $meta->image->imagePath;
318
+ // Get the mimetype of the image.
319
+ $type = ewww_image_optimizer_quick_mimetype( $file_path, 'i' );
320
+
321
+ // Check to see if we have a tool to handle the mimetype detected.
322
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN' ) ) {
323
+ ewww_image_optimizer_tool_init();
324
+ ewww_image_optimizer_notice_utils( 'quiet' );
325
+ }
326
+ $skip = ewww_image_optimizer_skip_tools();
327
+ switch ( $type ) {
328
+ case 'image/jpeg':
329
+ // If jpegtran is missing, tell the user.
330
+ if ( ! EWWW_IMAGE_OPTIMIZER_JPEGTRAN && ! $skip['jpegtran'] ) {
331
+ /* translators: %s: name of a tool like jpegtran */
332
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>jpegtran</em>' ) . '</div>';
333
+ }
334
+ break;
335
+ case 'image/png':
336
+ // If the PNG tools are missing, tell the user.
337
+ if ( ! EWWW_IMAGE_OPTIMIZER_PNGOUT && ! EWWW_IMAGE_OPTIMIZER_OPTIPNG && ! $skip['optipng'] && ! $skip['pngout'] ) {
338
+ /* translators: %s: name of a tool like jpegtran */
339
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>optipng/pngout</em>' ) . '</div>';
340
+ }
341
+ break;
342
+ case 'image/gif':
343
+ // If gifsicle is missing, tell the user.
344
+ if ( ! EWWW_IMAGE_OPTIMIZER_GIFSICLE && ! $skip['gifsicle'] ) {
345
+ /* translators: %s: name of a tool like jpegtran */
346
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>gifsicle</em>' ) . '</div>';
347
+ }
348
+ break;
349
+ default:
350
+ $msg = '<div>' . esc_html__( 'Unsupported file type', 'ewww-image-optimizer' ) . '</div>';
351
+ }
352
+ // File isn't in a format we can work with, we don't work with strangers.
353
+ if ( $msg ) {
354
+ if ( $return ) {
355
+ return $msg;
356
+ }
357
+ echo $msg;
358
+ return;
359
+ }
360
+ if ( ! empty( $status ) ) {
361
+ ewww_image_optimizer_update_file_from_meta( $file_path, 'nextcell', $id, 'full' );
362
+ $thumb_path = $meta->image->thumbPath;
363
+ ewww_image_optimizer_update_file_from_meta( $thumb_path, 'nextcell', $id, 'thumbnail' );
364
+ }
365
+ $backup_available = false;
366
+ global $wpdb;
367
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'nextcell' AND image_size <> 0 ORDER BY orig_size DESC", $id ), ARRAY_A );
368
+ $ewww_manual_nonce = wp_create_nonce( 'ewww-manual-' . $id );
369
+ // If we have a valid status, display it, the image size, and give a re-optimize link.
370
+ if ( ! empty( $optimized_images ) ) {
371
+ list( $detail_output, $converted, $backup_available ) = ewww_image_optimizer_custom_column_results( $id, $optimized_images );
372
+ $output .= $detail_output;
373
+ if ( current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
374
+ $output .= sprintf( '<a class="ewww-manual-optimize" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_ngg_manual&amp;ewww_manual_nonce=%2$s&amp;ewww_force=1&amp;ewww_attachment_ID=%1$d">%3$s</a>',
375
+ $id,
376
+ $ewww_manual_nonce,
377
+ esc_html__( 'Re-optimize', 'ewww-image-optimizer' )
378
+ );
379
+ if ( $backup_available ) {
380
+ $output .= sprintf( '<br><a class="ewww-manual-cloud-restore" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_ngg_cloud_restore&amp;ewww_manual_nonce=%2$s&amp;ewww_attachment_ID=%1$d">%3$s</a>',
381
+ $id,
382
+ $ewww_manual_nonce,
383
+ esc_html__( 'Restore original', 'ewww-image-optimizer' )
384
+ );
385
+ }
386
+ }
387
+ } elseif ( get_transient( 'ewwwio-background-in-progress-ngg-' . $id ) ) {
388
+ $output .= esc_html__( 'In Progress', 'ewww-image-optimizer' );
389
+ // Otherwise, give the image size, and a link to optimize right now.
390
+ } else {
391
+ if ( current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
392
+ $output .= sprintf( '<a class="ewww-manual-optimize" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_ngg_manual&amp;ewww_manual_nonce=%2$s&amp;ewww_attachment_ID=%1$d">%3$s</a>',
393
+ $id,
394
+ $ewww_manual_nonce,
395
+ esc_html__( 'Optimize now!', 'ewww-image-optimizer' )
396
+ );
397
+ }
398
+ }
399
+ $output .= '</div>';
400
+ if ( $return ) {
401
+ return $output;
402
+ }
403
+ echo $output;
404
+ } // End if().
405
+ }
406
+
407
+ /**
408
+ * Output the html for the bulk optimize page.
409
+ */
410
+ function ewww_ngg_bulk_preview() {
411
+ if ( ! empty( $_REQUEST['doaction'] ) ) {
412
+ // If there is no requested bulk action, do nothing.
413
+ if ( empty( $_REQUEST['bulkaction'] ) ) {
414
+ return;
415
+ }
416
+ // If there is no media to optimize, do nothing.
417
+ if ( empty( $_REQUEST['doaction'] ) || ! is_array( $_REQUEST['doaction'] ) ) {
418
+ return;
419
+ }
420
+ }
421
+ // Retrieve the attachments array from the db.
422
+ $attachments = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
423
+ // Make sure there are some attachments to process.
424
+ if ( count( $attachments ) < 1 ) {
425
+ echo '<p>' . esc_html__( 'You do not appear to have uploaded any images yet.', 'ewww-image-optimizer' ) . '</p>';
426
+ return;
427
+ }
428
+ ?>
429
+ <div class="wrap">
430
+ <h1><?php esc_html_e( 'Bulk Optimize', 'ewww-image-optimizer' ); ?></h1><?php
431
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
432
+ ewww_image_optimizer_cloud_verify();
433
+ echo '<a id="ewww-bulk-credits-available" target="_blank" class="page-title-action" style="float:right;" href="https://ewww.io/my-account/">' . esc_html__( 'Image credits available:', 'ewww-image-optimizer' ) . ' ' . ewww_image_optimizer_cloud_quota() . '</a>';
434
+ }
435
+ // Retrieve the value of the 'bulk resume' option and set the button text for the form to use.
436
+ $resume = get_option( 'ewww_image_optimizer_bulk_ngg_resume' );
437
+ if ( empty( $resume ) ) {
438
+ $button_text = esc_attr__( 'Start optimizing', 'ewww-image-optimizer' );
439
+ } else {
440
+ $button_text = esc_attr__( 'Resume previous bulk operation', 'ewww-image-optimizer' );
441
+ }
442
+ /* translators: %d: number of images */
443
+ $selected_images_text = sprintf( esc_html( _n( 'There is %d image ready to optimize.', 'There are %d images ready to optimize.', count( $attachments ), 'ewww-image-optimizer' ) ), count( $attachments ) );
444
+ ?>
445
+ <div id="ewww-bulk-loading"></div>
446
+ <div id="ewww-bulk-progressbar"></div>
447
+ <div id="ewww-bulk-counter"></div>
448
+ <form id="ewww-bulk-stop" style="display:none;" method="post" action="">
449
+ <br /><input type="submit" class="button-secondary action" value="<?php esc_attr_e( 'Stop Optimizing', 'ewww-image-optimizer' ); ?>" />
450
+ </form>
451
+ <div id="ewww-bulk-widgets" class="metabox-holder" style="display:none">
452
+ <div class="meta-box-sortables">
453
+ <div id="ewww-bulk-last" class="postbox">
454
+ <button type="button" class="handlediv button-link" aria-expanded="true">
455
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ) ?></span>
456
+ <span class="toggle-indicator" aria-hidden="true"></span>
457
+ </button>
458
+ <h2 class="hndle"><span><?php esc_html_e( 'Last Image Optimized', 'ewww-image-optimizer' ) ?></span></h2>
459
+ <div class="inside"></div>
460
+ </div>
461
+ </div>
462
+ <div class="meta-box-sortables">
463
+ <div id="ewww-bulk-status" class="postbox">
464
+ <button type="button" class="handlediv button-link" aria-expanded="true">
465
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ) ?></span>
466
+ <span class="toggle-indicator" aria-hidden="true"></span>
467
+ </button>
468
+ <h2 class="hndle"><span><?php esc_html_e( 'Optimization Log', 'ewww-image-optimizer' ) ?></span></h2>
469
+ <div class="inside"></div>
470
+ </div>
471
+ </div>
472
+ </div>
473
+ <div id="ewww-bulk-forms">
474
+ <p class="ewww-bulk-info"><?php echo $selected_images_text; ?><br />
475
+ <?php esc_html_e( 'Previously optimized images will be skipped by default.', 'ewww-image-optimizer' ); ?></p>
476
+ <form id="ewww-bulk-start" class="ewww-bulk-form" method="post" action="">
477
+ <input type="hidden" id="ewww-delay" name="ewww-delay" value="0">
478
+ <input type="submit" class="button-secondary action" value="<?php echo $button_text; ?>" />
479
+ </form>
480
+ <?php
481
+ // If there is a previous bulk operation to resume, give the user the option to reset the resume flag.
482
+ if ( ! empty( $resume ) ) { ?>
483
+ <p class="ewww-bulk-info"><?php esc_html_e( 'If you would like to start over again, press the Reset Status button to reset the bulk operation status.', 'ewww-image-optimizer' ); ?></p>
484
+ <form id="ewww-bulk-reset" class="ewww-bulk-form" method="post" action="">
485
+ <?php wp_nonce_field( 'ewww-image-optimizer-bulk-reset', 'ewww_wpnonce' ); ?>
486
+ <input type="hidden" name="ewww_reset" value="1">
487
+ <input type="submit" class="button-secondary action" value="<?php esc_attr_e( 'Reset Status', 'ewww-image-optimizer' ); ?>" />
488
+ </form>
489
+ <?php }
490
+ echo '</div></div>';
491
+ if ( ! empty( $_REQUEST['ewww_inline'] ) ) {
492
+ die();
493
+ }
494
+ return;
495
+ }
496
+
497
+ /**
498
+ * Prepares the javascript for a bulk operation.
499
+ *
500
+ * @global object $nggdb
501
+ *
502
+ * @param string $hook The hook identifier for the current page.
503
+ */
504
+ function ewww_ngg_bulk_script( $hook ) {
505
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
506
+ $i18ngg = strtolower( __( 'Galleries', 'nggallery' ) );
507
+ ewwwio_debug_message( "i18n string for galleries: $i18ngg" );
508
+ // Make sure we are on a legitimate page and that we have the proper POST variables if necessary.
509
+ if ( $i18ngg . '_page_ewww-ngg-bulk' != $hook && $i18ngg . '_page_nggallery-manage-gallery' != $hook ) {
510
+ return;
511
+ }
512
+ if ( $i18ngg . '_page_nggallery-manage-gallery' == $hook && ( empty( $_REQUEST['bulkaction'] ) || 'bulk_optimize' != $_REQUEST['bulkaction'] ) ) {
513
+ return;
514
+ }
515
+ if ( $i18ngg . '_page_nggallery-manage-gallery' == $hook && ( empty( $_REQUEST['doaction'] ) || ! is_array( $_REQUEST['doaction'] ) ) ) {
516
+ return;
517
+ }
518
+ $images = null;
519
+ // See if the user wants to reset the previous bulk status.
520
+ if ( ! empty( $_REQUEST['ewww_reset'] ) && wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk-reset' ) ) {
521
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
522
+ }
523
+ // See if there is a previous operation to resume.
524
+ $resume = get_option( 'ewww_image_optimizer_bulk_ngg_resume' );
525
+ // If we've been given a bulk action to perform.
526
+ if ( ! empty( $_REQUEST['doaction'] ) ) {
527
+ // If we are optimizing a specific group of images.
528
+ if ( 'manage-images' == $_REQUEST['page'] && 'bulk_optimize' == $_REQUEST['bulkaction'] ) {
529
+ ewwwio_debug_message( 'optimizing a group of images' );
530
+ check_admin_referer( 'ngg_updategallery' );
531
+ // Reset the resume status, not allowed here.
532
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
533
+ // Retrieve the image IDs from POST.
534
+ $images = array_map( 'intval', $_REQUEST['doaction'] );
535
+ }
536
+ // If we are optimizing a specific group of galleries.
537
+ if ( 'manage-galleries' == $_REQUEST['page'] && 'bulk_optimize' == $_REQUEST['bulkaction'] ) {
538
+ ewwwio_debug_message( 'optimizing a group of galleries' );
539
+ check_admin_referer( 'ngg_bulkgallery' );
540
+ global $nggdb;
541
+ // Reset the resume status, not allowed here.
542
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
543
+ $ids = array();
544
+ $gids = array_map( 'intval', $_REQUEST['doaction'] );
545
+ // For each gallery we are given.
546
+ foreach ( $gids as $gid ) {
547
+ // Get a list of IDs.
548
+ $gallery_list = $nggdb->get_gallery( $gid );
549
+ // For each ID.
550
+ foreach ( $gallery_list as $image ) {
551
+ // Add it to the array.
552
+ $images[] = $image->pid;
553
+ }
554
+ }
555
+ }
556
+ } elseif ( ! empty( $resume ) ) {
557
+ // Otherwise, if we have an operation to resume.
558
+ ewwwio_debug_message( 'resuming a previous operation (maybe)' );
559
+ // Get the list of attachment IDs from the db.
560
+ $images = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
561
+ // Otherwise, if we are on the standard bulk page, get all the images in the db.
562
+ } elseif ( $hook == $i18ngg . '_page_ewww-ngg-bulk' ) {
563
+ ewwwio_debug_message( 'starting from scratch, grabbing all the images' );
564
+ global $wpdb;
565
+ $images = $wpdb->get_col( "SELECT pid FROM $wpdb->nggpictures ORDER BY sortorder ASC" );
566
+ } else {
567
+ ewwwio_debug_message( $hook );
568
+ } // End if().
569
+
570
+ // Store the image IDs to process in the db.
571
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
572
+ // Add the EWWW IO script.
573
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
574
+ // Replacing the built-in nextgen styling rules for progressbar.
575
+ wp_register_style( 'ngg-jqueryui', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) );
576
+ // Enqueue the progressbar styling.
577
+ wp_enqueue_style( 'ngg-jqueryui' );
578
+ // Include all the vars we need for javascript.
579
+ wp_localize_script( 'ewwwbulkscript', 'ewww_vars', array(
580
+ '_wpnonce' => wp_create_nonce( 'ewww-image-optimizer-bulk' ),
581
+ 'gallery' => 'nextgen',
582
+ 'attachments' => count( $images ),
583
+ 'scan_fail' => esc_html__( 'Operation timed out, you may need to increase the max_execution_time for PHP', 'ewww-image-optimizer' ),
584
+ 'operation_stopped' => esc_html__( 'Optimization stopped, reload page to resume.', 'ewww-image-optimizer' ),
585
+ 'operation_interrupted' => esc_html__( 'Operation Interrupted', 'ewww-image-optimizer' ),
586
+ 'temporary_failure' => esc_html__( 'Temporary failure, seconds left to retry:', 'ewww-image-optimizer' ),
587
+ 'remove_failed' => esc_html__( 'Could not remove image from table.', 'ewww-image-optimizer' ),
588
+ 'optimized' => esc_html__( 'Optimized', 'ewww-image-optimizer' ),
589
+ )
590
+ );
591
+ }
592
+
593
+ /**
594
+ * Start the bulk operation.
595
+ */
596
+ function ewww_ngg_bulk_init() {
597
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
598
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
599
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
600
+ }
601
+ $output = array();
602
+ // Toggle the resume flag to indicate an operation is in progress.
603
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', 'true' );
604
+ // Get the list of attachments remaining from the db.
605
+ $attachments = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
606
+ if ( ! is_array( $attachments ) && ! empty( $attachments ) ) {
607
+ $attachments = unserialize( $attachments );
608
+ }
609
+ if ( ! is_array( $attachments ) ) {
610
+ $output['error'] = esc_html__( 'Error retrieving list of images' );
611
+ echo json_encode( $output );
612
+ die();
613
+ }
614
+ $id = array_shift( $attachments );
615
+ $file_name = $this->ewww_ngg_bulk_filename( $id );
616
+ // Let the user know we are starting.
617
+ $loading_image = plugins_url( '/images/wpspin.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
618
+ if ( empty( $file_name ) ) {
619
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' alt='loading'/></p>";
620
+ } else {
621
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . ' <b>' . $file_name . "</b>&nbsp;<img src='$loading_image' alt='loading'/></p>";
622
+ }
623
+ echo json_encode( $output );
624
+ die();
625
+ }
626
+
627
+ /**
628
+ * Retrieve the filename of the image being optimized.
629
+ *
630
+ * @param int $id The ID number of the image.
631
+ */
632
+ function ewww_ngg_bulk_filename( $id ) {
633
+ // Need this file to work with metadata.
634
+ require_once( WP_CONTENT_DIR . '/plugins/nextcellent-gallery-nextgen-legacy/lib/meta.php' );
635
+ // Get the meta for the image.
636
+ $meta = new nggMeta( $id );
637
+ // Get the filename for the image, and output our current status.
638
+ $file_name = esc_html( $meta->image->filename );
639
+ if ( $file_name ) {
640
+ return $file_name;
641
+ } else {
642
+ return false;
643
+ }
644
+ }
645
+
646
+ /**
647
+ * Process each image in the bulk queue.
648
+ *
649
+ * @global bool $ewww_defer Set to false to avoid deferring image optimization.
650
+ */
651
+ function ewww_ngg_bulk_loop() {
652
+ global $ewww_defer;
653
+ $ewww_defer = false;
654
+ $output = array();
655
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
656
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
657
+ $outupt['error'] = esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' );
658
+ echo json_encode( $output );
659
+ die();
660
+ }
661
+ session_write_close();
662
+ // Find out if our nonce is on it's last leg/tick.
663
+ $tick = wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' );
664
+ if ( 2 === $tick ) {
665
+ $output['new_nonce'] = wp_create_nonce( 'ewww-image-optimizer-bulk' );
666
+ } else {
667
+ $output['new_nonce'] = '';
668
+ }
669
+ // Need this file to work with metadata.
670
+ require_once( WP_CONTENT_DIR . '/plugins/nextcellent-gallery-nextgen-legacy/lib/meta.php' );
671
+ // Find out what time we started, in microseconds.
672
+ $started = microtime( true );
673
+ // Get the list of attachments remaining from the db.
674
+ $attachments = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
675
+ $id = array_shift( $attachments );
676
+ list( $fres, $tres ) = $this->ewww_ngg_optimize( $id );
677
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
678
+ if ( ! empty( $ewww_status ) && preg_match( '/exceeded/', $ewww_status ) ) {
679
+ $output['error'] = esc_html__( 'License Exceeded', 'ewww-image-optimizer' );
680
+ echo json_encode( $output );
681
+ die();
682
+ }
683
+ // Output the results of the optimization.
684
+ if ( $fres[0] ) {
685
+ $output['results'] = sprintf( '<p>' . esc_html__( 'Optimized image:', 'ewww-image-optimizer' ) . ' <strong>%s</strong><br>', esc_html( $fres[0] ) );
686
+ }
687
+ /* Translators: %s: The compression results/savings */
688
+ $output['results'] .= sprintf( esc_html__( 'Full size - %s', 'ewww-image-optimizer' ) . '<br>', esc_html( $fres[1] ) );
689
+ // Output the results of the thumb optimization.
690
+ /* Translators: %s: The compression results/savings */
691
+ $output['results'] .= sprintf( esc_html__( 'Thumbnail - %s', 'ewww-image-optimizer' ) . '<br>', esc_html( $tres[1] ) );
692
+ // Output how much time we spent.
693
+ $elapsed = microtime( true ) - $started;
694
+ /* Translators: %s: localized number of seconds */
695
+ $output['results'] .= sprintf( esc_html( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ) ) . '</p>', number_format_i18n( $elapsed ) );
696
+ $output['completed'] = 1;
697
+ // Store the list back in the db.
698
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $attachments, false );
699
+ if ( ! empty( $attachments ) ) {
700
+ $next_attachment = array_shift( $attachments );
701
+ $next_file = $this->ewww_ngg_bulk_filename( $next_attachment );
702
+ $loading_image = plugins_url( '/images/wpspin.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
703
+ if ( $next_file ) {
704
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <b>$next_file</b>&nbsp;<img src='$loading_image' alt='loading'/></p>";
705
+ } else {
706
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' alt='loading'/></p>";
707
+ }
708
+ } else {
709
+ $output['done'] = 1;
710
+ }
711
+ die( json_encode( $output ) );
712
+ }
713
+
714
+ /**
715
+ * Finish the bulk operation.
716
+ */
717
+ function ewww_ngg_bulk_cleanup() {
718
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
719
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
720
+ wp_die( esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) );
721
+ }
722
+ // Reset all the bulk options in the db...
723
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
724
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', '', false );
725
+ // and let the user know we are done.
726
+ echo '<p><b>' . esc_html__( 'Finished Optimization!', 'ewww-image-optimizer' ) . '</b></p>';
727
+ die();
728
+ }
729
+
730
+ /**
731
+ * Prepare javascript for one-click actions on manage gallery page.
732
+ *
733
+ * @param string $hook The hook value for the current page.
734
+ */
735
+ function ewww_ngg_manual_actions_script( $hook ) {
736
+ if ( 'galleries_page_nggallery-manage-gallery' != $hook ) {
737
+ return;
738
+ }
739
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
740
+ return;
741
+ }
742
+ add_thickbox();
743
+ wp_enqueue_script( 'ewwwnextcellentscript', plugins_url( '/includes/nextcellent.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery' ), EWWW_IMAGE_OPTIMIZER_VERSION );
744
+ wp_enqueue_style( 'jquery-ui-tooltip-custom', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array(), EWWW_IMAGE_OPTIMIZER_VERSION );
745
+ // Submit a couple variables needed for javascript functions.
746
+ $loading_image = plugins_url( '/images/spinner.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
747
+ wp_localize_script(
748
+ 'ewwwnextcellentscript',
749
+ 'ewww_vars',
750
+ array(
751
+ 'optimizing' => '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <img src='$loading_image' /></p>",
752
+ 'restoring' => '<p>' . esc_html__( 'Restoring', 'ewww-image-optimizer' ) . " <img src='$loading_image' /></p>",
753
+ )
754
+ );
755
+ }
756
+
757
+ /**
758
+ * Insert a bulk optimize option in the actions list for the gallery and image management pages (via javascript, since we have no hooks).
759
+ */
760
+ function ewww_ngg_bulk_actions_script() {
761
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_bulk_permissions', '' ) ) ) {
762
+ return;
763
+ }
764
+ ?> <script type="text/javascript">
765
+ jQuery(document).ready(function($){
766
+ $('select[name^="bulkaction"] option:last-child').after('<option value="bulk_optimize">Bulk Optimize</option>');
767
+ });
768
+ </script>
769
+ <?php }
770
+ }
771
+
772
+ global $ewwwngg;
773
+ $ewwwngg = new EWWW_Nextcellent();
774
+ } // End if().
classes/class-ewww-nextgen.php ADDED
@@ -0,0 +1,909 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class and methods to integrate EWWW IO and NextGEN Gallery.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EWWW_Image_Optimizer
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
13
+ /**
14
+ * Allows EWWW to integrate with the NextGEN Gallery plugin.
15
+ *
16
+ * Adds automatic optimization on upload, a bulk optimizer, and compression details when
17
+ * managing galleries.
18
+ */
19
+ class EWWW_Nextgen {
20
+ /**
21
+ * Initializes the nextgen integration functions.
22
+ */
23
+ public function __construct() {
24
+ add_filter( 'ngg_manage_images_number_of_columns', array( $this, 'ewww_manage_images_number_of_columns' ) );
25
+ add_filter( 'ngg_manage_images_row_actions', array( $this, 'ewww_manage_images_row_actions' ) );
26
+ if ( ewww_image_optimizer_test_background_opt() ) {
27
+ add_action( 'ngg_added_new_image', array( $this, 'queue_new_image' ) );
28
+ ewwwio_debug_message( 'background mode enabled for nextgen' );
29
+ } else {
30
+ add_action( 'ngg_added_new_image', array( $this, 'ewww_added_new_image' ) );
31
+ ewwwio_debug_message( 'background mode NOT enabled for nextgen' );
32
+ }
33
+ add_action( 'wp_ajax_ewww_ngg_manual', array( $this, 'ewww_ngg_manual' ) );
34
+ add_action( 'wp_ajax_ewww_ngg_cloud_restore', array( $this, 'ewww_ngg_cloud_restore' ) );
35
+ add_action( 'admin_action_ewww_ngg_manual', array( $this, 'ewww_ngg_manual' ) );
36
+ add_action( 'admin_enqueue_scripts', array( $this, 'ewww_ngg_manual_actions_script' ) );
37
+ add_action( 'admin_menu', array( $this, 'ewww_ngg_bulk_menu' ) );
38
+ add_action( 'admin_head', array( $this, 'ewww_ngg_bulk_actions_script' ) );
39
+ add_action( 'admin_enqueue_scripts', array( $this, 'ewww_ngg_bulk_script' ), 20 );
40
+ add_action( 'wp_ajax_bulk_ngg_preview', array( $this, 'ewww_ngg_bulk_preview' ) );
41
+ add_action( 'wp_ajax_bulk_ngg_init', array( $this, 'ewww_ngg_bulk_init' ) );
42
+ add_action( 'wp_ajax_bulk_ngg_filename', array( $this, 'ewww_ngg_bulk_filename' ) );
43
+ add_action( 'wp_ajax_bulk_ngg_loop', array( $this, 'ewww_ngg_bulk_loop' ) );
44
+ add_action( 'wp_ajax_bulk_ngg_cleanup', array( $this, 'ewww_ngg_bulk_cleanup' ) );
45
+ add_action( 'ngg_generated_image', array( $this, 'ewww_ngg_generated_image' ), 10, 2 );
46
+ }
47
+
48
+ /**
49
+ * Adds the Bulk Optimize page to the NextGEN menu.
50
+ */
51
+ function ewww_ngg_bulk_menu() {
52
+ if ( ! defined( 'NGGFOLDER' ) ) {
53
+ return;
54
+ }
55
+ add_submenu_page( NGGFOLDER, esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), apply_filters( 'ewww_image_optimizer_manual_permissions', '' ), 'ewww-ngg-bulk', array( &$this, 'ewww_ngg_bulk_preview' ) );
56
+ }
57
+
58
+ /**
59
+ * Adds a newly uploaded image to the optimization queue.
60
+ *
61
+ * @param object|array $image The new image.
62
+ * @param object $storage A nextgen storage object for finding metadata.
63
+ */
64
+ function queue_new_image( $image, $storage = null ) {
65
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
66
+ if ( empty( $storage ) ) {
67
+ // Creating the 'registry' object for working with nextgen.
68
+ $registry = C_Component_Registry::get_instance();
69
+ // Creating a database storage object from the 'registry' object.
70
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
71
+ }
72
+ // Find the image id.
73
+ if ( is_array( $image ) ) {
74
+ $image_id = $image['id'];
75
+ $image = $storage->object->_image_mapper->find( $image_id, true );
76
+ } else {
77
+ $image_id = $storage->object->_get_image_id( $image );
78
+ }
79
+ global $ewwwio_ngg2_background;
80
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
81
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
82
+ }
83
+ if ( ! is_object( $ewwwio_ngg2_background ) ) {
84
+ $ewwwio_ngg2_background = new EWWWIO_Ngg2_Background_Process();
85
+ }
86
+ ewwwio_debug_message( "backgrounding optimization for $image_id" );
87
+ $ewwwio_ngg2_background->push_to_queue( array(
88
+ 'id' => $image_id,
89
+ ) );
90
+ $ewwwio_ngg2_background->save()->dispatch();
91
+ set_transient( 'ewwwio-background-in-progress-ngg-' . $image_id, true, 24 * HOUR_IN_SECONDS );
92
+ ewww_image_optimizer_debug_log();
93
+ }
94
+
95
+ /**
96
+ * Optimizes an image (and derivatives).
97
+ *
98
+ * @global object $ewww_image Contains more information about the image currently being processed.
99
+ *
100
+ * @param object|array $image The new image.
101
+ * @param object $storage A nextgen storage object for finding metadata.
102
+ * @return object The image object with any modifications necessary.
103
+ */
104
+ function ewww_added_new_image( $image, $storage = null ) {
105
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
106
+ if ( empty( $storage ) ) {
107
+ // Creating the 'registry' object for working with nextgen.
108
+ $registry = C_Component_Registry::get_instance();
109
+ // Creating a database storage object from the 'registry' object.
110
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
111
+ }
112
+ global $ewww_image;
113
+ // Find the image id.
114
+ if ( is_array( $image ) ) {
115
+ $image_id = $image['id'];
116
+ $image = $storage->object->_image_mapper->find( $image_id, true );
117
+ } else {
118
+ $image_id = $storage->object->_get_image_id( $image );
119
+ }
120
+ ewwwio_debug_message( "image id: $image_id" );
121
+ // Get an array of sizes available for the $image.
122
+ $sizes = $storage->get_image_sizes();
123
+ // Run the optimizer on the image for each $size.
124
+ if ( ewww_image_optimizer_iterable( $sizes ) ) {
125
+ foreach ( $sizes as $size ) {
126
+ if ( 'full' === $size ) {
127
+ $full_size = true;
128
+ } else {
129
+ $full_size = false;
130
+ }
131
+ // Get the absolute path.
132
+ $file_path = $storage->get_image_abspath( $image, $size );
133
+ ewwwio_debug_message( "optimizing (nextgen): $file_path" );
134
+ $ewww_image = new EWWW_Image( $image_id, 'nextgen', $file_path );
135
+ $ewww_image->resize = $size;
136
+ // Optimize the image and grab the results.
137
+ $res = ewww_image_optimizer( $file_path, 2, false, false, $full_size );
138
+ ewwwio_debug_message( "results {$res[1]}" );
139
+ // Only if we're dealing with the full-size original.
140
+ if ( 'full' === $size ) {
141
+ // Update the metadata for the optimized image.
142
+ $image->meta_data['ewww_image_optimizer'] = $res[1];
143
+ } else {
144
+ $image->meta_data[ $size ]['ewww_image_optimizer'] = $res[1];
145
+ }
146
+ nggdb::update_image_meta( $image_id, $image->meta_data );
147
+ ewwwio_debug_message( 'storing results for full size image' );
148
+ }
149
+ }
150
+ return $image;
151
+ }
152
+
153
+ /**
154
+ * Optimizes a generated image.
155
+ *
156
+ * @global object $ewww_image Contains more information about the image currently being processed.
157
+ *
158
+ * @param object $image A nextgen image object.
159
+ * @param object $size The name of the size generated.
160
+ */
161
+ function ewww_ngg_generated_image( $image, $size ) {
162
+ global $ewww_image;
163
+ // Creating the 'registry' object for working with nextgen.
164
+ $registry = C_Component_Registry::get_instance();
165
+ // Creating a database storage object from the 'registry' object.
166
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
167
+ $filename = $storage->get_image_abspath( $image, $size );
168
+ $ewww_image = new EWWW_Image( $image->pid, 'nextgen', $filename );
169
+ $ewww_image->resize = $size;
170
+ if ( file_exists( $filename ) ) {
171
+ ewww_image_optimizer( $filename, 2 );
172
+ ewwwio_debug_message( "nextgen dynamic thumb saved: $filename" );
173
+ $image_size = ewww_image_optimizer_filesize( $filename );
174
+ ewwwio_debug_message( "optimized size: $image_size" );
175
+ }
176
+ ewww_image_optimizer_debug_log();
177
+ }
178
+
179
+ /**
180
+ * Manually process an image from the NextGEN Gallery.
181
+ */
182
+ function ewww_ngg_manual() {
183
+ // Check permission of current user.
184
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
185
+ if ( false === current_user_can( $permissions ) ) {
186
+ if ( ! wp_doing_ajax() ) {
187
+ wp_die( esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ) );
188
+ }
189
+ wp_die( json_encode( array(
190
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
191
+ ) ) );
192
+ }
193
+ // Make sure function wasn't called without an attachment to work with.
194
+ if ( false === isset( $_REQUEST['ewww_attachment_ID'] ) ) {
195
+ if ( ! wp_doing_ajax() ) {
196
+ wp_die( esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ) );
197
+ }
198
+ wp_die( json_encode( array(
199
+ 'error' => esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ),
200
+ ) ) );
201
+ }
202
+ // Sanitize the attachment $id.
203
+ $id = intval( $_REQUEST['ewww_attachment_ID'] );
204
+ if ( empty( $_REQUEST['ewww_manual_nonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_manual_nonce'], "ewww-manual-$id" ) ) {
205
+ if ( ! wp_doing_ajax() ) {
206
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
207
+ }
208
+ wp_die( json_encode( array(
209
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
210
+ ) ) );
211
+ }
212
+ // Creating the 'registry' object for working with nextgen.
213
+ $registry = C_Component_Registry::get_instance();
214
+ // Creating a database storage object from the 'registry' object.
215
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
216
+ // Get an image object.
217
+ $image = $storage->object->_image_mapper->find( $id );
218
+ $image = $this->ewww_added_new_image( $image, $storage );
219
+ $success = $this->ewww_manage_image_custom_column( '', $image );
220
+ if ( get_transient( 'ewww_image_optimizer_cloud_status' ) == 'exceeded' || ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_exceeded' ) > time() ) {
221
+ die( json_encode( array(
222
+ 'error' => esc_html__( 'License exceeded', 'ewww-image-optimizer' ),
223
+ ) ) );
224
+ }
225
+ if ( ! wp_doing_ajax() ) {
226
+ // Get the referring page, and send the user back there.
227
+ $sendback = wp_get_referer();
228
+ $sendback = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback );
229
+ wp_redirect( $sendback );
230
+ die;
231
+ }
232
+ die( json_encode( array(
233
+ 'success' => $success,
234
+ ) ) );
235
+ }
236
+
237
+ /**
238
+ * Restore an image from the NextGEN Gallery.
239
+ */
240
+ function ewww_ngg_cloud_restore() {
241
+ // Check permission of current user.
242
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
243
+ if ( false === current_user_can( $permissions ) ) {
244
+ if ( ! wp_doing_ajax() ) {
245
+ wp_die( esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ) );
246
+ }
247
+ wp_die( json_encode( array(
248
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
249
+ ) ) );
250
+ }
251
+ // Make sure function wasn't called without an attachment to work with.
252
+ if ( false === isset( $_REQUEST['ewww_attachment_ID'] ) ) {
253
+ if ( ! wp_doing_ajax() ) {
254
+ wp_die( esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ) );
255
+ }
256
+ wp_die( json_encode( array(
257
+ 'error' => esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ),
258
+ ) ) );
259
+ }
260
+ // Sanitize the attachment $id.
261
+ $id = intval( $_REQUEST['ewww_attachment_ID'] );
262
+ if ( empty( $_REQUEST['ewww_manual_nonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_manual_nonce'], "ewww-manual-$id" ) ) {
263
+ if ( ! wp_doing_ajax() ) {
264
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
265
+ }
266
+ wp_die( json_encode( array(
267
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
268
+ ) ) );
269
+ }
270
+ // Creating the 'registry' object for working with nextgen.
271
+ $registry = C_Component_Registry::get_instance();
272
+ // Creating a database storage object from the 'registry' object.
273
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
274
+ // Get an image object.
275
+ $image = $storage->object->_image_mapper->find( $id );
276
+ ewww_image_optimizer_cloud_restore_from_meta_data( $image->pid, 'nextgen' );
277
+ $success = $this->ewww_manage_image_custom_column( '', $image );
278
+ die( json_encode( array(
279
+ 'success' => $success,
280
+ ) ) );
281
+ }
282
+
283
+ /**
284
+ * Prepare javascript for one-click actions on manage gallery page.
285
+ *
286
+ * @param string $hook The hook value for the current page.
287
+ */
288
+ function ewww_ngg_manual_actions_script( $hook ) {
289
+ if ( 'gallery_page_nggallery-manage-gallery' != $hook ) {
290
+ return;
291
+ }
292
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
293
+ return;
294
+ }
295
+ add_thickbox();
296
+ wp_enqueue_script( 'ewwwnextgenscript', plugins_url( '/includes/nextgen.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery' ), EWWW_IMAGE_OPTIMIZER_VERSION );
297
+ wp_enqueue_style( 'jquery-ui-tooltip-custom', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array(), EWWW_IMAGE_OPTIMIZER_VERSION );
298
+ // Submit a couple variables needed for javascript functions.
299
+ $loading_image = plugins_url( '/images/spinner.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
300
+ wp_localize_script(
301
+ 'ewwwnextgenscript',
302
+ 'ewww_vars',
303
+ array(
304
+ 'optimizing' => '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>",
305
+ 'restoring' => '<p>' . esc_html__( 'Restoring', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>",
306
+ )
307
+ );
308
+ }
309
+
310
+ /**
311
+ * Filter for ngg_manage_images_number_of_columns hook, changed in NGG 2.0.50ish.
312
+ *
313
+ * @param int $count The number of columns for the table display.
314
+ * @return int The new number of columns.
315
+ */
316
+ function ewww_manage_images_number_of_columns( $count ) {
317
+ $count++;
318
+ add_filter( "ngg_manage_images_column_{$count}_header", array( &$this, 'ewww_manage_images_columns' ) );
319
+ add_filter( "ngg_manage_images_column_{$count}_content", array( &$this, 'ewww_manage_image_custom_column' ), 10, 2 );
320
+ return $count;
321
+ }
322
+
323
+ /**
324
+ * Outputs column header via ngg_manage_images_column_x_header hook.
325
+ *
326
+ * @param array|null $columns List of headers for the table.
327
+ * @return array|string The new list of headers, or the single header for EWWW IO.
328
+ */
329
+ function ewww_manage_images_columns( $columns = null ) {
330
+ if ( is_array( $columns ) ) {
331
+ $columns['ewww_image_optimizer'] = esc_html__( 'Image Optimizer', 'ewww-image-optimizer' );
332
+ return $columns;
333
+ } else {
334
+ return esc_html__( 'Image Optimizer', 'ewww-image-optimizer' );
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Outputs the image optimizer column data via ngg_manage_images_column_x_content hook.
340
+ *
341
+ * @global object $wpdb
342
+ *
343
+ * @param string $column_name The name of the current column.
344
+ * @param int $id The image id for the current row.
345
+ * @return string The column output, potentially echoed instead.
346
+ */
347
+ function ewww_manage_image_custom_column( $column_name, $id ) {
348
+ // Once we've found our custom column (newer versions will be blank).
349
+ if ( 'ewww_image_optimizer' == $column_name || '' == $column_name ) {
350
+ // Creating the 'registry' object for working with nextgen.
351
+ $registry = C_Component_Registry::get_instance();
352
+ // Creating a database storage object from the 'registry' object.
353
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
354
+ if ( is_object( $id ) ) {
355
+ $image = $id;
356
+ } else {
357
+ // Get an image object.
358
+ $image = $storage->object->_image_mapper->find( $id );
359
+ }
360
+ $output = "<div id='ewww-nextgen-status-$image->pid'>";
361
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) && ewww_image_optimizer_function_exists( 'print_r' ) ) {
362
+ $print_meta = print_r( $image->meta_data, true );
363
+ $print_meta = preg_replace( array( '/ /', '/\n+/' ), array( '&nbsp;', '<br />' ), esc_html( $print_meta ) );
364
+ $output .= '<div style="background-color:#ffff99;font-size: 10px;padding: 10px;margin:-10px -10px 10px;line-height: 1.1em">' . $print_meta . '</div>';
365
+ }
366
+ $msg = '';
367
+ // Get the absolute path.
368
+ $file_path = $storage->get_image_abspath( $image, 'full' );
369
+ // Get the mimetype of the image.
370
+ $type = ewww_image_optimizer_quick_mimetype( $file_path );
371
+ // Check to see if we have a tool to handle the mimetype detected.
372
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN' ) ) {
373
+ ewww_image_optimizer_tool_init();
374
+ ewww_image_optimizer_notice_utils( 'quiet' );
375
+ }
376
+ $skip = ewww_image_optimizer_skip_tools();
377
+ switch ( $type ) {
378
+ case 'image/jpeg':
379
+ // If jpegtran is missing, tell the user.
380
+ if ( ! EWWW_IMAGE_OPTIMIZER_JPEGTRAN && ! $skip['jpegtran'] ) {
381
+ /* translators: %s: name of a tool like jpegtran */
382
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>jpegtran</em>' ) . '</div>';
383
+ }
384
+ break;
385
+ case 'image/png':
386
+ // If the PNG tools are missing, tell the user.
387
+ if ( ! EWWW_IMAGE_OPTIMIZER_PNGOUT && ! EWWW_IMAGE_OPTIMIZER_OPTIPNG && ! $skip['optipng'] && ! $skip['pngout'] ) {
388
+ /* translators: %s: name of a tool like jpegtran */
389
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>optipng/pngout</em>' ) . '</div>';
390
+ }
391
+ break;
392
+ case 'image/gif':
393
+ // If gifsicle is missing, tell the user.
394
+ if ( ! EWWW_IMAGE_OPTIMIZER_GIFSICLE && ! $skip['gifsicle'] ) {
395
+ /* translators: %s: name of a tool like jpegtran */
396
+ $msg = '<div>' . sprintf( esc_html__( '%s is missing', 'ewww-image-optimizer' ), '<em>gifsicle</em>' ) . '</div>';
397
+ }
398
+ break;
399
+ default:
400
+ $msg = '<div>' . esc_html__( 'Unsupported file type', 'ewww-image-optimizer' ) . '</div>';
401
+ }
402
+ // File isn't in a format we can work with, we don't work with strangers.
403
+ if ( $msg ) {
404
+ if ( is_object( $id ) ) {
405
+ return $msg;
406
+ } else {
407
+ echo $msg;
408
+ return;
409
+ }
410
+ }
411
+ // If we have metadata, populate db from meta.
412
+ if ( ! empty( $image->meta_data['ewww_image_optimizer'] ) ) {
413
+ $sizes = $storage->get_image_sizes();
414
+ if ( ewww_image_optimizer_iterable( $sizes ) ) {
415
+ foreach ( $sizes as $size ) {
416
+ if ( 'full' === $size ) {
417
+ $full_size = true;
418
+ } else {
419
+ $file_path = $storage->get_image_abspath( $image, $size );
420
+ $full_size = false;
421
+ }
422
+ ewww_image_optimizer_update_file_from_meta( $file_path, 'nextgen', $image->pid, $size );
423
+ }
424
+ }
425
+ }
426
+ $backup_available = false;
427
+ global $wpdb;
428
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'nextgen' AND image_size <> 0 ORDER BY orig_size DESC", $image->pid ), ARRAY_A );
429
+ if ( ! empty( $optimized_images ) ) {
430
+ list( $detail_output, $converted, $backup_available ) = ewww_image_optimizer_custom_column_results( $image->pid, $optimized_images );
431
+ $output .= $detail_output;
432
+ // Display the optimization link with the appropriate text.
433
+ $output .= $this->ewww_render_optimize_action_link( $image->pid, null, true, $backup_available );
434
+ } elseif ( get_transient( 'ewwwio-background-in-progress-ngg-' . $image->pid ) ) {
435
+ $output .= esc_html( 'In Progress', 'ewww-image-optimizer' ) . '<br>';
436
+ // Otherwise, give the image size, and a link to optimize right now.
437
+ } else {
438
+ // Display the optimization link with the appropriate text.
439
+ $output .= $this->ewww_render_optimize_action_link( $image->pid, null, false, $backup_available );
440
+ }
441
+ $output .= '</div>';
442
+ if ( is_object( $id ) ) {
443
+ return $output;
444
+ } else {
445
+ echo $output;
446
+ }
447
+ } // End if().
448
+ }
449
+
450
+ /**
451
+ * Output the action link for the custom column.
452
+ *
453
+ * @global object $wpdb
454
+ *
455
+ * @param int $id The ID number of the nextgen image.
456
+ * @param object $image A nextgen image object.
457
+ * @param bool $optimized Optional. True if the image has already been optimized. Default false.
458
+ * @param bool $restorable Optional. True if the image can be restored via the API. Default false.
459
+ * @return string The link HTML to display.
460
+ */
461
+ function ewww_render_optimize_action_link( $id, $image = null, $optimized = false, $restorable = false ) {
462
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
463
+ return '';
464
+ }
465
+ if ( is_string( $id ) && 'optimize' == $id && is_object( $image ) && ! empty( $image->pid ) ) {
466
+ $id = $image->pid;
467
+ global $wpdb;
468
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'nextgen' AND image_size <> 0 ORDER BY orig_size DESC", $id ), ARRAY_A );
469
+ if ( ! empty( $optimized_images ) ) {
470
+ $optimized = true;
471
+ }
472
+ }
473
+ $ewww_manual_nonce = wp_create_nonce( 'ewww-manual-' . $id );
474
+ if ( $optimized ) {
475
+ $link = sprintf( '<a class="ewww-manual-optimize" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_ngg_manual&amp;ewww_manual_nonce=%2$s&amp;ewww_force=1&amp;ewww_attachment_ID=%1$d">%3$s</a>',
476
+ $id,
477
+ $ewww_manual_nonce,
478
+ esc_html__( 'Re-optimize', 'ewww-image-optimizer' )
479
+ );
480
+ if ( $restorable ) {
481
+ $link .= sprintf( '<br><a class="ewww-manual-cloud-restore" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_ngg_cloud_restore&amp;ewww_manual_nonce=%2$s&amp;ewww_attachment_ID=%1$d">%3$s</a>',
482
+ $id,
483
+ $ewww_manual_nonce,
484
+ esc_html__( 'Restore original', 'ewww-image-optimizer' )
485
+ );
486
+ }
487
+ } else {
488
+ $link = sprintf( '<a class="ewww-manual-optimize" data-id="%1$d" data-nonce="%2$s" href="admin.php?action=ewww_ngg_manual&amp;ewww_manual_nonce=%2$s&amp;ewww_attachment_ID=%1$d">%3$s</a>',
489
+ $id,
490
+ $ewww_manual_nonce,
491
+ esc_html__( 'Optimize now!', 'ewww-image-optimizer' )
492
+ );
493
+ }
494
+ return $link;
495
+ }
496
+
497
+ /**
498
+ * Append our action link to the list.
499
+ *
500
+ * @param array $actions A list of actions with to display under the image.
501
+ * @return array The updated list of actions.
502
+ */
503
+ function ewww_manage_images_row_actions( $actions ) {
504
+ $actions['optimize'] = array( &$this, 'ewww_render_optimize_action_link' );
505
+ return $actions;
506
+ }
507
+
508
+ /**
509
+ * Output the html for the bulk optimize page.
510
+ *
511
+ * @global string $ewww_debug In-memory debug log.
512
+ */
513
+ function ewww_ngg_bulk_preview() {
514
+ if ( ! empty( $_REQUEST['doaction'] ) ) {
515
+ // If there is no requested bulk action, do nothing.
516
+ if ( empty( $_REQUEST['bulkaction'] ) ) {
517
+ return;
518
+ }
519
+ // If there is no media to optimize, do nothing.
520
+ if ( empty( $_REQUEST['doaction'] ) || ! is_array( $_REQUEST['doaction'] ) ) {
521
+ return;
522
+ }
523
+ }
524
+ list( $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) = ewww_image_optimizer_count_optimized( 'ngg' );
525
+ // Make sure there are some attachments to process.
526
+ if ( $fullsize_count < 1 ) {
527
+ echo '<p>' . esc_html__( 'You do not appear to have uploaded any images yet.', 'ewww-image-optimizer' ) . '</p>';
528
+ return;
529
+ }
530
+ ?>
531
+ <div class="wrap">
532
+ <h1><?php esc_html_e( 'Bulk Optimize', 'ewww-image-optimizer' ); ?></h1><?php
533
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
534
+ ewww_image_optimizer_cloud_verify();
535
+ echo '<a id="ewww-bulk-credits-available" target="_blank" class="page-title-action" style="float:right;" href="https://ewww.io/my-account/">' . esc_html__( 'Image credits available:', 'ewww-image-optimizer' ) . ' ' . ewww_image_optimizer_cloud_quota() . '</a>';
536
+ }
537
+ // Retrieve the value of the 'bulk resume' option and set the button text for the form to use.
538
+ $resume = get_option( 'ewww_image_optimizer_bulk_ngg_resume' );
539
+ if ( empty( $resume ) ) {
540
+ $button_text = esc_attr__( 'Start optimizing', 'ewww-image-optimizer' );
541
+ } else {
542
+ $button_text = esc_attr__( 'Resume previous bulk operation', 'ewww-image-optimizer' );
543
+ }
544
+ $delay = ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) ? ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) : 0;
545
+ /* translators: 1-4: number(s) of images */
546
+ $selected_images_text = sprintf( esc_html__( '%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized).', 'ewww-image-optimizer' ), $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count );
547
+ ?>
548
+ <div id="ewww-bulk-loading"></div>
549
+ <div id="ewww-bulk-progressbar"></div>
550
+ <div id="ewww-bulk-counter"></div>
551
+ <form id="ewww-bulk-stop" style="display:none;" method="post" action="">
552
+ <br /><input type="submit" class="button-secondary action" value="<?php esc_attr_e( 'Stop Optimizing', 'ewww-image-optimizer' ); ?>" />
553
+ </form>
554
+ <div id="ewww-bulk-widgets" class="metabox-holder" style="display:none">
555
+ <div class="meta-box-sortables">
556
+ <div id="ewww-bulk-last" class="postbox">
557
+ <button type="button" class="handlediv button-link" aria-expanded="true">
558
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ) ?></span>
559
+ <span class="toggle-indicator" aria-hidden="true"></span>
560
+ </button>
561
+ <h2 class="hndle"><span><?php esc_html_e( 'Last Image Optimized', 'ewww-image-optimizer' ) ?></span></h2>
562
+ <div class="inside"></div>
563
+ </div>
564
+ </div>
565
+ <div class="meta-box-sortables">
566
+ <div id="ewww-bulk-status" class="postbox">
567
+ <button type="button" class="handlediv button-link" aria-expanded="true">
568
+ <span class="screen-reader-text"><?php esc_html_e( 'Click to toggle', 'ewww-image-optimizer' ) ?></span>
569
+ <span class="toggle-indicator" aria-hidden="true"></span>
570
+ </button>
571
+ <h2 class="hndle"><span><?php esc_html_e( 'Optimization Log', 'ewww-image-optimizer' ) ?></span></h2>
572
+ <div class="inside"></div>
573
+ </div>
574
+ </div>
575
+ </div>
576
+ <form class="ewww-bulk-form">
577
+ <p><label for="ewww-force" style="font-weight: bold"><?php esc_html_e( 'Force re-optimize', 'ewww-image-optimizer' ); ?></label>&emsp;<input type="checkbox" id="ewww-force" name="ewww-force"></p>
578
+ <p><label for="ewww-delay" style="font-weight: bold"><?php esc_html_e( 'Choose how long to pause between images (in seconds, 0 = disabled)', 'ewww-image-optimizer' ); ?></label>&emsp;<input type="text" id="ewww-delay" name="ewww-delay" value="<?php echo $delay; ?>"></p>
579
+ <div id="ewww-delay-slider" style="width:50%"></div>
580
+ </form>
581
+ <div id="ewww-bulk-forms">
582
+ <p class="ewww-bulk-info"><?php echo $selected_images_text; ?><br />
583
+ <?php esc_html_e( 'Previously optimized images will be skipped by default.', 'ewww-image-optimizer' ); ?></p>
584
+ <form id="ewww-bulk-start" class="ewww-bulk-form" method="post" action="">
585
+ <input type="submit" class="button-secondary action" value="<?php echo $button_text; ?>" />
586
+ </form>
587
+ <?php
588
+ // If there is a previous bulk operation to resume, give the user the option to reset the resume flag.
589
+ if ( ! empty( $resume ) ) { ?>
590
+ <p class="ewww-bulk-info"><?php esc_html_e( 'If you would like to start over again, press the Reset Status button to reset the bulk operation status.', 'ewww-image-optimizer' ); ?></p>
591
+ <form id="ewww-bulk-reset" class="ewww-bulk-form" method="post" action="">
592
+ <?php wp_nonce_field( 'ewww-image-optimizer-bulk-reset', 'ewww_wpnonce' ); ?>
593
+ <input type="hidden" name="ewww_reset" value="1">
594
+ <input type="submit" class="button-secondary action" value="<?php esc_attr_e( 'Reset Status', 'ewww-image-optimizer' ); ?>" />
595
+ </form>
596
+ <?php }
597
+ echo '</div></div>';
598
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
599
+ global $ewww_debug;
600
+ echo '<div style="background-color:#ffff99;">' . $ewww_debug . '</div>';
601
+ }
602
+ if ( ! empty( $_REQUEST['ewww_inline'] ) ) {
603
+ die();
604
+ }
605
+ return;
606
+ }
607
+
608
+ /**
609
+ * Removes the nextgen jquery styling when it gets in the way.
610
+ */
611
+ function ewww_ngg_style_remove() {
612
+ wp_deregister_style( 'jquery-ui-nextgen' );
613
+ }
614
+
615
+ /**
616
+ * Prepares the javascript for a bulk operation.
617
+ *
618
+ * @global object $wpdb
619
+ *
620
+ * @param string $hook Identifier for the page being loaded.
621
+ */
622
+ function ewww_ngg_bulk_script( $hook ) {
623
+ if ( strpos( $hook, 'ewww-ngg-bulk' ) === false && strpos( $hook, 'nggallery-manage-gallery' ) === false ) {
624
+ return;
625
+ }
626
+ if ( strpos( $hook, 'nggallery-manage-gallery' ) && ( empty( $_REQUEST['bulkaction'] ) || 'bulk_optimize' != $_REQUEST['bulkaction'] ) ) {
627
+ return;
628
+ }
629
+ if ( strpos( $hook, 'nggallery-manage-gallery' ) && ( empty( $_REQUEST['doaction'] ) || ! is_array( $_REQUEST['doaction'] ) ) ) {
630
+ return;
631
+ }
632
+ $images = null;
633
+ // See if the user wants to reset the previous bulk status.
634
+ if ( ! empty( $_REQUEST['ewww_reset'] ) && wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk-reset' ) ) {
635
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
636
+ }
637
+ // See if there is a previous operation to resume.
638
+ $resume = get_option( 'ewww_image_optimizer_bulk_ngg_resume' );
639
+ // If we've been given a bulk action to perform.
640
+ if ( ! empty( $_REQUEST['doaction'] ) ) {
641
+
642
+ // If we are optimizing a specific group of images.
643
+ if ( 'manage-images' == $_REQUEST['page'] && 'bulk_optimize' == $_REQUEST['bulkaction'] ) {
644
+ check_admin_referer( 'ngg_updategallery' );
645
+ // Reset the resume status, not allowed here.
646
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
647
+ // Retrieve the image IDs from POST.
648
+ $images = array_map( 'intval', $_REQUEST['doaction'] );
649
+ }
650
+ // If we are optimizing a specific group of galleries.
651
+ if ( 'manage-galleries' == $_REQUEST['page'] && 'bulk_optimize' == $_REQUEST['bulkaction'] ) {
652
+ check_admin_referer( 'ngg_bulkgallery' );
653
+ global $nggdb;
654
+ // Reset the resume status, not allowed here.
655
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
656
+ $ids = array();
657
+ $gids = array_map( 'intval', $_REQUEST['doaction'] );
658
+ // For each gallery we are given.
659
+ foreach ( $gids as $gid ) {
660
+ // Get a list of IDs.
661
+ $gallery_list = $nggdb->get_gallery( $gid );
662
+ // For each ID.
663
+ foreach ( $gallery_list as $image ) {
664
+ // Add it to the array.
665
+ $images[] = $image->pid;
666
+ }
667
+ }
668
+ }
669
+ } elseif ( ! empty( $resume ) ) {
670
+ // Otherwise, if we have an operation to resume...
671
+ // get the list of attachment IDs from the db.
672
+ $images = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
673
+ // Otherwise, if we are on the standard bulk page, get all the images in the db.
674
+ } elseif ( strpos( $hook, '_page_ewww-ngg-bulk' ) ) {
675
+ global $wpdb;
676
+ $images = $wpdb->get_col( "SELECT pid FROM $wpdb->nggpictures ORDER BY sortorder ASC" );
677
+ } // End if().
678
+ // Store the image IDs to process in the db.
679
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
680
+ // Add the EWWW IO script.
681
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
682
+ // Replacing the built-in nextgen styling rules for progressbar, partially because the bulk optimize page doesn't work without them.
683
+ wp_deregister_style( 'ngg-jqueryui' );
684
+ wp_deregister_style( 'ngg-jquery-ui' );
685
+ add_action( 'admin_head', array( &$this, 'ewww_ngg_style_remove' ) );
686
+ wp_register_style( 'jquery-ui-nextgen', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) );
687
+ // Enqueue the progressbar styling.
688
+ wp_enqueue_style( 'jquery-ui-nextgen' );
689
+ // Include all the vars we need for javascript.
690
+ wp_localize_script( 'ewwwbulkscript', 'ewww_vars', array(
691
+ '_wpnonce' => wp_create_nonce( 'ewww-image-optimizer-bulk' ),
692
+ 'gallery' => 'nextgen',
693
+ 'attachments' => count( $images ),
694
+ 'scan_fail' => esc_html__( 'Operation timed out, you may need to increase the max_execution_time for PHP', 'ewww-image-optimizer' ),
695
+ 'operation_stopped' => esc_html__( 'Optimization stopped, reload page to resume.', 'ewww-image-optimizer' ),
696
+ 'operation_interrupted' => esc_html__( 'Operation Interrupted', 'ewww-image-optimizer' ),
697
+ 'temporary_failure' => esc_html__( 'Temporary failure, seconds left to retry:', 'ewww-image-optimizer' ),
698
+ 'remove_failed' => esc_html__( 'Could not remove image from table.', 'ewww-image-optimizer' ),
699
+ 'optimized' => esc_html__( 'Optimized', 'ewww-image-optimizer' ),
700
+ )
701
+ );
702
+ }
703
+
704
+ /**
705
+ * Start the bulk operation.
706
+ */
707
+ function ewww_ngg_bulk_init() {
708
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
709
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
710
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
711
+ }
712
+ $output = array();
713
+ // Toggle the resume flag to indicate an operation is in progress.
714
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', 'true' );
715
+ $attachments = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
716
+ if ( ! is_array( $attachments ) && ! empty( $attachments ) ) {
717
+ $attachments = unserialize( $attachments );
718
+ }
719
+ if ( ! is_array( $attachments ) ) {
720
+ $output['error'] = esc_html__( 'Error retrieving list of images' );
721
+ echo json_encode( $output );
722
+ die();
723
+ }
724
+ $id = array_shift( $attachments );
725
+ $file = $this->ewww_ngg_bulk_filename( $id );
726
+ $loading_image = plugins_url( '/images/wpspin.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
727
+ if ( empty( $file ) ) {
728
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' alt='loading'/></p>";
729
+ } else {
730
+ $output['results'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <b>$file</b>&nbsp;<img src='$loading_image' alt='loading'/></p>";
731
+ }
732
+ echo json_encode( $output );
733
+ die();
734
+ }
735
+
736
+ /**
737
+ * Retrieve the filename of the image being optimized.
738
+ *
739
+ * @param int $id The ID number of the image.
740
+ * @return string|bool The name of the current file or false.
741
+ */
742
+ function ewww_ngg_bulk_filename( $id ) {
743
+ // Creating the 'registry' object for working with nextgen.
744
+ $registry = C_Component_Registry::get_instance();
745
+ // Creating a database storage object from the 'registry' object.
746
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
747
+ // Get an image object.
748
+ $image = $storage->object->_image_mapper->find( $id );
749
+ // Get the filename for the image, and output our current status.
750
+ $file_path = esc_html( $storage->get_image_abspath( $image, 'full' ) );
751
+ if ( ! empty( $file_path ) ) {
752
+ return $file_path;
753
+ } else {
754
+ return false;
755
+ }
756
+ }
757
+
758
+ /**
759
+ * Process each image in the bulk queue.
760
+ *
761
+ * @global bool $ewww_defer Set to false to avoid deferring image optimization.
762
+ */
763
+ function ewww_ngg_bulk_loop() {
764
+ global $ewww_defer;
765
+ $ewww_defer = false;
766
+ $output = array();
767
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
768
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
769
+ $output['error'] = esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' );
770
+ echo json_encode( $output );
771
+ die();
772
+ }
773
+ session_write_close();
774
+ // Find out if our nonce is on it's last leg/tick.
775
+ $tick = wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' );
776
+ if ( 2 === $tick ) {
777
+ $output['new_nonce'] = wp_create_nonce( 'ewww-image-optimizer-bulk' );
778
+ } else {
779
+ $output['new_nonce'] = '';
780
+ }
781
+ // Find out what time we started, in microseconds.
782
+ $started = microtime( true );
783
+ // Get the list of attachments remaining from the db.
784
+ $attachments = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
785
+ $id = array_shift( $attachments );
786
+ // Creating the 'registry' object for working with nextgen.
787
+ $registry = C_Component_Registry::get_instance();
788
+ // Creating a database storage object from the 'registry' object.
789
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
790
+ // Get an image object.
791
+ $image = $storage->object->_image_mapper->find( $id );
792
+ $image = $this->ewww_added_new_image( $image, $storage );
793
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
794
+ if ( ! empty( $ewww_status ) && preg_match( '/exceeded/', $ewww_status ) ) {
795
+ $output['error'] = esc_html__( 'License Exceeded', 'ewww-image-optimizer' );
796
+ echo json_encode( $output );
797
+ die();
798
+ }
799
+ // Output the results of the optimization.
800
+ $output['results'] = sprintf( '<p>' . esc_html__( 'Optimized image:', 'ewww-image-optimizer' ) . ' <strong>%s</strong><br>', esc_html( basename( $storage->object->get_image_abspath( $image, 'full' ) ) ) );
801
+ // Get an array of sizes available for the $image.
802
+ $sizes = $storage->get_image_sizes();
803
+ // Run the optimizer on the image for each $size.
804
+ if ( ewww_image_optimizer_iterable( $sizes ) ) {
805
+ foreach ( $sizes as $size ) {
806
+ if ( 'full' === $size ) {
807
+ /* Translators: %s: The compression results/savings */
808
+ $output['results'] .= sprintf( esc_html__( 'Full size - %s', 'ewww-image-optimizer' ) . '<br>', esc_html( $image->meta_data['ewww_image_optimizer'] ) );
809
+ } elseif ( 'thumbnail' === $size ) {
810
+ // Output the results of the thumb optimization.
811
+ /* Translators: %s: The compression results/savings */
812
+ $output['results'] .= sprintf( esc_html__( 'Thumbnail - %s', 'ewww-image-optimizer' ) . '<br>', esc_html( $image->meta_data[ $size ]['ewww_image_optimizer'] ) );
813
+ } else {
814
+ // Output savings for any other sizes, if they ever exist...
815
+ $output['results'] .= ucfirst( $size ) . ' - ' . esc_html( $image->meta_data[ $size ]['ewww_image_optimizer'] ) . '<br>';
816
+ }
817
+ }
818
+ }
819
+ // Output how much time we spent.
820
+ $elapsed = microtime( true ) - $started;
821
+ /* Translators: %s: The localized number of seconds */
822
+ $output['results'] .= sprintf( esc_html( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ) ) . '</p>', number_format_i18n( $elapsed ) );
823
+ $output['completed'] = 1;
824
+ // Store the list back in the db.
825
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $attachments, false );
826
+ if ( ! empty( $attachments ) ) {
827
+ $next_attachment = array_shift( $attachments );
828
+ $next_file = $this->ewww_ngg_bulk_filename( $next_attachment );
829
+ $loading_image = plugins_url( '/images/wpspin.gif', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
830
+ if ( $next_file ) {
831
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . " <b>$next_file</b>&nbsp;<img src='$loading_image' alt='loading'/></p>";
832
+ } else {
833
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' alt='loading'/></p>";
834
+ }
835
+ } else {
836
+ $output['done'] = 1;
837
+ }
838
+ die( json_encode( $output ) );
839
+ }
840
+
841
+ /**
842
+ * Finish the bulk operation.
843
+ */
844
+ function ewww_ngg_bulk_cleanup() {
845
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
846
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) {
847
+ wp_die( esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) );
848
+ }
849
+ // Reset all the bulk options in the db.
850
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
851
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', '', false );
852
+ echo '<p><b>' . esc_html__( 'Finished Optimization!', 'ewww-image-optimizer' ) . '</b></p>';
853
+ die();
854
+ }
855
+
856
+ /**
857
+ * Insert a bulk optimize option in the actions list for the gallery and image management pages (via javascript, since we have no hooks).
858
+ */
859
+ function ewww_ngg_bulk_actions_script() {
860
+ global $current_screen;
861
+ if ( ( strpos( $current_screen->id, 'nggallery-manage-images' ) === false && strpos( $current_screen->id, 'nggallery-manage-gallery' ) === false ) || ! current_user_can( apply_filters( 'ewww_image_optimizer_bulk_permissions', '' ) ) ) {
862
+ return;
863
+ }
864
+ ?> <script type="text/javascript">
865
+ jQuery(document).ready(function($){
866
+ $('select[name^="bulkaction"] option:last-child').after('<option value="bulk_optimize"><?php esc_html_e( 'Bulk Optimize', 'ewww-image-optimizer' ); ?></option>');
867
+ });
868
+ </script>
869
+ <?php }
870
+ }
871
+ // Initialize the plugin and the class.
872
+ global $ewwwngg;
873
+ $ewwwngg = new EWWW_Nextgen();
874
+ } // End if().
875
+
876
+ if ( ! empty( $_REQUEST['page'] ) && 'ngg_other_options' !== $_REQUEST['page'] && ! class_exists( 'EWWWIO_Gallery_Storage' ) && class_exists( 'Mixin' ) && class_exists( 'C_Gallery_Storage' ) ) {
877
+ /**
878
+ * Extension for NextGEN image generation.
879
+ */
880
+ class EWWWIO_Gallery_Storage extends Mixin {
881
+ /**
882
+ * Generates an image size (via the parent class) and then optimizes it.
883
+ *
884
+ * @param int|object $image A nextgen image object or the image ID number.
885
+ * @param string $size The name of the size.
886
+ * @param array $params Image generation parameters: width, height, and crop_frame.
887
+ * @param bool $skip_defaults I have no idea, ask the NextGEN devs...
888
+ */
889
+ function generate_image_size( $image, $size, $params = null, $skip_defaults = false ) {
890
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
891
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
892
+ ewww_image_optimizer_cloud_init();
893
+ }
894
+ $success = $this->call_parent( 'generate_image_size', $image, $size, $params, $skip_defaults );
895
+ if ( $success ) {
896
+ $filename = $success->fileName;
897
+ ewww_image_optimizer( $filename );
898
+ ewwwio_debug_message( "nextgen dynamic thumb saved via extension: $filename" );
899
+ $image_size = ewww_image_optimizer_filesize( $filename );
900
+ ewwwio_debug_message( "optimized size: $image_size" );
901
+ }
902
+ ewww_image_optimizer_debug_log();
903
+ ewwwio_memory( __FUNCTION__ );
904
+ return $success;
905
+ }
906
+ }
907
+ $storage = C_Gallery_Storage::get_instance();
908
+ $storage->get_wrapped_instance()->add_mixin( 'EWWWIO_Gallery_Storage' );
909
+ }
classes/class-ewwwdb.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class file for EwwwDB
4
+ *
5
+ * EwwwDB contains methods for working with the ewwwio_images table.
6
+ *
7
+ * @link https://ewww.io
8
+ * @package EWWW_Image_Optimizer
9
+ */
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+
16
+ /**
17
+ * DB class extension to for working with the ewwwio_images table.
18
+ *
19
+ * Provides functions needed to ensure that all data in the table is in utf8 format.
20
+ *
21
+ * @see wpdb
22
+ */
23
+ class EwwwDB extends wpdb {
24
+
25
+ /**
26
+ * Ensures use of some variant of utf8 for interacting with the images table.
27
+ */
28
+ function init_charset() {
29
+ parent::init_charset();
30
+ if ( strpos( $this->charset, 'utf8' ) === false ) {
31
+ $this->charset = 'utf8';
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Inserts multiple records into the table at once.
37
+ *
38
+ * Takes an associative array and creates a database record for each array item, using a single
39
+ * MySQL query. Column names are extracted from the key names of the first record.
40
+ *
41
+ * @param string $table Name of table to be used in INSERT statement.
42
+ * @param array $data Records to be inserted into table. Default none. Accepts an array of arrays,
43
+ * see example below.
44
+ * @param array $format List of formats for values in each record. Each sub-array should have the
45
+ * same number of items as $formats. Default '%s'. Accepts '%s', '%d', '%f'.
46
+ * @return int|false Number of rows affected or false on error.
47
+ */
48
+ function insert_multiple( $table, $data, $format ) {
49
+ if ( empty( $table ) || ! ewww_image_optimizer_iterable( $data ) || ! ewww_image_optimizer_iterable( $format ) ) {
50
+ return false;
51
+ }
52
+
53
+ /*
54
+ * Given a multi-dimensional array like so:
55
+ * array(
56
+ * [0] =>
57
+ * 'path' => '/some/image/path/here.jpg'
58
+ * 'gallery' => 'something'
59
+ * 'orig_size => 5678
60
+ * 'attachment_id => 2
61
+ * 'resize' => 'thumb'
62
+ * 'pending' => 1
63
+ * [1] =>
64
+ * 'path' => '/some/image/path/another.jpg'
65
+ * 'gallery' => 'something'
66
+ * 'orig_size => 1234
67
+ * 'attachment_id => 3
68
+ * 'resize' => 'full'
69
+ * 'pending' => 1
70
+ * )
71
+ */
72
+ ewwwio_debug_message( 'we have records to store via ewwwdb' );
73
+ $multi_formats = array();
74
+ $values = array();
75
+ foreach ( $data as $record ) {
76
+ if ( ! ewww_image_optimizer_iterable( $record ) ) {
77
+ continue;
78
+ }
79
+ $record = $this->process_fields( $table, $record, $format );
80
+ if ( false === $record ) {
81
+ return false;
82
+ }
83
+
84
+ $formats = array();
85
+ foreach ( $record as $value ) {
86
+ if ( is_null( $value['value'] ) ) {
87
+ $formats[] = 'NULL';
88
+ continue;
89
+ }
90
+
91
+ $formats[] = $value['format'];
92
+ $values[] = $value['value'];
93
+ }
94
+ $multi_formats[] = '(' . implode( ',', $formats ) . ')';
95
+ }
96
+ $first = reset( $data );
97
+ $fields = '`' . implode( '`, `', array_keys( $first ) ) . '`';
98
+ $multi_formats = implode( ',', $multi_formats );
99
+ $sql = "INSERT INTO `$table` ($fields) VALUES $multi_formats";
100
+ $this->check_current_query = false;
101
+ return $this->query( $this->prepare( $sql, $values ) );
102
+ }
103
+
104
+ }
classes/class-ewwwio-cli.php ADDED
@@ -0,0 +1,500 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class file for EWWWIO_CLI
4
+ *
5
+ * EWWWIO_CLI contains an extension to the WP_CLI_Command class to enable bulk optimizing from the
6
+ * command line using WP-CLI.
7
+ *
8
+ * @link https://ewww.io
9
+ * @package EWWW_Image_Optimizer
10
+ */
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+ /**
16
+ * Implements wp-cli extension for bulk optimizing.
17
+ */
18
+ class EWWWIO_CLI extends WP_CLI_Command {
19
+ /**
20
+ * Optimizes images from the selected 'gallery'.
21
+ *
22
+ * ## OPTIONS
23
+ *
24
+ * <library>
25
+ * : valid values are 'all' (default), 'media', 'nextgen', and 'flagallery'
26
+ * : media: Media Library, theme, and configured folders
27
+ * : nextgen: Nextcellent and NextGEN 2.x
28
+ * : flagallery: Grand FlaGallery
29
+ *
30
+ * <delay>
31
+ * : optional, number of seconds to pause between images
32
+ *
33
+ * <force>
34
+ * : optional, should the plugin re-optimize images that have already been processed.
35
+ * * <reset>
36
+ * : optional, start the optimizer back at the beginning instead of resuming from last position
37
+ *
38
+ * <noprompt>
39
+ * : do not prompt, just start optimizing
40
+ *
41
+ * ## EXAMPLES
42
+ *
43
+ * wp-cli ewwwio optimize media 5 --force --reset --noprompt
44
+ *
45
+ * @synopsis <library> [<delay>] [--force] [--reset] [--noprompt]
46
+ *
47
+ * @global bool $ewww_defer Gets set to false to make sure optimization happens inline.
48
+ * @global object $ngg
49
+ *
50
+ * @param array $args A numeric array of required arguments.
51
+ * @param array $assoc_args An associative array of optional arguments.
52
+ */
53
+ function optimize( $args, $assoc_args ) {
54
+ global $ewww_defer;
55
+ $ewww_defer = false;
56
+ // because NextGEN hasn't flushed it's buffers...
57
+ while ( @ob_end_flush() ) {}
58
+ $library = $args[0];
59
+ if ( empty( $args[1] ) ) {
60
+ $delay = ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' );
61
+ } else {
62
+ $delay = $args[1];
63
+ }
64
+ $ewww_reset = false;
65
+ if ( ! empty( $assoc_args['reset'] ) ) {
66
+ $ewww_reset = true;
67
+ }
68
+ if ( ! empty( $assoc_args['force'] ) ) {
69
+ WP_CLI::line( __( 'Forcing re-optimization of previously processed images.', 'ewww-image-optimizer' ) );
70
+ $_REQUEST['ewww_force'] = true;
71
+ }
72
+ /* translators: 1: type of images, like media, or nextgen 2: number of seconds */
73
+ WP_CLI::line( sprintf( _x( 'Optimizing %1$s with a %2$d second pause between images.', 'string will be something like "media" or "nextgen"', 'ewww-image-optimizer' ), $library, $delay ) );
74
+ // Let's get started, shall we?
75
+ ewww_image_optimizer_admin_init();
76
+ // And what shall we do?
77
+ switch ( $library ) {
78
+ case 'all':
79
+ if ( $ewww_reset ) {
80
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
81
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
82
+ update_option( 'ewww_image_optimizer_scanning_attachments', '', false );
83
+ update_option( 'ewww_image_optimizer_bulk_attachments', '', false );
84
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
85
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
86
+ ewww_image_optimizer_delete_pending();
87
+ WP_CLI::line( __( 'Bulk status has been reset, starting from the beginning.', 'ewww-image-optimizer' ) );
88
+ }
89
+ ewww_image_optimizer_bulk_script( 'media_page_ewww-image-optimizer-bulk' );
90
+ $fullsize_count = ewww_image_optimizer_count_optimized( 'media' );
91
+
92
+ /* translators: %d: number of images */
93
+ WP_CLI::line( sprintf( _n( '%1$d image in the Media Library has been selected.', '%1$d images in the Media Library have been selected.', $fullsize_count, 'ewww-image-optimizer' ), $fullsize_count ) );
94
+ WP_CLI::line( __( 'The active theme, BuddyPress, WP Symposium, and folders that you have configured will also be scanned for unoptimized images.', 'ewww-image-optimizer' ) );
95
+ WP_CLI::line( __( 'Scanning, this could take a while', 'ewww-image-optimizer' ) );
96
+ // Do a filter to increase the timeout to 999 or something crazy.
97
+ add_filter( 'ewww_image_optimizer_timeout', 'ewww_image_optimizer_cli_timeout', 200 );
98
+ ewww_image_optimizer_media_scan( 'ewww-image-optimizer-cli' );
99
+ $pending_count = ewww_image_optimizer_aux_images_script( 'ewww-image-optimizer-auto' );
100
+ if ( class_exists( 'EwwwNgg' ) ) {
101
+ global $ngg;
102
+ if ( preg_match( '/^2/', $ngg->version ) ) {
103
+ list( $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) = ewww_image_optimizer_count_optimized( 'ngg' );
104
+ /* translators: 1-4: number of images */
105
+ WP_CLI::line( 'Nextgen: ' . sprintf( __( '%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized).', 'ewww-image-optimizer' ), $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) );
106
+ } else {
107
+ $attachments = ewww_image_optimizer_scan_next();
108
+ /* translators: %d: number of images */
109
+ WP_CLI::line( 'Nextgen: ' . sprintf( _n( 'There is %d image ready to optimize.', 'There are %d images ready to optimize.', count( $attachments ), 'ewww-image-optimizer' ), count( $attachments ) ) );
110
+ }
111
+ }
112
+ if ( class_exists( 'ewwwflag' ) ) {
113
+ list( $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) = ewww_image_optimizer_count_optimized( 'flag' );
114
+ /* translators: 1-4: number of images */
115
+ WP_CLI::line( 'Flagallery: ' . sprintf( __( '%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized).', 'ewww-image-optimizer' ), $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) );
116
+ }
117
+ if ( empty( $assoc_args['noprompt'] ) ) {
118
+ /* translators: %d: number of images */
119
+ WP_CLI::confirm( sprintf( _n( 'There is %d image ready to optimize.', 'There are %d images ready to optimize.', $pending_count, 'ewww-image-optimizer' ), $pending_count ) );
120
+ }
121
+ /* ewww_image_optimizer_bulk_media( $delay ); */
122
+ $_REQUEST['ewww_batch_limit'] = 1;
123
+ while ( ewww_image_optimizer_bulk_loop( 'ewww-image-optimizer-cli', $delay ) ) {
124
+ $something = 1;
125
+ }
126
+ ewww_image_optimizer_bulk_media_cleanup();
127
+ if ( class_exists( 'Ewwwngg' ) ) {
128
+ global $ngg;
129
+ if ( preg_match( '/^2/', $ngg->version ) ) {
130
+ ewww_image_optimizer_bulk_ngg( $delay );
131
+ } else {
132
+ $attachments = ewww_image_optimizer_scan_next();
133
+ ewww_image_optimizer_bulk_next( $delay, $attachments );
134
+ }
135
+ }
136
+ if ( class_exists( 'ewwwflag' ) ) {
137
+ ewww_image_optimizer_bulk_flag( $delay );
138
+ }
139
+ break;
140
+ case 'media':
141
+ case 'other':
142
+ if ( $ewww_reset ) {
143
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
144
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
145
+ update_option( 'ewww_image_optimizer_scanning_attachments', '', false );
146
+ update_option( 'ewww_image_optimizer_bulk_attachments', '', false );
147
+ ewww_image_optimizer_delete_pending();
148
+ WP_CLI::line( __( 'Bulk status has been reset, starting from the beginning.', 'ewww-image-optimizer' ) );
149
+ }
150
+ ewww_image_optimizer_bulk_script( 'media_page_ewww-image-optimizer-bulk' );
151
+ $fullsize_count = ewww_image_optimizer_count_optimized( 'media' );
152
+ /* translators: %d: number of images */
153
+ WP_CLI::line( sprintf( __( '%1$d images in the Media Library have been selected.', 'ewww-image-optimizer' ), $fullsize_count ) );
154
+ WP_CLI::line( __( 'The active theme, BuddyPress, WP Symposium, and folders that you have configured will also be scanned for unoptimized images.', 'ewww-image-optimizer' ) );
155
+ WP_CLI::line( __( 'Scanning, this could take a while', 'ewww-image-optimizer' ) );
156
+ // Do a filter to increase the timeout to 999 or something crazy.
157
+ add_filter( 'ewww_image_optimizer_timeout', 'ewww_image_optimizer_cli_timeout', 200 );
158
+ ewww_image_optimizer_media_scan( 'ewww-image-optimizer-cli' );
159
+ $pending_count = ewww_image_optimizer_aux_images_script( 'ewww-image-optimizer-auto' );
160
+ if ( empty( $assoc_args['noprompt'] ) ) {
161
+ /* translators: %d: number of images */
162
+ WP_CLI::confirm( sprintf( _n( 'There is %d image ready to optimize.', 'There are %d images ready to optimize.', $pending_count, 'ewww-image-optimizer' ), $pending_count ) );
163
+ }
164
+ $_REQUEST['ewww_batch_limit'] = 1;
165
+ while ( ewww_image_optimizer_bulk_loop( 'ewww-image-optimizer-cli', $delay ) ) {
166
+ $something = 1;
167
+ }
168
+ ewww_image_optimizer_bulk_media_cleanup();
169
+ break;
170
+ case 'nextgen':
171
+ if ( $ewww_reset ) {
172
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
173
+ WP_CLI::line( __( 'Bulk status has been reset, starting from the beginning.', 'ewww-image-optimizer' ) );
174
+ }
175
+ if ( class_exists( 'EwwwNgg' ) ) {
176
+ global $ngg;
177
+ if ( preg_match( '/^2/', $ngg->version ) ) {
178
+ list( $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) = ewww_image_optimizer_count_optimized( 'ngg' );
179
+ if ( empty( $assoc_args['noprompt'] ) ) {
180
+ /* translators: 1-4: number of images */
181
+ WP_CLI::confirm( sprintf( __( '%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized).', 'ewww-image-optimizer' ), $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) );
182
+ }
183
+ ewww_image_optimizer_bulk_ngg( $delay );
184
+ } else {
185
+ $attachments = ewww_image_optimizer_scan_next();
186
+ if ( empty( $assoc_args['noprompt'] ) ) {
187
+ /* translators: %d: number of images */
188
+ WP_CLI::confirm( sprintf( _n( 'There is %d image ready to optimize.', 'There are %d images ready to optimize.', count( $attachments ), 'ewww-image-optimizer' ), count( $attachments ) ) );
189
+ }
190
+ ewww_image_optimizer_bulk_next( $delay, $attachments );
191
+ }
192
+ } else {
193
+ WP_CLI::error( __( 'NextGEN/Nextcellent not installed.', 'ewww-image-optimizer' ) );
194
+ }
195
+ break;
196
+ case 'flagallery':
197
+ if ( $ewww_reset ) {
198
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
199
+ WP_CLI::line( __( 'Bulk status has been reset, starting from the beginning.', 'ewww-image-optimizer' ) );
200
+ }
201
+ if ( class_exists( 'ewwwflag' ) ) {
202
+ list( $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) = ewww_image_optimizer_count_optimized( 'flag' );
203
+ if ( empty( $assoc_args['noprompt'] ) ) {
204
+ /* translators: 1-4: number of images */
205
+ WP_CLI::confirm( sprintf( __( '%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized).', 'ewww-image-optimizer' ), $fullsize_count, $unoptimized_count, $resize_count, $unoptimized_resize_count ) );
206
+ }
207
+ ewww_image_optimizer_bulk_flag( $delay );
208
+ } else {
209
+ WP_CLI::error( __( 'Grand Flagallery not installed.', 'ewww-image-optimizer' ) );
210
+ }
211
+ break;
212
+ default:
213
+ if ( $ewww_reset ) {
214
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
215
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
216
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
217
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
218
+ WP_CLI::success( __( 'Bulk status has been reset, the next bulk operation will start from the beginning.', 'ewww-image-optimizer' ) );
219
+ } else {
220
+ WP_CLI::line( __( 'Please specify a valid library option, see "wp-cli help ewwwio optimize" for more information.', 'ewww-image-optimizer' ) );
221
+ }
222
+ } // End switch().
223
+ }
224
+ }
225
+
226
+ WP_CLI::add_command( 'ewwwio', 'EWWWIO_CLI' );
227
+
228
+ /**
229
+ * Cleanup after ourselves after a bulk operation.
230
+ */
231
+ function ewww_image_optimizer_bulk_media_cleanup() {
232
+ // All done, so we can update the bulk options with empty values...
233
+ update_option( 'ewww_image_optimizer_bulk_resume', '' );
234
+ update_option( 'ewww_image_optimizer_aux_resume', '' );
235
+ update_option( 'ewww_image_optimizer_bulk_attachments', '', false );
236
+ // and let the user know we are done.
237
+ WP_CLI::success( __( 'Finished Optimization!', 'ewww-image-optimizer' ) );
238
+ }
239
+
240
+ /**
241
+ * Bulk Optimize all GRAND FlaGallery uploads from WP-CLI.
242
+ *
243
+ * @global object $wpdb
244
+ *
245
+ * @param int $delay Number of seconds to pause between images.
246
+ */
247
+ function ewww_image_optimizer_bulk_flag( $delay = 0 ) {
248
+ $ids = null;
249
+ if ( get_option( 'ewww_image_optimizer_bulk_flag_resume' ) ) {
250
+ // If there is an operation to resume, get those IDs from the db.
251
+ $ids = get_option( 'ewww_image_optimizer_bulk_flag_attachments' );
252
+ } else {
253
+ // Otherwise, if we are on the main bulk optimize page, just get all the IDs available.
254
+ global $wpdb;
255
+ $ids = $wpdb->get_col( "SELECT pid FROM $wpdb->flagpictures ORDER BY sortorder ASC" );
256
+ // Store the IDs to optimize in the options table of the db.
257
+ update_option( 'ewww_image_optimizer_bulk_flag_attachments', $ids, false );
258
+ }
259
+ // Set the resume flag to indicate the bulk operation is in progress.
260
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', 'true' );
261
+ // Need this file to work with flag meta.
262
+ require_once( WP_CONTENT_DIR . '/plugins/flash-album-gallery/lib/meta.php' );
263
+ if ( ! ewww_image_optimizer_iterable( $ids ) ) {
264
+ WP_CLI::line( __( 'You do not appear to have uploaded any images yet.', 'ewww-image-optimizer' ) );
265
+ return;
266
+ }
267
+ foreach ( $ids as $id ) {
268
+ if ( ewww_image_optimizer_function_exists( 'sleep' ) ) {
269
+ sleep( $delay );
270
+ }
271
+ // Record the starting time for the current image (in microseconds).
272
+ $started = microtime( true );
273
+ // Retrieve the meta for the current ID.
274
+ $meta = new flagMeta( $id );
275
+ $file_path = $meta->image->imagePath;
276
+ // Optimize the full-size version.
277
+ $fres = ewww_image_optimizer( $file_path, 3, false, false, true );
278
+ WP_CLI::line( __( 'Optimized image:', 'ewww-image-optimizer' ) . ' ' . esc_html( $meta->image->filename ) );
279
+ /* translators: %s: compression results */
280
+ WP_CLI::line( sprintf( __( 'Full size – %s', 'ewww-image-optimizer' ), html_entity_decode( $fres[1] ) ) );
281
+ if ( ! empty( $meta->image->meta_data['webview'] ) ) {
282
+ // Determine path of the webview.
283
+ $web_path = $meta->image->webimagePath;
284
+ $wres = ewww_image_optimizer( $web_path, 3, false, true );
285
+ /* translators: %s: compression results */
286
+ WP_CLI::line( sprintf( __( 'Optimized size – %s', 'ewww-image-optimizer' ), html_entity_decode( $wres[1] ) ) );
287
+ }
288
+ $thumb_path = $meta->image->thumbPath;
289
+ // Optimize the thumbnail.
290
+ $tres = ewww_image_optimizer( $thumb_path, 3, false, true );
291
+ /* translators: %s: compression results */
292
+ WP_CLI::line( sprintf( __( 'Thumbnail – %s', 'ewww-image-optimizer' ), html_entity_decode( $tres[1] ) ) );
293
+ // Determine how much time the image took to process...
294
+ $elapsed = microtime( true ) - $started;
295
+ // and output it to the user.
296
+ /* translators: %s: localized number of seconds */
297
+ WP_CLI::line( sprintf( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ), number_format_i18n( $elapsed ) ) );
298
+ // Retrieve the list of attachments left to work on.
299
+ $attachments = get_option( 'ewww_image_optimizer_bulk_flag_attachments' );
300
+ // Take the first image off the list.
301
+ if ( ! empty( $attachments ) ) {
302
+ array_shift( $attachments );
303
+ }
304
+ // And send the list back to the db.
305
+ update_option( 'ewww_image_optimizer_bulk_flag_attachments', $attachments, false );
306
+ } // End foreach().
307
+ // Reset the bulk flags in the db...
308
+ update_option( 'ewww_image_optimizer_bulk_flag_resume', '' );
309
+ update_option( 'ewww_image_optimizer_bulk_flag_attachments', '', false );
310
+ // and let the user know we are done.
311
+ WP_CLI::success( __( 'Finished Optimization!', 'ewww-image-optimizer' ) );
312
+ }
313
+
314
+ /**
315
+ * Search for all NextGEN uploads using WP-CLI command.
316
+ *
317
+ * @global object $wpdb
318
+ */
319
+ function ewww_image_optimizer_scan_ngg() {
320
+ $images = null;
321
+ if ( get_option( 'ewww_image_optimizer_bulk_ngg_resume' ) ) {
322
+ // Get the list of attachment IDs from the queue.
323
+ $images = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
324
+ } else {
325
+ // Otherwise, get all the images in the db.
326
+ global $wpdb;
327
+ $images = $wpdb->get_col( "SELECT pid FROM $wpdb->nggpictures ORDER BY sortorder ASC" );
328
+ }
329
+ // Store the image IDs to process in the db.
330
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
331
+ return $images;
332
+ }
333
+
334
+ /**
335
+ * Bulk Optimize all NextGEN uploads from WP-CLI.
336
+ *
337
+ * @global object $wpdb
338
+ * @global object $ewwwngg
339
+ *
340
+ * @param int $delay Number of seconds to pause between images.
341
+ */
342
+ function ewww_image_optimizer_bulk_ngg( $delay = 0 ) {
343
+ if ( get_option( 'ewww_image_optimizer_bulk_ngg_resume' ) ) {
344
+ // Get the list of attachment IDs from the db.
345
+ $images = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
346
+ } else {
347
+ // Otherwise, get all the images in the db.
348
+ global $wpdb;
349
+ $images = $wpdb->get_col( "SELECT pid FROM $wpdb->nggpictures ORDER BY sortorder ASC" );
350
+ // Store the image IDs to process in the db.
351
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
352
+ // Toggle the resume flag to indicate an operation is in progress.
353
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', 'true' );
354
+ }
355
+ if ( ! ewww_image_optimizer_iterable( $images ) ) {
356
+ WP_CLI::line( __( 'You do not appear to have uploaded any images yet.', 'ewww-image-optimizer' ) );
357
+ return;
358
+ }
359
+ global $ewwwngg;
360
+ foreach ( $images as $id ) {
361
+ if ( ewww_image_optimizer_function_exists( 'sleep' ) ) {
362
+ sleep( $delay );
363
+ }
364
+ // Find out what time we started, in microseconds.
365
+ $started = microtime( true );
366
+ // Creating the 'registry' object for working with nextgen.
367
+ $registry = C_Component_Registry::get_instance();
368
+ // Creating a database storage object from the 'registry' object.
369
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
370
+ // Get an image object.
371
+ $image = $storage->object->_image_mapper->find( $id );
372
+ $image = $ewwwngg->ewww_added_new_image( $image, $storage );
373
+ // Output the results of the optimization.
374
+ WP_CLI::line( __( 'Optimized image:', 'ewww-image-optimizer' ) . ' ' . basename( $storage->object->get_image_abspath( $image, 'full' ) ) );
375
+ // Get an array of sizes available for the $image.
376
+ $sizes = $storage->get_image_sizes();
377
+ if ( ewww_image_optimizer_iterable( $sizes ) ) {
378
+ // Output the results for each $size.
379
+ foreach ( $sizes as $size ) {
380
+ if ( 'full' === $size ) {
381
+ /* translators: %s: compression results */
382
+ WP_CLI::line( sprintf( __( 'Full size - %s', 'ewww-image-optimizer' ), html_entity_decode( $image->meta_data['ewww_image_optimizer'] ) ) );
383
+ } elseif ( 'thumbnail' === $size ) {
384
+ // Output the results of the thumb optimization.
385
+ /* translators: %s: compression results */
386
+ WP_CLI::line( sprintf( __( 'Thumbnail - %s', 'ewww-image-optimizer' ), html_entity_decode( $image->meta_data[ $size ]['ewww_image_optimizer'] ) ) );
387
+ } else {
388
+ // Output savings for any other sizes, if they ever exist...
389
+ WP_CLI::line( ucfirst( $size ) . ' - ' . html_entity_decode( $image->meta_data[ $size ]['ewww_image_optimizer'] ) );
390
+ }
391
+ }
392
+ }
393
+ // Output how much time we spent.
394
+ $elapsed = microtime( true ) - $started;
395
+ /* translators: %s: number of seconds */
396
+ WP_CLI::line( sprintf( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ), number_format_i18n( $elapsed ) ) );
397
+ // Get the list of attachments remaining from the db.
398
+ $attachments = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
399
+ // Remove the first item.
400
+ if ( ! empty( $attachments ) ) {
401
+ array_shift( $attachments );
402
+ }
403
+ // And store the list back in the db.
404
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $attachments, false );
405
+ } // End foreach().
406
+
407
+ // Reset all the bulk options in the db.
408
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
409
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', '', false );
410
+ WP_CLI::success( __( 'Finished Optimization!', 'ewww-image-optimizer' ) );
411
+ }
412
+
413
+ /**
414
+ * Search for all Nextcellent uploads using WP-CLI command.
415
+ *
416
+ * @global object $wpdb
417
+ */
418
+ function ewww_image_optimizer_scan_next() {
419
+ $images = null;
420
+ if ( get_option( 'ewww_image_optimizer_bulk_ngg_resume' ) ) {
421
+ // If we have an operation to resume...
422
+ // get the list of attachment IDs from the queue.
423
+ $images = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
424
+ } else {
425
+ // Otherwise, get all the images in the db.
426
+ global $wpdb;
427
+ $images = $wpdb->get_col( "SELECT pid FROM $wpdb->nggpictures ORDER BY sortorder ASC" );
428
+ }
429
+
430
+ // Store the image IDs to process in the queue.
431
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
432
+ return $images;
433
+ }
434
+
435
+ /**
436
+ * Bulk Optimize all NextGEN uploads from WP-CLI.
437
+ *
438
+ * @param int $delay Number of seconds to pause between images.
439
+ * @param array $attachments A list of image IDs to optimize.
440
+ */
441
+ function ewww_image_optimizer_bulk_next( $delay, $attachments ) {
442
+ // Toggle the resume flag to indicate an operation is in progress.
443
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', 'true' );
444
+ // Need this file to work with metadata.
445
+ require_once( WP_CONTENT_DIR . '/plugins/nextcellent-gallery-nextgen-legacy/lib/meta.php' );
446
+ foreach ( $attachments as $id ) {
447
+ if ( ewww_image_optimizer_function_exists( 'sleep' ) ) {
448
+ sleep( $delay );
449
+ }
450
+ // Find out what time we started, in microseconds.
451
+ $started = microtime( true );
452
+ // Get the metadata.
453
+ $meta = new nggMeta( $id );
454
+ // Retrieve the filepath.
455
+ $file_path = $meta->image->imagePath;
456
+ // Run the optimizer on the current image.
457
+ $fres = ewww_image_optimizer( $file_path, 2, false, false, true );
458
+ // Update the metadata of the optimized image.
459
+ nggdb::update_image_meta( $id, array(
460
+ 'ewww_image_optimizer' => $fres[1],
461
+ ) );
462
+ // Output the results of the optimization.
463
+ WP_CLI::line( __( 'Optimized image:', 'ewww-image-optimizer' ) . $meta->image->filename );
464
+ /* translators: %s: compression results */
465
+ WP_CLI::line( sprintf( __( 'Full size - %s', 'ewww-image-optimizer' ), html_entity_decode( $fres[1] ) ) );
466
+ // Get the filepath of the thumbnail image.
467
+ $thumb_path = $meta->image->thumbPath;
468
+ // Run the optimization on the thumbnail.
469
+ $tres = ewww_image_optimizer( $thumb_path, 2, false, true );
470
+ // Output the results of the thumb optimization.
471
+ /* translators: %s: compression results */
472
+ WP_CLI::line( sprintf( __( 'Thumbnail - %s', 'ewww-image-optimizer' ), html_entity_decode( $tres[1] ) ) );
473
+ // Output how much time we spent.
474
+ $elapsed = microtime( true ) - $started;
475
+ /* translators: %s: number of seconds */
476
+ WP_CLI::line( sprintf( _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' ), number_format_i18n( $elapsed ) ) );
477
+ // Get the list of attachments remaining from the db.
478
+ $attachments = get_option( 'ewww_image_optimizer_bulk_ngg_attachments' );
479
+ // Remove the first item.
480
+ if ( ! empty( $attachments ) ) {
481
+ array_shift( $attachments );
482
+ }
483
+ // and store the list back in the db queue.
484
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $attachments, false );
485
+ } // End foreach().
486
+ // Reset all the bulk options in the db.
487
+ update_option( 'ewww_image_optimizer_bulk_ngg_resume', '' );
488
+ update_option( 'ewww_image_optimizer_bulk_ngg_attachments', '', false );
489
+ WP_CLI::success( __( 'Finished Optimization!', 'ewww-image-optimizer' ) );
490
+ }
491
+
492
+ /**
493
+ * Increases the EWWW IO timeout for scanning images.
494
+ *
495
+ * @param int $time_limit The number of seconds before a timeout happens.
496
+ * @return int The number of seconds to wait before a timeout from the CLI.
497
+ */
498
+ function ewww_image_optimizer_cli_timeout( $time_limit ) {
499
+ return 9999;
500
+ }
classes/class-ewwwio-gd-editor.php ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class and methods to integrate with the WP_Image_Editor_GD class and other extensions.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EWWW_Image_Optimizer
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ if ( class_exists( 'Bbpp_Animated_Gif' ) ) {
13
+ /**
14
+ * Extension of the WP_Image_Editor_GD class to auto-compress edited images.
15
+ *
16
+ * @see WP_Image_Editor_GD
17
+ */
18
+ class EWWWIO_GD_Editor extends Bbpp_Animated_Gif {
19
+
20
+ /**
21
+ * Saves a file from the image editor.
22
+ *
23
+ * @param resource $image A GD image object.
24
+ * @param string $filename Optional. The name of the file to be saved to.
25
+ * @param string $mime_type Optional. The mimetype of the file.
26
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
27
+ */
28
+ protected function _save( $image, $filename = null, $mime_type = null ) {
29
+ global $ewww_defer;
30
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
31
+ if ( ! $filename ) {
32
+ $filename = $this->generate_filename( null, null, $extension );
33
+ }
34
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
35
+ ewwwio_debug_message( "detected existing file: $filename" );
36
+ $current_size = getimagesize( $filename );
37
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
38
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
39
+ return array(
40
+ 'path' => $filename,
41
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
42
+ 'width' => $this->size['width'],
43
+ 'height' => $this->size['height'],
44
+ 'mime-type' => $mime_type,
45
+ );
46
+ }
47
+ }
48
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
49
+ ewww_image_optimizer_cloud_init();
50
+ }
51
+ $saved = parent::_save( $image, $filename, $mimetype );
52
+ if ( ! is_wp_error( $saved ) ) {
53
+ if ( ! $filename ) {
54
+ $filename = $saved['path'];
55
+ }
56
+ if ( file_exists( $filename ) ) {
57
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
58
+ ewww_image_optimizer( $filename );
59
+ ewwwio_debug_message( "image editor (AGR gd) saved: $filename" );
60
+ $image_size = ewww_image_optimizer_filesize( $filename );
61
+ ewwwio_debug_message( "image editor size: $image_size" );
62
+
63
+ /*
64
+ } else {
65
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
66
+ global $ewwwio_image_background;
67
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
68
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
69
+ }
70
+ if ( ! is_object( $ewwwio_image_background ) ) {
71
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
72
+ }
73
+ $ewwwio_image_background->push_to_queue( $filename );
74
+ $ewwwio_image_background->save()->dispatch();
75
+ ewwwio_debug_message( "image editor (AGR gd) queued: $filename" );
76
+ }
77
+ */
78
+ }
79
+ ewww_image_optimizer_debug_log();
80
+ }
81
+ ewwwio_memory( __FUNCTION__ );
82
+ return $saved;
83
+ }
84
+ /**
85
+ * Resize multiple images from a single source.
86
+ *
87
+ * @param array $sizes An array of image size arrays. Default sizes are 'small', 'medium', 'medium_large', 'large'.
88
+ * @return array An array of resized images' metadata by size.
89
+ */
90
+ public function multi_resize( $sizes ) {
91
+ global $ewww_defer;
92
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
93
+ ewww_image_optimizer_cloud_init();
94
+ }
95
+ $metadata = parent::multi_resize( $sizes );
96
+ ewwwio_debug_message( 'image editor (AGR gd) multi resize' );
97
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
98
+ ewwwio_debug_message( print_r( $metadata, true ) );
99
+ ewwwio_debug_message( print_r( $this, true ) );
100
+ }
101
+ $info = pathinfo( $this->file );
102
+ $dir = $info['dirname'];
103
+ if ( ewww_image_optimizer_iterable( $metadata ) ) {
104
+ foreach ( $metadata as $size ) {
105
+ $filename = trailingslashit( $dir ) . $size['file'];
106
+ if ( file_exists( $filename ) ) {
107
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) {*/
108
+ ewww_image_optimizer( $filename );
109
+ ewwwio_debug_message( "image editor (AGR gd) saved: $filename" );
110
+ $image_size = ewww_image_optimizer_filesize( $filename );
111
+ ewwwio_debug_message( "image editor size: $image_size" );
112
+
113
+ /*
114
+ } else {
115
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
116
+ global $ewwwio_image_background;
117
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
118
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
119
+ }
120
+ if ( ! is_object( $ewwwio_image_background ) ) {
121
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
122
+ }
123
+ $ewwwio_image_background->push_to_queue( $filename );
124
+ $ewwwio_image_background->save()->dispatch();
125
+ ewwwio_debug_message( "image editor (AGR gd) queued: $filename" );
126
+ }
127
+ */
128
+ }
129
+ }
130
+ }
131
+ ewww_image_optimizer_debug_log();
132
+ ewwwio_memory( __FUNCTION__ );
133
+ return $metadata;
134
+ }
135
+ }
136
+ } elseif ( class_exists( 'WP_Thumb_Image_Editor_GD' ) ) {
137
+ /**
138
+ * Extension of the WP_Image_Editor_GD class to auto-compress edited images.
139
+ *
140
+ * @see WP_Image_Editor_GD
141
+ */
142
+ class EWWWIO_GD_Editor extends WP_Thumb_Image_Editor_GD {
143
+ /**
144
+ * Saves a file from the image editor.
145
+ *
146
+ * @param resource $image A GD image object.
147
+ * @param string $filename Optional. The name of the file to be saved to.
148
+ * @param string $mime_type Optional. The mimetype of the file.
149
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
150
+ */
151
+ protected function _save( $image, $filename = null, $mime_type = null ) {
152
+ global $ewww_defer;
153
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
154
+ if ( ! $filename ) {
155
+ $filename = $this->generate_filename( null, null, $extension );
156
+ }
157
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
158
+ ewwwio_debug_message( "detected existing file: $filename" );
159
+ $current_size = getimagesize( $filename );
160
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
161
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
162
+ return array(
163
+ 'path' => $filename,
164
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
165
+ 'width' => $this->size['width'],
166
+ 'height' => $this->size['height'],
167
+ 'mime-type' => $mime_type,
168
+ );
169
+ }
170
+ }
171
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
172
+ ewww_image_optimizer_cloud_init();
173
+ }
174
+ $saved = parent::_save( $image, $filename, $mime_type );
175
+ if ( ! is_wp_error( $saved ) ) {
176
+ if ( ! $filename ) {
177
+ $filename = $saved['path'];
178
+ }
179
+ if ( file_exists( $filename ) ) {
180
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
181
+ ewww_image_optimizer( $filename );
182
+ ewwwio_debug_message( "image editor (wpthumb GD) saved: $filename" );
183
+ $image_size = ewww_image_optimizer_filesize( $filename );
184
+ ewwwio_debug_message( "image editor size: $image_size" );
185
+
186
+ /*
187
+ } else {
188
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
189
+ global $ewwwio_image_background;
190
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
191
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
192
+ }
193
+ if ( ! is_object( $ewwwio_image_background ) ) {
194
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
195
+ }
196
+ $ewwwio_image_background->push_to_queue( $filename );
197
+ $ewwwio_image_background->save()->dispatch();
198
+ ewwwio_debug_message( "image editor (wpthumb GD) queued: $filename" );
199
+ }
200
+ */
201
+ }
202
+ ewww_image_optimizer_debug_log();
203
+ }
204
+ ewwwio_memory( __FUNCTION__ );
205
+ return $saved;
206
+ }
207
+ }
208
+ } elseif ( class_exists( 'BFI_Image_Editor_GD' ) ) {
209
+ /**
210
+ * Extension of the WP_Image_Editor_GD class to auto-compress edited images.
211
+ *
212
+ * @see WP_Image_Editor_GD
213
+ */
214
+ class EWWWIO_GD_Editor extends BFI_Image_Editor_GD {
215
+ /**
216
+ * Saves a file from the image editor.
217
+ *
218
+ * @param resource $image A GD image object.
219
+ * @param string $filename Optional. The name of the file to be saved to.
220
+ * @param string $mime_type Optional. The mimetype of the file.
221
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
222
+ */
223
+ protected function _save( $image, $filename = null, $mime_type = null ) {
224
+ global $ewww_defer;
225
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
226
+ if ( ! $filename ) {
227
+ $filename = $this->generate_filename( null, null, $extension );
228
+ }
229
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
230
+ ewwwio_debug_message( "detected existing file: $filename" );
231
+ $current_size = getimagesize( $filename );
232
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
233
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
234
+ return array(
235
+ 'path' => $filename,
236
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
237
+ 'width' => $this->size['width'],
238
+ 'height' => $this->size['height'],
239
+ 'mime-type' => $mime_type,
240
+ );
241
+ }
242
+ }
243
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
244
+ ewww_image_optimizer_cloud_init();
245
+ }
246
+ $saved = parent::_save( $image, $filename, $mime_type );
247
+ if ( ! is_wp_error( $saved ) ) {
248
+ if ( ! $filename ) {
249
+ $filename = $saved['path'];
250
+ }
251
+ if ( file_exists( $filename ) ) {
252
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
253
+ ewww_image_optimizer( $filename );
254
+ ewwwio_debug_message( "image editor (BFI GD) saved: $filename" );
255
+ $image_size = ewww_image_optimizer_filesize( $filename );
256
+ ewwwio_debug_message( "image editor size: $image_size" );
257
+
258
+ /*
259
+ } else {
260
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
261
+ global $ewwwio_image_background;
262
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
263
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
264
+ }
265
+ if ( ! is_object( $ewwwio_image_background ) ) {
266
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
267
+ }
268
+ $ewwwio_image_background->push_to_queue( $filename );
269
+ $ewwwio_image_background->save()->dispatch();
270
+ ewwwio_debug_message( "image editor (BFI GD) queued: $filename" );
271
+ }
272
+ */
273
+ }
274
+ ewww_image_optimizer_debug_log();
275
+ }
276
+ ewwwio_memory( __FUNCTION__ );
277
+ return $saved;
278
+ }
279
+ }
280
+ } else {
281
+ /**
282
+ * Extension of the WP_Image_Editor_GD class to auto-compress edited images.
283
+ *
284
+ * @see WP_Image_Editor_GD
285
+ */
286
+ class EWWWIO_GD_Editor extends WP_Image_Editor_GD {
287
+ /**
288
+ * Saves a file from the image editor.
289
+ *
290
+ * @param resource $image A GD image object.
291
+ * @param string $filename Optional. The name of the file to be saved to.
292
+ * @param string $mime_type Optional. The mimetype of the file.
293
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
294
+ */
295
+ protected function _save( $image, $filename = null, $mime_type = null ) {
296
+ global $ewww_defer;
297
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
298
+ if ( ! $filename ) {
299
+ $filename = $this->generate_filename( null, null, $extension );
300
+ }
301
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
302
+ ewwwio_debug_message( "detected existing file: $filename" );
303
+ $current_size = getimagesize( $filename );
304
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
305
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
306
+ return array(
307
+ 'path' => $filename,
308
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
309
+ 'width' => $this->size['width'],
310
+ 'height' => $this->size['height'],
311
+ 'mime-type' => $mime_type,
312
+ );
313
+ }
314
+ }
315
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
316
+ ewww_image_optimizer_cloud_init();
317
+ }
318
+ $saved = parent::_save( $image, $filename, $mime_type );
319
+ if ( ! is_wp_error( $saved ) ) {
320
+ if ( ! $filename ) {
321
+ $filename = $saved['path'];
322
+ }
323
+ if ( file_exists( $filename ) ) {
324
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
325
+ ewww_image_optimizer( $filename );
326
+ ewwwio_debug_message( "image editor (gd) saved: $filename" );
327
+ $image_size = ewww_image_optimizer_filesize( $filename );
328
+ ewwwio_debug_message( "image editor size: $image_size" );
329
+
330
+ /*
331
+ } else {
332
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
333
+ global $ewwwio_image_background;
334
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
335
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
336
+ }
337
+ if ( ! is_object( $ewwwio_image_background ) ) {
338
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
339
+ }
340
+ $ewwwio_image_background->push_to_queue( $filename );
341
+ $ewwwio_image_background->save()->dispatch();
342
+ ewwwio_debug_message( "image editor (gd) queued: $filename" );
343
+ }
344
+ */
345
+ }
346
+ ewww_image_optimizer_debug_log();
347
+ }
348
+ ewwwio_memory( __FUNCTION__ );
349
+ return $saved;
350
+ }
351
+ }
352
+ } // End if().
classes/class-ewwwio-gmagick-editor.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class and methods to integrate with the WP_Image_Editor_Gmagick class and other extensions.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EWWW_Image_Optimizer
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ if ( class_exists( 'WP_Image_Editor_Gmagick' ) ) {
13
+ /**
14
+ * Extension of the WP_Image_Editor_Gmagick class to auto-compress edited images.
15
+ *
16
+ * @see WP_Image_Editor_Gmagick
17
+ */
18
+ class EWWWIO_Gmagick_Editor extends WP_Image_Editor_Gmagick {
19
+ /**
20
+ * Saves a file from the image editor.
21
+ *
22
+ * @param resource $image A Gmagick image object.
23
+ * @param string $filename Optional. The name of the file to be saved to.
24
+ * @param string $mime_type Optional. The mimetype of the file.
25
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
26
+ */
27
+ protected function _save( $image, $filename = null, $mime_type = null ) {
28
+ global $ewww_defer;
29
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
30
+ if ( ! $filename ) {
31
+ $filename = $this->generate_filename( null, null, $extension );
32
+ }
33
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
34
+ ewwwio_debug_message( "detected existing file: $filename" );
35
+ $current_size = getimagesize( $filename );
36
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
37
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
38
+ return array(
39
+ 'path' => $filename,
40
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
41
+ 'width' => $this->size['width'],
42
+ 'height' => $this->size['height'],
43
+ 'mime-type' => $mime_type,
44
+ );
45
+ }
46
+ }
47
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
48
+ ewww_image_optimizer_cloud_init();
49
+ }
50
+ $saved = parent::_save( $image, $filename, $mime_type );
51
+ if ( ! is_wp_error( $saved ) ) {
52
+ if ( ! $filename ) {
53
+ $filename = $saved['path'];
54
+ }
55
+ if ( file_exists( $filename ) ) {
56
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
57
+ ewww_image_optimizer( $filename );
58
+ ewwwio_debug_message( "image editor (gmagick) saved: $filename" );
59
+ $image_size = ewww_image_optimizer_filesize( $filename );
60
+ ewwwio_debug_message( "image editor size: $image_size" );
61
+
62
+ /*
63
+ } else {
64
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
65
+ global $ewwwio_image_background;
66
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
67
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
68
+ }
69
+ if ( ! is_object( $ewwwio_image_background ) ) {
70
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
71
+ }
72
+ $ewwwio_image_background->push_to_queue( $filename );
73
+ $ewwwio_image_background->save()->dispatch();
74
+ ewwwio_debug_message( "image editor (gmagick) queued: $filename" );
75
+ }
76
+ */
77
+ }
78
+ ewww_image_optimizer_debug_log();
79
+ }
80
+ ewwwio_memory( __FUNCTION__ );
81
+ return $saved;
82
+ }
83
+ }
84
+ } // End if().
classes/class-ewwwio-hs-beacon.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Help integration functions for embedding the HS Beacon for users that have opted in.
4
+ *
5
+ * @package EWWW_Image_Optimizer
6
+ * @since 3.5.1
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Embed HS Beacon to give users more help when they need it.
15
+ *
16
+ * @since 3.5.1
17
+ */
18
+ class EWWWIO_HS_Beacon {
19
+
20
+ /**
21
+ * Get things going
22
+ */
23
+ public function __construct() {
24
+ add_action( 'admin_action_ewww_opt_into_hs_beacon', array( $this, 'check_for_optin' ) );
25
+ add_action( 'admin_action_ewww_opt_out_of_hs_beacon', array( $this, 'check_for_optout' ) );
26
+ }
27
+
28
+ /**
29
+ * Check for a new opt-in on settings save
30
+ *
31
+ * @param bool $input The enable_help setting.
32
+ * @return bool The unaltered setting.
33
+ */
34
+ public function check_for_settings_optin( $input ) {
35
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
36
+ if ( isset( $_POST['ewww_image_optimizer_enable_help'] ) && $_POST['ewww_image_optimizer_enable_help'] ) {
37
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_tracking_notice', 1 );
38
+ }
39
+ return $input;
40
+ }
41
+
42
+ /**
43
+ * Check for a new opt-in via the admin notice
44
+ */
45
+ public function check_for_optin() {
46
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
47
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_enable_help', 1 );
48
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_enable_help_notice', 1 );
49
+ wp_redirect( remove_query_arg( 'action', wp_get_referer() ) );
50
+ exit;
51
+ }
52
+
53
+ /**
54
+ * Check for a new opt-out via the admin notice
55
+ */
56
+ public function check_for_optout() {
57
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
58
+ delete_option( 'ewww_image_optimizer_enable_help' );
59
+ delete_network_option( null, 'ewww_image_optimizer_enable_help' );
60
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_enable_help_notice', 1 );
61
+ wp_redirect( remove_query_arg( 'action', wp_get_referer() ) );
62
+ exit;
63
+ }
64
+
65
+ /**
66
+ * Display the admin notice to users that have not opted-in or out
67
+ *
68
+ * @access public
69
+ * @param string $network_class A string that indicates where this is being displayed.
70
+ * @return void
71
+ */
72
+ public function admin_notice( $network_class = '' ) {
73
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
74
+ $hide_notice = ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help_notice' );
75
+ if ( 'network-multisite' == $network_class && get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) {
76
+ return;
77
+ }
78
+ if ( 'network-singlesite' == $network_class && ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) {
79
+ return;
80
+ }
81
+
82
+ if ( $hide_notice ) {
83
+ return;
84
+ }
85
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help' ) ) {
86
+ return;
87
+ }
88
+
89
+ if ( ! current_user_can( 'manage_options' ) ) {
90
+ return;
91
+ }
92
+
93
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
94
+ // Need to include the plugin library for the is_plugin_active function.
95
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
96
+ }
97
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) && ! current_user_can( 'manage_network_options' ) ) {
98
+ return;
99
+ }
100
+ $optin_url = 'admin.php?action=ewww_opt_into_hs_beacon';
101
+ $optout_url = 'admin.php?action=ewww_opt_out_of_hs_beacon';
102
+ echo '<div class="updated"><p>';
103
+ esc_html_e( 'Enable the support beacon, which gives you access to documentation and our support team right from your WordPress dashboard. To assist you more efficiently, we may collect the current url, IP address, browser/device information, and debugging information.', 'ewww-image-optimizer' );
104
+ echo '&nbsp;<a href="' . esc_url( $optin_url ) . '" class="button-secondary">' . esc_html__( 'Allow', 'ewww-image-optimizer' ) . '</a>';
105
+ echo '&nbsp;<a href="' . esc_url( $optout_url ) . '" class="button-secondary">' . esc_html__( 'Do not allow', 'ewww-image-optimizer' ) . '</a>';
106
+ echo '</p></div>';
107
+ }
108
+
109
+ }
110
+ $ewwwio_hs_beacon = new EWWWIO_HS_Beacon;
classes/class-ewwwio-imagick-editor.php ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class and methods to integrate with the WP_Image_Editor_Imagick class and other extensions.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EWWW_Image_Optimizer
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ if ( class_exists( 'WP_Thumb_Image_Editor_Imagick' ) ) {
13
+ /**
14
+ * Extension of the WP_Image_Editor_Imagick class to auto-compress edited images.
15
+ *
16
+ * @see WP_Image_Editor_Imagick
17
+ */
18
+ class EWWWIO_Imagick_Editor extends WP_Thumb_Image_Editor_Imagick {
19
+ /**
20
+ * Saves a file from the image editor.
21
+ *
22
+ * @param resource $image An Imagick image object.
23
+ * @param string $filename Optional. The name of the file to be saved to.
24
+ * @param string $mime_type Optional. The mimetype of the file.
25
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
26
+ */
27
+ protected function _save( $image, $filename = null, $mime_type = null ) {
28
+ global $ewww_defer;
29
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
30
+ if ( ! $filename ) {
31
+ $filename = $this->generate_filename( null, null, $extension );
32
+ }
33
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
34
+ ewwwio_debug_message( "detected existing file: $filename" );
35
+ $current_size = getimagesize( $filename );
36
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
37
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
38
+ return array(
39
+ 'path' => $filename,
40
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
41
+ 'width' => $this->size['width'],
42
+ 'height' => $this->size['height'],
43
+ 'mime-type' => $mime_type,
44
+ );
45
+ }
46
+ }
47
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
48
+ ewww_image_optimizer_cloud_init();
49
+ }
50
+ $saved = parent::_save( $image, $filename, $mime_type );
51
+ if ( ! is_wp_error( $saved ) ) {
52
+ if ( ! $filename ) {
53
+ $filename = $saved['path'];
54
+ }
55
+ if ( file_exists( $filename ) ) {
56
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
57
+ ewww_image_optimizer( $filename );
58
+ ewwwio_debug_message( "image editor (wpthumb imagick) saved: $filename" );
59
+ $image_size = ewww_image_optimizer_filesize( $filename );
60
+ ewwwio_debug_message( "image editor size: $image_size" );
61
+
62
+ /*
63
+ } else {
64
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
65
+ global $ewwwio_image_background;
66
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
67
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
68
+ }
69
+ if ( ! is_object( $ewwwio_image_background ) ) {
70
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
71
+ }
72
+ $ewwwio_image_background->push_to_queue( $filename );
73
+ $ewwwio_image_background->save()->dispatch();
74
+ ewwwio_debug_message( "image editor (wpthumb imagick) queued: $filename" );
75
+ }
76
+ */
77
+ }
78
+ ewww_image_optimizer_debug_log();
79
+ }
80
+ ewwwio_memory( __FUNCTION__ );
81
+ return $saved;
82
+ }
83
+ }
84
+ } elseif ( class_exists( 'BFI_Image_Editor_Imagick' ) ) {
85
+ /**
86
+ * Extension of the WP_Image_Editor_Imagick class to auto-compress edited images.
87
+ *
88
+ * @see WP_Image_Editor_Imagick
89
+ */
90
+ class EWWWIO_Imagick_Editor extends BFI_Image_Editor_Imagick {
91
+ /**
92
+ * Saves a file from the image editor.
93
+ *
94
+ * @param resource $image An Imagick image object.
95
+ * @param string $filename Optional. The name of the file to be saved to.
96
+ * @param string $mime_type Optional. The mimetype of the file.
97
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
98
+ */
99
+ protected function _save( $image, $filename = null, $mime_type = null ) {
100
+ global $ewww_defer;
101
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
102
+ if ( ! $filename ) {
103
+ $filename = $this->generate_filename( null, null, $extension );
104
+ }
105
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
106
+ ewwwio_debug_message( "detected existing file: $filename" );
107
+ $current_size = getimagesize( $filename );
108
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
109
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
110
+ return array(
111
+ 'path' => $filename,
112
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
113
+ 'width' => $this->size['width'],
114
+ 'height' => $this->size['height'],
115
+ 'mime-type' => $mime_type,
116
+ );
117
+ }
118
+ }
119
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
120
+ ewww_image_optimizer_cloud_init();
121
+ }
122
+ $saved = parent::_save( $image, $filename, $mime_type );
123
+ if ( ! is_wp_error( $saved ) ) {
124
+ if ( ! $filename ) {
125
+ $filename = $saved['path'];
126
+ }
127
+ if ( file_exists( $filename ) ) {
128
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
129
+ ewww_image_optimizer( $filename );
130
+ ewwwio_debug_message( "image editor (BFI imagick) saved: $filename" );
131
+ $image_size = ewww_image_optimizer_filesize( $filename );
132
+ ewwwio_debug_message( "image editor size: $image_size" );
133
+
134
+ /*
135
+ } else {
136
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
137
+ global $ewwwio_image_background;
138
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
139
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
140
+ }
141
+ if ( ! is_object( $ewwwio_image_background ) ) {
142
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
143
+ }
144
+ $ewwwio_image_background->push_to_queue( $filename );
145
+ $ewwwio_image_background->save()->dispatch();
146
+ ewwwio_debug_message( "image editor (BFI imagick) queued: $filename" );
147
+ }
148
+ */
149
+ }
150
+ ewww_image_optimizer_debug_log();
151
+ }
152
+ ewwwio_memory( __FUNCTION__ );
153
+ return $saved;
154
+ }
155
+ }
156
+ } elseif ( class_exists( 'WP_Image_Editor_Respimg' ) ) {
157
+ /**
158
+ * Extension of the WP_Image_Editor_Respimg class to auto-compress edited images.
159
+ *
160
+ * @see WP_Image_Editor_Respimg
161
+ */
162
+ class EWWWIO_Imagick_Editor extends WP_Image_Editor_Respimg {
163
+ /**
164
+ * Saves a file from the image editor.
165
+ *
166
+ * @param resource $image An Imagick image object.
167
+ * @param string $filename Optional. The name of the file to be saved to.
168
+ * @param string $mime_type Optional. The mimetype of the file.
169
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
170
+ */
171
+ protected function _save( $image, $filename = null, $mime_type = null ) {
172
+ global $ewww_defer;
173
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
174
+ if ( ! $filename ) {
175
+ $filename = $this->generate_filename( null, null, $extension );
176
+ }
177
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
178
+ ewwwio_debug_message( "detected existing file: $filename" );
179
+ $current_size = getimagesize( $filename );
180
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
181
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
182
+ return array(
183
+ 'path' => $filename,
184
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
185
+ 'width' => $this->size['width'],
186
+ 'height' => $this->size['height'],
187
+ 'mime-type' => $mime_type,
188
+ );
189
+ }
190
+ }
191
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
192
+ ewww_image_optimizer_cloud_init();
193
+ }
194
+ $saved = parent::_save( $image, $filename, $mime_type );
195
+ if ( ! is_wp_error( $saved ) ) {
196
+ if ( ! $filename ) {
197
+ $filename = $saved['path'];
198
+ }
199
+ if ( file_exists( $filename ) ) {
200
+ ewww_image_optimizer( $filename );
201
+ ewwwio_debug_message( "image editor (resp imagick) saved: $filename" );
202
+ $image_size = ewww_image_optimizer_filesize( $filename );
203
+ ewwwio_debug_message( "image editor size: $image_size" );
204
+ }
205
+ ewww_image_optimizer_debug_log();
206
+ }
207
+ ewwwio_memory( __FUNCTION__ );
208
+ return $saved;
209
+ }
210
+ }
211
+ } elseif ( class_exists( 'S3_Uploads_Image_Editor_Imagick' ) ) {
212
+ /**
213
+ * Extension of the WP_Image_Editor_Imagick class to auto-compress edited images.
214
+ *
215
+ * @see WP_Image_Editor_Imagick
216
+ */
217
+ class EWWWIO_Imagick_Editor extends WP_Image_Editor_Imagick {
218
+ /**
219
+ * Saves a file from the image editor and sends it to S3 after optimization.
220
+ *
221
+ * @param resource $image An Imagick image object.
222
+ * @param string $filename Optional. The name of the file to be saved to.
223
+ * @param string $mime_type Optional. The mimetype of the file.
224
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
225
+ */
226
+ protected function _save( $image, $filename = null, $mime_type = null ) {
227
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
228
+ if ( ! $filename ) {
229
+ $filename = $this->generate_filename( null, null, $extension );
230
+ }
231
+ global $s3_uploads_image;
232
+ $s3_uploads_image = $filename;
233
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
234
+ ewww_image_optimizer_cloud_init();
235
+ }
236
+ $upload_dir = wp_upload_dir();
237
+
238
+ if ( strpos( $filename, $upload_dir['basedir'] ) === 0 ) {
239
+ $temp_filename = tempnam( get_temp_dir(), 's3-uploads' );
240
+ }
241
+
242
+ $saved = parent::_save( $image, $temp_filename, $mime_type );
243
+
244
+ if ( is_wp_error( $saved ) ) {
245
+ unlink( $temp_filename );
246
+ unset( $s3_uploads_image );
247
+ return $saved;
248
+ }
249
+ if ( file_exists( $saved['path'] ) ) {
250
+ $temp_filename = $saved['path'];
251
+ ewww_image_optimizer( $temp_filename );
252
+ ewwwio_debug_message( "image editor (s3 uploads) saved: $temp_filename" );
253
+ $image_size = ewww_image_optimizer_filesize( $temp_filename );
254
+ ewwwio_debug_message( "image editor size: $image_size" );
255
+ }
256
+ $copy_result = copy( $temp_filename, $filename );
257
+ if ( file_exists( $saved['path'] ) ) {
258
+ unlink( $saved['path'] );
259
+ }
260
+ if ( file_exists( $temp_filename ) ) {
261
+ unlink( $temp_filename );
262
+ }
263
+ if ( ! $copy_result ) {
264
+ unset( $s3_uploads_image );
265
+ return new WP_Error( 'unable-to-copy-to-s3', 'Unable to copy the temp image to S3' );
266
+ }
267
+ $saved['path'] = $filename;
268
+ $saved['file'] = wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) );
269
+ unset( $s3_uploads_image );
270
+ ewwwio_memory( __FUNCTION__ );
271
+ return $saved;
272
+ }
273
+ }
274
+ } else {
275
+ /**
276
+ * Extension of the WP_Image_Editor_Imagick class to auto-compress edited images.
277
+ *
278
+ * @see WP_Image_Editor_Imagick
279
+ */
280
+ class EWWWIO_Imagick_Editor extends WP_Image_Editor_Imagick {
281
+ /**
282
+ * Saves a file from the image editor.
283
+ *
284
+ * @param resource $image An Imagick image object.
285
+ * @param string $filename Optional. The name of the file to be saved to.
286
+ * @param string $mime_type Optional. The mimetype of the file.
287
+ * @return WP_Error| array The full path, base filename, width, height, and mimetype.
288
+ */
289
+ protected function _save( $image, $filename = null, $mime_type = null ) {
290
+ global $ewww_defer;
291
+ list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
292
+ if ( ! $filename ) {
293
+ $filename = $this->generate_filename( null, null, $extension );
294
+ }
295
+ if ( ( ! defined( 'EWWWIO_EDITOR_OVERWRITE' ) || ! EWWWIO_EDITOR_OVERWRITE ) && is_file( $filename ) ) {
296
+ ewwwio_debug_message( "detected existing file: $filename" );
297
+ $current_size = getimagesize( $filename );
298
+ if ( $current_size && $this->size['width'] == $current_size[0] && $this->size['height'] == $current_size[1] ) {
299
+ ewwwio_debug_message( "existing file has same dimensions, not saving $filename" );
300
+ return array(
301
+ 'path' => $filename,
302
+ 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
303
+ 'width' => $this->size['width'],
304
+ 'height' => $this->size['height'],
305
+ 'mime-type' => $mime_type,
306
+ );
307
+ }
308
+ }
309
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
310
+ ewww_image_optimizer_cloud_init();
311
+ }
312
+ $saved = parent::_save( $image, $filename, $mime_type );
313
+ if ( ! is_wp_error( $saved ) ) {
314
+ if ( ! $filename ) {
315
+ $filename = $saved['path'];
316
+ }
317
+ if ( file_exists( $filename ) ) {
318
+ /* if ( ! ewww_image_optimizer_test_background_opt() ) { */
319
+ ewww_image_optimizer( $filename );
320
+ ewwwio_debug_message( "image editor (imagick) saved: $filename" );
321
+ $image_size = ewww_image_optimizer_filesize( $filename );
322
+ ewwwio_debug_message( "image editor size: $image_size" );
323
+
324
+ /*
325
+ } else {
326
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
327
+ global $ewwwio_image_background;
328
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
329
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
330
+ }
331
+ if ( ! is_object( $ewwwio_image_background ) ) {
332
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
333
+ }
334
+ $ewwwio_image_background->push_to_queue( $filename );
335
+ $ewwwio_image_background->save()->dispatch();
336
+ ewwwio_debug_message( "image editor (imagick) queued: $filename" );
337
+ }
338
+ */
339
+ }
340
+ ewww_image_optimizer_debug_log();
341
+ }
342
+ ewwwio_memory( __FUNCTION__ );
343
+ return $saved;
344
+ }
345
+ }
346
+ } // End if().
classes/class-ewwwio-media-background-process.php ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Classes for Background and Async processing.
4
+ *
5
+ * This file contains classes and methods that extend WP_Background_Process and
6
+ * WP_Async_Request to allow parallel and background processing of images.
7
+ *
8
+ * @link https://ewww.io
9
+ * @package EWWW_Image_Optimizer
10
+ */
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * The parent WP_Async_Request class file.
18
+ */
19
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'vendor/wp-async-request.php' );
20
+
21
+ /**
22
+ * The parent WP_Background_Process class file.
23
+ */
24
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'vendor/wp-background-process.php' );
25
+
26
+ /**
27
+ * Processes media uploads in background/async mode.
28
+ *
29
+ * Uses a dual-queue system to track uploads to be optimized, handling them one at a time.
30
+ *
31
+ * @see WP_Background_Process
32
+ */
33
+ class EWWWIO_Media_Background_Process extends WP_Background_Process {
34
+
35
+ /**
36
+ * The action name used to trigger this class extension.
37
+ *
38
+ * @access protected
39
+ * @var string $action
40
+ */
41
+ protected $action = 'ewwwio_media_optimize';
42
+
43
+ /**
44
+ * Runs task for an item from the Media Library queue.
45
+ *
46
+ * Makes sure an image upload has finished processing and has been stored in the database.
47
+ * Then runs the usual media optimization routine on the specified item.
48
+ *
49
+ * @access protected
50
+ * @global bool $ewww_defer True to defer optimization, false otherwise.
51
+ *
52
+ * @param array $item The id of the attachment, how many attempts have been made to process
53
+ * the item, the type of attachment, and whether it is a new upload.
54
+ * @return bool|array If the item is not complete, return it. False indicates completion.
55
+ */
56
+ protected function task( $item ) {
57
+ session_write_close();
58
+ global $ewww_defer;
59
+ $ewww_defer = false;
60
+ $max_attempts = 15;
61
+ $id = $item['id'];
62
+ if ( empty( $item['attempts'] ) ) {
63
+ $item['attempts'] = 0;
64
+ sleep( 4 ); // On the first attempt, hold off and wait for the db to catch up.
65
+ }
66
+ ewwwio_debug_message( "background processing $id, type: " . $item['type'] );
67
+ $type = $item['type'];
68
+ $image_types = array(
69
+ 'image/jpeg',
70
+ 'image/png',
71
+ 'image/gif',
72
+ );
73
+ $meta = wp_get_attachment_metadata( $id, true );
74
+ if ( in_array( $type, $image_types ) && empty( $meta ) && $item['attempts'] < $max_attempts ) {
75
+ $item['attempts']++;
76
+ sleep( 4 );
77
+ ewwwio_debug_message( "metadata is missing, requeueing {$item['attempts']}" );
78
+ ewww_image_optimizer_debug_log();
79
+ return $item;
80
+ } elseif ( in_array( $type, $image_types ) && empty( $meta ) ) {
81
+ ewwwio_debug_message( 'metadata is missing for image, out of attempts' );
82
+ ewww_image_optimizer_debug_log();
83
+ delete_transient( 'ewwwio-background-in-progress-' . $id );
84
+ return false;
85
+ }
86
+ $meta = ewww_image_optimizer_resize_from_meta_data( $meta, $id, true, $item['new'] );
87
+ if ( ! empty( $meta['processing'] ) ) {
88
+ $item['attempts']++;
89
+ ewwwio_debug_message( 'image not finished, try again' );
90
+ ewww_image_optimizer_debug_log();
91
+ return $item;
92
+ }
93
+ wp_update_attachment_metadata( $id, $meta );
94
+ ewww_image_optimizer_debug_log();
95
+ delete_transient( 'ewwwio-background-in-progress-' . $id );
96
+ return false;
97
+ }
98
+
99
+ /**
100
+ * Run when queue processing is complete.
101
+ *
102
+ * Flushes the debug information to the log and then runs the parent method.
103
+ *
104
+ * @access protected
105
+ */
106
+ protected function complete() {
107
+ ewww_image_optimizer_debug_log();
108
+ parent::complete();
109
+ }
110
+ }
111
+
112
+ global $ewwwio_media_background;
113
+ $ewwwio_media_background = new EWWWIO_Media_Background_Process();
114
+
115
+ /**
116
+ * Processes a single image in background/async mode.
117
+ *
118
+ * Uses a dual-queue system to track auto-generated images to be optimized, handling them one at a
119
+ * time. This is only used for Nextcellent thumbs currently.
120
+ *
121
+ * @deprecated 3.1.3
122
+ * @see WP_Background_Process
123
+ */
124
+ class EWWWIO_Image_Background_Process extends WP_Background_Process {
125
+
126
+ /**
127
+ * The action name used to trigger this class extension.
128
+ *
129
+ * @access protected
130
+ * @var string $action
131
+ */
132
+ protected $action = 'ewwwio_image_optimize';
133
+
134
+ /**
135
+ * Runs optimization for a file from the image queue.
136
+ *
137
+ * @access protected
138
+ *
139
+ * @param string $item The filename of the attachment.
140
+ * @return bool False indicates completion.
141
+ */
142
+ protected function task( $item ) {
143
+ session_write_close();
144
+ sleep( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) );
145
+ ewwwio_debug_message( "background processing $item" );
146
+ ewww_image_optimizer( $item );
147
+ ewww_image_optimizer_debug_log();
148
+ return false;
149
+ }
150
+
151
+ /**
152
+ * Run when queue processing is complete.
153
+ *
154
+ * Flushes the debug information to the log and then runs the parent method.
155
+ *
156
+ * @access protected
157
+ */
158
+ protected function complete() {
159
+ ewww_image_optimizer_debug_log();
160
+ parent::complete();
161
+ }
162
+ }
163
+
164
+ global $ewwwio_image_background;
165
+ $ewwwio_image_background = new EWWWIO_Image_Background_Process();
166
+
167
+ /**
168
+ * Processes FlaGallery uploads in background/async mode.
169
+ *
170
+ * Uses a dual-queue system to track uploads to be optimized, handling them one at a time.
171
+ *
172
+ * @see WP_Background_Process
173
+ */
174
+ class EWWWIO_Flag_Background_Process extends WP_Background_Process {
175
+
176
+ /**
177
+ * The action name used to trigger this class extension.
178
+ *
179
+ * @access protected
180
+ * @var string $action
181
+ */
182
+ protected $action = 'ewwwio_flag_optimize';
183
+
184
+ /**
185
+ * Runs task for an item from the FlaGallery queue.
186
+ *
187
+ * Makes sure an image upload has finished processing and has been stored in the database.
188
+ * Then runs the usual flag optimization routine on the specified item.
189
+ *
190
+ * @access protected
191
+ * @global bool $ewwwflag
192
+ *
193
+ * @param array $item The id of the upload, and how many attempts have been made so far.
194
+ * @return bool|array If the item is not complete, return it. False indicates completion.
195
+ */
196
+ protected function task( $item ) {
197
+ session_write_close();
198
+ $max_attempts = 15;
199
+ if ( empty( $item['attempts'] ) ) {
200
+ $item['attempts'] = 0;
201
+ }
202
+ $id = $item['id'];
203
+ ewwwio_debug_message( "background processing flagallery: $id" );
204
+ if ( ! class_exists( 'flagMeta' ) ) {
205
+ require_once( FLAG_ABSPATH . 'lib/meta.php' );
206
+ }
207
+ // Retrieve the metadata for the image.
208
+ $image = new flagMeta( $id );
209
+ if ( empty( $image ) && $item['attempts'] < $max_attempts ) {
210
+ $item['attempts']++;
211
+ sleep( 4 );
212
+ ewwwio_debug_message( "could not retrieve meta, requeueing {$item['attempts']}" );
213
+ ewww_image_optimizer_debug_log();
214
+ return $item;
215
+ } elseif ( empty( $image ) ) {
216
+ ewwwio_debug_message( 'could not retrieve meta, out of attempts' );
217
+ ewww_image_optimizer_debug_log();
218
+ delete_transient( 'ewwwio-background-in-progress-flag-' . $id );
219
+ return false;
220
+ }
221
+ global $ewwwflag;
222
+ $ewwwflag->ewww_added_new_image( $id, $image );
223
+ delete_transient( 'ewwwio-background-in-progress-flag-' . $id );
224
+ sleep( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) );
225
+ ewww_image_optimizer_debug_log();
226
+ return false;
227
+ }
228
+
229
+ /**
230
+ * Run when queue processing is complete.
231
+ *
232
+ * Flushes the debug information to the log and then runs the parent method.
233
+ *
234
+ * @access protected
235
+ */
236
+ protected function complete() {
237
+ ewww_image_optimizer_debug_log();
238
+ parent::complete();
239
+ }
240
+ }
241
+
242
+ global $ewwwio_flag_background;
243
+ $ewwwio_flag_background = new EWWWIO_Flag_Background_Process();
244
+
245
+ /**
246
+ * Processes Nextcellent uploads in background/async mode.
247
+ *
248
+ * Uses a dual-queue system to track uploads to be optimized, handling them one at a time.
249
+ *
250
+ * @see WP_Background_Process
251
+ */
252
+ class EWWWIO_Ngg_Background_Process extends WP_Background_Process {
253
+
254
+ /**
255
+ * The action name used to trigger this class extension.
256
+ *
257
+ * @access protected
258
+ * @var string $action
259
+ */
260
+ protected $action = 'ewwwio_ngg_optimize';
261
+
262
+ /**
263
+ * Runs task for an item from the Nextcellent queue.
264
+ *
265
+ * Makes sure an image upload has finished processing and has been stored in the database.
266
+ * Then runs the usual nextcellent optimization routine on the specified item.
267
+ *
268
+ * @access protected
269
+ * @global bool $ewwwngg
270
+ *
271
+ * @param array $item The id of the upload, and how many attempts have been made so far.
272
+ * @return bool|array If the item is not complete, return it. False indicates completion.
273
+ */
274
+ protected function task( $item ) {
275
+ session_write_close();
276
+ $max_attempts = 15;
277
+ if ( empty( $item['attempts'] ) ) {
278
+ $item['attempts'] = 0;
279
+ }
280
+ $id = $item['id'];
281
+ ewwwio_debug_message( "background processing nextcellent: $id" );
282
+ if ( ! class_exists( 'nggMeta' ) ) {
283
+ require_once( NGGALLERY_ABSPATH . '/lib/meta.php' );
284
+ }
285
+ // Retrieve the metadata for the image.
286
+ $image = new nggMeta( $id );
287
+ if ( empty( $image ) && $item['attempts'] < $max_attempts ) {
288
+ $item['attempts']++;
289
+ sleep( 4 );
290
+ ewwwio_debug_message( "could not retrieve meta, requeueing {$item['attempts']}" );
291
+ ewww_image_optimizer_debug_log();
292
+ return $item;
293
+ } elseif ( empty( $image ) ) {
294
+ ewwwio_debug_message( 'could not retrieve meta, out of attempts' );
295
+ ewww_image_optimizer_debug_log();
296
+ delete_transient( 'ewwwio-background-in-progress-ngg-' . $id );
297
+ return false;
298
+ }
299
+ global $ewwwngg;
300
+ $ewwwngg->ewww_added_new_image( $id, $image );
301
+ delete_transient( 'ewwwio-background-in-progress-ngg-' . $id );
302
+ sleep( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) );
303
+ ewww_image_optimizer_debug_log();
304
+ return false;
305
+ }
306
+
307
+ /**
308
+ * Run when queue processing is complete.
309
+ *
310
+ * Flushes the debug information to the log and then runs the parent method.
311
+ *
312
+ * @access protected
313
+ */
314
+ protected function complete() {
315
+ ewww_image_optimizer_debug_log();
316
+ parent::complete();
317
+ }
318
+ }
319
+
320
+ global $ewwwio_ngg_background;
321
+ $ewwwio_ngg_background = new EWWWIO_Ngg_Background_Process();
322
+
323
+ /**
324
+ * Processes NextGEN 2 uploads in background/async mode.
325
+ *
326
+ * Uses a dual-queue system to track uploads to be optimized, handling them one at a time.
327
+ *
328
+ * @see WP_Background_Process
329
+ */
330
+ class EWWWIO_Ngg2_Background_Process extends WP_Background_Process {
331
+
332
+ /**
333
+ * The action name used to trigger this class extension.
334
+ *
335
+ * @access protected
336
+ * @var string $action
337
+ */
338
+ protected $action = 'ewwwio_ngg2_optimize';
339
+
340
+ /**
341
+ * Runs task for an item from the NextGEN queue.
342
+ *
343
+ * Makes sure an image upload has finished processing and has been stored in the database.
344
+ * Then runs the usual nextgen optimization routine on the specified item.
345
+ *
346
+ * @access protected
347
+ * @global bool $ewwwngg
348
+ *
349
+ * @param array $item The id of the upload, and how many attempts have been made so far.
350
+ * @return bool|array If the item is not complete, return it. False indicates completion.
351
+ */
352
+ protected function task( $item ) {
353
+ session_write_close();
354
+ $max_attempts = 15;
355
+ if ( empty( $item['attempts'] ) ) {
356
+ $item['attempts'] = 0;
357
+ }
358
+ $id = $item['id'];
359
+ ewwwio_debug_message( "background processing nextgen2: $id" );
360
+ // Creating the 'registry' object for working with nextgen.
361
+ $registry = C_Component_Registry::get_instance();
362
+ // Creating a database storage object from the 'registry' object.
363
+ $storage = $registry->get_utility( 'I_Gallery_Storage' );
364
+ // Get a NextGEN image object.
365
+ $image = $storage->object->_image_mapper->find( $id );
366
+ if ( ! is_object( $image ) && $item['attempts'] < $max_attempts ) {
367
+ $item['attempts']++;
368
+ sleep( 4 );
369
+ ewwwio_debug_message( "could not retrieve image, requeueing {$item['attempts']}" );
370
+ ewww_image_optimizer_debug_log();
371
+ return $item;
372
+ } elseif ( empty( $image ) ) {
373
+ ewwwio_debug_message( 'could not retrieve image, out of attempts' );
374
+ ewww_image_optimizer_debug_log();
375
+ delete_transient( 'ewwwio-background-in-progress-ngg-' . $id );
376
+ return false;
377
+ }
378
+ global $ewwwngg;
379
+ $ewwwngg->ewww_added_new_image( $image, $storage );
380
+ delete_transient( 'ewwwio-background-in-progress-ngg-' . $id );
381
+ sleep( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) );
382
+ ewww_image_optimizer_debug_log();
383
+ return false;
384
+ }
385
+
386
+ /**
387
+ * Run when queue processing is complete.
388
+ *
389
+ * Flushes the debug information to the log and then runs the parent method.
390
+ *
391
+ * @access protected
392
+ */
393
+ protected function complete() {
394
+ ewww_image_optimizer_debug_log();
395
+ parent::complete();
396
+ }
397
+ }
398
+
399
+ global $ewwwio_ngg2_background;
400
+ $ewwwio_ngg2_background = new EWWWIO_Ngg2_Background_Process();
401
+
402
+ /**
403
+ * Handles an async request used to optimize a Media Library image.
404
+ *
405
+ * Used to optimize a single image, like a resize, retina, or the original upload for a
406
+ * Media Library attachment. Done in parallel to increase processing capability.
407
+ *
408
+ * @see WP_Async_Request
409
+ */
410
+ class EWWWIO_Async_Request extends WP_Async_Request {
411
+
412
+ /**
413
+ * The action name used to trigger this class extension.
414
+ *
415
+ * @access protected
416
+ * @var string $action
417
+ */
418
+ protected $action = 'ewwwio_async_optimize_media';
419
+
420
+ /**
421
+ * Handles the async media image optimization request.
422
+ *
423
+ * Called via a POST to optimize an image from a Media Library attachment using parallel optimization.
424
+ *
425
+ * @global object $ewww_image Tracks attributes of the image currently being optimized.
426
+ */
427
+ protected function handle() {
428
+ session_write_close();
429
+ if ( empty( $_POST['ewwwio_size'] ) ) {
430
+ $size = '';
431
+ } else {
432
+ $size = $_POST['ewwwio_size'];
433
+ }
434
+ if ( empty( $_POST['ewwwio_id'] ) ) {
435
+ $id = 0;
436
+ } else {
437
+ $id = (int) $_POST['ewwwio_id'];
438
+ }
439
+ global $ewww_image;
440
+ if ( ! empty( $_POST['ewwwio_path'] ) && 'full' == $size ) {
441
+ $file_path = $this->find_file( $_POST['ewwwio_path'] );
442
+ if ( ! empty( $file_path ) ) {
443
+ ewwwio_debug_message( "processing async optimization request for {$_POST['ewwwio_path']}" );
444
+ $ewww_image = new EWWW_Image( $id, 'media', $file_path );
445
+ $ewww_image->resize = 'full';
446
+ list( $file, $msg, $conv, $original ) = ewww_image_optimizer( $file_path, 1, false, false, true );
447
+ } else {
448
+ ewwwio_debug_message( "could not process async optimization request for {$_POST['ewwwio_path']}" );
449
+ }
450
+ } elseif ( ! empty( $_POST['ewwwio_path'] ) ) {
451
+ $file_path = $this->find_file( $_POST['ewwwio_path'] );
452
+ if ( ! empty( $file_path ) ) {
453
+ ewwwio_debug_message( "processing async optimization request for {$_POST['ewwwio_path']}" );
454
+ $ewww_image = new EWWW_Image( $id, 'media', $file_path );
455
+ $ewww_image->resize = ( empty( $size ) ? null : $size );
456
+ list( $file, $msg, $conv, $original ) = ewww_image_optimizer( $file_path );
457
+ } else {
458
+ ewwwio_debug_message( "could not process async optimization request for {$_POST['ewwwio_path']}" );
459
+ }
460
+ } else {
461
+ ewwwio_debug_message( 'ignored async optimization request' );
462
+ return;
463
+ }
464
+ ewww_image_optimizer_hidpi_optimize( $file_path );
465
+ ewwwio_debug_message( 'checking for: ' . $file_path . '.processing' );
466
+ if ( is_file( $file_path . '.processing' ) ) {
467
+ ewwwio_debug_message( 'removing ' . $file_path . '.processing' );
468
+ unlink( $file_path . '.processing' );
469
+ }
470
+ ewww_image_optimizer_debug_log();
471
+ }
472
+
473
+ /**
474
+ * Finds the absolute path of a file.
475
+ *
476
+ * Given a relative path (to avoid tripping security filters), it uses several methods to try and determine the original, absolute path.
477
+ *
478
+ * @param string $file_path A partial/relative file path.
479
+ * @return string The full file path, reconstructed using the upload folder for WP_CONTENT_DIR
480
+ */
481
+ public function find_file( $file_path ) {
482
+ if ( is_file( $file_path ) ) {
483
+ return $file_path;
484
+ }
485
+ // Retrieve the location of the wordpress upload folder.
486
+ $upload_dir = wp_upload_dir();
487
+ $upload_path = trailingslashit( $upload_dir['basedir'] );
488
+ $file = $upload_path . $file_path;
489
+ if ( is_file( $file ) ) {
490
+ return $file;
491
+ }
492
+ $upload_path = trailingslashit( WP_CONTENT_DIR );
493
+ $file = $upload_path . $file_path;
494
+ if ( is_file( $file ) ) {
495
+ return $file;
496
+ }
497
+ $upload_path .= 'uploads/';
498
+ $file = $upload_path . $file_path;
499
+ if ( is_file( $file ) ) {
500
+ return $file;
501
+ }
502
+ return '';
503
+ }
504
+ }
505
+
506
+ global $ewwwio_async_optimize_media;
507
+ $ewwwio_async_optimize_media = new EWWWIO_Async_Request();
508
+
509
+ /**
510
+ * Handles an async request for validating API keys.
511
+ *
512
+ * Allows periodic verification of an API key without slowing down normal operations.
513
+ *
514
+ * @see WP_Async_Request
515
+ */
516
+ class EWWWIO_Async_Key_Verification extends WP_Async_Request {
517
+
518
+ /**
519
+ * The action name used to trigger this class extension.
520
+ *
521
+ * @access protected
522
+ * @var string $action
523
+ */
524
+ protected $action = 'ewwwio_async_key_verification';
525
+
526
+ /**
527
+ * Handles the async key verification request.
528
+ *
529
+ * Called via a POST request to verify an API key asynchronously.
530
+ */
531
+ protected function handle() {
532
+ session_write_close();
533
+ ewww_image_optimizer_cloud_verify( false );
534
+ ewww_image_optimizer_debug_log();
535
+ }
536
+ }
537
+
538
+ global $ewwwio_async_key_verification;
539
+ $ewwwio_async_key_verification = new EWWWIO_Async_Key_Verification();
540
+
541
+ /**
542
+ * Handles an async request used to test the viability of using async requests
543
+ * elsewhere.
544
+ *
545
+ * During a plugin update, an async request is sent with a specific string
546
+ * value to validate that nothing is blocking admin-ajax.php requests from
547
+ * the server to itself. Once verified, full background/parallel processing
548
+ * can be used.
549
+ *
550
+ * @see WP_Async_Request
551
+ */
552
+ class EWWWIO_Test_Async_Handler extends WP_Async_Request {
553
+
554
+ /**
555
+ * The action name used to trigger this class extension.
556
+ *
557
+ * @access protected
558
+ * @var string $action
559
+ */
560
+ protected $action = 'ewwwio_test_optimize';
561
+
562
+ /**
563
+ * Handles the test async request.
564
+ *
565
+ * Called via a POST request to verify that nothing is blocking or altering requests from the server to itself.
566
+ */
567
+ protected function handle() {
568
+ session_write_close();
569
+ if ( empty( $_POST['ewwwio_test_verify'] ) ) {
570
+ return;
571
+ }
572
+ $item = $_POST['ewwwio_test_verify'];
573
+ ewwwio_debug_message( "testing async handling, received $item" );
574
+ if ( ewww_image_optimizer_detect_wpsf_location_lock() ) {
575
+ ewwwio_debug_message( 'detected location lock, not enabling background opt' );
576
+ ewww_image_optimizer_debug_log();
577
+ return;
578
+ }
579
+ if ( '949c34123cf2a4e4ce2f985135830df4a1b2adc24905f53d2fd3f5df5b162932' != $item ) {
580
+ ewwwio_debug_message( 'wrong item received, not enabling background opt' );
581
+ ewww_image_optimizer_debug_log();
582
+ return;
583
+ }
584
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_background_optimization', true );
585
+ ewww_image_optimizer_debug_log();
586
+ }
587
+ }
588
+
589
+ global $ewwwio_test_async;
590
+ $ewwwio_test_async = new EWWWIO_Test_Async_Handler();
classes/class-ewwwio-tracking.php ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tracking functions for reporting plugin usage to EWWW IO for users that have opted in.
4
+ *
5
+ * @package EWWW_Image_Optimizer
6
+ * @copyright Copyright (c) 2017, Pippin Williamson and Shane Bishop
7
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
+ * @since 3.3.2
9
+ */
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Usage tracking so we can make informed decisions.
17
+ *
18
+ * @since 3.3.2
19
+ */
20
+ class EWWWIO_Tracking {
21
+
22
+ /**
23
+ * The data to send to the API
24
+ *
25
+ * @access private
26
+ * @var array $data
27
+ */
28
+ private $data;
29
+
30
+ /**
31
+ * Get things going
32
+ */
33
+ public function __construct() {
34
+ add_action( 'admin_init', array( $this, 'schedule_send' ) );
35
+ add_action( 'ewww_image_optimizer_site_report', array( $this, 'send_checkin' ) );
36
+ add_action( 'admin_action_ewww_opt_into_tracking', array( $this, 'check_for_optin' ) );
37
+ add_action( 'admin_action_ewww_opt_out_of_tracking', array( $this, 'check_for_optout' ) );
38
+ add_action( 'admin_notices', array( $this, 'admin_notice' ) );
39
+ add_action( 'network_admin_notices', array( $this, 'admin_notice' ) );
40
+ register_deactivation_hook( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, array( $this, 'unschedule_send' ) );
41
+ }
42
+
43
+ /**
44
+ * Check if the user has opted into tracking
45
+ *
46
+ * @access private
47
+ * @return bool
48
+ */
49
+ private function tracking_allowed() {
50
+ return (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_allow_tracking' );
51
+ }
52
+
53
+ /**
54
+ * Setup the data that is going to be tracked
55
+ *
56
+ * @access private
57
+ * @return void
58
+ */
59
+ private function setup_data() {
60
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
61
+ $data = array();
62
+
63
+ // Retrieve current theme info.
64
+ $theme_data = wp_get_theme();
65
+ $theme = $theme_data->Name . ' ' . $theme_data->Version;
66
+ $data['email'] = get_bloginfo( 'admin_email' ); // Not tracked, used to issue free credits.
67
+ $data['site_id'] = md5( home_url() );
68
+ if ( strlen( ewww_image_optimizer_get_option( 'ewww_image_optimizer_tracking_site_id' ) ) == 32 && ctype_alnum( ewww_image_optimizer_get_option( 'ewww_image_optimizer_tracking_site_id' ) ) ) {
69
+ ewwwio_debug_message( 'using pre-existing site_id' );
70
+ $data['site_id'] = ewww_image_optimizer_get_option( 'ewww_image_optimizer_tracking_site_id' );
71
+ } else {
72
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_tracking_site_id', $data['site_id'] );
73
+ }
74
+ $data['ewwwio_version'] = EWWW_IMAGE_OPTIMIZER_VERSION;
75
+ $data['wp_version'] = get_bloginfo( 'version' );
76
+ $data['php_version'] = PHP_VERSION_ID;
77
+ $data['libxml_version'] = defined( 'LIBXML_VERSION' ) ? LIBXML_VERSION : '' ;
78
+ $data['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
79
+
80
+ $data['multisite'] = is_multisite();
81
+ $data['theme'] = $theme;
82
+
83
+ // Retrieve current plugin information.
84
+ if ( ! function_exists( 'get_plugins' ) ) {
85
+ require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
86
+ }
87
+
88
+ $plugins = array_keys( get_plugins() );
89
+ $active_plugins = get_option( 'active_plugins', array() );
90
+
91
+ foreach ( $plugins as $key => $plugin ) {
92
+ if ( in_array( $plugin, $active_plugins ) ) {
93
+ // Remove active plugins from list so we can show active and inactive separately.
94
+ unset( $plugins[ $key ] );
95
+ }
96
+ }
97
+
98
+ $data['active_plugins'] = $active_plugins;
99
+ $data['inactive_plugins'] = $plugins;
100
+ $data['locale'] = ( $data['wp_version'] >= 4.7 ) ? get_user_locale() : get_locale();
101
+ if ( ! function_exists( 'ewww_image_optimizer_aux_images_table_count_pending' ) ) {
102
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'aux-optimize.php' );
103
+ }
104
+ $data['images_optimized'] = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ? 0 : ewww_image_optimizer_aux_images_table_count();
105
+ $data['bytes_saved'] = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ? 0 : ewww_image_optimizer_savings();
106
+
107
+ $data['nextgen'] = class_exists( 'EWWW_Nextgen' ) ? true : false;
108
+ $data['nextcellent'] = class_exists( 'EWWW_Nextcellent' ) ? true : false;
109
+ $data['flagallery'] = class_exists( 'EWWW_Flag' ) ? true : false;
110
+ $data['memory_limit'] = ewwwio_memory_limit();
111
+ $data['time_limit'] = (int) ini_get( 'max_execution_time' );
112
+ $data['operating_system'] = ewww_image_optimizer_function_exists( 'php_uname' ) ? php_uname( 's' ) : '';
113
+
114
+ $data['cloud_api'] = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ? true : false;
115
+ $data['keep_metadata'] = ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) ? false : true;
116
+ $data['jpg_level'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' );
117
+ $data['png_level'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' );
118
+ $data['gif_level'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' );
119
+ $data['pdf_level'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' );
120
+ $data['bulk_delay'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' );
121
+ $data['backups'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_backup_files' );
122
+
123
+ $data['optipng_level'] = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ? 0 : (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' );
124
+ $data['disable_pngout'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' );
125
+ $data['pngout_level'] = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ? 9 : (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' );
126
+ $data['jpg_quality'] = (int) apply_filters( 'jpeg_quality', 82 );
127
+ $data['parallel_opt'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_parallel_optimization' );
128
+ $data['background_opt'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_background_optimization' );
129
+ $data['scheduled_opt'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_auto' );
130
+ $data['include_media_folders'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_include_media_paths' );
131
+ $data['folders_to_opt'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_aux_paths' );
132
+ $data['folders_to_ignore'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_exclude_paths' );
133
+ $data['resize_media_width'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediawidth' );
134
+ $data['resize_media_height'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediaheight' );
135
+ $data['resize_indirect_width'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherwidth' );
136
+ $data['resize_indirect_height'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherheight' );
137
+ $data['resize_existing'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_existing' );
138
+ $data['total_sizes'] = (int) count( ewww_image_optimizer_get_image_sizes() );
139
+ $data['disabled_opt_sizes'] = is_array( get_option( 'ewww_image_optimizer_disable_resizes_opt' ) ) ? count( get_option( 'ewww_image_optimizer_disable_resizes_opt' ) ) : 0;
140
+ $data['disabled_create_sizes'] = is_array( get_option( 'ewww_image_optimizer_disable_resizes' ) ) ? count( get_option( 'ewww_image_optimizer_disable_resizes' ) ) : 0;
141
+ $data['skip_small_images'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' );
142
+ $data['skip_large_pngs'] = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' );
143
+ $data['exclude_full_lossy'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_skip_full' );
144
+ $data['exclude_full_meta'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_skip_full' );
145
+ $data['system_paths'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_bundle' );
146
+
147
+ $data['hide_conversion'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_convert_links' );
148
+ $data['delete_originals'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' );
149
+ $data['jpg2png'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' );
150
+ $data['png2jpg'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' );
151
+ $data['gif2png'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_to_png' );
152
+ $data['fill_color'] = is_null( ewww_image_optimizer_jpg_background() ) ? '' : ewww_image_optimizer_jpg_background();
153
+
154
+ $data['webp_create'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' );
155
+ $data['webp_force'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' );
156
+ $data['webp_urls'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_paths' );
157
+ $data['alt_webp'] = (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' );
158
+ $this->data = $data;
159
+ }
160
+
161
+ /**
162
+ * Send the data to the EDD server
163
+ *
164
+ * @access private
165
+ *
166
+ * @param bool $override Optional. Force check-in regardless of stored option. Default false.
167
+ * @param bool $ignore_last_checkin Optional. Force check-in regardless of last attempted time. Default false.
168
+ *
169
+ * @return bool Was data sent.
170
+ */
171
+ public function send_checkin( $override = false, $ignore_last_checkin = false ) {
172
+ if ( ! $this->tracking_allowed() && ! $override ) {
173
+ return false;
174
+ }
175
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
176
+
177
+ // Send a maximum of once per week.
178
+ $last_send = $this->get_last_send();
179
+ if ( is_numeric( $last_send ) && $last_send > strtotime( '-1 week' ) && ! $ignore_last_checkin ) {
180
+ ewwwio_debug_message( 'has not been a week since we last reported' );
181
+ return false;
182
+ }
183
+
184
+ $this->setup_data();
185
+ ewwwio_debug_message( 'sending site data' );
186
+ $request = wp_remote_post( 'https://stats.exactlywww.com/stats/report.php', array(
187
+ 'timeout' => 5,
188
+ 'body' => $this->data,
189
+ 'user-agent' => 'EWWW/' . EWWW_IMAGE_OPTIMIZER_VERSION . '; ' . get_bloginfo( 'url' ),
190
+ ) );
191
+
192
+ ewwwio_debug_message( 'finished reporting' );
193
+ if ( is_wp_error( $request ) ) {
194
+ return $request;
195
+ }
196
+
197
+ ewwwio_debug_message( 'no error, recording time sent' );
198
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_tracking_last_send', time() );
199
+
200
+ return true;
201
+
202
+ }
203
+
204
+ /**
205
+ * Check for a new opt-in on settings save
206
+ *
207
+ * @param bool $input The tracking setting.
208
+ * @return bool The unaltered setting.
209
+ */
210
+ public function check_for_settings_optin( $input ) {
211
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
212
+ // Send an intial check in on settings save.
213
+ if ( isset( $_POST['ewww_image_optimizer_allow_tracking'] ) && $_POST['ewww_image_optimizer_allow_tracking'] ) {
214
+ $this->send_checkin( true );
215
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_tracking_notice', 1 );
216
+ }
217
+ return $input;
218
+ }
219
+
220
+ /**
221
+ * Check for a new opt-in via the admin notice
222
+ */
223
+ public function check_for_optin() {
224
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
225
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_allow_tracking', 1 );
226
+ $this->send_checkin( true );
227
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_tracking_notice', 1 );
228
+ wp_redirect( remove_query_arg( 'action', wp_get_referer() ) );
229
+ exit;
230
+ }
231
+
232
+ /**
233
+ * Check for a new opt-out via the admin notice
234
+ */
235
+ public function check_for_optout() {
236
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
237
+ delete_option( 'ewww_image_optimizer_allow_tracking' );
238
+ delete_network_option( null, 'ewww_image_optimizer_allow_tracking' );
239
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_tracking_notice', 1 );
240
+ wp_redirect( remove_query_arg( 'action', wp_get_referer() ) );
241
+ exit;
242
+ }
243
+
244
+ /**
245
+ * Get the last time a checkin was sent
246
+ *
247
+ * @access private
248
+ * @return false|string
249
+ */
250
+ private function get_last_send() {
251
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
252
+ return ewww_image_optimizer_get_option( 'ewww_image_optimizer_tracking_last_send' );
253
+ }
254
+
255
+ /**
256
+ * Schedule a weekly checkin.
257
+ */
258
+ public function schedule_send() {
259
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
260
+ if ( is_multisite() ) {
261
+ if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
262
+ // Need to include the plugin library for the is_plugin_active function.
263
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
264
+ }
265
+ if ( is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) && get_current_blog_id() > 1 ) {
266
+ return;
267
+ }
268
+ }
269
+ // We send once a week (while tracking is allowed) to check in, which can be used to determine active sites.
270
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_allow_tracking' ) == true && ! wp_next_scheduled( 'ewww_image_optimizer_site_report' ) ) {
271
+ ewwwio_debug_message( 'scheduling checkin' );
272
+ wp_schedule_event( time(), apply_filters( 'ewww_image_optimizer_schedule', 'daily', 'ewww_image_optimizer_site_report' ), 'ewww_image_optimizer_site_report' );
273
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_allow_tracking' ) == true ) {
274
+ ewwwio_debug_message( 'checkin already scheduled: ' . wp_next_scheduled( 'ewww_image_optimizer_site_report' ) );
275
+ } elseif ( wp_next_scheduled( 'ewww_image_optimizer_site_report' ) ) {
276
+ ewwwio_debug_message( 'un-scheduling checkin' );
277
+ wp_clear_scheduled_hook( 'ewww_image_optimizer_site_report' );
278
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
279
+ // Need to include the plugin library for the is_plugin_active function.
280
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
281
+ }
282
+ if ( is_multisite() && get_current_blog_id() > 1 && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
283
+ switch_to_blog( 1 );
284
+ wp_clear_scheduled_hook( 'ewww_image_optimizer_site_report' );
285
+ restore_current_blog();
286
+ }
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Un-schedule the weekly checkin.
292
+ */
293
+ public function unschedule_send() {
294
+ wp_clear_scheduled_hook( 'ewww_image_optimizer_site_report' );
295
+ }
296
+
297
+ /**
298
+ * Display the admin notice to users that have not opted-in or out
299
+ *
300
+ * @access public
301
+ * @return void
302
+ */
303
+ public function admin_notice() {
304
+ $hide_notice = ewww_image_optimizer_get_option( 'ewww_image_optimizer_tracking_notice' );
305
+ if ( is_multisite() && get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) && get_site_option( 'ewww_image_optimizer_tracking_notice' ) ) {
306
+ $hide_notice = true;
307
+ }
308
+
309
+ if ( $hide_notice ) {
310
+ return;
311
+ }
312
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_allow_tracking' ) ) {
313
+ return;
314
+ }
315
+
316
+ if ( ! current_user_can( 'manage_options' ) ) {
317
+ return;
318
+ }
319
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
320
+
321
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
322
+ // Need to include the plugin library for the is_plugin_active function.
323
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
324
+ }
325
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) && ! current_user_can( 'manage_network_options' ) ) {
326
+ return;
327
+ }
328
+ if (
329
+ stristr( network_site_url( '/' ), 'dev' ) !== false ||
330
+ stristr( network_site_url( '/' ), 'localhost' ) !== false ||
331
+ stristr( network_site_url( '/' ), ':8888' ) !== false // This is common with MAMP on OS X.
332
+ ) {
333
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_tracking_notice', 1 );
334
+ } else {
335
+ $optin_url = 'admin.php?action=ewww_opt_into_tracking';
336
+ $optout_url = 'admin.php?action=ewww_opt_out_of_tracking';
337
+ echo '<div class="updated"><p>';
338
+ esc_html_e( 'Allow EWWW Image Optimizer to track plugin usage? Opt-in to tracking to receive up to 500 free image credits. No sensitive data is tracked.', 'ewww-image-optimizer' );
339
+ echo '&nbsp;<a href="http://docs.ewww.io/article/23-usage-tracking">' . esc_html__( 'Learn more.', 'ewww-image-optimizer' ) . '</a>';
340
+ echo '&nbsp;<a href="' . esc_url( $optin_url ) . '" class="button-secondary">' . esc_html__( 'Allow', 'ewww-image-optimizer' ) . '</a>';
341
+ echo '&nbsp;<a href="' . esc_url( $optout_url ) . '" class="button-secondary">' . esc_html__( 'Do not allow', 'ewww-image-optimizer' ) . '</a>';
342
+ echo '</p></div>';
343
+ }
344
+ }
345
+
346
+ }
347
+ $ewwwio_tracking = new EWWWIO_Tracking;
common.php ADDED
@@ -0,0 +1,8102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Common functions for Standard and Cloud plugins
4
+ *
5
+ * This file contains functions that are shared by both the regular EWWW IO plugin, and the
6
+ * Cloud version. Functions that differ between the two are stored in the main
7
+ * ewww-image-optimizer(-cloud).php file.
8
+ *
9
+ * @link https://ewww.io
10
+ * @package EWWW_Image_Optimizer
11
+ */
12
+
13
+ // TODO: use <picture> element to serve webp.
14
+ // TODO: see if we can offer a rebuild option, to fill in missing thumbs.
15
+ // TODO: so, if lazy loading support stinks, can we roll our own? that's an image "optimization", right?...
16
+ // TODO: prevent bad ajax errors from firing when we click the toggle on the Optimization Log, and the plugin status from doing 403s...
17
+ // TODO: use a transient to do health checks on the schedule optimizer.
18
+ // TODO: add a column to track compression level used for each image, and later implement a way to (re)compress at a specific compression level.
19
+ // TODO: might be able to use the Custom Bulk Actions in 4.7 to support the bulk optimize drop-down menu.
20
+ // TODO: see if there is a way to query the last couple months (or 30 days) worth of attachments, but only when the user is not using year/month folders. This way, they can catch extraneous images if possible. Use the post_date field, and do a media_scan followed by the folder scan to catch remaining images.
21
+ // TODO: move resizing options into their own sub-menu.
22
+ // TODO: need to make the scheduler so it can resume without having to re-run the queue population, and then we can probably also flush the queue when scheduled opt starts, but later it would be nice to implement the bulk_loop as the aux_loop so that it could handle media properly.
23
+ // TODO: implement a search for the bulk table, or maybe we should just move it to it's own page?
24
+ // TODO: port bulk changes to NextGEN and FlaGallery.
25
+ // TODO: make a bulk restore function.
26
+ // TODO: Add a custom async function for parallel mode to store image as pending and use the row ID instead of relative path.
27
+ // TODO: write some tests for update_table and check_table, find_already_opt, and remove_dups.
28
+ // TODO: write some conversion tests.
29
+ // TODO: do a bottom paginator for the show optimized images table.
30
+ // TODO: use wp_http_supports( array( 'ssl' ) ) to detect whether we should use https.
31
+ // TODO: check this patch, to see if the use of 'full' causes any issues: https://core.trac.wordpress.org/ticket/37840 .
32
+ // TODO: perhaps have an optional footer thingy that says how many images have been optimized.
33
+ // TODO: integrate AGR, since it's "abandoned", but possibly using gifsicle for better GIFs.
34
+ if ( ! defined( 'ABSPATH' ) ) {
35
+ exit;
36
+ }
37
+
38
+ define( 'EWWW_IMAGE_OPTIMIZER_VERSION', '351.0' );
39
+
40
+ // Initialize a couple globals.
41
+ $ewww_debug = '';
42
+ $ewww_defer = true;
43
+
44
+ if ( WP_DEBUG ) {
45
+ $ewww_memory = 'plugin load: ' . memory_get_usage( true ) . "\n";
46
+ }
47
+
48
+ // Setup custom $wpdb attribute for our image-tracking table.
49
+ global $wpdb;
50
+ if ( ! isset( $wpdb->ewwwio_images ) ) {
51
+ $wpdb->ewwwio_images = $wpdb->prefix . 'ewwwio_images';
52
+ }
53
+
54
+ // Check for Pantheon environment, and turn on relative folder usage at level 3.
55
+ if ( ! empty( $_ENV['PANTHEON_ENVIRONMENT'] ) && in_array( $_ENV['PANTHEON_ENVIRONMENT'], array( 'test', 'live', 'dev' ) ) ) {
56
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_RELATIVE' ) ) {
57
+ define( 'EWWW_IMAGE_OPTIMIZER_RELATIVE', 3 );
58
+ }
59
+ }
60
+
61
+ // Used for manipulating exif info.
62
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'vendor/pel/autoload.php' );
63
+ use lsolesen\pel\PelJpeg;
64
+ use lsolesen\pel\PelTag;
65
+
66
+ /*
67
+ * Hooks
68
+ */
69
+
70
+ // If automatic optimization is NOT disabled.
71
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_noauto' ) ) {
72
+ if ( class_exists( 'S3_Uploads' ) ) {
73
+ // Resizes and auto-rotates images.
74
+ add_filter( 'wp_handle_upload_prefilter', 'ewww_image_optimizer_handle_upload', 8 );
75
+ } else {
76
+ // Resizes and auto-rotates images.
77
+ add_filter( 'wp_handle_upload', 'ewww_image_optimizer_handle_upload' );
78
+ }
79
+ // Turns off the ewwwio_image_editor during uploads.
80
+ add_action( 'add_attachment', 'ewww_image_optimizer_add_attachment' );
81
+ // Turns off ewwwio_image_editor during Enable Media Replace.
82
+ add_filter( 'emr_unfiltered_get_attached_file', 'ewww_image_optimizer_image_sizes' );
83
+ // Enables direct integration to the editor's save function.
84
+ add_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
85
+ // Processes an image via the metadata after upload.
86
+ add_filter( 'wp_generate_attachment_metadata', 'ewww_image_optimizer_resize_from_meta_data', 15, 2 );
87
+ // Add hook for PTE confirmation to make sure new resizes are optimized.
88
+ add_filter( 'wp_get_attachment_metadata', 'ewww_image_optimizer_pte_check' );
89
+ // Resizes and auto-rotates MediaPress images.
90
+ add_filter( 'mpp_handle_upload', 'ewww_image_optimizer_handle_mpp_upload' );
91
+ // Processes a MediaPress image via the metadata after upload.
92
+ add_filter( 'mpp_generate_metadata', 'ewww_image_optimizer_resize_from_meta_data', 15, 2 );
93
+ }
94
+ // Makes sure the optimizer never optimizes it's own testing images.
95
+ add_filter( 'ewww_image_optimizer_bypass', 'ewww_image_optimizer_ignore_self', 10, 2 );
96
+ // This filter turns off ewwwio_image_editor during save from the actual image editor and ensures that we parse the resizes list during the image editor save function.
97
+ add_filter( 'load_image_to_edit_path', 'ewww_image_optimizer_editor_save_pre' );
98
+ // Adds a column to the media library list view to display optimization results.
99
+ add_filter( 'manage_media_columns', 'ewww_image_optimizer_columns' );
100
+ // Outputs the actual column information for each attachment.
101
+ add_action( 'manage_media_custom_column', 'ewww_image_optimizer_custom_column', 10, 2 );
102
+ // Filters to set default permissions, admins can override these if they wish.
103
+ add_filter( 'ewww_image_optimizer_manual_permissions', 'ewww_image_optimizer_manual_permissions', 8 );
104
+ add_filter( 'ewww_image_optimizer_bulk_permissions', 'ewww_image_optimizer_admin_permissions', 8 );
105
+ add_filter( 'ewww_image_optimizer_admin_permissions', 'ewww_image_optimizer_admin_permissions', 8 );
106
+ add_filter( 'ewww_image_optimizer_superadmin_permissions', 'ewww_image_optimizer_superadmin_permissions', 8 );
107
+ // Add a link to the plugins page so the user can go straight to the settings page.
108
+ $ewww_plugin_slug = plugin_basename( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE );
109
+ add_filter( "plugin_action_links_$ewww_plugin_slug", 'ewww_image_optimizer_settings_link' );
110
+ // Filter the registered sizes so we can remove any that the user disabled.
111
+ add_filter( 'intermediate_image_sizes_advanced', 'ewww_image_optimizer_image_sizes_advanced' );
112
+ // Ditto for PDF files (or anything non-image).
113
+ add_filter( 'fallback_intermediate_image_sizes', 'ewww_image_optimizer_fallback_sizes' );
114
+ // Filters the settings page output when cloud settings are enabled.
115
+ add_filter( 'ewww_image_optimizer_settings', 'ewww_image_optimizer_filter_settings_page' );
116
+ // Processes screenshots imported with MyArcadePlugin.
117
+ add_filter( 'myarcade_filter_screenshot', 'ewww_image_optimizer_myarcade_thumbnail' );
118
+ // Processes thumbnails created by MyArcadePlugin.
119
+ add_filter( 'myarcade_filter_thumbnail', 'ewww_image_optimizer_myarcade_thumbnail' );
120
+ // Allows the user to override the default JPG quality used by WordPress.
121
+ add_filter( 'jpeg_quality', 'ewww_image_optimizer_set_jpg_quality' );
122
+ // Makes sure the plugin bypasses any files affected by the Folders to Ignore setting.
123
+ add_filter( 'ewww_image_optimizer_bypass', 'ewww_image_optimizer_ignore_file', 10, 2 );
124
+ // Loads the plugin translations.
125
+ add_action( 'plugins_loaded', 'ewww_image_optimizer_preinit' );
126
+ // Checks for nextgen/nextcellent/flagallery existence, and loads the appropriate classes.
127
+ add_action( 'init', 'ewww_image_optimizer_gallery_support' );
128
+ // Initializes the plugin for admin interactions, like saving network settings and scheduling cron jobs.
129
+ add_action( 'admin_init', 'ewww_image_optimizer_admin_init' );
130
+ // Legacy (non-AJAX) action hook for manually optimizing an image.
131
+ add_action( 'admin_action_ewww_image_optimizer_manual_optimize', 'ewww_image_optimizer_manual' );
132
+ // Legacy (non-AJAX) action hook for manually restoring a converted image.
133
+ add_action( 'admin_action_ewww_image_optimizer_manual_restore', 'ewww_image_optimizer_manual' );
134
+ // Legacy (non-AJAX) action hook for manually restoring a backup from the API.
135
+ add_action( 'admin_action_ewww_image_optimizer_manual_cloud_restore', 'ewww_image_optimizer_manual' );
136
+ // Legacy (non-AJAX) action hook for manually attempting conversion on an image.
137
+ add_action( 'admin_action_ewww_image_optimizer_manual_convert', 'ewww_image_optimizer_manual' );
138
+ // Cleanup routine when an attachment is deleted.
139
+ add_action( 'delete_attachment', 'ewww_image_optimizer_delete' );
140
+ // Adds the EWWW IO pages to the admin menu.
141
+ add_action( 'admin_menu', 'ewww_image_optimizer_admin_menu', 60 );
142
+ // Adds the EWWW IO settings to the network admin menu.
143
+ add_action( 'network_admin_menu', 'ewww_image_optimizer_network_admin_menu' );
144
+ // Adds the hook to modify the media library bulk actions via admin_print_footer_scripts.
145
+ add_action( 'load-upload.php', 'ewww_image_optimizer_load_admin_js' );
146
+ // Non-AJAX handler to reroute selected IDs to the bulk optimizer.
147
+ add_action( 'admin_action_bulk_optimize', 'ewww_image_optimizer_bulk_action_handler' );
148
+ // Ditto, but handles the actions from the bottom of the media page.
149
+ add_action( 'admin_action_-1', 'ewww_image_optimizer_bulk_action_handler' );
150
+ // Adds scripts to ajaxify the one-click actions on the media library, and register tooltips for conversion links.
151
+ add_action( 'admin_enqueue_scripts', 'ewww_image_optimizer_media_scripts' );
152
+ // Adds scripts for the EWWW IO settings page.
153
+ add_action( 'admin_enqueue_scripts', 'ewww_image_optimizer_settings_script' );
154
+ // Enables scheduled optimization via wp-cron.
155
+ add_action( 'ewww_image_optimizer_auto', 'ewww_image_optimizer_auto' );
156
+ // Correct any records in the table created during retina generation.
157
+ add_action( 'wr2x_retina_file_added', 'ewww_image_optimizer_retina', 20, 2 );
158
+ // AJAX action hook for inserting WebP rewrite rules into .htaccess.
159
+ add_action( 'wp_ajax_ewww_webp_rewrite', 'ewww_image_optimizer_webp_rewrite' );
160
+ // AJAX action hook for manually optimizing/converting an image.
161
+ add_action( 'wp_ajax_ewww_manual_optimize', 'ewww_image_optimizer_manual' );
162
+ // AJAX action hook for manually restoring a converted image.
163
+ add_action( 'wp_ajax_ewww_manual_restore', 'ewww_image_optimizer_manual' );
164
+ // AJAX action hook for manually restoring an attachment from backups on the API.
165
+ add_action( 'wp_ajax_ewww_manual_cloud_restore', 'ewww_image_optimizer_manual' );
166
+ // AJAX action hook for manually restoring a single image backup from the API.
167
+ add_action( 'wp_ajax_ewww_manual_cloud_restore_single', 'ewww_image_optimizer_cloud_restore_single_image_handler' );
168
+ // Adds script to highlight mis-sized images on the front-end (for logged in admins only).
169
+ add_action( 'wp_enqueue_scripts', 'ewww_image_optimizer_resize_detection_script' );
170
+ // Adds a button on the admin bar to allow highlighting mis-sized images on-demand.
171
+ add_action( 'admin_bar_init', 'ewww_image_optimizer_admin_bar_init' );
172
+ // Non-AJAX handler to delete the API key, and reroute back to the settings page.
173
+ add_action( 'admin_action_ewww_image_optimizer_remove_cloud_key', 'ewww_image_optimizer_remove_cloud_key' );
174
+ // Non-AJAX handler to delete the debug log, and reroute back to the settings page.
175
+ add_action( 'admin_action_ewww_image_optimizer_delete_debug_log', 'ewww_image_optimizer_delete_debug_log' );
176
+ // Makes sure to flush out any scheduled jobs on deactivation.
177
+ register_deactivation_hook( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, 'ewww_image_optimizer_network_deactivate' );
178
+ // Removes the .htaccess rules for webp when uninstalled.
179
+ register_uninstall_hook( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, 'ewww_image_optimizer_uninstall' );
180
+ // add_action( 'shutdown', 'ewwwio_memory_output' );.
181
+ // Makes sure we flush the debug info to the log on shutdown.
182
+ add_action( 'shutdown', 'ewww_image_optimizer_debug_log' );
183
+ // If Alt WebP Rewriting is enabled.
184
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) ) {
185
+ // Start an output buffer before any output starts.
186
+ add_action( 'template_redirect', 'ewww_image_optimizer_buffer_start' );
187
+ if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
188
+ // Load the non-minified, non-inline version of the webp rewrite script.
189
+ add_action( 'wp_enqueue_scripts', 'ewww_image_optimizer_webp_debug_script' );
190
+ } elseif ( defined( 'EWWW_IMAGE_OPTIMIZER_WEBP_EXTERNAL_SCRIPT' ) && EWWW_IMAGE_OPTIMIZER_WEBP_EXTERNAL_SCRIPT ) {
191
+ // Load the minified, non-inline version of the webp rewrite script.
192
+ add_action( 'wp_enqueue_scripts', 'ewww_image_optimizer_webp_min_script' );
193
+ } else {
194
+ // Loads jQuery and the minified inline webp rewrite script.
195
+ add_action( 'wp_enqueue_scripts', 'ewww_image_optimizer_webp_load_jquery' );
196
+ if ( ! function_exists( 'wp_add_inline_script' ) || ( defined( 'EWWW_IMAGE_OPTIMIZER_WEBP_INLINE_FALLBACK' ) && EWWW_IMAGE_OPTIMIZER_WEBP_INLINE_FALLBACK ) ) {
197
+ add_action( 'wp_head', 'ewww_image_optimizer_webp_inline_script' );
198
+ }
199
+ }
200
+ }
201
+
202
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
203
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewwwio-cli.php' );
204
+ }
205
+
206
+ /**
207
+ * Skips optimization when a file is within EWWW IO's own folder.
208
+ *
209
+ * @param bool $skip Defaults to false.
210
+ * @param string $filename The name of the file about to be optimized.
211
+ * @return bool True if the file is within the EWWW IO folder, unaltered otherwise.
212
+ */
213
+ function ewww_image_optimizer_ignore_self( $skip, $filename ) {
214
+ if ( 0 === strpos( $filename, EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH ) ) {
215
+ return true;
216
+ }
217
+ return $skip;
218
+ }
219
+
220
+ /**
221
+ * Gets the version number from a plugin file.
222
+ *
223
+ * @param string $plugin_file The path to the plugin's main file.
224
+ * @return array With a single index, 'Version'.
225
+ */
226
+ function ewww_image_optimizer_get_plugin_version( $plugin_file ) {
227
+ $default_headers = array(
228
+ 'Version' => 'Version',
229
+ );
230
+ $plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
231
+ return $plugin_data;
232
+ }
233
+
234
+ /**
235
+ * Checks to see if the WebP option from the Cache Enabler plugin is enabled.
236
+ *
237
+ * @return bool True if the WebP option for CE is enabled.
238
+ */
239
+ function ewww_image_optimizer_ce_webp_enabled() {
240
+ if ( class_exists( 'Cache_Enabler' ) ) {
241
+ $ce_options = Cache_Enabler::$options;
242
+ if ( $ce_options['webp'] ) {
243
+ ewwwio_debug_message( 'Cache Enabler webp option enabled' );
244
+ return true;
245
+ }
246
+ }
247
+ return false;
248
+ }
249
+
250
+ /**
251
+ * Starts an output buffer and registers the callback function to do WebP replacement. functions to capture all page output, replace image urls with webp derivatives, and add webp fallback
252
+ */
253
+ function ewww_image_optimizer_buffer_start() {
254
+ ob_start( 'ewww_image_optimizer_filter_page_output' );
255
+ }
256
+
257
+ /**
258
+ * Copies attributes from the original img element to the noscript element.
259
+ *
260
+ * @param object $image A DOMElement (extension of DOMNode) representing an img element.
261
+ * @param object $nscript Passed by reference, a DOMElement that will be given all the (known) attributes of $image.
262
+ * @param string $prefix Optional. Value to prepend to all attribute names. Default 'data-'.
263
+ */
264
+ function ewww_image_optimizer_webp_attr_copy( $image, &$nscript, $prefix = 'data-' ) {
265
+ if ( ! is_object( $image ) || ! is_object( $nscript ) ) {
266
+ return;
267
+ }
268
+ $attributes = array(
269
+ 'align',
270
+ 'alt',
271
+ 'border',
272
+ 'crossorigin',
273
+ 'height',
274
+ 'hspace',
275
+ 'ismap',
276
+ 'longdesc',
277
+ 'usemap',
278
+ 'vspace',
279
+ 'width',
280
+ 'accesskey',
281
+ 'class',
282
+ 'contenteditable',
283
+ 'contextmenu',
284
+ 'dir',
285
+ 'draggable',
286
+ 'dropzone',
287
+ 'hidden',
288
+ 'id',
289
+ 'lang',
290
+ 'spellcheck',
291
+ 'style',
292
+ 'tabindex',
293
+ 'title',
294
+ 'translate',
295
+ 'sizes',
296
+ 'data-lazy-type',
297
+ 'data-attachment-id',
298
+ 'data-permalink',
299
+ 'data-orig-size',
300
+ 'data-comments-opened',
301
+ 'data-image-meta',
302
+ 'data-image-title',
303
+ 'data-image-description',
304
+ );
305
+ foreach ( $attributes as $attribute ) {
306
+ if ( $image->getAttribute( $attribute ) ) {
307
+ $nscript->setAttribute( $prefix . $attribute, $image->getAttribute( $attribute ) );
308
+ }
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Replaces images within a srcset attribute with their .webp derivatives.
314
+ *
315
+ * @param string $srcset A valid srcset attribute from an img element.
316
+ * @param string $home_url The 'home' url for the WordPress site.
317
+ * @param bool $valid_path True if a CDN path has been specified and matches the img element.
318
+ * @return bool|string False if no changes were made, or the new srcset if any WebP images replaced the originals.
319
+ */
320
+ function ewww_image_optimizer_webp_srcset_replace( $srcset, $home_url, $valid_path ) {
321
+ $srcset_urls = explode( ' ', $srcset );
322
+ $found_webp = false;
323
+ if ( ewww_image_optimizer_iterable( $srcset_urls ) ) {
324
+ ewwwio_debug_message( 'found srcset urls' );
325
+ foreach ( $srcset_urls as $srcurl ) {
326
+ if ( is_numeric( substr( $srcurl, 0, 1 ) ) ) {
327
+ continue;
328
+ }
329
+ $srcfilepath = ABSPATH . str_replace( $home_url, '', $srcurl );
330
+ ewwwio_debug_message( "looking for srcurl on disk: $srcfilepath" );
331
+ if ( $valid_path || is_file( $srcfilepath . '.webp' ) ) {
332
+ $srcset = preg_replace( "|$srcurl|", $srcurl . '.webp', $srcset );
333
+ $found_webp = true;
334
+ ewwwio_debug_message( "replacing $srcurl in $srcset" );
335
+ }
336
+ }
337
+ }
338
+ if ( $found_webp ) {
339
+ return $srcset;
340
+ } else {
341
+ return false;
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Replaces images with the Jetpack data attributes with their .webp derivatives.
347
+ *
348
+ * @param object $image A DOMElement (extension of DOMNode) representing an img element.
349
+ * @param object $nscript Passed by reference, a DOMElement that will be assigned the jetpack data attributes.
350
+ * @param string $home_url The 'home' url for the WordPress site.
351
+ * @param bool $valid_path True if a CDN path has been specified and matches the img element.
352
+ */
353
+ function ewww_image_optimizer_webp_jetpack_replace( $image, &$nscript, $home_url, $valid_path ) {
354
+ if ( $image->getAttribute( 'data-orig-file' ) ) {
355
+ $origfilepath = ABSPATH . str_replace( $home_url, '', $image->getAttribute( 'data-orig-file' ) );
356
+ ewwwio_debug_message( "looking for data-orig-file on disk: $origfilepath" );
357
+ if ( $valid_path || is_file( $origfilepath . '.webp' ) ) {
358
+ $nscript->setAttribute( 'data-webp-orig-file', $image->getAttribute( 'data-orig-file' ) . '.webp' );
359
+ ewwwio_debug_message( "replacing $origfilepath in data-orig-file" );
360
+ }
361
+ }
362
+ if ( $image->getAttribute( 'data-medium-file' ) ) {
363
+ $mediumfilepath = ABSPATH . str_replace( $home_url, '', $image->getAttribute( 'data-medium-file' ) );
364
+ ewwwio_debug_message( "looking for data-medium-file on disk: $mediumfilepath" );
365
+ if ( $valid_path || is_file( $mediumfilepath . '.webp' ) ) {
366
+ $nscript->setAttribute( 'data-webp-medium-file', $image->getAttribute( 'data-medium-file' ) . '.webp' );
367
+ ewwwio_debug_message( "replacing $mediumfilepath in data-medium-file" );
368
+ }
369
+ }
370
+ if ( $image->getAttribute( 'data-large-file' ) ) {
371
+ $largefilepath = ABSPATH . str_replace( $home_url, '', $image->getAttribute( 'data-large-file' ) );
372
+ ewwwio_debug_message( "looking for data-large-file on disk: $largefilepath" );
373
+ if ( $valid_path || is_file( $largefilepath . '.webp' ) ) {
374
+ $nscript->setAttribute( 'data-webp-large-file', $image->getAttribute( 'data-large-file' ) . '.webp' );
375
+ ewwwio_debug_message( "replacing $largefilepath in data-large-file" );
376
+ }
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Search for amp-img elements and rewrite them with fallback elements for WebP replacement.
382
+ *
383
+ * Any amp-img elements will be given the fallback attribute and wrapped inside another amp-img
384
+ * element with a WebP src attribute if WebP derivatives exist.
385
+ *
386
+ * @param string $buffer The entire HTML page from the output buffer.
387
+ * @return string The altered buffer containing the full page with WebP images inserted.
388
+ */
389
+ function ewww_image_optimizer_filter_amp_webp( $buffer ) {
390
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
391
+ // Modify buffer here, and then return the updated code.
392
+ if ( class_exists( 'DOMDocument' ) ) {
393
+ $expanded_head = false;
394
+ $html = new DOMDocument;
395
+ $libxml_previous_error_reporting = libxml_use_internal_errors( true );
396
+ $html->formatOutput = false;
397
+ $html->encoding = 'utf-8';
398
+ if ( defined( 'LIBXML_VERSION' ) && LIBXML_VERSION < 20800 ) {
399
+ ewwwio_debug_message( 'libxml version too old for amp: ' . LIBXML_VERSION );
400
+ return $buffer;
401
+ } elseif ( ! defined( 'LIBXML_VERSION' ) ) {
402
+ // When libxml is too old, we dare not modify the buffer.
403
+ ewwwio_debug_message( 'cannot detect libxml version for amp' );
404
+ return $buffer;
405
+ }
406
+ $html->loadHTML( $buffer );
407
+ ewwwio_debug_message( 'parsing AMP as html' );
408
+ $html->encoding = 'utf-8';
409
+ $home_url = get_site_url();
410
+ $webp_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_paths' );
411
+ if ( ! is_array( $webp_paths ) ) {
412
+ $webp_paths = array();
413
+ }
414
+ $home_relative_url = preg_replace( '/https?:/', '', $home_url );
415
+ $images = $html->getElementsByTagName( 'amp-img' );
416
+ if ( ewww_image_optimizer_iterable( $images ) ) {
417
+ foreach ( $images as $image ) {
418
+ if ( 'amp-img' == $image->parentNode->tagName ) {
419
+ continue;
420
+ }
421
+ $srcset = '';
422
+ ewwwio_debug_message( 'parsing an AMP image' );
423
+ $file = $image->getAttribute( 'src' );
424
+ $valid_path = false;
425
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
426
+ // Check for CDN paths within the img src attribute.
427
+ foreach ( $webp_paths as $webp_path ) {
428
+ if ( strpos( $file, $webp_path ) !== false ) {
429
+ $valid_path = true;
430
+ }
431
+ }
432
+ }
433
+ if ( strpos( $file, $home_relative_url ) === 0 ) {
434
+ $filepath = str_replace( $home_relative_url, ABSPATH, $file );
435
+ } else {
436
+ $filepath = str_replace( $home_url, ABSPATH, $file );
437
+ }
438
+ ewwwio_debug_message( "the AMP image is at $filepath" );
439
+ // If a CDN path match was found, or .webp image existsence is confirmed, and this is not a lazy-load 'dummy' image.
440
+ if ( ( $valid_path || is_file( $filepath . '.webp' ) ) && ! strpos( $file, 'assets/images/dummy.png' ) && ! strpos( $file, 'base64,R0lGOD' ) && ! strpos( $file, 'lazy-load/images/1x1' ) ) {
441
+ $parent_image = $image->cloneNode();
442
+ $parent_image->setAttribute( 'src', $file . '.webp' );
443
+ if ( $image->getAttribute( 'srcset' ) ) {
444
+ $srcset = $image->getAttribute( 'srcset' );
445
+ $srcset_webp = ewww_image_optimizer_webp_srcset_replace( $srcset, $home_url, $valid_path );
446
+ if ( $srcset_webp ) {
447
+ $parent_image->setAttribute( 'srcset', $srcset_webp );
448
+ }
449
+ }
450
+ $fallback_attr = $html->createAttribute( 'fallback' );
451
+ $image->appendChild( $fallback_attr );
452
+ $image->parentNode->replaceChild( $parent_image, $image );
453
+ $parent_image->appendChild( $image );
454
+ }
455
+ } // End foreach().
456
+ } // End if().
457
+ ewwwio_debug_message( 'preparing to dump page back to $buffer' );
458
+ $buffer = $html->saveHTML( $html->documentElement );
459
+ libxml_clear_errors();
460
+ libxml_use_internal_errors( $libxml_previous_error_reporting );
461
+ if ( false ) { // Set to true for extra debugging.
462
+ ewwwio_debug_message( 'html head' );
463
+ ewwwio_debug_message( $html_head[0] );
464
+ ewwwio_debug_message( 'buffer beginning' );
465
+ ewwwio_debug_message( substr( $buffer, 0, 500 ) );
466
+ }
467
+ if ( false ) { // Set to true for extra debugging.
468
+ ewwwio_debug_message( 'buffer after replacement' );
469
+ ewwwio_debug_message( substr( $buffer, 0, 500 ) );
470
+ }
471
+ } // End if().
472
+ if ( false ) { // Set to true for extra logging.
473
+ ewww_image_optimizer_debug_log();
474
+ }
475
+ return $buffer;
476
+ }
477
+
478
+ /**
479
+ * Search for img elements and rewrite them with noscript elements for WebP replacement.
480
+ *
481
+ * Any img elements or elements that may be used in place of img elements by JS are checked to see
482
+ * if WebP derivatives exist. The element is then wrapped within a noscript element for fallback,
483
+ * and noscript element receives a copy of the attributes from the img along with webp replacement
484
+ * values for those attributes.
485
+ *
486
+ * @param string $buffer The full HTML page generated since the output buffer was started.
487
+ * @return string The altered buffer containing the full page with WebP images inserted.
488
+ */
489
+ function ewww_image_optimizer_filter_page_output( $buffer ) {
490
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
491
+ // If this is an admin page, don't filter it.
492
+ if ( ( empty( $buffer ) || is_admin() ) ) {
493
+ return $buffer;
494
+ }
495
+ // If Cache Enabler's WebP option is enabled, don't filter it.
496
+ if ( ewww_image_optimizer_ce_webp_enabled() ) {
497
+ return $buffer;
498
+ }
499
+ $uri = $_SERVER['REQUEST_URI'];
500
+ // Based on the uri, if this is a cornerstone editing page, don't filter the response.
501
+ if ( strpos( $uri, '&cornerstone=1' ) || strpos( $uri, 'cornerstone-endpoint' ) !== false ) {
502
+ return $buffer;
503
+ }
504
+ if ( ! empty( $_GET['et_fb'] ) ) {
505
+ return $buffer;
506
+ }
507
+ // Modify buffer here, and then return the updated code.
508
+ if ( class_exists( 'DOMDocument' ) ) {
509
+ // If this is XML (not XHTML), don't modify the page.
510
+ if ( preg_match( '/<\?xml/', $buffer ) ) {
511
+ return $buffer;
512
+ }
513
+ $expanded_head = false;
514
+ preg_match( '/.+<head ?\>/s', $buffer, $html_head );
515
+ if ( empty( $html_head ) ) {
516
+ ewwwio_debug_message( 'did not find head tag' );
517
+ preg_match( '/.+<head [^>]*>/s', $buffer, $html_head );
518
+ if ( empty( $html_head ) ) {
519
+ ewwwio_debug_message( 'did not find expanded head tag either' );
520
+ return $buffer;
521
+ }
522
+ }
523
+ if ( strpos( $buffer, 'amp-boilerplate' ) ) {
524
+ ewwwio_debug_message( 'AMP page processing' );
525
+ return $buffer;
526
+ }
527
+ $html = new DOMDocument;
528
+ $libxml_previous_error_reporting = libxml_use_internal_errors( true );
529
+ $html->formatOutput = false;
530
+ $html->encoding = 'utf-8';
531
+ if ( defined( 'LIBXML_VERSION' ) && LIBXML_VERSION < 20800 ) {
532
+ ewwwio_debug_message( 'libxml version: ' . LIBXML_VERSION );
533
+ // Converts the buffer from utf-8 to html-entities.
534
+ $buffer = mb_convert_encoding( $buffer, 'HTML-ENTITIES', 'UTF-8' );
535
+ } elseif ( ! defined( 'LIBXML_VERSION' ) ) {
536
+ // When libxml is too old, we dare not modify the buffer.
537
+ ewwwio_debug_message( 'cannot detect libxml version' );
538
+ return $buffer;
539
+ }
540
+ if ( preg_match( '/<.DOCTYPE.+xhtml/', $buffer ) ) {
541
+ $html->recover = true;
542
+ $xhtml_parse = $html->loadXML( $buffer );
543
+ ewwwio_debug_message( 'parsing as xhtml' );
544
+ } elseif ( empty( $xhtml_parse ) ) {
545
+ $html->loadHTML( $buffer );
546
+ ewwwio_debug_message( 'parsing as html' );
547
+ }
548
+ $html->encoding = 'utf-8';
549
+ $home_url = get_site_url();
550
+ $webp_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_paths' );
551
+ if ( ! is_array( $webp_paths ) ) {
552
+ $webp_paths = array();
553
+ }
554
+ $home_relative_url = preg_replace( '/https?:/', '', $home_url );
555
+ $images = $html->getElementsByTagName( 'img' );
556
+ if ( ewww_image_optimizer_iterable( $images ) ) {
557
+ foreach ( $images as $image ) {
558
+ if ( 'noscript' == $image->parentNode->tagName ) {
559
+ continue;
560
+ }
561
+ $srcset = '';
562
+ ewwwio_debug_message( 'parsing an image' );
563
+ $file = $image->getAttribute( 'src' );
564
+ $valid_path = false;
565
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
566
+ // Check for CDN paths within the img src attribute.
567
+ foreach ( $webp_paths as $webp_path ) {
568
+ if ( strpos( $file, $webp_path ) !== false ) {
569
+ $valid_path = true;
570
+ }
571
+ }
572
+ }
573
+ if ( strpos( $file, $home_relative_url ) === 0 ) {
574
+ $filepath = str_replace( $home_relative_url, ABSPATH, $file );
575
+ } else {
576
+ $filepath = str_replace( $home_url, ABSPATH, $file );
577
+ }
578
+ ewwwio_debug_message( "the image is at $filepath" );
579
+ // If a CDN path match was found, or .webp image existsence is confirmed, and this is not a lazy-load 'dummy' image.
580
+ if ( ( $valid_path || is_file( $filepath . '.webp' ) ) && ! strpos( $file, 'assets/images/dummy.png' ) && ! strpos( $file, 'base64,R0lGOD' ) && ! strpos( $file, 'lazy-load/images/1x1' ) ) {
581
+ $nscript = $html->createElement( 'noscript' );
582
+ $nscript->setAttribute( 'data-img', $file );
583
+ $nscript->setAttribute( 'data-webp', $file . '.webp' );
584
+ if ( $image->getAttribute( 'srcset' ) ) {
585
+ $srcset = $image->getAttribute( 'srcset' );
586
+ $srcset_webp = ewww_image_optimizer_webp_srcset_replace( $srcset, $home_url, $valid_path );
587
+ if ( $srcset_webp ) {
588
+ $nscript->setAttribute( 'data-srcset-webp', $srcset_webp );
589
+ }
590
+ $nscript->setAttribute( 'data-srcset-img', $srcset );
591
+ }
592
+ if ( $image->getAttribute( 'data-orig-file' ) && $image->getAttribute( 'data-medium-file' ) && $image->getAttribute( 'data-large-file' ) ) {
593
+ ewww_image_optimizer_webp_jetpack_replace( $image, $nscript, $home_url, $valid_path );
594
+ }
595
+ ewww_image_optimizer_webp_attr_copy( $image, $nscript );
596
+ $nscript->setAttribute( 'class', 'ewww_webp' );
597
+ $image->parentNode->replaceChild( $nscript, $image );
598
+ $nscript->appendChild( $image );
599
+ }
600
+ // Look for NextGEN attributes that need to be altered.
601
+ if ( empty( $file ) && $image->getAttribute( 'data-src' ) && $image->getAttribute( 'data-thumbnail' ) ) {
602
+ $file = $image->getAttribute( 'data-src' );
603
+ $thumb = $image->getAttribute( 'data-thumbnail' );
604
+ ewwwio_debug_message( "checking webp for ngg data-src: $file" );
605
+ $filepath = ABSPATH . str_replace( $home_url, '', $file );
606
+ ewwwio_debug_message( "checking webp for ngg data-thumbnail: $thumb" );
607
+ $thumbpath = ABSPATH . str_replace( $home_url, '', $thumb );
608
+ if ( ! $valid_path && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
609
+ // Check the image for configured CDN paths.
610
+ foreach ( $webp_paths as $webp_path ) {
611
+ if ( strpos( $file, $webp_path ) !== false ) {
612
+ $valid_path = true;
613
+ }
614
+ }
615
+ }
616
+ if ( $valid_path || is_file( $filepath . '.webp' ) ) {
617
+ ewwwio_debug_message( "found webp for ngg data-src: $filepath" );
618
+ $image->setAttribute( 'data-webp', $file . '.webp' );
619
+ }
620
+ if ( $valid_path || is_file( $thumbpath . '.webp' ) ) {
621
+ ewwwio_debug_message( "found webp for ngg data-thumbnail: $thumbpath" );
622
+ $image->setAttribute( 'data-webp-thumbnail', $thumb . '.webp' );
623
+ }
624
+ }
625
+ // NOTE: lazy loads are shutoff for now, since they don't work consistently
626
+ // WP Retina 2x lazy loads.
627
+ if ( false && empty( $file ) && $image->getAttribute( 'data-srcset' ) && strpos( $image->getAttribute( 'class' ), 'lazyload' ) ) {
628
+ $srcset = $image->getAttribute( 'data-srcset' );
629
+ if ( ! $valid_path && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
630
+ // Check the image for configured CDN paths.
631
+ foreach ( $webp_paths as $webp_path ) {
632
+ if ( strpos( $srcset, $webp_path ) !== false ) {
633
+ $valid_path = true;
634
+ }
635
+ }
636
+ }
637
+ $srcset_webp = ewww_image_optimizer_webp_srcset_replace( $srcset, $home_url, $valid_path );
638
+ if ( $srcset_webp ) {
639
+ $nimage = $html->createElement( 'img' );
640
+ $nimage->setAttribute( 'data-srcset-webp', $srcset_webp );
641
+ $nimage->setAttribute( 'data-srcset-img', $srcset );
642
+ ewww_image_optimizer_webp_attr_copy( $image, $nimage, '' );
643
+ $nimage->setAttribute( 'class', $image->getAttribute( 'class' ) . ' ewww_webp_lazy_retina' );
644
+ $image->parentNode->replaceChild( $nimage, $image );
645
+ }
646
+ }
647
+ // Hueman theme lazy-loads.
648
+ if ( false && ! empty( $file ) && strpos( $file, 'image/gif;base64,R0lGOD' ) && $image->getAttribute( 'data-src' ) && $image->getAttribute( 'data-srcset' ) ) {
649
+ $dummy = $file;
650
+ $file = $image->getAttribute( 'data-src' );
651
+ ewwwio_debug_message( "checking webp for hueman data-src: $file" );
652
+ $filepath = ABSPATH . str_replace( $home_url, '', $file );
653
+ if ( ! $valid_path && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
654
+ // Check the image for configured CDN paths.
655
+ foreach ( $webp_paths as $webp_path ) {
656
+ if ( strpos( $file, $webp_path ) !== false ) {
657
+ $valid_path = true;
658
+ }
659
+ }
660
+ }
661
+ if ( $valid_path || is_file( $filepath . '.webp' ) ) {
662
+ ewwwio_debug_message( "found webp for Hueman lazyload: $filepath" );
663
+ $nscript = $html->createElement( 'noscript' );
664
+ $nscript->setAttribute( 'data-src', $dummy );
665
+ $nscript->setAttribute( 'data-img', $file );
666
+ $nscript->setAttribute( 'data-webp-src', $file . '.webp' );
667
+ $image->setAttribute( 'src', $file );
668
+ if ( $image->getAttribute( 'data-srcset' ) ) {
669
+ $srcset = $image->getAttribute( 'data-srcset' );
670
+ $srcset_webp = ewww_image_optimizer_webp_srcset_replace( $srcset, $home_url, $valid_path );
671
+ if ( $srcset_webp ) {
672
+ $nscript->setAttribute( 'data-srcset-webp', $srcset_webp );
673
+ }
674
+ $nscript->setAttribute( 'data-srcset-img', $srcset );
675
+ }
676
+ ewww_image_optimizer_webp_attr_copy( $image, $nscript );
677
+ $nscript->setAttribute( 'class', 'ewww_webp_lazy_hueman' );
678
+ $image->parentNode->replaceChild( $nscript, $image );
679
+ $nscript->appendChild( $image );
680
+ }
681
+ }
682
+ // Lazy Load plugin (and hopefully Cherry variant) and BJ Lazy Load.
683
+ if ( false && ! empty( $file ) && ( strpos( $file, 'image/gif;base64,R0lGOD' ) || strpos( $file, 'lazy-load/images/1x1' ) ) && $image->getAttribute( 'data-lazy-src' ) && ! empty( $image->nextSibling ) && 'noscript' == $image->nextSibling->nodeName ) {
684
+ $dummy = $file;
685
+ $nimage = $html->createElement( 'img' );
686
+ $nimage->setAttribute( 'src', $dummy );
687
+ $file = $image->getAttribute( 'data-lazy-src' );
688
+ ewwwio_debug_message( "checking webp for Lazy Load data-lazy-src: $file" );
689
+ $filepath = ABSPATH . str_replace( $home_url, '', $file );
690
+ if ( ! $valid_path && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
691
+ // Check the image for configured CDN paths.
692
+ foreach ( $webp_paths as $webp_path ) {
693
+ if ( strpos( $file, $webp_path ) !== false ) {
694
+ $valid_path = true;
695
+ }
696
+ }
697
+ }
698
+ if ( $valid_path || is_file( $filepath . '.webp' ) ) {
699
+ ewwwio_debug_message( "found webp for Lazy Load: $filepath" );
700
+ $nimage->setAttribute( 'data-lazy-img-src', $file );
701
+ $nimage->setAttribute( 'data-lazy-webp-src', $file . '.webp' );
702
+ if ( $image->getAttribute( 'srcset' ) ) {
703
+ $srcset = $image->getAttribute( 'srcset' );
704
+ $srcset_webp = ewww_image_optimizer_webp_srcset_replace( $srcset, $home_url, $valid_path );
705
+ if ( $srcset_webp ) {
706
+ $nimage->setAttribute( 'data-srcset-webp', $srcset_webp );
707
+ }
708
+ $nimage->setAttribute( 'data-srcset', $srcset );
709
+ }
710
+ if ( $image->getAttribute( 'data-lazy-srcset' ) ) {
711
+ $srcset = $image->getAttribute( 'data-lazy-srcset' );
712
+ $srcset_webp = ewww_image_optimizer_webp_srcset_replace( $srcset, $home_url, $valid_path );
713
+ if ( $srcset_webp ) {
714
+ $nimage->setAttribute( 'data-lazy-srcset-webp', $srcset_webp );
715
+ }
716
+ $nimage->setAttribute( 'data-lazy-srcset-img', $srcset );
717
+ }
718
+ ewww_image_optimizer_webp_attr_copy( $image, $nimage, '' );
719
+ $nimage->setAttribute( 'class', $image->getAttribute( 'class' ) . ' ewww_webp_lazy_load' );
720
+ $image->parentNode->replaceChild( $nimage, $image );
721
+ }
722
+ } // End if().
723
+ if ( $image->getAttribute( 'data-lazyload' ) ) {
724
+ $lazyload = $image->getAttribute( 'data-lazyload' );
725
+ if ( ! empty( $lazyload ) ) {
726
+ $lazyloadpath = ABSPATH . str_replace( $home_url, '', $lazyload );
727
+ if ( ! $valid_path && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
728
+ // Check the image for configured CDN paths.
729
+ foreach ( $webp_paths as $webp_path ) {
730
+ if ( strpos( $lazyload, $webp_path ) !== false ) {
731
+ $valid_path = true;
732
+ }
733
+ }
734
+ }
735
+ if ( $valid_path || is_file( $lazyloadpath . '.webp' ) ) {
736
+ ewwwio_debug_message( "found webp for data-lazyload: $filepath" );
737
+ $image->setAttribute( 'data-webp-lazyload', $lazyload . '.webp' );
738
+ }
739
+ }
740
+ }
741
+ } // End foreach().
742
+ } // End if().
743
+ // NextGEN slides listed as 'a' elements.
744
+ $links = $html->getElementsByTagName( 'a' );
745
+ if ( ewww_image_optimizer_iterable( $links ) ) {
746
+ foreach ( $links as $link ) {
747
+ ewwwio_debug_message( 'parsing a link' );
748
+ if ( $link->getAttribute( 'data-src' ) && $link->getAttribute( 'data-thumbnail' ) ) {
749
+ $file = $link->getAttribute( 'data-src' );
750
+ $thumb = $link->getAttribute( 'data-thumbnail' );
751
+ $valid_path = false;
752
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
753
+ // Check the image for configured CDN paths.
754
+ foreach ( $webp_paths as $webp_path ) {
755
+ if ( strpos( $file, $webp_path ) !== false ) {
756
+ $valid_path = true;
757
+ }
758
+ }
759
+ }
760
+ ewwwio_debug_message( "checking webp for ngg data-src: $file" );
761
+ $filepath = ABSPATH . str_replace( $home_url, '', $file );
762
+ ewwwio_debug_message( "checking webp for ngg data-thumbnail: $thumb" );
763
+ $thumbpath = ABSPATH . str_replace( $home_url, '', $thumb );
764
+ if ( $valid_path || is_file( $filepath . '.webp' ) ) {
765
+ ewwwio_debug_message( "found webp for ngg data-src: $filepath" );
766
+ $link->setAttribute( 'data-webp', $file . '.webp' );
767
+ }
768
+ if ( $valid_path || is_file( $thumbpath . '.webp' ) ) {
769
+ ewwwio_debug_message( "found webp for ngg data-thumbnail: $thumbpath" );
770
+ $link->setAttribute( 'data-webp-thumbnail', $thumb . '.webp' );
771
+ }
772
+ }
773
+ }
774
+ }
775
+ // Revolution Slider 'li' elements.
776
+ $listitems = $html->getElementsByTagName( 'li' );
777
+ if ( ewww_image_optimizer_iterable( $listitems ) ) {
778
+ foreach ( $listitems as $listitem ) {
779
+ ewwwio_debug_message( 'parsing a listitem' );
780
+ if ( $listitem->getAttribute( 'data-title' ) === 'Slide' && ( $listitem->getAttribute( 'data-lazyload' ) || $listitem->getAttribute( 'data-thumb' ) ) ) {
781
+ $thumb = $listitem->getAttribute( 'data-thumb' );
782
+ $valid_path = false;
783
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
784
+ // Check the image for configured CDN paths.
785
+ foreach ( $webp_paths as $webp_path ) {
786
+ if ( strpos( $thumb, $webp_path ) !== false ) {
787
+ $valid_path = true;
788
+ }
789
+ }
790
+ }
791
+ ewwwio_debug_message( "checking webp for revslider data-thumb: $thumb" );
792
+ $thumbpath = str_replace( $home_url, ABSPATH, $thumb );
793
+ if ( $valid_path || is_file( $thumbpath . '.webp' ) ) {
794
+ ewwwio_debug_message( "found webp for revslider data-thumb: $thumbpath" );
795
+ $listitem->setAttribute( 'data-webp-thumb', $thumb . '.webp' );
796
+ }
797
+ $param_num = 1;
798
+ while ( $param_num < 11 ) {
799
+ $parameter = '';
800
+ if ( $listitem->getAttribute( 'data-param' . $param_num ) ) {
801
+ $parameter = $listitem->getAttribute( 'data-param' . $param_num );
802
+ ewwwio_debug_message( "checking webp for revslider data-param$param_num: $parameter" );
803
+ if ( ! empty( $parameter ) && strpos( $parameter, 'http' ) === 0 ) {
804
+ $parameter_path = str_replace( $home_url, ABSPATH, $parameter );
805
+ ewwwio_debug_message( "looking for $parameter_path" );
806
+ if ( $valid_path || is_file( $parameter_path . '.webp' ) ) {
807
+ ewwwio_debug_message( "found webp for data-param$param_num: $parameter_path" );
808
+ $listitem->setAttribute( 'data-webp-param' . $param_num, $parameter . '.webp' );
809
+ }
810
+ }
811
+ }
812
+ $param_num++;
813
+ }
814
+ }
815
+ } // End foreach().
816
+ } // End if().
817
+ // Video elements, looking for poster attributes that are images.
818
+ $videos = $html->getElementsByTagName( 'video' );
819
+ if ( ewww_image_optimizer_iterable( $videos ) ) {
820
+ foreach ( $videos as $video ) {
821
+ ewwwio_debug_message( 'parsing a video element' );
822
+ if ( $link->getAttribute( 'poster' ) ) {
823
+ $file = $link->getAttribute( 'poster' );
824
+ $valid_path = false;
825
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
826
+ // Check the image for configured CDN paths.
827
+ foreach ( $webp_paths as $webp_path ) {
828
+ if ( strpos( $file, $webp_path ) !== false ) {
829
+ $valid_path = true;
830
+ }
831
+ }
832
+ }
833
+ ewwwio_debug_message( "checking webp for video poster: $file" );
834
+ $filepath = ABSPATH . str_replace( $home_url, '', $file );
835
+ if ( $valid_path || is_file( $filepath . '.webp' ) ) {
836
+ ewwwio_debug_message( "found webp for video poster: $filepath" );
837
+ $video->setAttribute( 'data-poster-webp', $file . '.webp' );
838
+ $video->setAttribute( 'data-poster-image', $file );
839
+ $video->removeAttribute( 'poster' );
840
+ }
841
+ }
842
+ }
843
+ }
844
+ ewwwio_debug_message( 'preparing to dump page back to $buffer' );
845
+ if ( ! empty( $xhtml_parse ) ) {
846
+ $buffer = $html->saveXML( $html->documentElement );
847
+ } else {
848
+ $buffer = $html->saveHTML( $html->documentElement );
849
+ }
850
+ libxml_clear_errors();
851
+ libxml_use_internal_errors( $libxml_previous_error_reporting );
852
+ if ( false ) { // Set to true for extra debugging.
853
+ ewwwio_debug_message( 'html head' );
854
+ ewwwio_debug_message( $html_head[0] );
855
+ ewwwio_debug_message( 'buffer beginning' );
856
+ ewwwio_debug_message( substr( $buffer, 0, 500 ) );
857
+ }
858
+ if ( ! empty( $html_head ) ) {
859
+ $buffer = preg_replace( '/<html.+>\s.*<head>/', $html_head[0], $buffer );
860
+ }
861
+ // Do some cleanup for the Easy Social Share Buttons for WordPress plugin (can't have <li> elements with newlines between them).
862
+ $buffer = preg_replace( '/\s(<li class="essb_item)/', '$1', $buffer );
863
+ if ( false ) { // Set to true for extra debugging.
864
+ ewwwio_debug_message( 'buffer after replacement' );
865
+ ewwwio_debug_message( substr( $buffer, 0, 500 ) );
866
+ }
867
+ } // End if().
868
+ if ( false ) { // Set to true for extra logging.
869
+ ewww_image_optimizer_debug_log();
870
+ }
871
+ return $buffer;
872
+ }
873
+
874
+ /**
875
+ * Set default permissions for manual operations (single image optimize, convert, restore).
876
+ *
877
+ * @param string $permissions A valid WP capability level.
878
+ * @return string Either the original value, unchanged, or the default capability level.
879
+ */
880
+ function ewww_image_optimizer_manual_permissions( $permissions ) {
881
+ if ( empty( $permissions ) ) {
882
+ return 'edit_others_posts';
883
+ }
884
+ return $permissions;
885
+ }
886
+
887
+ /**
888
+ * Set default permissions for admin (configuration) and bulk operations.
889
+ *
890
+ * @param string $permissions A valid WP capability level.
891
+ * @return string Either the original value, unchanged, or the default capability level.
892
+ */
893
+ function ewww_image_optimizer_admin_permissions( $permissions ) {
894
+ if ( empty( $permissions ) ) {
895
+ return 'activate_plugins';
896
+ }
897
+ return $permissions;
898
+ }
899
+
900
+ /**
901
+ * Set default permissions for multisite/network admin (configuration) operations.
902
+ *
903
+ * @param string $permissions A valid WP capability level.
904
+ * @return string Either the original value, unchanged, or the default capability level.
905
+ */
906
+ function ewww_image_optimizer_superadmin_permissions( $permissions ) {
907
+ if ( empty( $permissions ) ) {
908
+ return 'manage_network_options';
909
+ }
910
+ return $permissions;
911
+ }
912
+
913
+ if ( ! function_exists( 'boolval' ) ) {
914
+ /**
915
+ * Cast a value to boolean.
916
+ *
917
+ * @param mixed $value Any value that can be cast to boolean.
918
+ * @return bool The boolean version of the provided value.
919
+ */
920
+ function boolval( $value ) {
921
+ return (bool) $value;
922
+ }
923
+ }
924
+
925
+ /**
926
+ * Find out if set_time_limit() is allowed
927
+ */
928
+ function ewww_image_optimizer_stl_check() {
929
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
930
+ if ( ewww_image_optimizer_safemode_check() ) {
931
+ return false;
932
+ }
933
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_DISABLE_STL' ) && EWWW_IMAGE_OPTIMIZER_DISABLE_STL ) {
934
+ ewwwio_debug_message( 'stl disabled by user' );
935
+ return false;
936
+ }
937
+ return ewww_image_optimizer_function_exists( 'set_time_limit' );
938
+ }
939
+
940
+ /**
941
+ * Checks if a function is disabled or does not exist.
942
+ *
943
+ * @param string $function The name of a function to test.
944
+ * @return bool True if the function is available, False if not.
945
+ */
946
+ function ewww_image_optimizer_function_exists( $function ) {
947
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
948
+ if ( function_exists( 'ini_get' ) ) {
949
+ $disabled = @ini_get( 'disable_functions' );
950
+ ewwwio_debug_message( "disable_functions: $disabled" );
951
+ }
952
+ if ( extension_loaded( 'suhosin' ) && function_exists( 'ini_get' ) ) {
953
+ $suhosin_disabled = @ini_get( 'suhosin.executor.func.blacklist' );
954
+ ewwwio_debug_message( "suhosin_blacklist: $suhosin_disabled" );
955
+ if ( ! empty( $suhosin_disabled ) ) {
956
+ $suhosin_disabled = explode( ',', $suhosin_disabled );
957
+ $suhosin_disabled = array_map( 'trim', $suhosin_disabled );
958
+ $suhosin_disabled = array_map( 'strtolower', $suhosin_disabled );
959
+ if ( function_exists( $function ) && ! in_array( $function, $suhosin_disabled ) ) {
960
+ return true;
961
+ }
962
+ return false;
963
+ }
964
+ }
965
+ return function_exists( $function );
966
+ }
967
+
968
+ /**
969
+ * Runs on 'plugins_loaded' to make make sure the language files are loaded early.
970
+ */
971
+ function ewww_image_optimizer_preinit() {
972
+ load_plugin_textdomain( 'ewww-image-optimizer', false, EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'languages/' );
973
+ }
974
+
975
+ /**
976
+ * Checks to see if NextGEN, Nextcellent, or GRAND FlaGallery are enabled.
977
+ *
978
+ * If a supported gallery is found, loads the class(es) to support integration with that gallery.
979
+ */
980
+ function ewww_image_optimizer_gallery_support() {
981
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
982
+ $active_plugins = get_option( 'active_plugins' );
983
+ if ( is_multisite() && is_array( $active_plugins ) ) {
984
+ $sitewide_plugins = get_site_option( 'active_sitewide_plugins' );
985
+ if ( is_array( $sitewide_plugins ) ) {
986
+ $active_plugins = array_merge( $active_plugins, array_flip( $sitewide_plugins ) );
987
+ }
988
+ }
989
+ if ( ewww_image_optimizer_iterable( $active_plugins ) ) {
990
+ foreach ( $active_plugins as $active_plugin ) {
991
+ if ( strpos( $active_plugin, '/nggallery.php' ) || strpos( $active_plugin, '\nggallery.php' ) ) {
992
+ $ngg = ewww_image_optimizer_get_plugin_version( trailingslashit( WP_PLUGIN_DIR ) . $active_plugin );
993
+ // Include the file that loads the nextgen gallery optimization functions.
994
+ ewwwio_debug_message( 'Nextgen version: ' . $ngg['Version'] );
995
+ if ( preg_match( '/^2\./', $ngg['Version'] ) ) { // For Nextgen 2 support.
996
+ ewwwio_debug_message( "loading nextgen2 support for $active_plugin" );
997
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewww-nextgen.php' );
998
+ } else {
999
+ preg_match( '/\d+\.\d+\.(\d+)/', $ngg['Version'], $nextgen_minor_version );
1000
+ if ( ! empty( $nextgen_minor_version[1] ) && $nextgen_minor_version[1] < 14 ) {
1001
+ ewwwio_debug_message( "NOT loading nextgen legacy support for $active_plugin" );
1002
+ } elseif ( ! empty( $nextgen_minor_version[1] ) && $nextgen_minor_version[1] > 13 ) {
1003
+ ewwwio_debug_message( "loading nextcellent support for $active_plugin" );
1004
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewww-nextcellent.php' );
1005
+ }
1006
+ }
1007
+ }
1008
+ if ( strpos( $active_plugin, '/flag.php' ) || strpos( $active_plugin, '\flag.php' ) ) {
1009
+ ewwwio_debug_message( "loading flagallery support for $active_plugin" );
1010
+ // Include the file that loads the grand flagallery optimization functions.
1011
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewww-flag.php' );
1012
+ }
1013
+ }
1014
+ }
1015
+ }
1016
+
1017
+ /**
1018
+ * Plugin upgrade function
1019
+ *
1020
+ * @global object $wpdb
1021
+ */
1022
+ function ewww_image_optimizer_upgrade() {
1023
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1024
+ ewwwio_memory( __FUNCTION__ );
1025
+ if ( get_option( 'ewww_image_optimizer_version' ) < EWWW_IMAGE_OPTIMIZER_VERSION ) {
1026
+ if ( wp_doing_ajax() ) {
1027
+ return;
1028
+ }
1029
+ ewww_image_optimizer_enable_background_optimization();
1030
+ ewww_image_optimizer_install_table();
1031
+ ewww_image_optimizer_set_defaults();
1032
+ if ( get_option( 'ewww_image_optimizer_version' ) < 297.5 ) {
1033
+ // Cleanup background test mess.
1034
+ wp_clear_scheduled_hook( 'wp_ewwwio_test_optimize_cron' );
1035
+ global $wpdb;
1036
+
1037
+ if ( is_multisite() ) {
1038
+ $wpdb->query( "DELETE FROM $wpdb->sitemeta WHERE meta_key LIKE 'wp_ewwwio_test_optimize_batch_%'" );
1039
+ }
1040
+
1041
+ $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'wp_ewwwio_test_optimize_batch_%'" );
1042
+
1043
+ }
1044
+ if ( get_option( 'ewww_image_optimizer_version' ) < 280 ) {
1045
+ ewww_image_optimizer_migrate_settings_to_levels();
1046
+ }
1047
+ ewww_image_optimizer_remove_obsolete_settings();
1048
+ update_option( 'ewww_image_optimizer_version', EWWW_IMAGE_OPTIMIZER_VERSION );
1049
+ }
1050
+ ewwwio_memory( __FUNCTION__ );
1051
+ }
1052
+
1053
+ /**
1054
+ * Tests background optimization.
1055
+ *
1056
+ * Send a known packet to admin-ajax.php via the EWWWIO_Test_Async_Handler class.
1057
+ *
1058
+ * @global object An instance of the test async class.
1059
+ */
1060
+ function ewww_image_optimizer_enable_background_optimization() {
1061
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1062
+ if ( ewww_image_optimizer_detect_wpsf_location_lock() ) {
1063
+ return;
1064
+ }
1065
+ global $ewwwio_test_async;
1066
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
1067
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
1068
+ }
1069
+ if ( ! is_object( $ewwwio_test_async ) ) {
1070
+ $ewwwio_test_async = new EWWWIO_Test_Async_Handler();
1071
+ }
1072
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_background_optimization', false );
1073
+ ewwwio_debug_message( 'running test async handler' );
1074
+ $ewwwio_test_async->data( array(
1075
+ 'ewwwio_test_verify' => '949c34123cf2a4e4ce2f985135830df4a1b2adc24905f53d2fd3f5df5b162932',
1076
+ ) )->dispatch();
1077
+ ewww_image_optimizer_debug_log();
1078
+ }
1079
+
1080
+ /**
1081
+ * Plugin initialization for admin area.
1082
+ *
1083
+ * Saves settings when run network-wide, registers all 'common' settings, schedules wp-cron tasks,
1084
+ * includes necessary files for bulk operations, runs tool initialization, and ensures
1085
+ * compatibility with AJAX calls from other media generation plugins.
1086
+ */
1087
+ function ewww_image_optimizer_admin_init() {
1088
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1089
+ ewwwio_memory( __FUNCTION__ );
1090
+ ewww_image_optimizer_cloud_init();
1091
+ ewww_image_optimizer_upgrade();
1092
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
1093
+ // Need to include the plugin library for the is_plugin_active function.
1094
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1095
+ }
1096
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
1097
+ // Set the common network settings if they have been POSTed.
1098
+ if ( isset( $_POST['ewww_image_optimizer_delay'] ) && current_user_can( 'manage_network_options' ) && ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'ewww_image_optimizer_options-options' ) ) {
1099
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
1100
+ ewwwio_debug_message( print_r( $_POST, true ) );
1101
+ }
1102
+ $_POST['ewww_image_optimizer_debug'] = ( empty( $_POST['ewww_image_optimizer_debug'] ) ? false : true );
1103
+ update_site_option( 'ewww_image_optimizer_debug', $_POST['ewww_image_optimizer_debug'] );
1104
+ $_POST['ewww_image_optimizer_jpegtran_copy'] = ( empty( $_POST['ewww_image_optimizer_jpegtran_copy'] ) ? false : true );
1105
+ update_site_option( 'ewww_image_optimizer_jpegtran_copy', $_POST['ewww_image_optimizer_jpegtran_copy'] );
1106
+ $_POST['ewww_image_optimizer_jpg_level'] = empty( $_POST['ewww_image_optimizer_jpg_level'] ) ? '' : $_POST['ewww_image_optimizer_jpg_level'];
1107
+ update_site_option( 'ewww_image_optimizer_jpg_level', (int) $_POST['ewww_image_optimizer_jpg_level'] );
1108
+ $_POST['ewww_image_optimizer_png_level'] = empty( $_POST['ewww_image_optimizer_png_level'] ) ? '' : $_POST['ewww_image_optimizer_png_level'];
1109
+ update_site_option( 'ewww_image_optimizer_png_level', (int) $_POST['ewww_image_optimizer_png_level'] );
1110
+ $_POST['ewww_image_optimizer_gif_level'] = empty( $_POST['ewww_image_optimizer_gif_level'] ) ? '' : $_POST['ewww_image_optimizer_gif_level'];
1111
+ update_site_option( 'ewww_image_optimizer_gif_level', (int) $_POST['ewww_image_optimizer_gif_level'] );
1112
+ $_POST['ewww_image_optimizer_pdf_level'] = empty( $_POST['ewww_image_optimizer_pdf_level'] ) ? '' : $_POST['ewww_image_optimizer_pdf_level'];
1113
+ update_site_option( 'ewww_image_optimizer_pdf_level', (int) $_POST['ewww_image_optimizer_pdf_level'] );
1114
+ $_POST['ewww_image_optimizer_lossy_skip_full'] = ( empty( $_POST['ewww_image_optimizer_lossy_skip_full'] ) ? false : true );
1115
+ update_site_option( 'ewww_image_optimizer_lossy_skip_full', $_POST['ewww_image_optimizer_lossy_skip_full'] );
1116
+ $_POST['ewww_image_optimizer_metadata_skip_full'] = ( empty( $_POST['ewww_image_optimizer_metadata_skip_full'] ) ? false : true );
1117
+ update_site_option( 'ewww_image_optimizer_metadata_skip_full', $_POST['ewww_image_optimizer_metadata_skip_full'] );
1118
+ $_POST['ewww_image_optimizer_delete_originals'] = ( empty( $_POST['ewww_image_optimizer_delete_originals'] ) ? false : true );
1119
+ update_site_option( 'ewww_image_optimizer_delete_originals', $_POST['ewww_image_optimizer_delete_originals'] );
1120
+ $_POST['ewww_image_optimizer_jpg_to_png'] = ( empty( $_POST['ewww_image_optimizer_jpg_to_png'] ) ? false : true );
1121
+ update_site_option( 'ewww_image_optimizer_jpg_to_png', $_POST['ewww_image_optimizer_jpg_to_png'] );
1122
+ $_POST['ewww_image_optimizer_png_to_jpg'] = ( empty( $_POST['ewww_image_optimizer_png_to_jpg'] ) ? false : true );
1123
+ update_site_option( 'ewww_image_optimizer_png_to_jpg', $_POST['ewww_image_optimizer_png_to_jpg'] );
1124
+ $_POST['ewww_image_optimizer_gif_to_png'] = ( empty( $_POST['ewww_image_optimizer_gif_to_png'] ) ? false : true );
1125
+ update_site_option( 'ewww_image_optimizer_gif_to_png', $_POST['ewww_image_optimizer_gif_to_png'] );
1126
+ $_POST['ewww_image_optimizer_webp'] = ( empty( $_POST['ewww_image_optimizer_webp'] ) ? false : true );
1127
+ update_site_option( 'ewww_image_optimizer_webp', $_POST['ewww_image_optimizer_webp'] );
1128
+ $_POST['ewww_image_optimizer_jpg_background'] = empty( $_POST['ewww_image_optimizer_jpg_background'] ) ? '' : $_POST['ewww_image_optimizer_jpg_background'];
1129
+ update_site_option( 'ewww_image_optimizer_jpg_background', ewww_image_optimizer_jpg_background( $_POST['ewww_image_optimizer_jpg_background'] ) );
1130
+ $_POST['ewww_image_optimizer_jpg_quality'] = empty( $_POST['ewww_image_optimizer_jpg_quality'] ) ? '' : $_POST['ewww_image_optimizer_jpg_quality'];
1131
+ update_site_option( 'ewww_image_optimizer_jpg_quality', ewww_image_optimizer_jpg_quality( $_POST['ewww_image_optimizer_jpg_quality'] ) );
1132
+ $_POST['ewww_image_optimizer_disable_convert_links'] = ( empty( $_POST['ewww_image_optimizer_disable_convert_links'] ) ? false : true );
1133
+ update_site_option( 'ewww_image_optimizer_disable_convert_links', $_POST['ewww_image_optimizer_disable_convert_links'] );
1134
+ $_POST['ewww_image_optimizer_backup_files'] = ( empty( $_POST['ewww_image_optimizer_backup_files'] ) ? false : true );
1135
+ update_site_option( 'ewww_image_optimizer_backup_files', $_POST['ewww_image_optimizer_backup_files'] );
1136
+ $_POST['ewww_image_optimizer_cloud_key'] = empty( $_POST['ewww_image_optimizer_cloud_key'] ) ? '' : $_POST['ewww_image_optimizer_cloud_key'];
1137
+ update_site_option( 'ewww_image_optimizer_cloud_key', ewww_image_optimizer_cloud_key_sanitize( $_POST['ewww_image_optimizer_cloud_key'] ) );
1138
+ $_POST['ewww_image_optimizer_auto'] = ( empty( $_POST['ewww_image_optimizer_auto'] ) ? false : true );
1139
+ update_site_option( 'ewww_image_optimizer_auto', $_POST['ewww_image_optimizer_auto'] );
1140
+ $_POST['ewww_image_optimizer_aux_paths'] = empty( $_POST['ewww_image_optimizer_aux_paths'] ) ? '' : $_POST['ewww_image_optimizer_aux_paths'];
1141
+ update_site_option( 'ewww_image_optimizer_aux_paths', ewww_image_optimizer_aux_paths_sanitize( $_POST['ewww_image_optimizer_aux_paths'] ) );
1142
+ $_POST['ewww_image_optimizer_exclude_paths'] = empty( $_POST['ewww_image_optimizer_exclude_paths'] ) ? '' : $_POST['ewww_image_optimizer_exclude_paths'];
1143
+ update_site_option( 'ewww_image_optimizer_exclude_paths', ewww_image_optimizer_exclude_paths_sanitize( $_POST['ewww_image_optimizer_exclude_paths'] ) );
1144
+ $_POST['ewww_image_optimizer_enable_cloudinary'] = ( empty( $_POST['ewww_image_optimizer_enable_cloudinary'] ) ? false : true );
1145
+ update_site_option( 'ewww_image_optimizer_enable_cloudinary', $_POST['ewww_image_optimizer_enable_cloudinary'] );
1146
+ $_POST['ewww_image_optimizer_delay'] = empty( $_POST['ewww_image_optimizer_delay'] ) ? '' : $_POST['ewww_image_optimizer_delay'];
1147
+ update_site_option( 'ewww_image_optimizer_delay', (int) $_POST['ewww_image_optimizer_delay'] );
1148
+ $_POST['ewww_image_optimizer_maxmediawidth'] = empty( $_POST['ewww_image_optimizer_maxmediawidth'] ) ? 0 : $_POST['ewww_image_optimizer_maxmediawidth'];
1149
+ update_site_option( 'ewww_image_optimizer_maxmediawidth', (int) $_POST['ewww_image_optimizer_maxmediawidth'] );
1150
+ $_POST['ewww_image_optimizer_maxmediaheight'] = empty( $_POST['ewww_image_optimizer_maxmediaheight'] ) ? 0 : $_POST['ewww_image_optimizer_maxmediaheight'];
1151
+ update_site_option( 'ewww_image_optimizer_maxmediaheight', (int) $_POST['ewww_image_optimizer_maxmediaheight'] );
1152
+ $_POST['ewww_image_optimizer_maxotherwidth'] = empty( $_POST['ewww_image_optimizer_maxotherwidth'] ) ? 0 : $_POST['ewww_image_optimizer_maxotherwidth'];
1153
+ update_site_option( 'ewww_image_optimizer_maxotherwidth', (int) $_POST['ewww_image_optimizer_maxotherwidth'] );
1154
+ $_POST['ewww_image_optimizer_maxotherheight'] = empty( $_POST['ewww_image_optimizer_maxotherheight'] ) ? 0 : $_POST['ewww_image_optimizer_maxotherheight'];
1155
+ update_site_option( 'ewww_image_optimizer_maxotherheight', (int) $_POST['ewww_image_optimizer_maxotherheight'] );
1156
+ $_POST['ewww_image_optimizer_resize_detection'] = ( empty( $_POST['ewww_image_optimizer_resize_detection'] ) ? false : true );
1157
+ update_site_option( 'ewww_image_optimizer_resize_detection', $_POST['ewww_image_optimizer_resize_detection'] );
1158
+ $_POST['ewww_image_optimizer_resize_existing'] = ( empty( $_POST['ewww_image_optimizer_resize_existing'] ) ? false : true );
1159
+ update_site_option( 'ewww_image_optimizer_resize_existing', $_POST['ewww_image_optimizer_resize_existing'] );
1160
+ $_POST['ewww_image_optimizer_skip_size'] = empty( $_POST['ewww_image_optimizer_skip_size'] ) ? '' : $_POST['ewww_image_optimizer_skip_size'];
1161
+ update_site_option( 'ewww_image_optimizer_skip_size', (int) $_POST['ewww_image_optimizer_skip_size'] );
1162
+ $_POST['ewww_image_optimizer_skip_png_size'] = empty( $_POST['ewww_image_optimizer_skip_png_size'] ) ? '' : $_POST['ewww_image_optimizer_skip_png_size'];
1163
+ update_site_option( 'ewww_image_optimizer_skip_png_size', (int) $_POST['ewww_image_optimizer_skip_png_size'] );
1164
+ $_POST['ewww_image_optimizer_parallel_optimization'] = ( empty( $_POST['ewww_image_optimizer_parallel_optimization'] ) ? false : true );
1165
+ update_site_option( 'ewww_image_optimizer_parallel_optimization', $_POST['ewww_image_optimizer_parallel_optimization'] );
1166
+ $_POST['ewww_image_optimizer_include_media_paths'] = ( empty( $_POST['ewww_image_optimizer_include_media_paths'] ) ? false : true );
1167
+ update_site_option( 'ewww_image_optimizer_include_media_paths', $_POST['ewww_image_optimizer_include_media_paths'] );
1168
+ $_POST['ewww_image_optimizer_webp_for_cdn'] = ( empty( $_POST['ewww_image_optimizer_webp_for_cdn'] ) ? false : true );
1169
+ update_site_option( 'ewww_image_optimizer_webp_for_cdn', $_POST['ewww_image_optimizer_webp_for_cdn'] );
1170
+ $_POST['ewww_image_optimizer_webp_force'] = ( empty( $_POST['ewww_image_optimizer_webp_force'] ) ? false : true );
1171
+ update_site_option( 'ewww_image_optimizer_webp_force', $_POST['ewww_image_optimizer_webp_force'] );
1172
+ $_POST['ewww_image_optimizer_webp_paths'] = ( empty( $_POST['ewww_image_optimizer_webp_paths'] ) ? '' : $_POST['ewww_image_optimizer_webp_paths'] );
1173
+ update_site_option( 'ewww_image_optimizer_webp_paths', ewww_image_optimizer_webp_paths_sanitize( $_POST['ewww_image_optimizer_webp_paths'] ) );
1174
+ $_POST['ewww_image_optimizer_allow_multisite_override'] = empty( $_POST['ewww_image_optimizer_allow_multisite_override'] ) ? false : true;
1175
+ update_site_option( 'ewww_image_optimizer_allow_multisite_override', $_POST['ewww_image_optimizer_allow_multisite_override'] );
1176
+ $_POST['ewww_image_optimizer_enable_help'] = empty( $_POST['ewww_image_optimizer_enable_help'] ) ? false : true;
1177
+ update_site_option( 'ewww_image_optimizer_enable_help', $_POST['ewww_image_optimizer_enable_help'] );
1178
+ global $ewwwio_tracking;
1179
+ $_POST['ewww_image_optimizer_allow_tracking'] = empty( $_POST['ewww_image_optimizer_allow_tracking'] ) ? false : $ewwwio_tracking->check_for_settings_optin( $_POST['ewww_image_optimizer_allow_tracking'] );
1180
+ update_site_option( 'ewww_image_optimizer_allow_tracking', $_POST['ewww_image_optimizer_allow_tracking'] );
1181
+ add_action( 'network_admin_notices', 'ewww_image_optimizer_network_settings_saved' );
1182
+ } elseif ( isset( $_POST['ewww_image_optimizer_allow_multisite_override_active'] ) && current_user_can( 'manage_network_options' ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'ewww_image_optimizer_options-options' ) ) {
1183
+ $_POST['ewww_image_optimizer_allow_multisite_override'] = empty( $_POST['ewww_image_optimizer_allow_multisite_override'] ) ? false : true;
1184
+ update_site_option( 'ewww_image_optimizer_allow_multisite_override', $_POST['ewww_image_optimizer_allow_multisite_override'] );
1185
+ global $ewwwio_tracking;
1186
+ $_POST['ewww_image_optimizer_allow_tracking'] = empty( $_POST['ewww_image_optimizer_allow_tracking'] ) ? false : $ewwwio_tracking->check_for_settings_optin( $_POST['ewww_image_optimizer_allow_tracking'] );
1187
+ update_site_option( 'ewww_image_optimizer_allow_tracking', $_POST['ewww_image_optimizer_allow_tracking'] );
1188
+ add_action( 'network_admin_notices', 'ewww_image_optimizer_network_settings_saved' );
1189
+ } // End if().
1190
+ } // End if().
1191
+ if ( is_multisite() && get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) &&
1192
+ ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) &&
1193
+ ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) &&
1194
+ ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) &&
1195
+ ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' )
1196
+ ) {
1197
+ ewww_image_optimizer_set_defaults();
1198
+ update_option( 'ewww_image_optimizer_disable_pngout', true );
1199
+ update_option( 'ewww_image_optimizer_optipng_level', 2 );
1200
+ update_option( 'ewww_image_optimizer_pngout_level', 2 );
1201
+ update_option( 'ewww_image_optimizer_jpegtran_copy', true );
1202
+ update_option( 'ewww_image_optimizer_jpg_level', '10' );
1203
+ update_option( 'ewww_image_optimizer_png_level', '10' );
1204
+ update_option( 'ewww_image_optimizer_gif_level', '10' );
1205
+ }
1206
+ // Register all the common EWWW IO settings.
1207
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_debug', 'boolval' );
1208
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_jpegtran_copy', 'boolval' );
1209
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_jpg_level', 'intval' );
1210
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_png_level', 'intval' );
1211
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_gif_level', 'intval' );
1212
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_pdf_level', 'intval' );
1213
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_lossy_skip_full', 'boolval' );
1214
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_metadata_skip_full', 'boolval' );
1215
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_delete_originals', 'boolval' );
1216
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_jpg_to_png', 'boolval' );
1217
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_png_to_jpg', 'boolval' );
1218
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_gif_to_png', 'boolval' );
1219
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_webp', 'boolval' );
1220
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_jpg_background', 'ewww_image_optimizer_jpg_background' );
1221
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_jpg_quality', 'ewww_image_optimizer_jpg_quality' );
1222
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_disable_convert_links', 'boolval' );
1223
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_backup_files', 'boolval' );
1224
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_cloud_key', 'ewww_image_optimizer_cloud_key_sanitize' );
1225
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_auto', 'boolval' );
1226
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_aux_paths', 'ewww_image_optimizer_aux_paths_sanitize' );
1227
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_exclude_paths', 'ewww_image_optimizer_exclude_paths_sanitize' );
1228
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_enable_cloudinary', 'boolval' );
1229
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_delay', 'intval' );
1230
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_maxmediawidth', 'intval' );
1231
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_maxmediaheight', 'intval' );
1232
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_maxotherwidth', 'intval' );
1233
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_maxotherheight', 'intval' );
1234
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_resize_detection', 'boolval' );
1235
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_resize_existing', 'boolval' );
1236
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_disable_resizes', 'ewww_image_optimizer_disable_resizes_sanitize' );
1237
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_disable_resizes_opt', 'ewww_image_optimizer_disable_resizes_sanitize' );
1238
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_skip_size', 'intval' );
1239
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_skip_png_size', 'intval' );
1240
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_parallel_optimization', 'boolval' );
1241
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_include_media_paths', 'boolval' );
1242
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_webp_for_cdn', 'boolval' );
1243
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_webp_force', 'boolval' );
1244
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_webp_paths', 'ewww_image_optimizer_webp_paths_sanitize' );
1245
+ global $ewwwio_tracking;
1246
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_allow_tracking', array( $ewwwio_tracking, 'check_for_settings_optin' ) );
1247
+ register_setting( 'ewww_image_optimizer_options', 'ewww_image_optimizer_enable_help', 'boolval' );
1248
+ ewww_image_optimizer_exec_init();
1249
+ ewww_image_optimizer_cron_setup( 'ewww_image_optimizer_auto' );
1250
+ /**
1251
+ * Require the file that does the bulk processing.
1252
+ */
1253
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'bulk.php' );
1254
+ /**
1255
+ * Require the files that contain functions for the images table and bulk processing images outside the library.
1256
+ */
1257
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'aux-optimize.php' );
1258
+ /**
1259
+ * Require the files that migrate WebP images from extension replacement to extension appending.
1260
+ */
1261
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'mwebp.php' );
1262
+ // Queue the function that contains custom styling for our progressbars.
1263
+ add_action( 'admin_enqueue_scripts', 'ewww_image_optimizer_progressbar_style' );
1264
+ // Alert user if multiple re-optimizations detected.
1265
+ add_action( 'network_admin_notices', 'ewww_image_optimizer_notice_reoptimization' );
1266
+ add_action( 'admin_notices', 'ewww_image_optimizer_notice_reoptimization' );
1267
+ ewww_image_optimizer_ajax_compat_check();
1268
+ ewwwio_memory( __FUNCTION__ );
1269
+ }
1270
+
1271
+ /**
1272
+ * Setup wp_cron tasks for scheduled optimization.
1273
+ *
1274
+ * @global object $wpdb
1275
+ *
1276
+ * @param string $event Name of cron hook to schedule.
1277
+ */
1278
+ function ewww_image_optimizer_cron_setup( $event ) {
1279
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1280
+ // Setup scheduled optimization if the user has enabled it, and it isn't already scheduled.
1281
+ if ( ewww_image_optimizer_get_option( $event ) == true && ! wp_next_scheduled( $event ) ) {
1282
+ ewwwio_debug_message( "scheduling $event" );
1283
+ wp_schedule_event( time(), apply_filters( 'ewww_image_optimizer_schedule', 'hourly', $event ), $event );
1284
+ } elseif ( ewww_image_optimizer_get_option( $event ) == true ) {
1285
+ ewwwio_debug_message( "$event already scheduled: " . wp_next_scheduled( $event ) );
1286
+ } elseif ( wp_next_scheduled( $event ) ) {
1287
+ ewwwio_debug_message( "un-scheduling $event" );
1288
+ wp_clear_scheduled_hook( $event );
1289
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
1290
+ // Need to include the plugin library for the is_plugin_active function.
1291
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1292
+ }
1293
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
1294
+ global $wpdb;
1295
+ $blogs = $wpdb->get_results( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d", $wpdb->siteid ), ARRAY_A );
1296
+ if ( ewww_image_optimizer_iterable( $blogs ) ) {
1297
+ foreach ( $blogs as $blog ) {
1298
+ switch_to_blog( $blog['blog_id'] );
1299
+ wp_clear_scheduled_hook( $event );
1300
+ restore_current_blog();
1301
+ }
1302
+ }
1303
+ }
1304
+ }
1305
+ }
1306
+
1307
+ /**
1308
+ * Checks to see if this is an AJAX request, and whether the WP_Image_Editor hooks should be undone.
1309
+ *
1310
+ * @since 3.3.0
1311
+ */
1312
+ function ewww_image_optimizer_ajax_compat_check() {
1313
+ if ( ! wp_doing_ajax() ) {
1314
+ return;
1315
+ }
1316
+ // Check for (Force) Regenerate Thumbnails action (includes MLP regnerate).
1317
+ if ( ! empty( $_REQUEST['action'] ) ) {
1318
+ if ( 'regeneratethumbnail' == $_REQUEST['action'] ||
1319
+ 'meauh_save_image' == $_REQUEST['action'] ||
1320
+ 'hotspot_save' == $_REQUEST['action']
1321
+ ) {
1322
+ ewww_image_optimizer_image_sizes( false );
1323
+ }
1324
+ }
1325
+ // Check for other MLP actions, including multi-regen.
1326
+ if ( ! empty( $_REQUEST['action'] ) && class_exists( 'MaxGalleriaMediaLib' ) && ( 'regen_mlp_thumbnails' == $_REQUEST['action'] || 'move_media' == $_REQUEST['action'] || 'copy_media' == $_REQUEST['action'] || 'maxgalleria_rename_image' == $_REQUEST['action'] ) ) {
1327
+ ewww_image_optimizer_image_sizes( false );
1328
+ }
1329
+ // Check for MLP upload.
1330
+ if ( ! empty( $_REQUEST['action'] ) && class_exists( 'MaxGalleriaMediaLib' ) && ! empty( $_REQUEST['nonce'] ) && 'upload_attachment' == $_REQUEST['action'] ) {
1331
+ ewww_image_optimizer_image_sizes( false );
1332
+ }
1333
+ }
1334
+
1335
+ if ( ! function_exists( 'wp_doing_ajax' ) ) {
1336
+ /**
1337
+ * Checks to see if this is an AJAX request.
1338
+ *
1339
+ * For backwards compatiblity with WordPress < 4.7.0.
1340
+ *
1341
+ * @since 3.3.0
1342
+ *
1343
+ * @return bool True if this is an AJAX request.
1344
+ */
1345
+ function wp_doing_ajax() {
1346
+ return apply_filters( 'wp_doing_ajax', defined( 'DOING_AJAX' ) && DOING_AJAX );
1347
+ }
1348
+ }
1349
+
1350
+ /**
1351
+ * Disables all the local tools by setting their constants to false.
1352
+ */
1353
+ function ewww_image_optimizer_disable_tools() {
1354
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1355
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN' ) ) {
1356
+ define( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN', false );
1357
+ }
1358
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_OPTIPNG' ) ) {
1359
+ define( 'EWWW_IMAGE_OPTIMIZER_OPTIPNG', false );
1360
+ }
1361
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_PNGOUT' ) ) {
1362
+ define( 'EWWW_IMAGE_OPTIMIZER_PNGOUT', false );
1363
+ }
1364
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_GIFSICLE' ) ) {
1365
+ define( 'EWWW_IMAGE_OPTIMIZER_GIFSICLE', false );
1366
+ }
1367
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_PNGQUANT' ) ) {
1368
+ define( 'EWWW_IMAGE_OPTIMIZER_PNGQUANT', false );
1369
+ }
1370
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_WEBP' ) ) {
1371
+ define( 'EWWW_IMAGE_OPTIMIZER_WEBP', false );
1372
+ }
1373
+ ewwwio_memory( __FUNCTION__ );
1374
+ }
1375
+
1376
+ /**
1377
+ * Generates css include for progressbars to match admin style.
1378
+ */
1379
+ function ewww_image_optimizer_progressbar_style() {
1380
+ wp_add_inline_style( 'jquery-ui-progressbar', '.ui-widget-header { background-color: ' . ewww_image_optimizer_admin_background() . '; }' );
1381
+ ewwwio_memory( __FUNCTION__ );
1382
+ }
1383
+
1384
+ /**
1385
+ * Determines the background color to use based on the selected admin theme.
1386
+ */
1387
+ function ewww_image_optimizer_admin_background() {
1388
+ if ( function_exists( 'wp_add_inline_style' ) ) {
1389
+ $user_info = wp_get_current_user();
1390
+ switch ( $user_info->admin_color ) {
1391
+ case 'midnight':
1392
+ return '#e14d43';
1393
+ case 'blue':
1394
+ return '#096484';
1395
+ case 'light':
1396
+ return '#04a4cc';
1397
+ case 'ectoplasm':
1398
+ return '#a3b745';
1399
+ case 'coffee':
1400
+ return '#c7a589';
1401
+ case 'ocean':
1402
+ return '#9ebaa0';
1403
+ case 'sunrise':
1404
+ return '#dd823b';
1405
+ default:
1406
+ return '#0073aa';
1407
+ }
1408
+ }
1409
+ ewwwio_memory( __FUNCTION__ );
1410
+ }
1411
+
1412
+ /**
1413
+ * If a multisite is over 1000 sites, tells WP this is a 'large network' when querying image stats.
1414
+ *
1415
+ * @param bool $large_network Normally only true with 10,000+ users or sites.
1416
+ * @param string $criteria The criteria for determining a large network, 'sites' or 'users'.
1417
+ * @param int $count The number of sites/users.
1418
+ * @return bool True if this is a 'large network'.
1419
+ */
1420
+ function ewww_image_optimizer_large_network( $large_network, $criteria, $count ) {
1421
+ if ( 'sites' == $criteria && $count > 1000 ) {
1422
+ return true;
1423
+ }
1424
+ return false;
1425
+ }
1426
+
1427
+ /**
1428
+ * Adds/upgrades table in db for storing status of all images that have been optimized.
1429
+ *
1430
+ * @global object $wpdb
1431
+ */
1432
+ function ewww_image_optimizer_install_table() {
1433
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1434
+ global $wpdb;
1435
+ $wpdb->ewwwio_images = $wpdb->prefix . 'ewwwio_images';
1436
+
1437
+ // Get the current wpdb charset and collation.
1438
+ $db_collation = $wpdb->get_charset_collate();
1439
+ ewwwio_debug_message( "current collation: $db_collation" );
1440
+
1441
+ // See if the path column exists, and what collation it uses to determine the column index size.
1442
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->ewwwio_images'" ) == $wpdb->ewwwio_images ) {
1443
+ ewwwio_debug_message( 'upgrading table and checking collation for path, table exists' );
1444
+ $wpdb->query( "UPDATE $wpdb->ewwwio_images SET updated = '1971-01-01 00:00:00' WHERE updated < '1001-01-01 00:00:01'" );
1445
+ $column_collate = $wpdb->get_col_charset( $wpdb->ewwwio_images, 'path' );
1446
+ if ( ! empty( $column_collate ) && 'utf8mb4' !== $column_collate ) {
1447
+ $path_index_size = 255;
1448
+ ewwwio_debug_message( "current column collation: $column_collate" );
1449
+ if ( strpos( $column_collate, 'utf8' ) === false ) {
1450
+ ewwwio_debug_message( 'converting path column to utf8' );
1451
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images CHANGE path path BLOB" );
1452
+ if ( $wpdb->has_cap( 'utf8mb4_520' && strpos( $db_collation, 'utf8mb4' ) ) ) {
1453
+ ewwwio_debug_message( 'using mb4 version 5.20' );
1454
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images DROP INDEX path_image_size" );
1455
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images CONVERT TO CHARACTER SET utf8mb4, CHANGE path path TEXT" );
1456
+ unset( $path_index_size );
1457
+ } elseif ( $wpdb->has_cap( 'utf8mb4' ) && strpos( $db_collation, 'utf8mb4' ) ) {
1458
+ ewwwio_debug_message( 'using mb4 version 4' );
1459
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images DROP INDEX path_image_size" );
1460
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images CONVERT TO CHARACTER SET utf8mb4, CHANGE path path TEXT" );
1461
+ unset( $path_index_size );
1462
+ } else {
1463
+ ewwwio_debug_message( 'using plain old utf8' );
1464
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images CONVERT TO CHARACTER SET utf8, CHANGE path path TEXT" );
1465
+ }
1466
+ } elseif ( strpos( $column_collate, 'utf8mb4' ) === false && strpos( $db_collation, 'utf8mb4' ) ) {
1467
+ if ( $wpdb->has_cap( 'utf8mb4_520' ) ) {
1468
+ ewwwio_debug_message( 'using mb4 version 5.20' );
1469
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images DROP INDEX path_image_size" );
1470
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images CONVERT TO CHARACTER SET utf8mb4, CHANGE path path TEXT" );
1471
+ unset( $path_index_size );
1472
+ } elseif ( $wpdb->has_cap( 'utf8mb4' ) ) {
1473
+ ewwwio_debug_message( 'using mb4 version 4' );
1474
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images DROP INDEX path_image_size" );
1475
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images CONVERT TO CHARACTER SET utf8mb4, CHANGE path path TEXT" );
1476
+ unset( $path_index_size );
1477
+ }
1478
+ }
1479
+ }
1480
+ } // End if().
1481
+
1482
+ // If the path column doesn't yet exist, and the default collation is utf8mb4, then we need to lower the column index size.
1483
+ if ( empty( $path_index_size ) && strpos( $db_collation, 'utf8mb4' ) ) {
1484
+ $path_index_size = 191;
1485
+ } else {
1486
+ $path_index_size = 255;
1487
+ }
1488
+ ewwwio_debug_message( "path index size: $path_index_size" );
1489
+
1490
+ /*
1491
+ * Create a table with 15 columns:
1492
+ * id: unique for each record/image,
1493
+ * attachment_id: the unique id within the media library, nextgen, or flag
1494
+ * gallery: 'media', 'nextgen', 'nextcell', or 'flag',
1495
+ * resize: size of the image,
1496
+ * path: filename of the image, potentially replaced with ABSPATH or WP_CONTENT_DIR,
1497
+ * converted: filename of the image before conversion,
1498
+ * results: human-readable savings message,
1499
+ * image_size: optimized size of the image,
1500
+ * orig_size: original size of the image,
1501
+ * backup: hash where the image is stored on the API servers,
1502
+ * level: the optimization level used on the image,
1503
+ * pending: 1 if the image is queued for optimization,
1504
+ * updates: how many times an image has been optimized,
1505
+ * updated: when the image was last optimized,
1506
+ * trace: tracelog from the last optimization if debugging was enabled.
1507
+ */
1508
+ $sql = "CREATE TABLE $wpdb->ewwwio_images (
1509
+ id int(14) unsigned NOT NULL AUTO_INCREMENT,
1510
+ attachment_id bigint(20) unsigned,
1511
+ gallery varchar(10),
1512
+ resize varchar(75),
1513
+ path text NOT NULL,
1514
+ converted text NOT NULL,
1515
+ results varchar(75) NOT NULL,
1516
+ image_size int(10) unsigned,
1517
+ orig_size int(10) unsigned,
1518
+ backup varchar(100),
1519
+ level int(5) unsigned,
1520
+ pending tinyint(1) NOT NULL DEFAULT 0,
1521
+ updates int(5) unsigned,
1522
+ updated timestamp DEFAULT '1971-01-01 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
1523
+ trace blob,
1524
+ UNIQUE KEY id (id),
1525
+ KEY path_image_size (path($path_index_size),image_size),
1526
+ KEY attachment_info (gallery(3),attachment_id)
1527
+ ) $db_collation;";
1528
+
1529
+ // Include the upgrade library to install/upgrade a table.
1530
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1531
+ $updates = dbDelta( $sql );
1532
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
1533
+ ewwwio_debug_message( 'db upgrade results: ' . print_r( $updates, true ) );
1534
+ }
1535
+
1536
+ // Make sure some of our options are not autoloaded (since they can be huge).
1537
+ $bulk_attachments = get_option( 'ewww_image_optimizer_bulk_attachments', '' );
1538
+ delete_option( 'ewww_image_optimizer_bulk_attachments' );
1539
+ add_option( 'ewww_image_optimizer_bulk_attachments', $bulk_attachments, '', 'no' );
1540
+ $bulk_attachments = get_option( 'ewww_image_optimizer_flag_attachments', '' );
1541
+ delete_option( 'ewww_image_optimizer_flag_attachments' );
1542
+ add_option( 'ewww_image_optimizer_flag_attachments', $bulk_attachments, '', 'no' );
1543
+ $bulk_attachments = get_option( 'ewww_image_optimizer_ngg_attachments', '' );
1544
+ delete_option( 'ewww_image_optimizer_ngg_attachments' );
1545
+ add_option( 'ewww_image_optimizer_ngg_attachments', $bulk_attachments, '', 'no' );
1546
+ delete_option( 'ewww_image_optimizer_aux_attachments' );
1547
+ delete_option( 'ewww_image_optimizer_defer_attachments' );
1548
+ }
1549
+
1550
+ /**
1551
+ * Migrates old cloud/compression settings to compression levels.
1552
+ */
1553
+ function ewww_image_optimizer_migrate_settings_to_levels() {
1554
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1555
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_jpegtran' ) ) {
1556
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_jpg_level', 0 );
1557
+ }
1558
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_jpegtran' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
1559
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_jpg_level', 10 );
1560
+ }
1561
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_jpg' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_lossy' ) ) {
1562
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_jpg_level', 20 );
1563
+ }
1564
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_jpg' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_lossy' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_fast' ) ) {
1565
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_jpg_level', 30 );
1566
+ }
1567
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_jpg' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_lossy' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_fast' ) ) {
1568
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_jpg_level', 40 );
1569
+ }
1570
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_optipng' ) ) {
1571
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 0 );
1572
+ }
1573
+ if ( ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) || ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_optipng' ) ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
1574
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 10 );
1575
+ }
1576
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_png' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_lossy' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_png_compress' ) ) {
1577
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 20 );
1578
+ }
1579
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_png' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_lossy' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_png_compress' ) ) {
1580
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 30 );
1581
+ }
1582
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_png' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_lossy' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_fast' ) ) {
1583
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 40 );
1584
+ }
1585
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_png' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_lossy' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_fast' ) ) {
1586
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 50 );
1587
+ }
1588
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_gifsicle' ) ) {
1589
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_gif_level', 0 );
1590
+ }
1591
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_gifsicle' ) ) {
1592
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_gif_level', 10 );
1593
+ }
1594
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_pdf' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_lossy' ) ) {
1595
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_pdf_level', 10 );
1596
+ }
1597
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_pdf' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_lossy' ) ) {
1598
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_pdf_level', 20 );
1599
+ }
1600
+ }
1601
+
1602
+ /**
1603
+ * Removes settings which are no longer used.
1604
+ */
1605
+ function ewww_image_optimizer_remove_obsolete_settings() {
1606
+ delete_option( 'ewww_image_optimizer_disable_jpegtran' );
1607
+ delete_option( 'ewww_image_optimizer_cloud_jpg' );
1608
+ delete_option( 'ewww_image_optimizer_jpg_lossy' );
1609
+ delete_option( 'ewww_image_optimizer_lossy_fast' );
1610
+ delete_option( 'ewww_image_optimizer_cloud_png' );
1611
+ delete_option( 'ewww_image_optimizer_png_lossy' );
1612
+ delete_option( 'ewww_image_optimizer_cloud_png_compress' );
1613
+ delete_option( 'ewww_image_optimizer_disable_gifsicle' );
1614
+ delete_option( 'ewww_image_optimizer_cloud_gif' );
1615
+ delete_option( 'ewww_image_optimizer_cloud_pdf' );
1616
+ delete_option( 'ewww_image_optimizer_pdf_lossy' );
1617
+ delete_option( 'ewww_image_optimizer_skip_check' );
1618
+ delete_option( 'ewww_image_optimizer_disable_optipng' );
1619
+ delete_option( 'ewww_image_optimizer_interval' );
1620
+ delete_option( 'ewww_image_optimizer_jpegtran_path' );
1621
+ delete_option( 'ewww_image_optimizer_optipng_path' );
1622
+ delete_option( 'ewww_image_optimizer_gifsicle_path' );
1623
+ delete_option( 'ewww_image_optimizer_import_status' );
1624
+ delete_option( 'ewww_image_optimizer_bulk_image_count' );
1625
+ delete_option( 'ewww_image_optimizer_maxwidth' );
1626
+ delete_option( 'ewww_image_optimizer_maxheight' );
1627
+ }
1628
+
1629
+ /**
1630
+ * Lets the user know their network settings have been saved.
1631
+ */
1632
+ function ewww_image_optimizer_network_settings_saved() {
1633
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1634
+ echo "<div id='ewww-image-optimizer-settings-saved' class='updated fade'><p><strong>" . esc_html__( 'Settings saved', 'ewww-image-optimizer' ) . '.</strong></p></div>';
1635
+ }
1636
+
1637
+ /**
1638
+ * Alert the user when 5 images have been re-optimized more than 10 times.
1639
+ *
1640
+ * @global object $wpdb
1641
+ */
1642
+ function ewww_image_optimizer_notice_reoptimization() {
1643
+ // Allows the user to reset all images back to 1 optimization, which clears the alert.
1644
+ if ( ! empty( $_GET['ewww_reset_reopt_nonce'] ) && wp_verify_nonce( $_GET['ewww_reset_reopt_nonce'], 'reset_reoptimization_counters' ) ) {
1645
+ global $wpdb;
1646
+ $debug_images = $wpdb->query( "UPDATE $wpdb->ewwwio_images SET updates=1 WHERE updates > 1" );
1647
+ delete_transient( 'ewww_image_optimizer_images_reoptimized' );
1648
+ } else {
1649
+ $reoptimized = get_transient( 'ewww_image_optimizer_images_reoptimized' );
1650
+ if ( empty( $reoptimized ) ) {
1651
+ global $wpdb;
1652
+ $reoptimized = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->ewwwio_images WHERE updates > 10 AND path NOT LIKE '%wp-content/themes%' AND path NOT LIKE '%wp-content/plugins%' LIMIT 10" );
1653
+ if ( empty( $reoptimized ) ) {
1654
+ set_transient( 'ewww_image_optimizer_images_reoptimized', 'zero', HOUR_IN_SECONDS );
1655
+ } else {
1656
+ set_transient( 'ewww_image_optimizer_images_reoptimized', $reoptimized, HOUR_IN_SECONDS );
1657
+ }
1658
+ } elseif ( 'zero' == $reoptimized ) {
1659
+ $reoptimized = 0;
1660
+ }
1661
+ // Do a check for 10+ optimizations on 5+ images.
1662
+ if ( ! empty( $reoptimized ) && $reoptimized > 5 ) {
1663
+ $debugging_page = admin_url( 'upload.php?page=ewww-image-optimizer-dynamic-debug' );
1664
+ $reset_page = wp_nonce_url( $_SERVER['REQUEST_URI'], 'reset_reoptimization_counters', 'ewww_reset_reopt_nonce' );
1665
+ // Display an alert, and let the user reset the warning if they wish.
1666
+ echo "<div id='ewww-image-optimizer-warning-reoptimizations' class='error'><p>" .
1667
+ sprintf(
1668
+ /* translators: %s: A link to the Dynamic Image Debugging page */
1669
+ esc_html__( 'The EWWW Image Optimizer has detected excessive re-optimization of multiple images. Please turn on the Debugging setting, wait for approximately 12 hours, and then visit the %s page.', 'ewww-image-optimizer' ),
1670
+ "<a href='$debugging_page'>" . esc_html__( 'Dynamic Image Debugging', 'ewww-image-optimizer' ) . '</a>'
1671
+ ) .
1672
+ " <a href='$reset_page'>" . esc_html__( 'Reset Counters' ) . '</a></p></div>';
1673
+ }
1674
+ }
1675
+ }
1676
+
1677
+ /**
1678
+ * Loads the class to extend WP_Image_Editor for automatic optimization of generated images.
1679
+ *
1680
+ * @param array $editors List of image editors available to WordPress.
1681
+ * @return array Modified list of editors, with our custom class added at the top.
1682
+ */
1683
+ function ewww_image_optimizer_load_editor( $editors ) {
1684
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1685
+ if ( ! class_exists( 'EWWWIO_GD_Editor' ) && ! class_exists( 'EWWWIO_Imagick_Editor' ) ) {
1686
+ require_once( plugin_dir_path( __FILE__ ) . '/classes/class-ewwwio-gd-editor.php' );
1687
+ require_once( plugin_dir_path( __FILE__ ) . '/classes/class-ewwwio-imagick-editor.php' );
1688
+ require_once( plugin_dir_path( __FILE__ ) . '/classes/class-ewwwio-gmagick-editor.php' );
1689
+ }
1690
+ if ( ! in_array( 'EWWWIO_GD_Editor', $editors ) ) {
1691
+ array_unshift( $editors, 'EWWWIO_GD_Editor' );
1692
+ }
1693
+ if ( ! in_array( 'EWWWIO_Imagick_Editor', $editors ) ) {
1694
+ array_unshift( $editors, 'EWWWIO_Imagick_Editor' );
1695
+ }
1696
+ if ( ! in_array( 'EWWWIO_Gmagick_Editor', $editors ) && class_exists( 'WP_Image_Editor_Gmagick' ) ) {
1697
+ array_unshift( $editors, 'EWWWIO_Gmagick_Editor' );
1698
+ }
1699
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
1700
+ ewwwio_debug_message( 'loading image editors: ' . print_r( $editors, true ) );
1701
+ }
1702
+ ewwwio_memory( __FUNCTION__ );
1703
+ return $editors;
1704
+ }
1705
+
1706
+ /**
1707
+ * Registers the filter that will remove the image_editor hooks when an attachment is added.
1708
+ */
1709
+ function ewww_image_optimizer_add_attachment() {
1710
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1711
+ add_filter( 'intermediate_image_sizes_advanced', 'ewww_image_optimizer_image_sizes', 200 );
1712
+ add_filter( 'fallback_intermediate_image_sizes', 'ewww_image_optimizer_image_sizes', 200 );
1713
+ }
1714
+
1715
+ /**
1716
+ * Removes the image editor filter, and adds a new filter that will restore it later.
1717
+ *
1718
+ * @param array $sizes A list of sizes to be generated by WordPress.
1719
+ * @return array The unaltered list of sizes to be generated.
1720
+ */
1721
+ function ewww_image_optimizer_image_sizes( $sizes ) {
1722
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1723
+ remove_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
1724
+ add_filter( 'wp_generate_attachment_metadata', 'ewww_image_optimizer_restore_editor_hooks', 1 );
1725
+ add_filter( 'mpp_generate_metadata', 'ewww_image_optimizer_restore_editor_hooks', 1 );
1726
+ return $sizes;
1727
+ }
1728
+
1729
+ /**
1730
+ * Restores the image editor filter after the resizes have been generated.
1731
+ *
1732
+ * Also removes the retina filter, and adds our own wrapper around the retina generation function.
1733
+ *
1734
+ * @param array $metadata The attachment metadata that has been generated.
1735
+ * @return array The unaltered attachment metadata.
1736
+ */
1737
+ function ewww_image_optimizer_restore_editor_hooks( $metadata = false ) {
1738
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1739
+ add_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
1740
+ if ( function_exists( 'wr2x_wp_generate_attachment_metadata' ) ) {
1741
+ remove_filter( 'wp_generate_attachment_metadata', 'wr2x_wp_generate_attachment_metadata' );
1742
+ add_filter( 'wp_generate_attachment_metadata', 'ewww_image_optimizer_retina_wrapper' );
1743
+ }
1744
+ return $metadata;
1745
+ }
1746
+
1747
+ /**
1748
+ * Removes image editor filter when an attachment is being saved, and adds a filter to restore it.
1749
+ *
1750
+ * This prevents resizes from being optimized prematurely when saving the new attachment.
1751
+ *
1752
+ * @param string $image The filename of the edited image.
1753
+ * @return string The unaltered filename.
1754
+ */
1755
+ function ewww_image_optimizer_editor_save_pre( $image ) {
1756
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1757
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_noauto' ) ) {
1758
+ remove_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
1759
+ add_filter( 'wp_update_attachment_metadata', 'ewww_image_optimizer_restore_editor_hooks', 1 );
1760
+ add_filter( 'wp_update_attachment_metadata', 'ewww_image_optimizer_resize_from_meta_data', 15, 2 );
1761
+ }
1762
+ add_filter( 'intermediate_image_sizes', 'ewww_image_optimizer_image_sizes_advanced' );
1763
+ return $image;
1764
+ }
1765
+
1766
+ /**
1767
+ * Ensures that images saved with PTE are optimized.
1768
+ *
1769
+ * Checks for Post Thumbnail Editor's confirm, separate from crop&save, and registers a filter to
1770
+ * process any modified resizes.
1771
+ *
1772
+ * @param array $data The attachment metadata requested by PTE.
1773
+ * @return array The unaltered attachment metadata.
1774
+ */
1775
+ function ewww_image_optimizer_pte_check( $data ) {
1776
+ if ( ! empty( $_GET['pte-action'] ) ) {
1777
+ if ( 'confirm-images' == $_GET['pte-action'] ) {
1778
+ add_filter( 'wp_update_attachment_metadata', 'ewww_image_optimizer_resize_from_meta_data', 15, 2 );
1779
+ }
1780
+ }
1781
+ return $data;
1782
+ }
1783
+
1784
+ /**
1785
+ * Wraps around the retina generation function to prevent premature optimization.
1786
+ *
1787
+ * @since 3.3.0
1788
+ *
1789
+ * @param array $meta The attachment metadata.
1790
+ * @return array The unaltered metadata.
1791
+ */
1792
+ function ewww_image_optimizer_retina_wrapper( $meta ) {
1793
+ remove_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
1794
+ $meta = wr2x_wp_generate_attachment_metadata( $meta );
1795
+ add_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
1796
+ return $meta;
1797
+ }
1798
+
1799
+ /**
1800
+ * Filters image sizes generated by Wordpress, themes, and plugins allowing users to disable sizes.
1801
+ *
1802
+ * @param array $sizes A list of sizes to be generated.
1803
+ * @return array A list of sizes, minus any the user wants disabled.
1804
+ */
1805
+ function ewww_image_optimizer_image_sizes_advanced( $sizes ) {
1806
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1807
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes' );
1808
+ $flipped = false;
1809
+ if ( ! empty( $disabled_sizes ) ) {
1810
+ if ( ! empty( $sizes[0] ) ) {
1811
+ $sizes = array_flip( $sizes );
1812
+ $flipped = true;
1813
+ }
1814
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
1815
+ ewwwio_debug_message( print_r( $sizes, true ) );
1816
+ }
1817
+ if ( ewww_image_optimizer_iterable( $disabled_sizes ) ) {
1818
+ foreach ( $disabled_sizes as $size => $disabled ) {
1819
+ if ( ! empty( $disabled ) ) {
1820
+ ewwwio_debug_message( "size disabled: $size" );
1821
+ unset( $sizes[ $size ] );
1822
+ }
1823
+ }
1824
+ }
1825
+ if ( $flipped ) {
1826
+ $sizes = array_flip( $sizes );
1827
+ }
1828
+ }
1829
+ return $sizes;
1830
+ }
1831
+
1832
+ /**
1833
+ * Filter the image previews generated for pdf files and other non-image types.
1834
+ *
1835
+ * @param array $sizes A list of sizes to be generated.
1836
+ * @return array A list of sizes, minus any the user wants disabled.
1837
+ */
1838
+ function ewww_image_optimizer_fallback_sizes( $sizes ) {
1839
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1840
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes' );
1841
+ $flipped = false;
1842
+ if ( ! empty( $disabled_sizes ) ) {
1843
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
1844
+ ewwwio_debug_message( print_r( $sizes, true ) );
1845
+ }
1846
+ if ( ewww_image_optimizer_iterable( $sizes ) && ewww_image_optimizer_iterable( $disabled_sizes ) ) {
1847
+ if ( ! empty( $disabled_sizes['pdf-full'] ) ) {
1848
+ return array();
1849
+ }
1850
+ foreach ( $sizes as $i => $size ) {
1851
+ if ( ! empty( $disabled_sizes[ $size ] ) ) {
1852
+ ewwwio_debug_message( "size disabled: $size" );
1853
+ unset( $sizes[ $i ] );
1854
+ }
1855
+ }
1856
+ }
1857
+ }
1858
+ return $sizes;
1859
+ }
1860
+
1861
+ /**
1862
+ * Wrapper around the upload handler for MediaPress.
1863
+ *
1864
+ * @param array $params Parameters related to the file being uploaded.
1865
+ * @return array The unaltered parameters, we only need to pass them on.
1866
+ */
1867
+ function ewww_image_optimizer_handle_mpp_upload( $params ) {
1868
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1869
+ add_filter( 'mpp_intermediate_image_sizes', 'ewww_image_optimizer_image_sizes', 200 );
1870
+ return ewww_image_optimizer_handle_upload( $params );
1871
+ }
1872
+
1873
+ /**
1874
+ * During an upload, handles resizing, auto-rotation, and sets the 'new_image' global.
1875
+ *
1876
+ * @global bool $ewww_new_image True if there is a new image being uploaded.
1877
+ *
1878
+ * @param array $params Parameters related to the file being uploaded.
1879
+ * @return array The unaltered parameters, we only need to read them.
1880
+ */
1881
+ function ewww_image_optimizer_handle_upload( $params ) {
1882
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1883
+ global $ewww_new_image;
1884
+ $ewww_new_image = true;
1885
+ if ( empty( $params['file'] ) ) {
1886
+ if ( ! empty( $params['tmp_name'] ) ) {
1887
+ $file_path = $params['tmp_name'];
1888
+ } else {
1889
+ return $params;
1890
+ }
1891
+ } else {
1892
+ $file_path = $params['file'];
1893
+ }
1894
+ ewww_image_optimizer_autorotate( $file_path );
1895
+ // NOTE: if you use the ewww_image_optimizer_defer_resizing filter to defer the resize operation, only the "other" dimensions will apply
1896
+ // resize here unless the user chose to defer resizing or imsanity is enabled with a max size.
1897
+ if ( ! apply_filters( 'ewww_image_optimizer_defer_resizing', false ) && ! function_exists( 'imsanity_get_max_width_height' ) ) {
1898
+ if ( empty( $params['type'] ) ) {
1899
+ $mime_type = ewww_image_optimizer_mimetype( $file_path, 'i' );
1900
+ } else {
1901
+ $mime_type = $params['type'];
1902
+ }
1903
+ if ( ( ! is_wp_error( $params ) ) && is_file( $file_path ) && in_array( $mime_type, array( 'image/png', 'image/gif', 'image/jpeg' ) ) ) {
1904
+ ewww_image_optimizer_resize_upload( $file_path );
1905
+ }
1906
+ }
1907
+ return $params;
1908
+ }
1909
+
1910
+ /**
1911
+ * Makes sure W3TC uploads all modified files to any configured CDNs.
1912
+ *
1913
+ * @global array $ewww_attachment {
1914
+ * Stores the ID and meta for later use with W3TC.
1915
+ *
1916
+ * @int int $id The attachment ID number.
1917
+ * @array array $meta The attachment metadata from the postmeta table.
1918
+ * }
1919
+ *
1920
+ * @param array $files Files being updated by W3TC.
1921
+ * @return array Original array plus information about full-size image so that it also is updated.
1922
+ */
1923
+ function ewww_image_optimizer_w3tc_update_files( $files ) {
1924
+ global $ewww_attachment;
1925
+ list( $file, $upload_path ) = ewww_image_optimizer_attachment_path( $ewww_attachment['meta'], $ewww_attachment['id'] );
1926
+ $file_info = array();
1927
+ if ( function_exists( 'w3_upload_info' ) ) {
1928
+ $upload_info = w3_upload_info();
1929
+ } else {
1930
+ $upload_info = ewww_image_optimizer_upload_info();
1931
+ }
1932
+ if ( $upload_info ) {
1933
+ $remote_file = ltrim( $upload_info['baseurlpath'] . $ewww_attachment['meta']['file'], '/' );
1934
+ $home_url = get_site_url();
1935
+ $original_url = $home_url . $file;
1936
+ $file_info[] = array(
1937
+ 'local_path' => $file,
1938
+ 'remote_path' => $remote_file,
1939
+ 'original_url' => $original_url,
1940
+ );
1941
+ $files = array_merge( $files, $file_info );
1942
+ }
1943
+ return $files;
1944
+ }
1945
+
1946
+ /**
1947
+ * Wrapper around wp_upload_dir that adds the base url to the uploads folder as 'baseurlpath' key.
1948
+ *
1949
+ * @return array|bool Information about the uploads dir, or false on failure.
1950
+ */
1951
+ function ewww_image_optimizer_upload_info() {
1952
+ $upload_info = @wp_upload_dir( null, false );
1953
+
1954
+ if ( empty( $upload_info['error'] ) ) {
1955
+ // Errors silenced because of PHP < 5.3.3, yuck.
1956
+ $parse_url = @parse_url( $upload_info['baseurl'] );
1957
+
1958
+ if ( $parse_url ) {
1959
+ $baseurlpath = ( ! empty( $parse_url['path'] ) ? trim( $parse_url['path'], '/' ) : '' );
1960
+ } else {
1961
+ $baseurlpath = 'wp-content/uploads';
1962
+ }
1963
+ $upload_info['baseurlpath'] = '/' . $baseurlpath . '/';
1964
+ } else {
1965
+ $upload_info = false;
1966
+ }
1967
+ return $upload_info;
1968
+ }
1969
+
1970
+ /**
1971
+ * Runs scheduled optimization of various images.
1972
+ *
1973
+ * Regularly compresses any preconfigured folders including Buddypress, the active theme,
1974
+ * metaslider, and WP Symposium. Also includes any user-configured folders, along with the last two
1975
+ * months of media uploads.
1976
+ *
1977
+ * @global bool $ewww_defer Gets set to false to make sure optimization happens inline.
1978
+ * @global object $wpdb
1979
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
1980
+ */
1981
+ function ewww_image_optimizer_auto() {
1982
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1983
+ if ( get_transient( 'ewww_image_optimizer_no_scheduled_optimization' ) ) {
1984
+ ewwwio_debug_message( 'detected bulk operation in progress, bailing' );
1985
+ ewww_image_optimizer_debug_log();
1986
+ return;
1987
+ }
1988
+ global $ewww_defer;
1989
+ $ewww_defer = false;
1990
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'bulk.php' );
1991
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'aux-optimize.php' );
1992
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_auto' ) == true ) {
1993
+ ewwwio_debug_message( 'running scheduled optimization' );
1994
+ ewww_image_optimizer_aux_images_script( 'ewww-image-optimizer-auto' );
1995
+ // Generate our own unique nonce value, because wp_create_nonce() will return the same value for 12-24 hours.
1996
+ $nonce = wp_hash( time() . '|' . 'ewww-image-optimizer-auto' );
1997
+ update_option( 'ewww_image_optimizer_aux_resume', $nonce );
1998
+ $delay = ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' );
1999
+ $count = ewww_image_optimizer_aux_images_table_count_pending();
2000
+ if ( ! empty( $count ) ) {
2001
+ global $wpdb;
2002
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
2003
+ ewww_image_optimizer_db_init();
2004
+ global $ewwwdb;
2005
+ } else {
2006
+ $ewwwdb = $wpdb;
2007
+ }
2008
+ $i = 0;
2009
+ while ( $i < $count &&
2010
+ $attachment = $ewwwdb->get_row( "SELECT id,path FROM $ewwwdb->ewwwio_images WHERE pending=1 LIMIT 1", ARRAY_A )
2011
+ ) {
2012
+ // If the nonce has changed since we started, bail out, since that means another aux scan/optimize is running.
2013
+ // Do a direct query using $wpdb, because get_option() is cached.
2014
+ $current_nonce = $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'ewww_image_optimizer_aux_resume'" );
2015
+ if ( $nonce !== $current_nonce ) {
2016
+ ewwwio_debug_message( 'detected another optimization, nonce changed, bailing' );
2017
+ ewww_image_optimizer_debug_log();
2018
+ return;
2019
+ } else {
2020
+ ewwwio_debug_message( "$nonce is fine, compared to $current_nonce" );
2021
+ }
2022
+ if ( ! empty( $attachment['path'] ) ) {
2023
+ $attachment['path'] = ewww_image_optimizer_relative_path_replace( $attachment['path'] );
2024
+ }
2025
+ ewww_image_optimizer_aux_images_loop( $attachment, true );
2026
+ if ( ! empty( $delay ) && ewww_image_optimizer_function_exists( 'sleep' ) ) {
2027
+ sleep( $delay );
2028
+ }
2029
+ ewww_image_optimizer_debug_log();
2030
+ $i++;
2031
+ }
2032
+ }
2033
+ ewww_image_optimizer_aux_images_cleanup( true );
2034
+ } // End if().
2035
+ ewwwio_memory( __FUNCTION__ );
2036
+ return;
2037
+ }
2038
+
2039
+ /**
2040
+ * Clears scheduled jobs for multisite when the plugin is deactivated.
2041
+ *
2042
+ * @global object $wpdb
2043
+ *
2044
+ * @param bool $network_wide True if plugin was network-activated.
2045
+ */
2046
+ function ewww_image_optimizer_network_deactivate( $network_wide ) {
2047
+ global $wpdb;
2048
+ wp_clear_scheduled_hook( 'ewww_image_optimizer_auto' );
2049
+ wp_clear_scheduled_hook( 'ewww_image_optimizer_defer' );
2050
+ if ( $network_wide ) {
2051
+ $blogs = $wpdb->get_results( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d", $wpdb->siteid ), ARRAY_A );
2052
+ if ( ewww_image_optimizer_iterable( $blogs ) ) {
2053
+ foreach ( $blogs as $blog ) {
2054
+ switch_to_blog( $blog['blog_id'] );
2055
+ wp_clear_scheduled_hook( 'ewww_image_optimizer_auto' );
2056
+ wp_clear_scheduled_hook( 'ewww_image_optimizer_defer' );
2057
+ restore_current_blog();
2058
+ }
2059
+ }
2060
+ }
2061
+ }
2062
+
2063
+ /**
2064
+ * Removes rules from .htaccess file added by EWWW for WebP rewriting.
2065
+ */
2066
+ function ewww_image_optimizer_uninstall() {
2067
+ insert_with_markers( ewww_image_optimizer_htaccess_path(), 'EWWWIO', '' );
2068
+ }
2069
+
2070
+ /**
2071
+ * Adds a global settings page to the network admin settings menu.
2072
+ */
2073
+ function ewww_image_optimizer_network_admin_menu() {
2074
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
2075
+ // Need to include the plugin library for the is_plugin_active function.
2076
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
2077
+ }
2078
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
2079
+ // Add options page to the settings menu.
2080
+ $permissions = apply_filters( 'ewww_image_optimizer_superadmin_permissions', '' );
2081
+ $ewww_network_options_page = add_submenu_page(
2082
+ 'settings.php', // Slug of parent
2083
+ 'EWWW Image Optimizer', // Page Title
2084
+ 'EWWW Image Optimizer', // Menu title
2085
+ $permissions, // Capability
2086
+ EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, // Slug
2087
+ 'ewww_image_optimizer_network_options' // Function to call.
2088
+ );
2089
+ }
2090
+ }
2091
+
2092
+ /**
2093
+ * Simulates regenerating a resize for an attachment.
2094
+ */
2095
+ function ewww_image_optimizer_resize_dup_check() {
2096
+ $meta = wp_get_attachment_metadata( 34 );
2097
+ list( $file, $upload_path ) = ewww_image_optimizer_attachment_path( $meta, 34 );
2098
+ $editor = wp_get_image_editor( $file );
2099
+ $resized_image = $editor->resize( 150, 150, true );
2100
+ $new_file = $editor->generate_filename();
2101
+ echo $new_file;
2102
+ if ( is_file( $new_file ) ) {
2103
+ echo '<br>file already exists<br>';
2104
+ }
2105
+ $saved = $editor->save( $new_file );
2106
+ }
2107
+
2108
+ /**
2109
+ * Adds various items to the admin menu.
2110
+ */
2111
+ function ewww_image_optimizer_admin_menu() {
2112
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
2113
+ // Adds bulk optimize to the media library menu.
2114
+ $ewww_bulk_page = add_media_page( esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ), $permissions, 'ewww-image-optimizer-bulk', 'ewww_image_optimizer_bulk_preview' );
2115
+ // TEST TODO: move this to a unit test or something!
2116
+ /* add_media_page( esc_html__( 'Resize checker', 'ewww-image-optimizer' ), esc_html__( 'Duplicate Resize Check', 'ewww-image-optimizer' ), $permissions, 'ewww-image-optimizer-resize-dup-check', 'ewww_image_optimizer_resize_dup_check' ); */
2117
+ $ewww_webp_migrate_page = add_submenu_page( null, esc_html__( 'Migrate WebP Images', 'ewww-image-optimizer' ), esc_html__( 'Migrate WebP Images', 'ewww-image-optimizer' ), $permissions, 'ewww-image-optimizer-webp-migrate', 'ewww_image_optimizer_webp_migrate_preview' );
2118
+ if ( ! function_exists( 'is_plugin_active' ) ) {
2119
+ // Need to include the plugin library for the is_plugin_active function.
2120
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
2121
+ }
2122
+ if ( ! is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
2123
+ // Add options page to the settings menu.
2124
+ $ewww_options_page = add_options_page(
2125
+ 'EWWW Image Optimizer', // Page title
2126
+ 'EWWW Image Optimizer', // Menu title
2127
+ apply_filters( 'ewww_image_optimizer_admin_permissions', 'manage_options' ), // Capability
2128
+ EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, // Slug
2129
+ 'ewww_image_optimizer_options' // Function to call.
2130
+ );
2131
+ } else {
2132
+ // Add options page to the single-site settings menu.
2133
+ $ewww_options_page = add_options_page(
2134
+ 'EWWW Image Optimizer', // Page title
2135
+ 'EWWW Image Optimizer', // Menu title
2136
+ apply_filters( 'ewww_image_optimizer_admin_permissions', 'manage_options' ), // Capability
2137
+ EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, // Slug
2138
+ 'ewww_image_optimizer_network_singlesite_options' // Function to call.
2139
+ );
2140
+ }
2141
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
2142
+ // Add Dynamic Image Debugging page for image regeneration issues.
2143
+ add_media_page( esc_html__( 'Dynamic Image Debugging', 'ewww-image-optimizer' ), esc_html__( 'Dynamic Image Debugging', 'ewww-image-optimizer' ), $permissions, 'ewww-image-optimizer-dynamic-debug', 'ewww_image_optimizer_dynamic_image_debug' );
2144
+ // Add Image Queue Debugging to allow clearing and checking queues.
2145
+ add_media_page( esc_html__( 'Image Queue Debugging', 'ewww-image-optimizer' ), esc_html__( 'Image Queue Debugging', 'ewww-image-optimizer' ), $permissions, 'ewww-image-optimizer-queue-debug', 'ewww_image_optimizer_image_queue_debug' );
2146
+ }
2147
+ if ( is_plugin_active( 'image-store/ImStore.php' ) || is_plugin_active_for_network( 'image-store/ImStore.php' ) ) {
2148
+ // Adds an optimize page for Image Store galleries and images.
2149
+ $ims_menu = 'edit.php?post_type=ims_gallery';
2150
+ $ewww_ims_page = add_submenu_page( $ims_menu, esc_html__( 'Image Store Optimize', 'ewww-image-optimizer' ), esc_html__( 'Optimize', 'ewww-image-optimizer' ), 'ims_change_settings', 'ewww-ims-optimize', 'ewww_image_optimizer_ims' );
2151
+ }
2152
+ }
2153
+
2154
+ /**
2155
+ * Checks WP Retina images to fix filenames in the database.
2156
+ *
2157
+ * @param int $id The attachment ID with which this retina image is associated.
2158
+ * @param string $retina_path The filename of the retina image that was generated.
2159
+ */
2160
+ function ewww_image_optimizer_retina( $id, $retina_path ) {
2161
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2162
+ $file_info = pathinfo( $retina_path );
2163
+ $extension = '.' . $file_info['extension'];
2164
+ preg_match( '/-(\d+x\d+)@2x$/', $file_info['filename'], $fileresize );
2165
+ $dimensions = explode( 'x', $fileresize[1] );
2166
+ $no_ext_path = $file_info['dirname'] . '/' . preg_replace( '/\d+x\d+@2x$/', '', $file_info['filename'] ) . $dimensions[0] * 2 . 'x' . $dimensions[1] * 2 . '-tmp';
2167
+ $temp_path = $no_ext_path . $extension;
2168
+ ewwwio_debug_message( "temp path: $temp_path" );
2169
+ // Check for any orphaned webp retina images, and fix their paths.
2170
+ ewwwio_debug_message( "retina path: $retina_path" );
2171
+ $webp_path = $temp_path . '.webp';
2172
+ ewwwio_debug_message( "retina webp path: $webp_path" );
2173
+ if ( is_file( $webp_path ) ) {
2174
+ rename( $webp_path, $retina_path . '.webp' );
2175
+ }
2176
+ $opt_size = ewww_image_optimizer_filesize( $retina_path );
2177
+ ewwwio_debug_message( "retina size: $opt_size" );
2178
+ $optimized_query = ewww_image_optimizer_find_already_optimized( $temp_path );
2179
+ if ( is_array( $optimized_query ) && $optimized_query['image_size'] == $opt_size ) {
2180
+ global $wpdb;
2181
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
2182
+ ewww_image_optimizer_db_init();
2183
+ global $ewwwdb;
2184
+ } else {
2185
+ $ewwwdb = $wpdb;
2186
+ }
2187
+ // Replace the 'temp' path in the database with the real path.
2188
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
2189
+ array(
2190
+ 'path' => ewww_image_optimizer_relative_path_remove( $retina_path ),
2191
+ 'attachment_id' => $id,
2192
+ 'gallery' => 'media',
2193
+ ),
2194
+ array(
2195
+ 'id' => $optimized_query['id'],
2196
+ )
2197
+ );
2198
+ }
2199
+ ewwwio_memory( __FUNCTION__ );
2200
+ }
2201
+
2202
+ /**
2203
+ * List IMS images and optimization status.
2204
+ *
2205
+ * @global object $wpdb
2206
+ */
2207
+ function ewww_image_optimizer_ims() {
2208
+ global $wpdb;
2209
+ $ims_columns = get_column_headers( 'ims_gallery' );
2210
+ echo "<div class='wrap'><h1>" . esc_html__( 'Image Store Optimization', 'ewww-image-optimizer' ) . '</h1>';
2211
+ if ( empty( $_REQUEST['ewww_gid'] ) ) {
2212
+ $galleries = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'ims_gallery' ORDER BY ID" );
2213
+ if ( ewww_image_optimizer_iterable( $galleries ) ) {
2214
+ $gallery_string = implode( ',', $galleries );
2215
+ echo '<p>' . esc_html__( 'Choose a gallery or', 'ewww-image-optimizer' ) . " <a href='upload.php?page=ewww-image-optimizer-bulk&ids=$gallery_string'>" . esc_html__( 'optimize all galleries', 'ewww-image-optimizer' ) . '</a></p>';
2216
+ echo '<table class="wp-list-table widefat media" cellspacing="0"><thead><tr><th>' . esc_html__( 'Gallery ID', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Gallery Name', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Images', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Image Optimizer', 'ewww-image-optimizer' ) . '</th></tr></thead>';
2217
+ foreach ( $galleries as $gid ) {
2218
+ $image_count = $wpdb->get_var( $wpdb->prepare( "SELECT count(ID) FROM $wpdb->posts WHERE post_type = 'ims_image' AND post_mime_type LIKE '%%image%%' AND post_parent = %d", $gid ) );
2219
+ $gallery_name = get_the_title( $gid );
2220
+ echo "<tr><td>$gid</td>";
2221
+ echo "<td><a href='edit.php?post_type=ims_gallery&page=ewww-ims-optimize&ewww_gid=$gid'>$gallery_name</a></td>";
2222
+ echo "<td>$image_count</td>";
2223
+ echo "<td><a href='upload.php?page=ewww-image-optimizer-bulk&ids=$gid'>" . esc_html__( 'Optimize Gallery', 'ewww-image-optimizer' ) . '</a></td></tr>';
2224
+ }
2225
+ echo '</table>';
2226
+ } else {
2227
+ echo '<p>' . esc_html__( 'No galleries found', 'ewww-image-optimizer' ) . '</p>';
2228
+ }
2229
+ } else {
2230
+ $gid = (int) $_REQUEST['ewww_gid'];
2231
+ $attachments = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'ims_image' AND post_mime_type LIKE '%%image%%' AND post_parent = %d ORDER BY ID", $gid ) );
2232
+ if ( ewww_image_optimizer_iterable( $attachments ) ) {
2233
+ echo "<p><a href='upload.php?page=ewww-image-optimizer-bulk&ids=$gid'>" . esc_html__( 'Optimize Gallery', 'ewww-image-optimizer' ) . '</a></p>';
2234
+ echo '<table class="wp-list-table widefat media" cellspacing="0"><thead><tr><th>ID</th><th>&nbsp;</th><th>' . esc_html__( 'Title', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Gallery', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Image Optimizer', 'ewww-image-optimizer' ) . '</th></tr></thead>';
2235
+ $alternate = true;
2236
+ foreach ( $attachments as $id ) {
2237
+ $meta = get_metadata( 'post', $id );
2238
+ if ( empty( $meta['_wp_attachment_metadata'] ) ) {
2239
+ continue;
2240
+ }
2241
+ $meta = maybe_unserialize( $meta['_wp_attachment_metadata'][0] );
2242
+ $image_name = get_the_title( $id );
2243
+ $gallery_name = get_the_title( $gid );
2244
+ $image_url = esc_url( $meta['sizes']['mini']['url'] );
2245
+ ?> <tr<?php if ( $alternate ) { echo " class='alternate'"; } ?>><td><?php echo $id; ?></td>
2246
+ <?php echo "<td style='width:80px' class='column-icon'><img src='$image_url' /></td>";
2247
+ echo "<td class='title'>$image_name</td>";
2248
+ echo "<td>$gallery_name</td><td>";
2249
+ ewww_image_optimizer_custom_column( 'ewww-image-optimizer', $id );
2250
+ echo '</td></tr>';
2251
+ $alternate = ! $alternate;
2252
+ }
2253
+ echo '</table>';
2254
+ } else {
2255
+ echo '<p>' . esc_html__( 'No images found', 'ewww-image-optimizer' ) . '</p>';
2256
+ }
2257
+ } // End if().
2258
+ echo '</div>';
2259
+ return;
2260
+ }
2261
+
2262
+ /**
2263
+ * Optimize MyArcade screenshots and thumbs.
2264
+ *
2265
+ * @param string $url The address of the image to be processed.
2266
+ * @return string The unaltered url/address.
2267
+ */
2268
+ function ewww_image_optimizer_myarcade_thumbnail( $url ) {
2269
+ ewwwio_debug_message( "thumb url passed: $url" );
2270
+ if ( ! empty( $url ) ) {
2271
+ $thumb_path = str_replace( get_option( 'siteurl' ) . '/', ABSPATH, $url );
2272
+ ewwwio_debug_message( "myarcade thumb path generated: $thumb_path" );
2273
+ ewww_image_optimizer( $thumb_path );
2274
+ }
2275
+ return $url;
2276
+ }
2277
+
2278
+ /**
2279
+ * Load full webp script when SCRIPT_DEBUG is enabled.
2280
+ */
2281
+ function ewww_image_optimizer_webp_debug_script() {
2282
+ if ( ! ewww_image_optimizer_ce_webp_enabled() ) {
2283
+ wp_enqueue_script( 'ewww-webp-load-script', plugins_url( '/includes/load_webp.js', __FILE__ ), array( 'jquery' ), EWWW_IMAGE_OPTIMIZER_VERSION );
2284
+ }
2285
+ }
2286
+
2287
+ /**
2288
+ * Load minified webp script when EWWW_IMAGE_OPTIMIZER_WEBP_EXTERNAL_SCRIPT is set.
2289
+ */
2290
+ function ewww_image_optimizer_webp_min_script() {
2291
+ if ( ! ewww_image_optimizer_ce_webp_enabled() ) {
2292
+ wp_enqueue_script( 'ewww-webp-load-script', plugins_url( '/includes/load_webp.min.js', __FILE__ ), array( 'jquery' ), EWWW_IMAGE_OPTIMIZER_VERSION );
2293
+ }
2294
+ }
2295
+
2296
+ /**
2297
+ * Enqueue script dependency for alt webp rewriting when running inline.
2298
+ */
2299
+ function ewww_image_optimizer_webp_load_jquery() {
2300
+ if ( ! ewww_image_optimizer_ce_webp_enabled() ) {
2301
+ wp_enqueue_script( 'jquery' );
2302
+ if ( function_exists( 'wp_add_inline_script' ) && ( ! defined( 'EWWW_IMAGE_OPTIMIZER_WEBP_INLINE_FALLBACK' ) || ! EWWW_IMAGE_OPTIMIZER_WEBP_INLINE_FALLBACK ) ) {
2303
+ ewwwio_debug_message( 'loading webp script with wp_add_inline_script' );
2304
+ wp_add_inline_script( 'jquery-migrate',
2305
+ 'function check_webp_feature(a,b){var c={alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",animation:"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"},d=!1,e=new Image;e.onload=function(){var a=e.width>0&&e.height>0;d=!0,b(a)},e.onerror=function(){d=!1,b(!1)},e.src="data:image/webp;base64,"+c[a]}function ewww_load_images(a){jQuery(document).arrive(".ewww_webp",function(){ewww_load_images(a)}),function(b){function d(a,d){for(var e=["align","alt","border","crossorigin","height","hspace","ismap","longdesc","usemap","vspace","width","accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","lang","spellcheck","style","tabindex","title","translate","sizes","data-attachment-id","data-permalink","data-orig-size","data-comments-opened","data-image-meta","data-image-title","data-image-description"],f=0,g=e.length;f<g;f++){var h=b(a).attr(c+e[f]);void 0!==h&&!1!==h&&b(d).attr(e[f],h)}return d}var c="data-";a&&(b(".batch-image img, .image-wrapper a, .ngg-pro-masonry-item a").each(function(){var a=b(this).attr("data-webp");void 0!==a&&!1!==a&&b(this).attr("data-src",a);var a=b(this).attr("data-webp-thumbnail");void 0!==a&&!1!==a&&b(this).attr("data-thumbnail",a)}),b(".image-wrapper a, .ngg-pro-masonry-item a").each(function(){var a=b(this).attr("data-webp");void 0!==a&&!1!==a&&b(this).attr("href",a)}),b(".rev_slider ul li").each(function(){var a=b(this).attr("data-webp-thumb");void 0!==a&&!1!==a&&b(this).attr("data-thumb",a);for(var c=1;c<11;){var a=b(this).attr("data-webp-param"+c);void 0!==a&&!1!==a&&b(this).attr("data-param"+c,a),c++}}),b(".rev_slider img").each(function(){var a=b(this).attr("data-webp-lazyload");void 0!==a&&!1!==a&&b(this).attr("data-lazyload",a)})),b("img.ewww_webp_lazy_retina").each(function(){if(a){var c=b(this).attr("data-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("data-srcset",c)}else{var c=b(this).attr("data-srcset-img");void 0!==c&&!1!==c&&b(this).attr("data-srcset",c)}b(this).removeClass("ewww_webp_lazy_retina")}),b("video").each(function(){if(a){var c=b(this).attr("data-poster-webp");void 0!==c&&!1!==c&&b(this).attr("poster",c)}else{var c=b(this).attr("data-poster-image");void 0!==c&&!1!==c&&b(this).attr("poster",c)}}),b("img.ewww_webp_lazy_load").each(function(){if(a){b(this).attr("data-lazy-src",b(this).attr("data-lazy-webp-src"));var c=b(this).attr("data-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("srcset",c);var c=b(this).attr("data-lazy-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("data-lazy-srcset",c)}else{b(this).attr("data-lazy-src",b(this).attr("data-lazy-img-src"));var c=b(this).attr("data-srcset");void 0!==c&&!1!==c&&b(this).attr("srcset",c);var c=b(this).attr("data-lazy-srcset-img");void 0!==c&&!1!==c&&b(ewww_img).attr("data-lazy-srcset",c)}b(this).removeClass("ewww_webp_lazy_load")}),b(".ewww_webp_lazy_hueman").each(function(){var c=document.createElement("img");if(b(c).attr("src",b(this).attr("data-src")),a){b(c).attr("data-src",b(this).attr("data-webp-src"));var e=b(this).attr("data-srcset-webp");void 0!==e&&!1!==e&&b(c).attr("data-srcset",e)}else{b(c).attr("data-src",b(this).attr("data-img"));var e=b(this).attr("data-srcset-img");void 0!==e&&!1!==e&&b(c).attr("data-srcset",e)}c=d(this,c),b(this).after(c),b(this).removeClass("ewww_webp_lazy_hueman")}),b(".ewww_webp").each(function(){var c=document.createElement("img");if(a){b(c).attr("src",b(this).attr("data-webp"));var e=b(this).attr("data-srcset-webp");void 0!==e&&!1!==e&&b(c).attr("srcset",e);var e=b(this).attr("data-webp-orig-file");void 0!==e&&!1!==e&&b(c).attr("data-orig-file",e);var e=b(this).attr("data-webp-medium-file");void 0!==e&&!1!==e&&b(c).attr("data-medium-file",e);var e=b(this).attr("data-webp-large-file");void 0!==e&&!1!==e&&b(c).attr("data-large-file",e)}else{b(c).attr("src",b(this).attr("data-img"));var e=b(this).attr("data-srcset-img");void 0!==e&&!1!==e&&b(c).attr("srcset",e)}c=d(this,c),b(this).after(c),b(this).removeClass("ewww_webp")})}(jQuery),jQuery.fn.isotope&&jQuery.fn.imagesLoaded&&(jQuery(".fusion-posts-container-infinite").imagesLoaded(function(){jQuery(".fusion-posts-container-infinite").hasClass("isotope")&&jQuery(".fusion-posts-container-infinite").isotope()}),jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").imagesLoaded(function(){jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").isotope()}))}var Arrive=function(a,b,c){"use strict";function l(a,b,c){e.addMethod(b,c,a.unbindEvent),e.addMethod(b,c,a.unbindEventWithSelectorOrCallback),e.addMethod(b,c,a.unbindEventWithSelectorAndCallback)}function m(a){a.arrive=j.bindEvent,l(j,a,"unbindArrive"),a.leave=k.bindEvent,l(k,a,"unbindLeave")}if(a.MutationObserver&&"undefined"!=typeof HTMLElement){var d=0,e=function(){var b=HTMLElement.prototype.matches||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector;return{matchesSelector:function(a,c){return a instanceof HTMLElement&&b.call(a,c)},addMethod:function(a,b,c){var d=a[b];a[b]=function(){return c.length==arguments.length?c.apply(this,arguments):"function"==typeof d?d.apply(this,arguments):void 0}},callCallbacks:function(a){for(var c,b=0;c=a[b];b++)c.callback.call(c.elem)},checkChildNodesRecursively:function(a,b,c,d){for(var g,f=0;g=a[f];f++)c(g,b,d)&&d.push({callback:b.callback,elem:g}),g.childNodes.length>0&&e.checkChildNodesRecursively(g.childNodes,b,c,d)},mergeArrays:function(a,b){var d,c={};for(d in a)c[d]=a[d];for(d in b)c[d]=b[d];return c},toElementsArray:function(b){return void 0===b||"number"==typeof b.length&&b!==a||(b=[b]),b}}}(),f=function(){var a=function(){this._eventsBucket=[],this._beforeAdding=null,this._beforeRemoving=null};return a.prototype.addEvent=function(a,b,c,d){var e={target:a,selector:b,options:c,callback:d,firedElems:[]};return this._beforeAdding&&this._beforeAdding(e),this._eventsBucket.push(e),e},a.prototype.removeEvent=function(a){for(var c,b=this._eventsBucket.length-1;c=this._eventsBucket[b];b--)a(c)&&(this._beforeRemoving&&this._beforeRemoving(c),this._eventsBucket.splice(b,1))},a.prototype.beforeAdding=function(a){this._beforeAdding=a},a.prototype.beforeRemoving=function(a){this._beforeRemoving=a},a}(),g=function(b,d){var g=new f,h=this,i={fireOnAttributesModification:!1};return g.beforeAdding(function(c){var i,e=c.target;c.selector,c.callback;e!==a.document&&e!==a||(e=document.getElementsByTagName("html")[0]),i=new MutationObserver(function(a){d.call(this,a,c)});var j=b(c.options);i.observe(e,j),c.observer=i,c.me=h}),g.beforeRemoving(function(a){a.observer.disconnect()}),this.bindEvent=function(a,b,c){b=e.mergeArrays(i,b);for(var d=e.toElementsArray(this),f=0;f<d.length;f++)g.addEvent(d[f],a,b,c)},this.unbindEvent=function(){var a=e.toElementsArray(this);g.removeEvent(function(b){for(var d=0;d<a.length;d++)if(this===c||b.target===a[d])return!0;return!1})},this.unbindEventWithSelectorOrCallback=function(a){var f,b=e.toElementsArray(this),d=a;f="function"==typeof a?function(a){for(var e=0;e<b.length;e++)if((this===c||a.target===b[e])&&a.callback===d)return!0;return!1}:function(d){for(var e=0;e<b.length;e++)if((this===c||d.target===b[e])&&d.selector===a)return!0;return!1},g.removeEvent(f)},this.unbindEventWithSelectorAndCallback=function(a,b){var d=e.toElementsArray(this);g.removeEvent(function(e){for(var f=0;f<d.length;f++)if((this===c||e.target===d[f])&&e.selector===a&&e.callback===b)return!0;return!1})},this},h=function(){function h(a){var b={attributes:!1,childList:!0,subtree:!0};return a.fireOnAttributesModification&&(b.attributes=!0),b}function i(a,b){a.forEach(function(a){var c=a.addedNodes,d=a.target,f=[];null!==c&&c.length>0?e.checkChildNodesRecursively(c,b,k,f):"attributes"===a.type&&k(d,b,f)&&f.push({callback:b.callback,elem:node}),e.callCallbacks(f)})}function k(a,b,f){if(e.matchesSelector(a,b.selector)&&(a._id===c&&(a._id=d++),-1==b.firedElems.indexOf(a._id))){if(b.options.onceOnly){if(0!==b.firedElems.length)return;b.me.unbindEventWithSelectorAndCallback.call(b.target,b.selector,b.callback)}b.firedElems.push(a._id),f.push({callback:b.callback,elem:a})}}var f={fireOnAttributesModification:!1,onceOnly:!1,existing:!1};j=new g(h,i);var l=j.bindEvent;return j.bindEvent=function(a,b,c){void 0===c?(c=b,b=f):b=e.mergeArrays(f,b);var d=e.toElementsArray(this);if(b.existing){for(var g=[],h=0;h<d.length;h++)for(var i=d[h].querySelectorAll(a),j=0;j<i.length;j++)g.push({callback:c,elem:i[j]});if(b.onceOnly&&g.length)return c.call(g[0].elem);setTimeout(e.callCallbacks,1,g)}l.call(this,a,b,c)},j},i=function(){function d(a){return{childList:!0,subtree:!0}}function f(a,b){a.forEach(function(a){var c=a.removedNodes,f=(a.target,[]);null!==c&&c.length>0&&e.checkChildNodesRecursively(c,b,h,f),e.callCallbacks(f)})}function h(a,b){return e.matchesSelector(a,b.selector)}var c={};k=new g(d,f);var i=k.bindEvent;return k.bindEvent=function(a,b,d){void 0===d?(d=b,b=c):b=e.mergeArrays(c,b),i.call(this,a,b,d)},k},j=new h,k=new i;b&&m(b.fn),m(HTMLElement.prototype),m(NodeList.prototype),m(HTMLCollection.prototype),m(HTMLDocument.prototype),m(Window.prototype);var n={};return l(j,n,"unbindAllArrive"),l(k,n,"unbindAllLeave"),n}}(window,"undefined"==typeof jQuery?null:jQuery,void 0);"undefined"!=typeof jQuery&&check_webp_feature("alpha",ewww_load_images);'
2306
+ );
2307
+ }
2308
+ }
2309
+ }
2310
+
2311
+ /**
2312
+ * Load minified inline version of webp script (from jscompress.com).
2313
+ */
2314
+ function ewww_image_optimizer_webp_inline_script() {
2315
+ if ( ! ewww_image_optimizer_ce_webp_enabled() ) {
2316
+ ewwwio_debug_message( 'loading webp script without wp_add_inline_script' );
2317
+ ?>
2318
+ <script>
2319
+ function check_webp_feature(a,b){var c={alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",animation:"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"},d=!1,e=new Image;e.onload=function(){var a=e.width>0&&e.height>0;d=!0,b(a)},e.onerror=function(){d=!1,b(!1)},e.src="data:image/webp;base64,"+c[a]}function ewww_load_images(a){jQuery(document).arrive(".ewww_webp",function(){ewww_load_images(a)}),function(b){function d(a,d){for(var e=["align","alt","border","crossorigin","height","hspace","ismap","longdesc","usemap","vspace","width","accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","lang","spellcheck","style","tabindex","title","translate","sizes","data-attachment-id","data-permalink","data-orig-size","data-comments-opened","data-image-meta","data-image-title","data-image-description"],f=0,g=e.length;f<g;f++){var h=b(a).attr(c+e[f]);void 0!==h&&!1!==h&&b(d).attr(e[f],h)}return d}var c="data-";a&&(b(".batch-image img, .image-wrapper a, .ngg-pro-masonry-item a").each(function(){var a=b(this).attr("data-webp");void 0!==a&&!1!==a&&b(this).attr("data-src",a);var a=b(this).attr("data-webp-thumbnail");void 0!==a&&!1!==a&&b(this).attr("data-thumbnail",a)}),b(".image-wrapper a, .ngg-pro-masonry-item a").each(function(){var a=b(this).attr("data-webp");void 0!==a&&!1!==a&&b(this).attr("href",a)}),b(".rev_slider ul li").each(function(){var a=b(this).attr("data-webp-thumb");void 0!==a&&!1!==a&&b(this).attr("data-thumb",a);for(var c=1;c<11;){var a=b(this).attr("data-webp-param"+c);void 0!==a&&!1!==a&&b(this).attr("data-param"+c,a),c++}}),b(".rev_slider img").each(function(){var a=b(this).attr("data-webp-lazyload");void 0!==a&&!1!==a&&b(this).attr("data-lazyload",a)})),b("img.ewww_webp_lazy_retina").each(function(){if(a){var c=b(this).attr("data-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("data-srcset",c)}else{var c=b(this).attr("data-srcset-img");void 0!==c&&!1!==c&&b(this).attr("data-srcset",c)}b(this).removeClass("ewww_webp_lazy_retina")}),b("video").each(function(){if(a){var c=b(this).attr("data-poster-webp");void 0!==c&&!1!==c&&b(this).attr("poster",c)}else{var c=b(this).attr("data-poster-image");void 0!==c&&!1!==c&&b(this).attr("poster",c)}}),b("img.ewww_webp_lazy_load").each(function(){if(a){b(this).attr("data-lazy-src",b(this).attr("data-lazy-webp-src"));var c=b(this).attr("data-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("srcset",c);var c=b(this).attr("data-lazy-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("data-lazy-srcset",c)}else{b(this).attr("data-lazy-src",b(this).attr("data-lazy-img-src"));var c=b(this).attr("data-srcset");void 0!==c&&!1!==c&&b(this).attr("srcset",c);var c=b(this).attr("data-lazy-srcset-img");void 0!==c&&!1!==c&&b(ewww_img).attr("data-lazy-srcset",c)}b(this).removeClass("ewww_webp_lazy_load")}),b(".ewww_webp_lazy_hueman").each(function(){var c=document.createElement("img");if(b(c).attr("src",b(this).attr("data-src")),a){b(c).attr("data-src",b(this).attr("data-webp-src"));var e=b(this).attr("data-srcset-webp");void 0!==e&&!1!==e&&b(c).attr("data-srcset",e)}else{b(c).attr("data-src",b(this).attr("data-img"));var e=b(this).attr("data-srcset-img");void 0!==e&&!1!==e&&b(c).attr("data-srcset",e)}c=d(this,c),b(this).after(c),b(this).removeClass("ewww_webp_lazy_hueman")}),b(".ewww_webp").each(function(){var c=document.createElement("img");if(a){b(c).attr("src",b(this).attr("data-webp"));var e=b(this).attr("data-srcset-webp");void 0!==e&&!1!==e&&b(c).attr("srcset",e);var e=b(this).attr("data-webp-orig-file");void 0!==e&&!1!==e&&b(c).attr("data-orig-file",e);var e=b(this).attr("data-webp-medium-file");void 0!==e&&!1!==e&&b(c).attr("data-medium-file",e);var e=b(this).attr("data-webp-large-file");void 0!==e&&!1!==e&&b(c).attr("data-large-file",e)}else{b(c).attr("src",b(this).attr("data-img"));var e=b(this).attr("data-srcset-img");void 0!==e&&!1!==e&&b(c).attr("srcset",e)}c=d(this,c),b(this).after(c),b(this).removeClass("ewww_webp")})}(jQuery),jQuery.fn.isotope&&jQuery.fn.imagesLoaded&&(jQuery(".fusion-posts-container-infinite").imagesLoaded(function(){jQuery(".fusion-posts-container-infinite").hasClass("isotope")&&jQuery(".fusion-posts-container-infinite").isotope()}),jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").imagesLoaded(function(){jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").isotope()}))}var Arrive=function(a,b,c){"use strict";function l(a,b,c){e.addMethod(b,c,a.unbindEvent),e.addMethod(b,c,a.unbindEventWithSelectorOrCallback),e.addMethod(b,c,a.unbindEventWithSelectorAndCallback)}function m(a){a.arrive=j.bindEvent,l(j,a,"unbindArrive"),a.leave=k.bindEvent,l(k,a,"unbindLeave")}if(a.MutationObserver&&"undefined"!=typeof HTMLElement){var d=0,e=function(){var b=HTMLElement.prototype.matches||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector;return{matchesSelector:function(a,c){return a instanceof HTMLElement&&b.call(a,c)},addMethod:function(a,b,c){var d=a[b];a[b]=function(){return c.length==arguments.length?c.apply(this,arguments):"function"==typeof d?d.apply(this,arguments):void 0}},callCallbacks:function(a){for(var c,b=0;c=a[b];b++)c.callback.call(c.elem)},checkChildNodesRecursively:function(a,b,c,d){for(var g,f=0;g=a[f];f++)c(g,b,d)&&d.push({callback:b.callback,elem:g}),g.childNodes.length>0&&e.checkChildNodesRecursively(g.childNodes,b,c,d)},mergeArrays:function(a,b){var d,c={};for(d in a)c[d]=a[d];for(d in b)c[d]=b[d];return c},toElementsArray:function(b){return void 0===b||"number"==typeof b.length&&b!==a||(b=[b]),b}}}(),f=function(){var a=function(){this._eventsBucket=[],this._beforeAdding=null,this._beforeRemoving=null};return a.prototype.addEvent=function(a,b,c,d){var e={target:a,selector:b,options:c,callback:d,firedElems:[]};return this._beforeAdding&&this._beforeAdding(e),this._eventsBucket.push(e),e},a.prototype.removeEvent=function(a){for(var c,b=this._eventsBucket.length-1;c=this._eventsBucket[b];b--)a(c)&&(this._beforeRemoving&&this._beforeRemoving(c),this._eventsBucket.splice(b,1))},a.prototype.beforeAdding=function(a){this._beforeAdding=a},a.prototype.beforeRemoving=function(a){this._beforeRemoving=a},a}(),g=function(b,d){var g=new f,h=this,i={fireOnAttributesModification:!1};return g.beforeAdding(function(c){var i,e=c.target;c.selector,c.callback;e!==a.document&&e!==a||(e=document.getElementsByTagName("html")[0]),i=new MutationObserver(function(a){d.call(this,a,c)});var j=b(c.options);i.observe(e,j),c.observer=i,c.me=h}),g.beforeRemoving(function(a){a.observer.disconnect()}),this.bindEvent=function(a,b,c){b=e.mergeArrays(i,b);for(var d=e.toElementsArray(this),f=0;f<d.length;f++)g.addEvent(d[f],a,b,c)},this.unbindEvent=function(){var a=e.toElementsArray(this);g.removeEvent(function(b){for(var d=0;d<a.length;d++)if(this===c||b.target===a[d])return!0;return!1})},this.unbindEventWithSelectorOrCallback=function(a){var f,b=e.toElementsArray(this),d=a;f="function"==typeof a?function(a){for(var e=0;e<b.length;e++)if((this===c||a.target===b[e])&&a.callback===d)return!0;return!1}:function(d){for(var e=0;e<b.length;e++)if((this===c||d.target===b[e])&&d.selector===a)return!0;return!1},g.removeEvent(f)},this.unbindEventWithSelectorAndCallback=function(a,b){var d=e.toElementsArray(this);g.removeEvent(function(e){for(var f=0;f<d.length;f++)if((this===c||e.target===d[f])&&e.selector===a&&e.callback===b)return!0;return!1})},this},h=function(){function h(a){var b={attributes:!1,childList:!0,subtree:!0};return a.fireOnAttributesModification&&(b.attributes=!0),b}function i(a,b){a.forEach(function(a){var c=a.addedNodes,d=a.target,f=[];null!==c&&c.length>0?e.checkChildNodesRecursively(c,b,k,f):"attributes"===a.type&&k(d,b,f)&&f.push({callback:b.callback,elem:node}),e.callCallbacks(f)})}function k(a,b,f){if(e.matchesSelector(a,b.selector)&&(a._id===c&&(a._id=d++),-1==b.firedElems.indexOf(a._id))){if(b.options.onceOnly){if(0!==b.firedElems.length)return;b.me.unbindEventWithSelectorAndCallback.call(b.target,b.selector,b.callback)}b.firedElems.push(a._id),f.push({callback:b.callback,elem:a})}}var f={fireOnAttributesModification:!1,onceOnly:!1,existing:!1};j=new g(h,i);var l=j.bindEvent;return j.bindEvent=function(a,b,c){void 0===c?(c=b,b=f):b=e.mergeArrays(f,b);var d=e.toElementsArray(this);if(b.existing){for(var g=[],h=0;h<d.length;h++)for(var i=d[h].querySelectorAll(a),j=0;j<i.length;j++)g.push({callback:c,elem:i[j]});if(b.onceOnly&&g.length)return c.call(g[0].elem);setTimeout(e.callCallbacks,1,g)}l.call(this,a,b,c)},j},i=function(){function d(a){return{childList:!0,subtree:!0}}function f(a,b){a.forEach(function(a){var c=a.removedNodes,f=(a.target,[]);null!==c&&c.length>0&&e.checkChildNodesRecursively(c,b,h,f),e.callCallbacks(f)})}function h(a,b){return e.matchesSelector(a,b.selector)}var c={};k=new g(d,f);var i=k.bindEvent;return k.bindEvent=function(a,b,d){void 0===d?(d=b,b=c):b=e.mergeArrays(c,b),i.call(this,a,b,d)},k},j=new h,k=new i;b&&m(b.fn),m(HTMLElement.prototype),m(NodeList.prototype),m(HTMLCollection.prototype),m(HTMLDocument.prototype),m(Window.prototype);var n={};return l(j,n,"unbindAllArrive"),l(k,n,"unbindAllLeave"),n}}(window,"undefined"==typeof jQuery?null:jQuery,void 0);"undefined"!=typeof jQuery&&check_webp_feature("alpha",ewww_load_images);
2320
+ </script>
2321
+ <?php } // End if().
2322
+ // Current length 9473.
2323
+ }
2324
+
2325
+ /**
2326
+ * Enqueue custom jquery stylesheet and scripts for the media library AJAX functions.
2327
+ *
2328
+ * @param string $hook The unique hook of the page being loaded in the WP admin.
2329
+ */
2330
+ function ewww_image_optimizer_media_scripts( $hook ) {
2331
+ if ( 'upload.php' == $hook || 'ims_gallery_page_ewww-ims-optimize' == $hook ) {
2332
+ add_thickbox();
2333
+ wp_enqueue_script( 'jquery-ui-tooltip' );
2334
+ wp_enqueue_script( 'ewwwmediascript', plugins_url( '/includes/media.js', __FILE__ ), array( 'jquery' ), EWWW_IMAGE_OPTIMIZER_VERSION );
2335
+ wp_enqueue_style( 'jquery-ui-tooltip-custom', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', __FILE__ ), array(), EWWW_IMAGE_OPTIMIZER_VERSION );
2336
+ // Submit a couple variables to the javascript to work with.
2337
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
2338
+ $loading_image = plugins_url( '/images/spinner.gif', __FILE__ );
2339
+ wp_localize_script(
2340
+ 'ewwwmediascript',
2341
+ 'ewww_vars',
2342
+ array(
2343
+ 'optimizing' => '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>",
2344
+ 'restoring' => '<p>' . esc_html__( 'Restoring', 'ewww-image-optimizer' ) . "&nbsp;<img src='$loading_image' /></p>",
2345
+ )
2346
+ );
2347
+ }
2348
+ }
2349
+
2350
+ /**
2351
+ * Adds a link on the Plugins page for the EWWW IO settings.
2352
+ *
2353
+ * @param array $links A list of links to display next to the plugin listing.
2354
+ * @return array The new list of links to be displayed.
2355
+ */
2356
+ function ewww_image_optimizer_settings_link( $links ) {
2357
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
2358
+ // Need to include the plugin library for the is_plugin_active function.
2359
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
2360
+ }
2361
+ // Load the html for the settings link.
2362
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
2363
+ $settings_link = '<a href="network/settings.php?page=' . plugin_basename( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) . '">' . esc_html__( 'Settings', 'ewww-image-optimizer' ) . '</a>';
2364
+ } else {
2365
+ $settings_link = '<a href="options-general.php?page=' . plugin_basename( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) . '">' . esc_html__( 'Settings', 'ewww-image-optimizer' ) . '</a>';
2366
+ }
2367
+ // Load the settings link into the plugin links array.
2368
+ array_unshift( $links, $settings_link );
2369
+ // Send back the plugin links array.
2370
+ return $links;
2371
+ }
2372
+
2373
+ /**
2374
+ * Check for GD support of both PNG and JPG.
2375
+ *
2376
+ * @return bool True if full GD support is detected.
2377
+ */
2378
+ function ewww_image_optimizer_gd_support() {
2379
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2380
+ if ( function_exists( 'gd_info' ) ) {
2381
+ $gd_support = gd_info();
2382
+ ewwwio_debug_message( 'GD found, supports:' );
2383
+ if ( ewww_image_optimizer_iterable( $gd_support ) ) {
2384
+ foreach ( $gd_support as $supports => $supported ) {
2385
+ ewwwio_debug_message( "$supports: $supported" );
2386
+ }
2387
+ ewwwio_memory( __FUNCTION__ );
2388
+ if ( ( ! empty( $gd_support['JPEG Support'] ) || ! empty( $gd_support['JPG Support'] ) ) && ! empty( $gd_support['PNG Support'] ) ) {
2389
+ return true;
2390
+ }
2391
+ }
2392
+ }
2393
+ return false;
2394
+ }
2395
+
2396
+ /**
2397
+ * Check for IMagick support of both PNG and JPG.
2398
+ *
2399
+ * @return bool True if full Imagick support is detected.
2400
+ */
2401
+ function ewww_image_optimizer_imagick_support() {
2402
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2403
+ if ( extension_loaded( 'imagick' ) ) {
2404
+ $imagick = new Imagick();
2405
+ $formats = $imagick->queryFormats();
2406
+ if ( in_array( 'PNG', $formats ) && in_array( 'JPG', $formats ) ) {
2407
+ return true;
2408
+ }
2409
+ }
2410
+ return false;
2411
+ }
2412
+
2413
+ /**
2414
+ * Check for GMagick support of both PNG and JPG.
2415
+ *
2416
+ * @return bool True if full Gmagick support is detected.
2417
+ */
2418
+ function ewww_image_optimizer_gmagick_support() {
2419
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2420
+ if ( extension_loaded( 'gmagick' ) ) {
2421
+ $gmagick = new Gmagick();
2422
+ $formats = $gmagick->queryFormats();
2423
+ if ( in_array( 'PNG', $formats ) && in_array( 'JPG', $formats ) ) {
2424
+ return true;
2425
+ }
2426
+ }
2427
+ return false;
2428
+ }
2429
+
2430
+ /**
2431
+ * Sanitize the list of disabled resizes.
2432
+ *
2433
+ * @param array $disabled_resizes A list of sizes, like 'medium_large', 'thumb', etc.
2434
+ * @return array|string The sanitized list of sizes, or an empty string.
2435
+ */
2436
+ function ewww_image_optimizer_disable_resizes_sanitize( $disabled_resizes ) {
2437
+ if ( is_array( $disabled_resizes ) ) {
2438
+ return $disabled_resizes;
2439
+ } else {
2440
+ return '';
2441
+ }
2442
+ }
2443
+
2444
+ /**
2445
+ * Sanitize the list of folders to optimize.
2446
+ *
2447
+ * @param string $input A list of filesystem paths, from a textarea.
2448
+ * @return array The list of paths, validated, and converted to an array.
2449
+ */
2450
+ function ewww_image_optimizer_aux_paths_sanitize( $input ) {
2451
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2452
+ if ( empty( $input ) ) {
2453
+ return '';
2454
+ }
2455
+ $path_array = array();
2456
+ $paths = explode( "\n", $input );
2457
+ if ( ewww_image_optimizer_iterable( $paths ) ) {
2458
+ $i = 0;
2459
+ foreach ( $paths as $path ) {
2460
+ $i++;
2461
+ $path = sanitize_text_field( $path );
2462
+ ewwwio_debug_message( "validating auxiliary path: $path" );
2463
+ // Retrieve the location of the wordpress upload folder.
2464
+ $upload_dir = apply_filters( 'ewww_image_optimizer_folder_restriction', wp_upload_dir( null, false ) );
2465
+ // Retrieve the path of the upload folder from the array.
2466
+ $upload_path = trailingslashit( $upload_dir['basedir'] );
2467
+ if ( is_dir( $path ) && ( strpos( $path, ABSPATH ) === 0 || strpos( $path, $upload_path ) === 0 ) ) {
2468
+ $path_array[] = $path;
2469
+ continue;
2470
+ }
2471
+ // If they put in a relative path.
2472
+ if ( is_dir( ABSPATH . ltrim( $path, '/' ) ) ) {
2473
+ $path_array[] = ABSPATH . ltrim( $path, '/' );
2474
+ continue;
2475
+ }
2476
+ // Or a path relative to the upload dir?
2477
+ if ( is_dir( $upload_path . ltrim( $path, '/' ) ) ) {
2478
+ $path_array[] = $upload_path . ltrim( $path, '/' );
2479
+ continue;
2480
+ }
2481
+ // What if they put in a url?
2482
+ $pathabsurl = ABSPATH . ltrim( str_replace( get_site_url(), '', $path ), '/' );
2483
+ if ( is_dir( $pathabsurl ) ) {
2484
+ $path_array[] = $pathabsurl;
2485
+ continue;
2486
+ }
2487
+ // Or a url in the uploads folder?
2488
+ $pathupurl = $upload_path . ltrim( str_replace( $upload_dir['baseurl'], '', $path ), '/' );
2489
+ if ( is_dir( $pathupurl ) ) {
2490
+ $path_array[] = $pathupurl;
2491
+ continue;
2492
+ }
2493
+ if ( ! empty( $path ) ) {
2494
+ add_settings_error( 'ewww_image_optimizer_aux_paths', "ewwwio-aux-paths-$i", sprintf(
2495
+ /* translators: %s: A file system path */
2496
+ esc_html__( 'Could not save Folder to Optimize: %s. Please ensure that it is a valid location on the server.', 'ewww-image-optimizer' ), esc_html( $path )
2497
+ ) );
2498
+ }
2499
+ } // End foreach().
2500
+ } // End if().
2501
+ ewwwio_memory( __FUNCTION__ );
2502
+ return $path_array;
2503
+ }
2504
+
2505
+ /**
2506
+ * Sanitize the folders/patterns to exclude from optimization.
2507
+ *
2508
+ * @param string $input A list of filesystem paths, from a textarea.
2509
+ * @return array The sanitized list of paths/patterns to exclude.
2510
+ */
2511
+ function ewww_image_optimizer_exclude_paths_sanitize( $input ) {
2512
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2513
+ if ( empty( $input ) ) {
2514
+ return '';
2515
+ }
2516
+ $path_array = array();
2517
+ $paths = explode( "\n", $input );
2518
+ if ( ewww_image_optimizer_iterable( $paths ) ) {
2519
+ $i = 0;
2520
+ foreach ( $paths as $path ) {
2521
+ $i++;
2522
+ ewwwio_debug_message( "validating path exclusion: $path" );
2523
+ $path = sanitize_text_field( $path );
2524
+ if ( ! empty( $path ) ) {
2525
+ $path_array[] = $path;
2526
+ }
2527
+ }
2528
+ }
2529
+ ewwwio_memory( __FUNCTION__ );
2530
+ return $path_array;
2531
+ }
2532
+
2533
+ /**
2534
+ * Sanitize the url patterns used for WebP rewriting.
2535
+ *
2536
+ * @param string $paths A list of urls/patterns, from a textarea.
2537
+ * @return array The sanitize list of url patterns for WebP matching.
2538
+ */
2539
+ function ewww_image_optimizer_webp_paths_sanitize( $paths ) {
2540
+ if ( empty( $paths ) ) {
2541
+ return '';
2542
+ }
2543
+ $paths_entered = explode( "\n", $paths );
2544
+ $paths_saved = array();
2545
+ if ( ewww_image_optimizer_iterable( $paths_entered ) ) {
2546
+ $i = 0;
2547
+ foreach ( $paths_entered as $path ) {
2548
+ $i++;
2549
+ $original_path = esc_html( $path );
2550
+ $path = esc_url( $path, null, 'db' );
2551
+ if ( ! empty( $path ) ) {
2552
+ if ( ! substr_count( $path, '.' ) ) {
2553
+ add_settings_error( 'ewww_image_optimizer_webp_paths', "ewwwio-webp-paths-$i", sprintf(
2554
+ /* translators: %s: A url or domain name */
2555
+ esc_html__( 'Could not save WebP URL: %s.', 'ewww-image-optimizer' ), esc_html( $original_path )
2556
+ ) . ' ' . esc_html__( 'Please enter a valid url including the domain name.', 'ewww-image-optimizer' ) );
2557
+ continue;
2558
+ }
2559
+ $paths_saved[] = str_replace( 'http://', '', $path );
2560
+ }
2561
+ }
2562
+ }
2563
+ return $paths_saved;
2564
+ }
2565
+
2566
+ /**
2567
+ * Replacement for escapeshellarg() that won't kill non-ASCII characters.
2568
+ *
2569
+ * @param string $arg A value to sanitize/escape for commmand-line usage.
2570
+ * @return string The value after being escaped.
2571
+ */
2572
+ function ewww_image_optimizer_escapeshellarg( $arg ) {
2573
+ if ( PHP_OS === 'WINNT' ) {
2574
+ $safe_arg = '"' . $arg . '"';
2575
+ } else {
2576
+ $safe_arg = "'" . str_replace( "'", "'\"'\"'", $arg ) . "'";
2577
+ }
2578
+ ewwwio_memory( __FUNCTION__ );
2579
+ return $safe_arg;
2580
+ }
2581
+
2582
+ /**
2583
+ * Retrieves/sanitizes jpg background fill setting or returns null for png2jpg conversions.
2584
+ *
2585
+ * @param string $background The hexadecimal value entered by the user.
2586
+ * @return string The background color sanitized.
2587
+ */
2588
+ function ewww_image_optimizer_jpg_background( $background = null ) {
2589
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2590
+ if ( null === $background ) {
2591
+ // Retrieve the user-supplied value for jpg background color.
2592
+ $background = ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_background' );
2593
+ }
2594
+ // Verify that the supplied value is in hex notation.
2595
+ if ( preg_match( '/^\#*([0-9a-fA-F]){6}$/', $background ) ) {
2596
+ // We remove a leading # symbol, since we take care of it later.
2597
+ $background = ltrim( $background, '#' );
2598
+ // Send back the verified, cleaned-up background color.
2599
+ ewwwio_debug_message( "background: $background" );
2600
+ ewwwio_memory( __FUNCTION__ );
2601
+ return $background;
2602
+ } else {
2603
+ if ( ! empty( $background ) ) {
2604
+ add_settings_error( 'ewww_image_optimizer_jpg_background', 'ewwwio-jpg-background', esc_html__( 'Could not save the JPG background color, please enter a six-character, hexadecimal value.', 'ewww-image-optimizer' ) );
2605
+ }
2606
+ // Send back a blank value.
2607
+ ewwwio_memory( __FUNCTION__ );
2608
+ return null;
2609
+ }
2610
+ }
2611
+
2612
+ /**
2613
+ * Retrieves/sanitizes the jpg quality setting for png2jpg conversion or returns null.
2614
+ *
2615
+ * @param int $quality The JPG quality level as set by the user.
2616
+ * @return int The sanitize JPG quality level.
2617
+ */
2618
+ function ewww_image_optimizer_jpg_quality( $quality = null ) {
2619
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2620
+ if ( null === $quality ) {
2621
+ // Retrieve the user-supplied value for jpg quality.
2622
+ $quality = ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_quality' );
2623
+ }
2624
+ // Verify that the quality level is an integer, 1-100.
2625
+ if ( preg_match( '/^(100|[1-9][0-9]?)$/', $quality ) ) {
2626
+ ewwwio_debug_message( "quality: $quality" );
2627
+ // Send back the valid quality level.
2628
+ ewwwio_memory( __FUNCTION__ );
2629
+ return $quality;
2630
+ } else {
2631
+ if ( ! empty( $quality ) ) {
2632
+ add_settings_error( 'ewww_image_optimizer_jpg_quality', 'ewwwio-jpg-quality', esc_html__( 'Could not save the JPG quality, please enter an integer between 1 and 100.', 'ewww-image-optimizer' ) );
2633
+ }
2634
+ // Send back nothing.
2635
+ ewwwio_memory( __FUNCTION__ );
2636
+ return null;
2637
+ }
2638
+ }
2639
+
2640
+ /**
2641
+ * Filter the filename past any folders the user chose to ignore.
2642
+ *
2643
+ * @param bool $bypass True to skip optimization, defaults to false.
2644
+ * @param string $filename The file about to be optimized.
2645
+ * @return bool True if the file matches any folders to ignore.
2646
+ */
2647
+ function ewww_image_optimizer_ignore_file( $bypass, $filename ) {
2648
+ $ignore_folders = ewww_image_optimizer_get_option( 'ewww_image_optimizer_exclude_paths' );
2649
+ if ( ! ewww_image_optimizer_iterable( $ignore_folders ) ) {
2650
+ return $bypass;
2651
+ }
2652
+ foreach ( $ignore_folders as $ignore_folder ) {
2653
+ if ( strpos( $filename, $ignore_folder ) !== false ) {
2654
+ return true;
2655
+ }
2656
+ }
2657
+ return $bypass;
2658
+ }
2659
+
2660
+ /**
2661
+ * Overrides the default JPG quality for WordPress image editing operations.
2662
+ *
2663
+ * @param int $quality The default JPG quality level.
2664
+ * @return int The default quality, or the user configured level.
2665
+ */
2666
+ function ewww_image_optimizer_set_jpg_quality( $quality ) {
2667
+ $new_quality = ewww_image_optimizer_jpg_quality();
2668
+ if ( ! empty( $new_quality ) ) {
2669
+ return $new_quality;
2670
+ }
2671
+ return $quality;
2672
+ }
2673
+
2674
+ /**
2675
+ * Check filesize, and prevent errors by ensuring file exists, and that the cache has been cleared.
2676
+ *
2677
+ * @param string $file The name of the file.
2678
+ * @return int The size of the file or zero.
2679
+ */
2680
+ function ewww_image_optimizer_filesize( $file ) {
2681
+ if ( is_file( $file ) ) {
2682
+ // Flush the cache for filesize.
2683
+ clearstatcache();
2684
+ // Find out the size of the new PNG file.
2685
+ return filesize( $file );
2686
+ } else {
2687
+ return 0;
2688
+ }
2689
+ }
2690
+
2691
+ /**
2692
+ * Make sure an array/object can be parsed by a foreach().
2693
+ *
2694
+ * @param mixed $var A variable to test for iteration ability.
2695
+ * @return bool True if the variable is iterable.
2696
+ */
2697
+ function ewww_image_optimizer_iterable( $var ) {
2698
+ return ! empty( $var ) && ( is_array( $var ) || is_object( $var ) );
2699
+ }
2700
+
2701
+ /**
2702
+ * Manually process an image from the Media Library
2703
+ *
2704
+ * @global bool $ewww_defer True if the image optimization should be deferred.
2705
+ */
2706
+ function ewww_image_optimizer_manual() {
2707
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2708
+ global $ewww_defer;
2709
+ $ewww_defer = false;
2710
+ // Check permissions of current user.
2711
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
2712
+ if ( false === current_user_can( $permissions ) ) {
2713
+ // Display error message if insufficient permissions.
2714
+ if ( ! wp_doing_ajax() ) {
2715
+ wp_die( esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ) );
2716
+ }
2717
+ wp_die( json_encode( array(
2718
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
2719
+ ) ) );
2720
+ }
2721
+ // Make sure we didn't accidentally get to this page without an attachment to work on.
2722
+ if ( false === isset( $_REQUEST['ewww_attachment_ID'] ) ) {
2723
+ // Display an error message since we don't have anything to work on.
2724
+ if ( ! wp_doing_ajax() ) {
2725
+ wp_die( esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ) );
2726
+ }
2727
+ wp_die( json_encode( array(
2728
+ 'error' => esc_html__( 'No attachment ID was provided.', 'ewww-image-optimizer' ),
2729
+ ) ) );
2730
+ }
2731
+ session_write_close();
2732
+ // Store the attachment ID value.
2733
+ $attachment_id = intval( $_REQUEST['ewww_attachment_ID'] );
2734
+ if ( empty( $_REQUEST['ewww_manual_nonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_manual_nonce'], 'ewww-manual' ) ) {
2735
+ if ( ! wp_doing_ajax() ) {
2736
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
2737
+ }
2738
+ wp_die( json_encode( array(
2739
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
2740
+ ) ) );
2741
+ }
2742
+ // Retrieve the existing attachment metadata.
2743
+ $original_meta = wp_get_attachment_metadata( $attachment_id );
2744
+ // If the call was to optimize...
2745
+ if ( 'ewww_image_optimizer_manual_optimize' === $_REQUEST['action'] || 'ewww_manual_optimize' === $_REQUEST['action'] ) {
2746
+ // Call the optimize from metadata function and store the resulting new metadata.
2747
+ $new_meta = ewww_image_optimizer_resize_from_meta_data( $original_meta, $attachment_id );
2748
+ } elseif ( 'ewww_image_optimizer_manual_restore' === $_REQUEST['action'] || 'ewww_manual_restore' === $_REQUEST['action'] ) {
2749
+ $new_meta = ewww_image_optimizer_restore_from_meta_data( $original_meta, $attachment_id );
2750
+ } elseif ( 'ewww_image_optimizer_manual_cloud_restore' === $_REQUEST['action'] || 'ewww_manual_cloud_restore' === $_REQUEST['action'] ) {
2751
+ ewww_image_optimizer_cloud_restore_from_meta_data( $attachment_id, 'media' );
2752
+ $new_meta = $original_meta;
2753
+ } else {
2754
+ if ( ! wp_doing_ajax() ) {
2755
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
2756
+ }
2757
+ wp_die( json_encode( array(
2758
+ 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ),
2759
+ ) ) );
2760
+ }
2761
+ $basename = '';
2762
+ if ( is_array( $new_meta ) && ! empty( $new_meta['file'] ) ) {
2763
+ $basename = basename( $new_meta['file'] );
2764
+ }
2765
+ // Update the attachment metadata in the database.
2766
+ $meta_saved = wp_update_attachment_metadata( $attachment_id, $new_meta );
2767
+ if ( ! $meta_saved ) {
2768
+ ewwwio_debug_message( 'failed to save meta' );
2769
+ }
2770
+ if ( get_transient( 'ewww_image_optimizer_cloud_status' ) == 'exceeded' || ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_exceeded' ) > time() ) {
2771
+ if ( ! wp_doing_ajax() ) {
2772
+ wp_die( esc_html__( 'License exceeded', 'ewww-image-optimizer' ) );
2773
+ }
2774
+ wp_die( json_encode( array(
2775
+ 'error' => esc_html__( 'License exceeded', 'ewww-image-optimizer' ),
2776
+ ) ) );
2777
+ }
2778
+ $success = ewww_image_optimizer_custom_column( 'ewww-image-optimizer', $attachment_id, $new_meta, true );
2779
+ ewww_image_optimizer_debug_log();
2780
+ // Do a redirect, if this was called via GET.
2781
+ if ( ! wp_doing_ajax() ) {
2782
+ // Store the referring webpage location.
2783
+ $sendback = wp_get_referer();
2784
+ // Sanitize the referring webpage location.
2785
+ $sendback = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:]|i', '', $sendback );
2786
+ // Send the user back where they came from.
2787
+ wp_redirect( $sendback );
2788
+ return;
2789
+ }
2790
+ ewwwio_memory( __FUNCTION__ );
2791
+ die( json_encode( array(
2792
+ 'success' => $success,
2793
+ 'basename' => $basename,
2794
+ ) ) );
2795
+ }
2796
+
2797
+ /**
2798
+ * Manually restore a converted image.
2799
+ *
2800
+ * @global object $wpdb
2801
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
2802
+ *
2803
+ * @param array $meta The attachment metadata.
2804
+ * @param int $id The attachment id number.
2805
+ * @return array The attachment metadata.
2806
+ */
2807
+ function ewww_image_optimizer_restore_from_meta_data( $meta, $id ) {
2808
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2809
+ global $wpdb;
2810
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
2811
+ ewww_image_optimizer_db_init();
2812
+ global $ewwwdb;
2813
+ } else {
2814
+ $ewwwdb = $wpdb;
2815
+ }
2816
+ $db_image = $ewwwdb->get_results( "SELECT id,path,converted FROM $ewwwdb->ewwwio_images WHERE attachment_id = $id AND gallery = 'media' AND resize = 'full'", ARRAY_A );
2817
+ if ( empty( $db_image ) || ! is_array( $db_image ) || empty( $db_image['path'] ) ) {
2818
+ // Get the filepath based on the meta and id.
2819
+ list( $file_path, $upload_path ) = ewww_image_optimizer_attachment_path( $meta, $id );
2820
+ $db_image = ewww_image_optimizer_find_already_optimized( $file_path );
2821
+ if ( empty( $db_image ) || ! is_array( $db_image ) || empty( $db_image['path'] ) ) {
2822
+ return $meta;
2823
+ }
2824
+ }
2825
+ $ewww_image = new EWWW_Image( $id, 'media', ewww_image_optimizer_relative_path_replace( $db_image['path'] ) );
2826
+ return $ewww_image->restore_with_meta( $meta );
2827
+ }
2828
+
2829
+ /**
2830
+ * Manually restore an attachment from the API
2831
+ *
2832
+ * @global object $wpdb
2833
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
2834
+ *
2835
+ * @param int $id The attachment id number.
2836
+ * @param string $gallery Optional. The gallery from whence we came. Default 'media'.
2837
+ */
2838
+ function ewww_image_optimizer_cloud_restore_from_meta_data( $id, $gallery = 'media' ) {
2839
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2840
+ global $wpdb;
2841
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
2842
+ ewww_image_optimizer_db_init();
2843
+ global $ewwwdb;
2844
+ } else {
2845
+ $ewwwdb = $wpdb;
2846
+ }
2847
+ $images = $ewwwdb->get_results( "SELECT id,path,backup FROM $ewwwdb->ewwwio_images WHERE attachment_id = $id AND gallery = '$gallery'", ARRAY_A );
2848
+ foreach ( $images as $image ) {
2849
+ if ( ! empty( $image['path'] ) ) {
2850
+ $image['path'] = ewww_image_optimizer_relative_path_replace( $image['path'] );
2851
+ }
2852
+ ewww_image_optimizer_cloud_restore_single_image( $image );
2853
+ }
2854
+ }
2855
+
2856
+ /**
2857
+ * Handle the AJAX call for a single image restore.
2858
+ */
2859
+ function ewww_image_optimizer_cloud_restore_single_image_handler() {
2860
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2861
+ // Check permissions of current user.
2862
+ $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
2863
+ if ( false === current_user_can( $permissions ) ) {
2864
+ // Display error message if insufficient permissions.
2865
+ wp_die( json_encode( array(
2866
+ 'error' => esc_html__( 'You do not have permission to optimize images.', 'ewww-image-optimizer' ),
2867
+ ) ) );
2868
+ }
2869
+ // Make sure we didn't accidentally get to this page without an attachment to work on.
2870
+ if ( empty( $_REQUEST['ewww_image_id'] ) ) {
2871
+ // Display an error message since we don't have anything to work on.
2872
+ wp_die( json_encode( array(
2873
+ 'error' => esc_html__( 'No image ID was provided.', 'ewww-image-optimizer' ),
2874
+ ) ) );
2875
+ }
2876
+ if ( empty( $_REQUEST['ewww_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) ) {
2877
+ wp_die( json_encode( array(
2878
+ 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ),
2879
+ ) ) );
2880
+ }
2881
+ session_write_close();
2882
+ $image = (int) $_REQUEST['ewww_image_id'];
2883
+ if ( ewww_image_optimizer_cloud_restore_single_image( $image ) ) {
2884
+ wp_die( json_encode( array(
2885
+ 'success' => 1,
2886
+ ) ) );
2887
+ }
2888
+ wp_die( json_encode( array(
2889
+ 'error' => esc_html__( 'Unable to restore image.', 'ewww-image-optimizer' ),
2890
+ ) ) );
2891
+ }
2892
+
2893
+ /**
2894
+ * Restores a single image from the API.
2895
+ *
2896
+ * @global object $wpdb
2897
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
2898
+ *
2899
+ * @param string $image The filename of the image to restore.
2900
+ * @return bool True if the image was restored successfully.
2901
+ */
2902
+ function ewww_image_optimizer_cloud_restore_single_image( $image ) {
2903
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2904
+ global $wpdb;
2905
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
2906
+ ewww_image_optimizer_db_init();
2907
+ global $ewwwdb;
2908
+ } else {
2909
+ $ewwwdb = $wpdb;
2910
+ }
2911
+ if ( ! is_array( $image ) && ! empty( $image ) && is_numeric( $image ) ) {
2912
+ $image = $ewwwdb->get_row( "SELECT id,path,backup FROM $ewwwdb->ewwwio_images WHERE id = $image", ARRAY_A );
2913
+ }
2914
+ if ( ! empty( $image['path'] ) ) {
2915
+ $image['path'] = ewww_image_optimizer_relative_path_replace( $image['path'] );
2916
+ }
2917
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
2918
+ if ( empty( $ewww_cloud_transport ) ) {
2919
+ if ( ! ewww_image_optimizer_cloud_verify() ) {
2920
+ return false;
2921
+ } else {
2922
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
2923
+ }
2924
+ }
2925
+ if ( empty( $ewww_cloud_transport ) ) {
2926
+ $ewww_cloud_transport = 'https';
2927
+ }
2928
+ $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
2929
+ $domain = parse_url( get_site_url(), PHP_URL_HOST );
2930
+ $url = "$ewww_cloud_transport://optimize.exactlywww.com/backup/restore.php";
2931
+ $result = wp_remote_post( $url, array(
2932
+ 'timeout' => 30,
2933
+ 'sslverify' => false,
2934
+ 'body' => array(
2935
+ 'api_key' => $api_key,
2936
+ 'domain' => $domain,
2937
+ 'hash' => $image['backup'],
2938
+ ),
2939
+ ) );
2940
+ if ( is_wp_error( $result ) ) {
2941
+ $error_message = $result->get_error_message();
2942
+ ewwwio_debug_message( "quota request failed: $error_message" );
2943
+ ewwwio_memory( __FUNCTION__ );
2944
+ return false;
2945
+ } elseif ( ! empty( $result['body'] ) && strpos( $result['body'], 'missing' ) === false ) {
2946
+ $enabled_types = array( 'image/jpeg', 'image/png', 'image/gif', 'application/pdf' );
2947
+ file_put_contents( $image['path'] . '.tmp', $result['body'] );
2948
+ $new_type = ewww_image_optimizer_mimetype( $image['path'] . '.tmp', 'i' );
2949
+ $old_type = '';
2950
+ if ( is_file( $image['path'] ) ) {
2951
+ $old_type = ewww_image_optimizer_mimetype( $image['path'] );
2952
+ }
2953
+ if ( ! in_array( $new_type, $enabled_types ) ) {
2954
+ return false;
2955
+ }
2956
+ if ( empty( $old_type ) || $old_type == $new_type ) {
2957
+ if ( rename( $image['path'] . '.tmp', $image['path'] ) ) {
2958
+ // Set the results to nothing.
2959
+ $ewwwdb->query( "UPDATE $ewwwdb->ewwwio_images SET results = '', image_size = 0, updates = 0, updated=updated, level = 0 WHERE id = {$image['id']}" );
2960
+ return true;
2961
+ }
2962
+ }
2963
+ }
2964
+ return false;
2965
+ }
2966
+
2967
+ /**
2968
+ * Cleans up when an attachment is being deleted.
2969
+ *
2970
+ * Removes any .webp images, backups from conversion, and removes related database records.
2971
+ *
2972
+ * @global object $wpdb
2973
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
2974
+ *
2975
+ * @param int $id The id number for the attachment being deleted.
2976
+ */
2977
+ function ewww_image_optimizer_delete( $id ) {
2978
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2979
+ global $wpdb;
2980
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
2981
+ ewww_image_optimizer_db_init();
2982
+ global $ewwwdb;
2983
+ } else {
2984
+ $ewwwdb = $wpdb;
2985
+ }
2986
+ // Finds non-meta images to remove from disk, and from db, as well as converted originals.
2987
+ if (
2988
+ $optimized_images = $ewwwdb->get_results( "SELECT path,converted FROM $ewwwdb->ewwwio_images WHERE attachment_id = $id AND gallery = 'media'", ARRAY_A )
2989
+ ) {
2990
+ if ( ewww_image_optimizer_iterable( $optimized_images ) ) {
2991
+ foreach ( $optimized_images as $image ) {
2992
+ if ( ! empty( $image['path'] ) ) {
2993
+ $image['path'] = ewww_image_optimizer_relative_path_replace( $image['path'] );
2994
+ }
2995
+ if ( ! empty( $image['path'] ) && is_file( $image['path'] ) ) {
2996
+ unlink( $image['path'] );
2997
+ if ( is_file( $image['path'] . '.webp' ) ) {
2998
+ unlink( $image['path'] . '.webp' );
2999
+ }
3000
+ }
3001
+ if ( ! empty( $image['converted'] ) ) {
3002
+ $image['converted'] = ewww_image_optimizer_relative_path_replace( $image['converted'] );
3003
+ }
3004
+ if ( ! empty( $image['converted'] ) && is_file( $image['converted'] ) ) {
3005
+ unlink( $image['converted'] );
3006
+ if ( is_file( $image['converted'] . '.webp' ) ) {
3007
+ unlink( $image['converted'] . '.webp' );
3008
+ }
3009
+ }
3010
+ }
3011
+ }
3012
+ $ewwwdb->delete( $ewwwdb->ewwwio_images, array(
3013
+ 'attachment_id' => $id,
3014
+ ) );
3015
+ }
3016
+ // Retrieve the image metadata.
3017
+ $meta = wp_get_attachment_metadata( $id );
3018
+ // If the attachment has an original file set.
3019
+ if ( ! empty( $meta['orig_file'] ) ) {
3020
+ unset( $rows );
3021
+ // Get the filepath from the metadata.
3022
+ $file_path = $meta['orig_file'];
3023
+ // Get the base filename.
3024
+ $filename = basename( $file_path );
3025
+ // Delete any residual webp versions.
3026
+ $webpfile = $filename . '.webp';
3027
+ $webpfileold = preg_replace( '/\.\w+$/', '.webp', $filename );
3028
+ if ( is_file( $webpfile ) ) {
3029
+ unlink( $webpfile );
3030
+ }
3031
+ if ( is_file( $webpfileold ) ) {
3032
+ unlink( $webpfileold );
3033
+ }
3034
+ // Retrieve any posts that link the original image.
3035
+ $esql = "SELECT ID, post_content FROM $ewwwdb->posts WHERE post_content LIKE '%$filename%' LIMIT 1";
3036
+ $rows = $ewwwdb->get_row( $esql );
3037
+ // If the original file still exists and no posts contain links to the image.
3038
+ if ( is_file( $file_path ) && empty( $rows ) ) {
3039
+ unlink( $file_path );
3040
+ $ewwwdb->delete( $ewwwdb->ewwwio_images, array(
3041
+ 'path' => ewww_image_optimizer_relative_path_remove( $file_path ),
3042
+ ) );
3043
+ }
3044
+ }
3045
+ // Remove the regular image from the ewwwio_images tables.
3046
+ list( $file_path, $upload_path ) = ewww_image_optimizer_attachment_path( $meta, $id );
3047
+ $ewwwdb->delete( $ewwwdb->ewwwio_images, array(
3048
+ 'path' => ewww_image_optimizer_relative_path_remove( $file_path ),
3049
+ ) );
3050
+ // Resized versions, so we can continue.
3051
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
3052
+ // One way or another, $file_path is now set, and we can get the base folder name.
3053
+ $base_dir = dirname( $file_path ) . '/';
3054
+ foreach ( $meta['sizes'] as $size => $data ) {
3055
+ // Delete any residual webp versions.
3056
+ $webpfile = $base_dir . $data['file'] . '.webp';
3057
+ $webpfileold = preg_replace( '/\.\w+$/', '.webp', $base_dir . $data['file'] );
3058
+ if ( is_file( $webpfile ) ) {
3059
+ unlink( $webpfile );
3060
+ }
3061
+ if ( is_file( $webpfileold ) ) {
3062
+ unlink( $webpfileold );
3063
+ }
3064
+ $ewwwdb->delete( $ewwwdb->ewwwio_images, array(
3065
+ 'path' => ewww_image_optimizer_relative_path_remove( $base_dir . $data['file'] ),
3066
+ ) );
3067
+ // If the original resize is set, and still exists.
3068
+ if ( ! empty( $data['orig_file'] ) && is_file( $base_dir . $data['orig_file'] ) ) {
3069
+ unset( $srows );
3070
+ // Retrieve the filename from the metadata.
3071
+ $filename = $data['orig_file'];
3072
+ // Retrieve any posts that link the image.
3073
+ $esql = "SELECT ID, post_content FROM $ewwwdb->posts WHERE post_content LIKE '%$filename%' LIMIT 1";
3074
+ $srows = $ewwwdb->get_row( $esql );
3075
+ // If there are no posts containing links to the original, delete it.
3076
+ if ( empty( $srows ) ) {
3077
+ unlink( $base_dir . $data['orig_file'] );
3078
+ $ewwwdb->delete( $ewwwdb->ewwwio_images, array(
3079
+ 'path' => ewww_image_optimizer_relative_path_remove( $base_dir . $data['orig_file'] ),
3080
+ ) );
3081
+ }
3082
+ }
3083
+ }
3084
+ }
3085
+ ewwwio_memory( __FUNCTION__ );
3086
+ return;
3087
+ }
3088
+
3089
+ /**
3090
+ * Sanitizes and verifies an API key for the cloud service.
3091
+ *
3092
+ * @param string $key An API key entered by the user.
3093
+ * @return string A sanitized and validated API key.
3094
+ */
3095
+ function ewww_image_optimizer_cloud_key_sanitize( $key ) {
3096
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3097
+ $key = trim( $key );
3098
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
3099
+ ewwwio_debug_message( print_r( $_REQUEST, true ) );
3100
+ }
3101
+ if ( ewww_image_optimizer_cloud_verify( false, $key ) ) {
3102
+ add_settings_error( 'ewww_image_optimizer_cloud_key', 'ewwwio-cloud-key', esc_html__( 'Successfully validated API key, happy optimizing!', 'ewww-image-optimizer' ), 'updated' );
3103
+ ewwwio_debug_message( 'sanitize (verification) successful' );
3104
+ ewwwio_memory( __FUNCTION__ );
3105
+ return $key;
3106
+ } else {
3107
+ if ( ! empty( $key ) ) {
3108
+ add_settings_error( 'ewww_image_optimizer_cloud_key', 'ewwwio-cloud-key', esc_html__( 'Could not validate API key, please copy and paste your key to ensure it is correct.', 'ewww-image-optimizer' ) );
3109
+ }
3110
+ ewwwio_debug_message( 'sanitize (verification) failed' );
3111
+ ewwwio_memory( __FUNCTION__ );
3112
+ return '';
3113
+ }
3114
+ }
3115
+
3116
+ /**
3117
+ * Checks to see if all images should be processed via the API.
3118
+ *
3119
+ * @return bool True if all 'cloud' options are enabled.
3120
+ */
3121
+ function ewww_image_optimizer_full_cloud() {
3122
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) > 10 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 ) {
3123
+ return true;
3124
+ } elseif ( 'ewww-image-optimizer' == 'ewww-image-optimizer-cloud' ) {
3125
+ return true;
3126
+ }
3127
+ return false;
3128
+ }
3129
+
3130
+ /**
3131
+ * Used to turn on the cloud settings when they are all disabled.
3132
+ */
3133
+ function ewww_image_optimizer_cloud_enable() {
3134
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3135
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_jpg_level', 30 );
3136
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 50 );
3137
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_gif_level', 10 );
3138
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_pdf_level', 10 );
3139
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_backup_files', 1 );
3140
+ }
3141
+
3142
+ /**
3143
+ * Adds the EWWW IO version to the useragent for http requests.
3144
+ *
3145
+ * @param string $useragent The current useragent used in http requests.
3146
+ * @return string The useragent with the EWWW IO version appended.
3147
+ */
3148
+ function ewww_image_optimizer_cloud_useragent( $useragent ) {
3149
+ $useragent .= ' EWWW/' . EWWW_IMAGE_OPTIMIZER_VERSION . ' ';
3150
+ ewwwio_memory( __FUNCTION__ );
3151
+ return $useragent;
3152
+ }
3153
+
3154
+ /**
3155
+ * Submits the api key for verification. Will retrieve the key option if parameter not provided.
3156
+ *
3157
+ * @global object $ewwwio_async_key_verification
3158
+ *
3159
+ * @param bool $cache Optional. True to return cached verification results. Default true.
3160
+ * @param string $api_key Optional. The API key to verify. Default empty string.
3161
+ * @return string|bool False if verification fails, status message otherwise: great/exceeded.
3162
+ */
3163
+ function ewww_image_optimizer_cloud_verify( $cache = true, $api_key = '' ) {
3164
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3165
+ if ( empty( $api_key ) && ! ( ! empty( $_REQUEST['option_page'] ) && 'ewww_image_optimizer_options' == $_REQUEST['option_page'] ) ) {
3166
+ $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
3167
+ } elseif ( empty( $api_key ) && ! empty( $_POST['ewww_image_optimizer_cloud_key'] ) ) {
3168
+ $api_key = $_POST['ewww_image_optimizer_cloud_key'];
3169
+ }
3170
+ if ( empty( $api_key ) ) {
3171
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) > 10 ) {
3172
+ update_site_option( 'ewww_image_optimizer_jpg_level', 10 );
3173
+ update_option( 'ewww_image_optimizer_jpg_level', 10 );
3174
+ }
3175
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) != 40 ) {
3176
+ update_site_option( 'ewww_image_optimizer_png_level', 10 );
3177
+ update_option( 'ewww_image_optimizer_png_level', 10 );
3178
+ }
3179
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) > 0 ) {
3180
+ update_site_option( 'ewww_image_optimizer_pdf_level', 0 );
3181
+ update_option( 'ewww_image_optimizer_pdf_level', 0 );
3182
+ }
3183
+ update_site_option( 'ewww_image_optimizer_backup_files', '' );
3184
+ update_option( 'ewww_image_optimizer_backup_files', '' );
3185
+ return false;
3186
+ }
3187
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_exceeded' ) > time() ) {
3188
+ set_transient( 'ewww_image_optimizer_cloud_status', 'exceeded', 3600 );
3189
+ ewwwio_debug_message( 'license exceeded notice has not expired' );
3190
+ return 'exceeded';
3191
+ }
3192
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
3193
+ $ewww_cloud_status = get_transient( 'ewww_image_optimizer_cloud_status' );
3194
+ $ewww_cloud_ip = get_transient( 'ewww_image_optimizer_cloud_ip' );
3195
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
3196
+ if ( ! ewww_image_optimizer_detect_wpsf_location_lock() && $cache && preg_match( '/^(\d{1,3}\.){3}\d{1,3}$/', $ewww_cloud_ip ) && preg_match( '/http/', $ewww_cloud_transport ) && preg_match( '/great/', $ewww_cloud_status ) ) {
3197
+ ewwwio_debug_message( 'using cached verification' );
3198
+ global $ewwwio_async_key_verification;
3199
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
3200
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
3201
+ }
3202
+ if ( ! is_object( $ewwwio_async_key_verification ) ) {
3203
+ $ewwwio_async_key_verification = new EWWWIO_Async_Key_Verification();
3204
+ }
3205
+ $ewwwio_async_key_verification->dispatch();
3206
+ return $ewww_cloud_status;
3207
+ }
3208
+ if ( 'https' !== $ewww_cloud_transport && 'http' !== $ewww_cloud_transport ) {
3209
+ $ewww_cloud_transport = 'https';
3210
+ }
3211
+ if ( preg_match( '/^(\d{1,3}\.){3}\d{1,3}$/', $ewww_cloud_ip ) ) {
3212
+ ewwwio_debug_message( 'using cached ip' );
3213
+ $result = ewww_image_optimizer_cloud_post_key( $ewww_cloud_ip, $ewww_cloud_transport, $api_key );
3214
+ if ( is_wp_error( $result ) ) {
3215
+ $ewww_cloud_transport = 'http';
3216
+ $error_message = $result->get_error_message();
3217
+ ewwwio_debug_message( "verification failed: $error_message" );
3218
+ $result = ewww_image_optimizer_cloud_post_key( $ewww_cloud_ip, $ewww_cloud_transport, $api_key );
3219
+ }
3220
+ if ( is_wp_error( $result ) ) {
3221
+ $error_message = $result->get_error_message();
3222
+ ewwwio_debug_message( "verification failed: $error_message" );
3223
+ } elseif ( ! empty( $result['body'] ) && preg_match( '/(great|exceeded)/', $result['body'] ) ) {
3224
+ $verified = $result['body'];
3225
+ ewwwio_debug_message( "verification success via: $ewww_cloud_transport://$ewww_cloud_ip" );
3226
+ } else {
3227
+ ewwwio_debug_message( "verification failed via: $ewww_cloud_ip" );
3228
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
3229
+ ewwwio_debug_message( print_r( $result, true ) );
3230
+ }
3231
+ }
3232
+ }
3233
+ if ( empty( $verified ) ) {
3234
+ $ewww_cloud_transport = 'https';
3235
+ $servers = gethostbynamel( 'optimize.exactlywww.com' );
3236
+ if ( empty( $servers ) ) {
3237
+ ewwwio_debug_message( 'unable to resolve servers' );
3238
+ return false;
3239
+ }
3240
+ if ( ewww_image_optimizer_iterable( $servers ) ) {
3241
+ foreach ( $servers as $ip ) {
3242
+ $result = ewww_image_optimizer_cloud_post_key( $ip, $ewww_cloud_transport, $api_key );
3243
+ if ( is_wp_error( $result ) ) {
3244
+ $ewww_cloud_transport = 'http';
3245
+ $error_message = $result->get_error_message();
3246
+ ewwwio_debug_message( "verification failed via $ewww_cloud_transport://$ip: $error_message" );
3247
+ } elseif ( ! empty( $result['body'] ) && preg_match( '/(great|exceeded)/', $result['body'] ) ) {
3248
+ $verified = $result['body'];
3249
+ if ( preg_match( '/exceeded/', $verified ) ) {
3250
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_cloud_exceeded', time() + 300 );
3251
+ }
3252
+ $ewww_cloud_ip = $ip;
3253
+ ewwwio_debug_message( "verification success via: $ewww_cloud_transport://$ewww_cloud_ip" );
3254
+ break;
3255
+ } else {
3256
+ ewwwio_debug_message( "verification failed via: $ip" );
3257
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
3258
+ ewwwio_debug_message( print_r( $result, true ) );
3259
+ }
3260
+ }
3261
+ }
3262
+ } else {
3263
+ ewwwio_debug_message( 'unable to parse server list' );
3264
+ }
3265
+ }
3266
+ if ( empty( $verified ) ) {
3267
+ ewwwio_memory( __FUNCTION__ );
3268
+ return false;
3269
+ } else {
3270
+ set_transient( 'ewww_image_optimizer_cloud_status', $verified, 3600 );
3271
+ set_transient( 'ewww_image_optimizer_cloud_ip', $ewww_cloud_ip, 3600 );
3272
+ set_transient( 'ewww_image_optimizer_cloud_transport', $ewww_cloud_transport, 3600 );
3273
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) < 20 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) < 20 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) < 20 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) == 0 ) {
3274
+ ewww_image_optimizer_cloud_enable();
3275
+ }
3276
+ ewwwio_debug_message( "verification body contents: {$result['body']}" );
3277
+ ewwwio_memory( __FUNCTION__ );
3278
+ return $verified;
3279
+ }
3280
+ }
3281
+
3282
+ /**
3283
+ * POSTs the API key to the API for verification.
3284
+ *
3285
+ * @param string $ip IP address of the server to use.
3286
+ * @param string $transport The transport to use: http or https.
3287
+ * @param string $key The API key to submit via POST.
3288
+ * @return array The results of the http POST request.
3289
+ */
3290
+ function ewww_image_optimizer_cloud_post_key( $ip, $transport, $key ) {
3291
+ $result = wp_remote_post( "$transport://$ip/verify/", array(
3292
+ 'timeout' => 5,
3293
+ 'sslverify' => false,
3294
+ 'body' => array(
3295
+ 'api_key' => $key,
3296
+ ),
3297
+ ) );
3298
+ return $result;
3299
+ }
3300
+
3301
+ /**
3302
+ * Checks the configured API key for quota information.
3303
+ *
3304
+ * @return string A message with how many credits they have used/left and possibly a renwal date.
3305
+ */
3306
+ function ewww_image_optimizer_cloud_quota() {
3307
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3308
+ // $ewww_cloud_ip = get_transient( 'ewww_image_optimizer_cloud_ip' );
3309
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
3310
+ if ( empty( $ewww_cloud_transport ) ) {
3311
+ if ( ! ewww_image_optimizer_cloud_verify() ) {
3312
+ return '';
3313
+ } else {
3314
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
3315
+ }
3316
+ }
3317
+ if ( empty( $ewww_cloud_transport ) ) {
3318
+ $ewww_cloud_transport = 'https';
3319
+ }
3320
+ $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
3321
+ $url = "$ewww_cloud_transport://optimize.exactlywww.com/quota/";
3322
+ $result = wp_remote_post( $url, array(
3323
+ 'timeout' => 5,
3324
+ 'sslverify' => false,
3325
+ 'body' => array(
3326
+ 'api_key' => $api_key,
3327
+ ),
3328
+ ) );
3329
+ if ( is_wp_error( $result ) ) {
3330
+ $error_message = $result->get_error_message();
3331
+ ewwwio_debug_message( "quota request failed: $error_message" );
3332
+ ewwwio_memory( __FUNCTION__ );
3333
+ return '';
3334
+ } elseif ( ! empty( $result['body'] ) ) {
3335
+ ewwwio_debug_message( "quota data retrieved: {$result['body']}" );
3336
+ $quota = explode( ' ', $result['body'] );
3337
+ ewwwio_memory( __FUNCTION__ );
3338
+ if ( 0 == $quota[0] && $quota[1] > 0 ) {
3339
+ return esc_html( sprintf(
3340
+ /* translators: 1: Number of images 2: Number of days until renewal */
3341
+ _n( 'optimized %1$d images, usage will reset in %2$d day.', 'optimized %1$d images, usage will reset in %2$d days.', $quota[2], 'ewww-image-optimizer' ), $quota[1], $quota[2]
3342
+ ) );
3343
+ } elseif ( 0 == $quota[0] && $quota[1] < 0 ) {
3344
+ return esc_html( sprintf(
3345
+ /* translators: 1: Number of images */
3346
+ _n( '%1$d image credit remaining.', '%1$d image credits remaining.', abs( $quota[1] ), 'ewww-image-optimizer' ), abs( $quota[1] )
3347
+ ) );
3348
+ } elseif ( $quota[0] > 0 && $quota[1] < 0 ) {
3349
+ $real_quota = $quota[0] - $quota[1];
3350
+ return esc_html( sprintf(
3351
+ /* translators: 1: Number of images */
3352
+ _n( '%1$d image credit remaining.', '%1$d image credits remaining.', $real_quota, 'ewww-image-optimizer' ), $real_quota
3353
+ ) );
3354
+ } else {
3355
+ return esc_html( sprintf(
3356
+ /* translators: 1: Number of image credits used 2: Number of image credits available 3: days until subscription renewal */
3357
+ _n( 'used %1$d of %2$d, usage will reset in %3$d day.', 'used %1$d of %2$d, usage will reset in %3$d days.', $quota[2], 'ewww-image-optimizer' ), $quota[1], $quota[0], $quota[2]
3358
+ ) );
3359
+ }
3360
+ }
3361
+ }
3362
+
3363
+ /**
3364
+ * Submits an image to the cloud optimizer and saves the optimized image to disk.
3365
+ *
3366
+ * Returns an array of the $file, $converted, possibly a $msg, and the $new_size.
3367
+ *
3368
+ * @global object $ewww_image Contains more information about the image currently being processed.
3369
+ *
3370
+ * @param string $file Full absolute path to the image file.
3371
+ * @param string $type Mimetype of $file.
3372
+ * @param bool $convert Optional. True if we want to attempt conversion of $file. Default false.
3373
+ * @param string $newfile Optional. Filename to be used if image is converted. Default null.
3374
+ * @param string $newtype Optional. Mimetype expected if image is converted. Default null.
3375
+ * @param bool $fullsize Optional. True if this is an original upload. Default false.
3376
+ * @param array $jpg_fill Optional. Fill color for PNG to JPG conversion in hex format.
3377
+ * @param int $jpg_quality Optional. JPG quality level. Default null. Accepts 1-100.
3378
+ * @return array {
3379
+ * Information about the cloud optimization.
3380
+ *
3381
+ * @type string Filename of the optimized version.
3382
+ * @type bool True if the image was converted.
3383
+ * @type string Set to 'exceeded' if the API key is out of credits.
3384
+ * @type int File size of the (new) image.
3385
+ * }
3386
+ */
3387
+ function ewww_image_optimizer_cloud_optimizer( $file, $type, $convert = false, $newfile = null, $newtype = null, $fullsize = false, $jpg_fill = '', $jpg_quality = 82 ) {
3388
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3389
+ $ewww_cloud_ip = get_transient( 'ewww_image_optimizer_cloud_ip' );
3390
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
3391
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
3392
+ $started = microtime( true );
3393
+ if ( empty( $ewww_cloud_ip ) || empty( $ewww_cloud_transport ) || preg_match( '/exceeded/', $ewww_status ) ) {
3394
+ if ( ! ewww_image_optimizer_cloud_verify() ) {
3395
+ return array( $file, false, 'key verification failed', 0, '' );
3396
+ } else {
3397
+ $ewww_cloud_ip = get_transient( 'ewww_image_optimizer_cloud_ip' );
3398
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
3399
+ }
3400
+ }
3401
+ // Calculate how much time has elapsed since we started.
3402
+ $elapsed = microtime( true ) - $started;
3403
+ ewwwio_debug_message( "Cloud verify took $elapsed seconds" );
3404
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
3405
+ if ( ( ! empty( $ewww_status ) && preg_match( '/exceeded/', $ewww_status ) ) || ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_exceeded' ) > time() ) {
3406
+ ewwwio_debug_message( 'license exceeded, image not processed' );
3407
+ return array( $file, false, 'exceeded', 0, '' );
3408
+ }
3409
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_skip_full' ) && $fullsize ) {
3410
+ $metadata = 1;
3411
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) ) {
3412
+ // Don't copy metadata.
3413
+ $metadata = 0;
3414
+ } else {
3415
+ // Copy all the metadata.
3416
+ $metadata = 1;
3417
+ }
3418
+ if ( empty( $convert ) ) {
3419
+ $convert = 0;
3420
+ } else {
3421
+ $convert = 1;
3422
+ }
3423
+ $lossy_fast = 0;
3424
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_skip_full' ) && $fullsize ) {
3425
+ $lossy = 0;
3426
+ } elseif ( 'image/png' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) >= 40 ) {
3427
+ $lossy = 1;
3428
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 ) {
3429
+ $lossy_fast = 1;
3430
+ }
3431
+ } elseif ( 'image/jpeg' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) >= 30 ) {
3432
+ $lossy = 1;
3433
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 30 ) {
3434
+ $lossy_fast = 1;
3435
+ }
3436
+ } elseif ( 'application/pdf' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) == 20 ) {
3437
+ $lossy = 1;
3438
+ } else {
3439
+ $lossy = 0;
3440
+ }
3441
+ if ( 'image/webp' == $newtype ) {
3442
+ $webp = 1;
3443
+ $jpg_quality = apply_filters( 'jpeg_quality', $jpg_quality, 'image/webp' );
3444
+ } else {
3445
+ $webp = 0;
3446
+ }
3447
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 30 ) {
3448
+ $png_compress = 1;
3449
+ } else {
3450
+ $png_compress = 0;
3451
+ }
3452
+ if ( ! $webp && ewww_image_optimizer_get_option( 'ewww_image_optimizer_backup_files' )
3453
+ && strpos( $file, '/wp-admin/' ) === false
3454
+ && strpos( $file, '/wp-includes/' ) === false
3455
+ && strpos( $file, '/wp-content/themes/' ) === false
3456
+ && strpos( $file, '/wp-content/plugins/' ) === false
3457
+ && strpos( $file, '/cache/' ) === false
3458
+ && strpos( $file, '/dynamic/' ) === false // Nextgen dynamic images.
3459
+ ) {
3460
+ global $ewww_image;
3461
+ if ( is_object( $ewww_image ) && $ewww_image->file == $file && ! empty( $ewww_image->backup ) ) {
3462
+ $hash = $ewww_image->backup;
3463
+ }
3464
+ if ( empty( $hash ) && ! empty( $_REQUEST['ewww_force'] ) ) {
3465
+ $image = ewww_image_optimizer_find_already_optimized( $file );
3466
+ if ( ! empty( $image ) && is_array( $image ) && ! empty( $image['backup'] ) ) {
3467
+ $hash = $image['backup'];
3468
+ }
3469
+ }
3470
+ if ( empty( $hash ) ) {
3471
+ $hash = uniqid() . hash( 'sha256', $file );
3472
+ }
3473
+ $domain = parse_url( get_site_url(), PHP_URL_HOST );
3474
+ } else {
3475
+ $hash = '';
3476
+ $domain = parse_url( get_site_url(), PHP_URL_HOST );
3477
+ }
3478
+ ewwwio_debug_message( "file: $file " );
3479
+ ewwwio_debug_message( "type: $type" );
3480
+ ewwwio_debug_message( "convert: $convert" );
3481
+ ewwwio_debug_message( "newfile: $newfile" );
3482
+ ewwwio_debug_message( "newtype: $newtype" );
3483
+ ewwwio_debug_message( "webp: $webp" );
3484
+ ewwwio_debug_message( "jpg fill: $jpg_fill" );
3485
+ ewwwio_debug_message( "jpg quality: $jpg_quality" );
3486
+ $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
3487
+ $url = "$ewww_cloud_transport://$ewww_cloud_ip/v2/";
3488
+ $boundary = wp_generate_password( 24, false );
3489
+
3490
+ $headers = array(
3491
+ 'content-type' => 'multipart/form-data; boundary=' . $boundary,
3492
+ 'timeout' => 90,
3493
+ 'httpversion' => '1.0',
3494
+ 'blocking' => true,
3495
+ );
3496
+ $post_fields = array(
3497
+ 'filename' => $file,
3498
+ 'convert' => $convert,
3499
+ 'metadata' => $metadata,
3500
+ 'api_key' => $api_key,
3501
+ 'jpg_fill' => $jpg_fill,
3502
+ 'quality' => $jpg_quality,
3503
+ 'compress' => $png_compress,
3504
+ 'lossy' => $lossy,
3505
+ 'lossy_fast' => $lossy_fast,
3506
+ 'webp' => $webp,
3507
+ 'backup' => $hash,
3508
+ 'domain' => $domain,
3509
+ );
3510
+
3511
+ $payload = '';
3512
+ foreach ( $post_fields as $name => $value ) {
3513
+ $payload .= '--' . $boundary;
3514
+ $payload .= "\r\n";
3515
+ $payload .= 'Content-Disposition: form-data; name="' . $name . '"' . "\r\n\r\n";
3516
+ $payload .= $value;
3517
+ $payload .= "\r\n";
3518
+ }
3519
+
3520
+ $payload .= '--' . $boundary;
3521
+ $payload .= "\r\n";
3522
+ $payload .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $file ) . '"' . "\r\n";
3523
+ $payload .= 'Content-Type: ' . $type . "\r\n";
3524
+ $payload .= "\r\n";
3525
+ $payload .= file_get_contents( $file );
3526
+ $payload .= "\r\n";
3527
+ $payload .= '--' . $boundary;
3528
+ $payload .= 'Content-Disposition: form-data; name="submitHandler"' . "\r\n";
3529
+ $payload .= "\r\n";
3530
+ $payload .= "Upload\r\n";
3531
+ $payload .= '--' . $boundary . '--';
3532
+
3533
+ // Retrieve the time when the optimizer starts.
3534
+ $response = wp_remote_post( $url, array(
3535
+ 'timeout' => 90,
3536
+ 'headers' => $headers,
3537
+ 'sslverify' => false,
3538
+ 'body' => $payload,
3539
+ ) );
3540
+ if ( is_wp_error( $response ) ) {
3541
+ $error_message = $response->get_error_message();
3542
+ ewwwio_debug_message( "optimize failed: $error_message" );
3543
+ return array( $file, false, 'cloud optimize failed', 0, '' );
3544
+ } else {
3545
+ $tempfile = $file . '.tmp';
3546
+ file_put_contents( $tempfile, $response['body'] );
3547
+ $orig_size = filesize( $file );
3548
+ $newsize = $orig_size;
3549
+ $converted = false;
3550
+ $msg = '';
3551
+ if ( preg_match( '/exceeded/', $response['body'] ) ) {
3552
+ ewwwio_debug_message( 'License Exceeded' );
3553
+ set_transient( 'ewww_image_optimizer_cloud_status', 'exceeded', 3600 );
3554
+ $msg = 'exceeded';
3555
+ unlink( $tempfile );
3556
+ } elseif ( ewww_image_optimizer_mimetype( $tempfile, 'i' ) == $type ) {
3557
+ $newsize = filesize( $tempfile );
3558
+ ewwwio_debug_message( "cloud results: $newsize (new) vs. $orig_size (original)" );
3559
+ rename( $tempfile, $file );
3560
+ } elseif ( ewww_image_optimizer_mimetype( $tempfile, 'i' ) == 'image/webp' ) {
3561
+ $newsize = filesize( $tempfile );
3562
+ ewwwio_debug_message( "cloud results: $newsize (new) vs. $orig_size (original)" );
3563
+ rename( $tempfile, $newfile );
3564
+ } elseif ( ewww_image_optimizer_mimetype( $tempfile, 'i' ) == $newtype ) {
3565
+ $converted = true;
3566
+ $newsize = filesize( $tempfile );
3567
+ ewwwio_debug_message( "cloud results: $newsize (new) vs. $orig_size (original)" );
3568
+ rename( $tempfile, $newfile );
3569
+ $file = $newfile;
3570
+ } else {
3571
+ unlink( $tempfile );
3572
+ }
3573
+ ewwwio_memory( __FUNCTION__ );
3574
+ return array( $file, $converted, $msg, $newsize, $hash );
3575
+ } // End if().
3576
+ }
3577
+
3578
+ /**
3579
+ * Automatically corrects JPG rotation using API servers.
3580
+ *
3581
+ * @param string $file Name of the file to fix.
3582
+ * @param string $type File type of the file.
3583
+ *
3584
+ * @return bool True if the rotation was successful.
3585
+ */
3586
+ function ewww_image_optimizer_cloud_autorotate( $file, $type ) {
3587
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3588
+ $ewww_cloud_ip = get_transient( 'ewww_image_optimizer_cloud_ip' );
3589
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
3590
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
3591
+ $started = microtime( true );
3592
+ if ( empty( $ewww_cloud_ip ) || empty( $ewww_cloud_transport ) || preg_match( '/exceeded/', $ewww_status ) ) {
3593
+ if ( ! ewww_image_optimizer_cloud_verify() ) {
3594
+ ewwwio_debug_message( 'cloud verify failed, image not rotated' );
3595
+ return false;
3596
+ } else {
3597
+ $ewww_cloud_ip = get_transient( 'ewww_image_optimizer_cloud_ip' );
3598
+ $ewww_cloud_transport = get_transient( 'ewww_image_optimizer_cloud_transport' );
3599
+ }
3600
+ }
3601
+ // Calculate how much time has elapsed since we started.
3602
+ $elapsed = microtime( true ) - $started;
3603
+ ewwwio_debug_message( "Cloud verify took $elapsed seconds" );
3604
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
3605
+ if ( ( ! empty( $ewww_status ) && preg_match( '/exceeded/', $ewww_status ) ) || ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_exceeded' ) > time() ) {
3606
+ ewwwio_debug_message( 'license exceeded, image not rotated' );
3607
+ return false;
3608
+ }
3609
+ ewwwio_debug_message( "file: $file " );
3610
+ ewwwio_debug_message( "type: $type" );
3611
+ $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
3612
+ $url = "$ewww_cloud_transport://$ewww_cloud_ip/rotate/";
3613
+ $boundary = wp_generate_password( 24, false );
3614
+
3615
+ $headers = array(
3616
+ 'content-type' => 'multipart/form-data; boundary=' . $boundary,
3617
+ 'timeout' => 60,
3618
+ 'httpversion' => '1.0',
3619
+ 'blocking' => true,
3620
+ );
3621
+ $post_fields = array(
3622
+ 'filename' => $file,
3623
+ 'api_key' => $api_key,
3624
+ );
3625
+
3626
+ $payload = '';
3627
+ foreach ( $post_fields as $name => $value ) {
3628
+ $payload .= '--' . $boundary;
3629
+ $payload .= "\r\n";
3630
+ $payload .= 'Content-Disposition: form-data; name="' . $name . '"' . "\r\n\r\n";
3631
+ $payload .= $value;
3632
+ $payload .= "\r\n";
3633
+ }
3634
+
3635
+ $payload .= '--' . $boundary;
3636
+ $payload .= "\r\n";
3637
+ $payload .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $file ) . '"' . "\r\n";
3638
+ $payload .= 'Content-Type: ' . $type . "\r\n";
3639
+ $payload .= "\r\n";
3640
+ $payload .= file_get_contents( $file );
3641
+ $payload .= "\r\n";
3642
+ $payload .= '--' . $boundary;
3643
+ $payload .= 'Content-Disposition: form-data; name="submitHandler"' . "\r\n";
3644
+ $payload .= "\r\n";
3645
+ $payload .= "Upload\r\n";
3646
+ $payload .= '--' . $boundary . '--';
3647
+
3648
+ $response = wp_remote_post( $url, array(
3649
+ 'timeout' => 60,
3650
+ 'headers' => $headers,
3651
+ 'sslverify' => false,
3652
+ 'body' => $payload,
3653
+ ) );
3654
+ if ( is_wp_error( $response ) ) {
3655
+ $error_message = $response->get_error_message();
3656
+ ewwwio_debug_message( "rotate failed: $error_message" );
3657
+ return false;
3658
+ } else {
3659
+ $tempfile = $file . '.tmp';
3660
+ file_put_contents( $tempfile, $response['body'] );
3661
+ $orig_size = filesize( $file );
3662
+ $newsize = $orig_size;
3663
+ if ( preg_match( '/exceeded/', $response['body'] ) ) {
3664
+ ewwwio_debug_message( 'License Exceeded' );
3665
+ set_transient( 'ewww_image_optimizer_cloud_status', 'exceeded', 3600 );
3666
+ unlink( $tempfile );
3667
+ } elseif ( ewww_image_optimizer_mimetype( $tempfile, 'i' ) == $type ) {
3668
+ $newsize = filesize( $tempfile );
3669
+ ewwwio_debug_message( "cloud rotation success: $newsize (new) vs. $orig_size (original)" );
3670
+ rename( $tempfile, $file );
3671
+ return true;
3672
+ } else {
3673
+ unlink( $tempfile );
3674
+ }
3675
+ ewwwio_memory( __FUNCTION__ );
3676
+ return false;
3677
+ }
3678
+ }
3679
+
3680
+ /**
3681
+ * Setup our own database connection with full utf8 capability.
3682
+ *
3683
+ * @global object $ewwwdb A new database connection with super powers.
3684
+ * @global string $table_prefix The table prefix for the WordPress database.
3685
+ */
3686
+ function ewww_image_optimizer_db_init() {
3687
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3688
+ global $ewwwdb, $table_prefix;
3689
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewwwdb.php' );
3690
+ if ( ! isset( $ewwwdb ) ) {
3691
+ $ewwwdb = new EwwwDB( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
3692
+ }
3693
+
3694
+ if ( ! empty( $ewwwdb->error ) ) {
3695
+ dead_db();
3696
+ }
3697
+
3698
+ $ewwwdb->field_types = array(
3699
+ 'post_author' => '%d',
3700
+ 'post_parent' => '%d',
3701
+ 'menu_order' => '%d',
3702
+ 'term_id' => '%d',
3703
+ 'term_group' => '%d',
3704
+ 'term_taxonomy_id' => '%d',
3705
+ 'parent' => '%d',
3706
+ 'count' => '%d',
3707
+ 'object_id' => '%d',
3708
+ 'term_order' => '%d',
3709
+ 'ID' => '%d',
3710
+ 'comment_ID' => '%d',
3711
+ 'comment_post_ID' => '%d',
3712
+ 'comment_parent' => '%d',
3713
+ 'user_id' => '%d',
3714
+ 'link_id' => '%d',
3715
+ 'link_owner' => '%d',
3716
+ 'link_rating' => '%d',
3717
+ 'option_id' => '%d',
3718
+ 'blog_id' => '%d',
3719
+ 'meta_id' => '%d',
3720
+ 'post_id' => '%d',
3721
+ 'user_status' => '%d',
3722
+ 'umeta_id' => '%d',
3723
+ 'comment_karma' => '%d',
3724
+ 'comment_count' => '%d',
3725
+ // multisite.
3726
+ 'active' => '%d',
3727
+ 'cat_id' => '%d',
3728
+ 'deleted' => '%d',
3729
+ 'lang_id' => '%d',
3730
+ 'mature' => '%d',
3731
+ 'public' => '%d',
3732
+ 'site_id' => '%d',
3733
+ 'spam' => '%d',
3734
+ );
3735
+
3736
+ $prefix = $ewwwdb->set_prefix( $table_prefix );
3737
+
3738
+ if ( is_wp_error( $prefix ) ) {
3739
+ wp_load_translations_early();
3740
+ wp_die(
3741
+ /* translators: 1: $table_prefix 2: wp-config.php */
3742
+ sprintf( __( '<strong>ERROR</strong>: %1$s in %2$s can only contain numbers, letters, and underscores.' ),
3743
+ '<code>$table_prefix</code>',
3744
+ '<code>wp-config.php</code>'
3745
+ )
3746
+ );
3747
+ }
3748
+ if ( ! isset( $ewwwdb->ewwwio_images ) ) {
3749
+ $ewwwdb->ewwwio_images = $ewwwdb->prefix . 'ewwwio_images';
3750
+ }
3751
+ }
3752
+
3753
+ /**
3754
+ * Inserts multiple records into the table at once.
3755
+ *
3756
+ * Each sub-array in $images should have the same number of items as $format.
3757
+ *
3758
+ * @global object $ewwwdb A new database connection with super powers.
3759
+ *
3760
+ * @param string $table The table to insert records into.
3761
+ * @param array $images Can be any multi-dimensional array with records to insert.
3762
+ * @param array $format A list of formats for the values in each record of $images.
3763
+ */
3764
+ function ewww_image_optimizer_mass_insert( $table, $images, $format ) {
3765
+ if ( empty( $table ) || ! ewww_image_optimizer_iterable( $images ) || ! ewww_image_optimizer_iterable( $format ) ) {
3766
+ return false;
3767
+ }
3768
+ ewww_image_optimizer_db_init();
3769
+ global $ewwwdb;
3770
+ $ewwwdb->insert_multiple( $table, $images, $format );
3771
+ }
3772
+
3773
+ /**
3774
+ * Search the database to see if we've done this image before.
3775
+ *
3776
+ * @global object $wpdb
3777
+ *
3778
+ * @param string $file The filename of the image.
3779
+ * @param int $orig_size The current filesize of the image.
3780
+ * @return array The image record from the table, if found.
3781
+ */
3782
+ function ewww_image_optimizer_check_table( $file, $orig_size ) {
3783
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3784
+ ewwwio_debug_message( "checking for $file with size: $orig_size" );
3785
+ global $s3_uploads_image;
3786
+ if ( class_exists( 'S3_Uploads' ) && ! empty( $s3_uploads_image ) && $s3_uploads_image != $file ) {
3787
+ $file = $s3_uploads_image;
3788
+ ewwwio_debug_message( "overriding check with: $file" );
3789
+ }
3790
+ $image = ewww_image_optimizer_find_already_optimized( $file );
3791
+ if ( is_array( $image ) && $image['image_size'] == $orig_size ) {
3792
+ $prev_string = ' - ' . __( 'Previously Optimized', 'ewww-image-optimizer' );
3793
+ if ( preg_match( '/' . __( 'License exceeded', 'ewww-image-optimizer' ) . '/', $image['results'] ) ) {
3794
+ return;
3795
+ }
3796
+ $already_optimized = preg_replace( "/$prev_string/", '', $image['results'] );
3797
+ $already_optimized = $already_optimized . $prev_string;
3798
+ ewwwio_debug_message( "already optimized: {$image['path']} - $already_optimized" );
3799
+ ewwwio_memory( __FUNCTION__ );
3800
+ // Make sure the image isn't pending.
3801
+ if ( $image['pending'] ) {
3802
+ global $wpdb;
3803
+ $wpdb->update( $wpdb->ewwwio_images,
3804
+ array(
3805
+ 'pending' => 0,
3806
+ ),
3807
+ array(
3808
+ 'id' => $image['id'],
3809
+ )
3810
+ );
3811
+ }
3812
+ return $already_optimized;
3813
+ }
3814
+ }
3815
+
3816
+ /**
3817
+ * Inserts or updates an image record in the database.
3818
+ *
3819
+ * @global object $wpdb
3820
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
3821
+ * @global object $ewww_image Contains more information about the image currently being processed.
3822
+ *
3823
+ * @param string $attachment The filename of the image.
3824
+ * @param int $opt_size The new size of the image.
3825
+ * @param int $orig_size The original size of the image.
3826
+ * @param string $original Optional. The name of the file before it was converted. Default ''.
3827
+ * @param string $backup_hash Optional. A unique identifier for this file. Default ''.
3828
+ */
3829
+ function ewww_image_optimizer_update_table( $attachment, $opt_size, $orig_size, $original = '', $backup_hash = '' ) {
3830
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3831
+ global $wpdb;
3832
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
3833
+ ewww_image_optimizer_db_init();
3834
+ global $ewwwdb;
3835
+ } else {
3836
+ $ewwwdb = $wpdb;
3837
+ }
3838
+ global $ewww_image;
3839
+ // First check if the image was converted, so we don't orphan records.
3840
+ if ( $original && $original != $attachment ) {
3841
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $original );
3842
+ $converted = $original;
3843
+ } else {
3844
+ global $s3_uploads_image;
3845
+ if ( class_exists( 'S3_Uploads' ) && ! empty( $s3_uploads_image ) && $s3_uploads_image != $attachment ) {
3846
+ $attachment = $s3_uploads_image;
3847
+ ewwwio_debug_message( "overriding update with: $attachment" );
3848
+ }
3849
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $attachment );
3850
+ if ( is_array( $already_optimized ) && ! empty( $already_optimized['converted'] ) ) {
3851
+ $converted = $already_optimized['converted'];
3852
+ } else {
3853
+ $converted = '';
3854
+ }
3855
+ }
3856
+ if ( is_array( $already_optimized ) && ! empty( $already_optimized['updates'] ) && $opt_size >= $orig_size ) {
3857
+ $prev_string = ' - ' . __( 'Previously Optimized', 'ewww-image-optimizer' );
3858
+ } else {
3859
+ $prev_string = '';
3860
+ }
3861
+ if ( is_array( $already_optimized ) && ! empty( $already_optimized['orig_size'] ) && $already_optimized['orig_size'] > $orig_size ) {
3862
+ $orig_size = $already_optimized['orig_size'];
3863
+ }
3864
+ ewwwio_debug_message( "savings: $opt_size (new) vs. $orig_size (orig)" );
3865
+ // Calculate how much space was saved.
3866
+ $results_msg = ewww_image_optimizer_image_results( $orig_size, $opt_size, $prev_string );
3867
+
3868
+ $updates = array(
3869
+ 'path' => ewww_image_optimizer_relative_path_remove( $attachment ),
3870
+ 'converted' => $converted,
3871
+ 'image_size' => $opt_size,
3872
+ 'results' => $results_msg,
3873
+ 'updates' => 1,
3874
+ 'backup' => preg_replace( '/[^\w]/', '', $backup_hash ),
3875
+ );
3876
+ if ( ! seems_utf8( $updates['path'] ) ) {
3877
+ $updates['path'] = utf8_encode( $updates['path'] );
3878
+ }
3879
+ // Store info on the current image for future reference.
3880
+ if ( empty( $already_optimized ) || ! is_array( $already_optimized ) ) {
3881
+ ewwwio_debug_message( "creating new record, path: $attachment, size: $opt_size" );
3882
+ if ( is_object( $ewww_image ) && $ewww_image instanceof EWWW_Image && $ewww_image->gallery ) {
3883
+ $updates['gallery'] = $ewww_image->gallery;
3884
+ }
3885
+ if ( is_object( $ewww_image ) && $ewww_image instanceof EWWW_Image && $ewww_image->attachment_id ) {
3886
+ $updates['attachment_id'] = $ewww_image->attachment_id;
3887
+ }
3888
+ if ( is_object( $ewww_image ) && $ewww_image instanceof EWWW_Image && $ewww_image->resize ) {
3889
+ $updates['resize'] = $ewww_image->resize;
3890
+ }
3891
+ $updates['orig_size'] = $orig_size;
3892
+ $updates['updated'] = date( 'Y-m-d H:i:s' );
3893
+ $ewwwdb->insert( $ewwwdb->ewwwio_images, $updates );
3894
+ } else {
3895
+ if ( is_array( $already_optimized ) && empty( $already_optimized['orig_size'] ) ) {
3896
+ $updates['orig_size'] = $orig_size;
3897
+ }
3898
+ ewwwio_debug_message( "updating existing record ({$already_optimized['id']}), path: $attachment, size: $opt_size" );
3899
+ if ( $already_optimized['updates'] ) {
3900
+ $updates['updates'] = $already_optimized['updates']++;
3901
+ }
3902
+ $updates['pending'] = 0;
3903
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) && $already_optimized['updates'] > 1 ) {
3904
+ $updates['trace'] = ewwwio_debug_backtrace();
3905
+ }
3906
+ if ( empty( $already_optimized['gallery'] ) && is_object( $ewww_image ) && $ewww_image instanceof EWWW_Image && $ewww_image->gallery ) {
3907
+ $updates['gallery'] = $ewww_image->gallery;
3908
+ }
3909
+ if ( empty( $already_optimized['attachment_id'] ) && is_object( $ewww_image ) && $ewww_image instanceof EWWW_Image && $ewww_image->attachment_id ) {
3910
+ $updates['attachment_id'] = $ewww_image->attachment_id;
3911
+ }
3912
+ if ( empty( $already_optimized['resize'] ) && is_object( $ewww_image ) && $ewww_image instanceof EWWW_Image && $ewww_image->resize ) {
3913
+ $updates['resize'] = $ewww_image->resize;
3914
+ }
3915
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
3916
+ ewwwio_debug_message( print_r( $updates, true ) );
3917
+ }
3918
+ // Update information for the image.
3919
+ $record_updated = $ewwwdb->update( $ewwwdb->ewwwio_images,
3920
+ $updates,
3921
+ array(
3922
+ 'id' => $already_optimized['id'],
3923
+ )
3924
+ );
3925
+ if ( false === $record_updated ) {
3926
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
3927
+ ewwwio_debug_message( 'db error: ' . print_r( $wpdb->last_error, true ) );
3928
+ }
3929
+ } else {
3930
+ ewwwio_debug_message( "updated $record_updated records successfully" );
3931
+ }
3932
+ } // End if().
3933
+ ewwwio_memory( __FUNCTION__ );
3934
+ $ewwwdb->flush();
3935
+ ewwwio_memory( __FUNCTION__ );
3936
+ return $results_msg;
3937
+ }
3938
+
3939
+ /**
3940
+ * Creates a human-readable message based on the original and optimized sizes.
3941
+ *
3942
+ * @param int $orig_size The original size of the image.
3943
+ * @param int $opt_size The new size of the image.
3944
+ * @param string $prev_string Optional. A message to append for previously optimized images.
3945
+ * @return string A message with the percentage and size savings.
3946
+ */
3947
+ function ewww_image_optimizer_image_results( $orig_size, $opt_size, $prev_string = '' ) {
3948
+ if ( $opt_size >= $orig_size ) {
3949
+ ewwwio_debug_message( 'original and new file are same size (or something weird made the new one larger), no savings' );
3950
+ $results_msg = __( 'No savings', 'ewww-image-optimizer' );
3951
+ } else {
3952
+ // Calculate how much space was saved.
3953
+ $savings = intval( $orig_size ) - intval( $opt_size );
3954
+ $savings_str = ewww_image_optimizer_size_format( $savings );
3955
+ // Determine the percentage savings.
3956
+ $percent = number_format_i18n( 100 - ( 100 * ( $opt_size / $orig_size ) ), 1 ) . '%';
3957
+ // Use the percentage and the savings size to output a nice message to the user.
3958
+ $results_msg = sprintf(
3959
+ /* translators: 1: Size of savings in bytes, kb, mb 2: Percentage savings */
3960
+ __( 'Reduced by %1$s (%2$s)', 'ewww-image-optimizer' ),
3961
+ $percent,
3962
+ $savings_str
3963
+ ) . $prev_string;
3964
+ ewwwio_debug_message( "original and new file are different size: $results_msg" );
3965
+ }
3966
+ return $results_msg;
3967
+ }
3968
+
3969
+ /**
3970
+ * Wrapper around size_format to remove the decimal from sizes in bytes.
3971
+ *
3972
+ * @param int $size A filesize in bytes.
3973
+ * @param int $precision Number of places after the decimal separator.
3974
+ * @return string Human-readable filesize.
3975
+ */
3976
+ function ewww_image_optimizer_size_format( $size, $precision = 1 ) {
3977
+ // Convert it to human readable format.
3978
+ $size_str = size_format( $size, $precision );
3979
+ // Remove spaces and extra decimals when measurement is in bytes.
3980
+ return preg_replace( '/\.0+ B ?/', ' B', $size_str );
3981
+ }
3982
+
3983
+ /**
3984
+ * Called to process each image during scheduled optimization.
3985
+ *
3986
+ * @global object $wpdb
3987
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
3988
+ * @global bool $ewww_defer Set to false to avoid deferring image optimization.
3989
+ * @global string $ewww_debug Contains in-memory debug log.
3990
+ *
3991
+ * @param array $attachment {
3992
+ * Optional. The file to optimize. Default null.
3993
+ *
3994
+ * @type int $id The id number in the ewwwio_images table.
3995
+ * @type string $path The filename of the image.
3996
+ * }
3997
+ * @param bool $auto Optional. True if scheduled optimization is running. Default false.
3998
+ * @param bool $cli Optional. True if WP-CLI is running. Default false.
3999
+ * @return string When called from WP-CLI, a message with compression results.
4000
+ */
4001
+ function ewww_image_optimizer_aux_images_loop( $attachment = null, $auto = false, $cli = false ) {
4002
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4003
+ global $wpdb;
4004
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
4005
+ ewww_image_optimizer_db_init();
4006
+ global $ewwwdb;
4007
+ } else {
4008
+ $ewwwdb = $wpdb;
4009
+ }
4010
+ global $ewww_defer;
4011
+ $ewww_defer = false;
4012
+ $output = array();
4013
+ // Verify that an authorized user has started the optimizer.
4014
+ $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
4015
+ if ( ! $auto && ( empty( $_REQUEST['ewww_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) ) {
4016
+ $output['error'] = esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' );
4017
+ echo json_encode( $output );
4018
+ die();
4019
+ }
4020
+ session_write_close();
4021
+ if ( ! empty( $_REQUEST['ewww_wpnonce'] ) ) {
4022
+ // Find out if our nonce is on it's last leg/tick.
4023
+ $tick = wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-bulk' );
4024
+ if ( 2 === $tick ) {
4025
+ ewwwio_debug_message( 'nonce on its last leg' );
4026
+ $output['new_nonce'] = wp_create_nonce( 'ewww-image-optimizer-bulk' );
4027
+ } else {
4028
+ ewwwio_debug_message( 'nonce still alive and kicking' );
4029
+ $output['new_nonce'] = '';
4030
+ }
4031
+ }
4032
+ // Retrieve the time when the optimizer starts.
4033
+ $started = microtime( true );
4034
+ if ( ewww_image_optimizer_stl_check() && ini_get( 'max_execution_time' ) < 60 ) {
4035
+ set_time_limit( 0 );
4036
+ }
4037
+ // Get the next image in the queue.
4038
+ if ( empty( $attachment ) ) {
4039
+ list( $id, $attachment ) = $ewwwdb->get_row( "SELECT id,path FROM $ewwwdb->ewwwio_images WHERE pending=1 LIMIT 1", ARRAY_N );
4040
+ } else {
4041
+ $id = $attachment['id'];
4042
+ $attachment = $attachment['path'];
4043
+ }
4044
+ if ( $attachment ) {
4045
+ $attachment = ewww_image_optimizer_relative_path_replace( $attachment );
4046
+ }
4047
+ // Do the optimization for the current image.
4048
+ $results = ewww_image_optimizer( $attachment );
4049
+ if ( ! $results[0] && is_numeric( $id ) ) {
4050
+ $ewwwdb->delete(
4051
+ $ewwwdb->ewwwio_images,
4052
+ array(
4053
+ 'id' => $id,
4054
+ ),
4055
+ array(
4056
+ '%d'
4057
+ )
4058
+ );
4059
+ }
4060
+ $ewww_status = get_transient( 'ewww_image_optimizer_cloud_status' );
4061
+ if ( ! empty( $ewww_status ) && preg_match( '/exceeded/', $ewww_status ) ) {
4062
+ if ( ! $auto ) {
4063
+ $output['error'] = esc_html__( 'License Exceeded', 'ewww-image-optimizer' );
4064
+ echo json_encode( $output );
4065
+ }
4066
+ if ( $cli ) {
4067
+ WP_CLI::error( __( 'License Exceeded', 'ewww-image-optimizer' ) );
4068
+ }
4069
+ die();
4070
+ }
4071
+ if ( ! $auto ) {
4072
+ // Output the path.
4073
+ $output['results'] = '<p>' . esc_html__( 'Optimized', 'ewww-image-optimizer' ) . ' <strong>' . esc_html( $attachment ) . '</strong><br>';
4074
+ // Tell the user what the results were for the original image.
4075
+ $output['results'] .= $results[1] . '<br>';
4076
+ // Calculate how much time has elapsed since we started.
4077
+ $elapsed = microtime( true ) - $started;
4078
+ // Output how much time has elapsed since we started.
4079
+ $output['results'] .= sprintf( esc_html(
4080
+ /* translators: %s: number of seconds */
4081
+ _n( 'Elapsed: %s second', 'Elapsed: %s seconds', $elapsed, 'ewww-image-optimizer' )
4082
+ ) . '</p>', number_format_i18n( $elapsed ) );
4083
+ if ( get_site_option( 'ewww_image_optimizer_debug' ) ) {
4084
+ global $ewww_debug;
4085
+ $output['results'] .= '<div style="background-color:#ffff99;">' . $ewww_debug . '</div>';
4086
+ }
4087
+ $next_file = ewww_image_optimizer_relative_path_replace( $wpdb->get_var( "SELECT path FROM $wpdb->ewwwio_images WHERE pending=1 LIMIT 1" ) );
4088
+ if ( ! empty( $next_file ) ) {
4089
+ $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
4090
+ $output['next_file'] = '<p>' . esc_html__( 'Optimizing', 'ewww-image-optimizer' ) . ' <b>' . esc_html( $next_file ) . "</b>&nbsp;<img src='$loading_image' alt='loading'/></p>";
4091
+ }
4092
+ echo json_encode( $output );
4093
+ ewwwio_memory( __FUNCTION__ );
4094
+ die();
4095
+ }
4096
+ if ( $cli ) {
4097
+ return $results[1];
4098
+ }
4099
+ ewwwio_memory( __FUNCTION__ );
4100
+ }
4101
+
4102
+ /**
4103
+ * Looks for a retina version of the original file so that we can optimize that too.
4104
+ *
4105
+ * @global object $ewww_image Contains more information about the image currently being processed.
4106
+ *
4107
+ * @param string $orig_path Filename of the 'normal' image.
4108
+ * @param bool $return_path True returns the path of the retina image and skips optimization.
4109
+ * @param bool $validate_file True verifies the file exists.
4110
+ * @return string The filename of the retina image.
4111
+ */
4112
+ function ewww_image_optimizer_hidpi_optimize( $orig_path, $return_path = false, $validate_file = true ) {
4113
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4114
+ $hidpi_suffix = apply_filters( 'ewww_image_optimizer_hidpi_suffix', '@2x' );
4115
+ $pathinfo = pathinfo( $orig_path );
4116
+ if ( empty( $pathinfo['dirname'] ) || empty( $pathinfo['filename'] ) || empty( $pathinfo['extension'] ) ) {
4117
+ return;
4118
+ }
4119
+ $hidpi_path = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . $hidpi_suffix . '.' . $pathinfo['extension'];
4120
+ if ( $validate_file && ! is_file( $hidpi_path ) ) {
4121
+ return;
4122
+ }
4123
+ if ( $return_path ) {
4124
+ ewwwio_debug_message( "found retina at $hidpi_path" );
4125
+ return $hidpi_path;
4126
+ }
4127
+ global $ewww_image;
4128
+ if ( is_object( $ewww_image ) && $ewww_image instanceof EWWW_Image ) {
4129
+ $id = $ewww_image->attachment_id;
4130
+ $gallery = 'media';
4131
+ $size = $ewww_image->resize;
4132
+ } else {
4133
+ $id = 0;
4134
+ $gallery = '';
4135
+ $size = null;
4136
+ }
4137
+ $ewww_image = new EWWW_Image( $id, $gallery, $hidpi_path );
4138
+ $ewww_image->resize = $size . '-retina';
4139
+ ewww_image_optimizer( $hidpi_path );
4140
+ }
4141
+
4142
+ /**
4143
+ * Fetches images from S3 or Azure storage so that they can be optimized locally.
4144
+ *
4145
+ * @global object $as3cf
4146
+ *
4147
+ * @param int $id The attachment ID number.
4148
+ * @param array $meta The attachment metadata.
4149
+ * @return string|bool The filename of the image fetched, false on failure.
4150
+ */
4151
+ function ewww_image_optimizer_remote_fetch( $id, $meta ) {
4152
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4153
+ if ( ! function_exists( 'download_url' ) ) {
4154
+ require_once( ABSPATH . '/wp-admin/includes/file.php' );
4155
+ }
4156
+ if ( class_exists( 'Amazon_S3_And_CloudFront' ) ) {
4157
+ global $as3cf;
4158
+ $full_url = get_attached_file( $id );
4159
+ if ( strpos( $full_url, 's3' ) === 0 ) {
4160
+ $full_url = $as3cf->get_attachment_url( $id, null, null, $meta );
4161
+ }
4162
+ $filename = get_attached_file( $id, true );
4163
+ ewwwio_debug_message( "amazon s3 fullsize url: $full_url" );
4164
+ ewwwio_debug_message( "unfiltered fullsize path: $filename" );
4165
+ $temp_file = download_url( $full_url );
4166
+ if ( ! is_wp_error( $temp_file ) ) {
4167
+ rename( $temp_file, $filename );
4168
+ }
4169
+ // Resized versions, so we'll grab those too.
4170
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
4171
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes_opt' );
4172
+ ewwwio_debug_message( 'retrieving resizes' );
4173
+ // Meta sizes don't contain a path, so we calculate one.
4174
+ $base_dir = trailingslashit( dirname( $filename ) );
4175
+ $processed = array();
4176
+ foreach ( $meta['sizes'] as $size => $data ) {
4177
+ ewwwio_debug_message( "processing size: $size" );
4178
+ if ( preg_match( '/webp/', $size ) ) {
4179
+ continue;
4180
+ }
4181
+ if ( ! empty( $disabled_sizes[ $size ] ) ) {
4182
+ continue;
4183
+ }
4184
+ if ( empty( $data['file'] ) ) {
4185
+ continue;
4186
+ }
4187
+ $dup_size = false;
4188
+ // Check through all the sizes we've processed so far.
4189
+ foreach ( $processed as $proc => $scan ) {
4190
+ // If a previous resize had identical dimensions.
4191
+ if ( $scan['height'] == $data['height'] && $scan['width'] == $data['width'] ) {
4192
+ // Found a duplicate resize.
4193
+ $dup_size = true;
4194
+ }
4195
+ }
4196
+ // If this is a unique size.
4197
+ if ( ! $dup_size ) {
4198
+ $resize_path = $base_dir . $data['file'];
4199
+ $resize_url = $as3cf->get_attachment_url( $id, null, $size, $meta );
4200
+ ewwwio_debug_message( "fetching $resize_url to $resize_path" );
4201
+ $temp_file = download_url( $resize_url );
4202
+ if ( ! is_wp_error( $temp_file ) ) {
4203
+ rename( $temp_file, $resize_path );
4204
+ }
4205
+ }
4206
+ // Store info on the sizes we've processed, so we can check the list for duplicate sizes.
4207
+ $processed[ $size ]['width'] = $data['width'];
4208
+ $processed[ $size ]['height'] = $data['height'];
4209
+ }
4210
+ } // End if().
4211
+ } // End if().
4212
+ if ( class_exists( 'WindowsAzureStorageUtil' ) && get_option( 'azure_storage_use_for_default_upload' ) ) {
4213
+ $full_url = $meta['url'];
4214
+ $filename = $meta['file'];
4215
+ ewwwio_debug_message( "azure fullsize url: $full_url" );
4216
+ ewwwio_debug_message( "fullsize path: $filename" );
4217
+ $temp_file = download_url( $full_url );
4218
+ if ( ! is_wp_error( $temp_file ) ) {
4219
+ rename( $temp_file, $filename );
4220
+ }
4221
+ // Resized versions, so we'll grab those too.
4222
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
4223
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes_opt' );
4224
+ ewwwio_debug_message( 'retrieving resizes' );
4225
+ // Meta sizes don't contain a path, so we calculate one.
4226
+ $base_dir = trailingslashit( dirname( $filename ) );
4227
+ $base_url = trailingslashit( dirname( $full_url ) );
4228
+ // Process each resized version.
4229
+ $processed = array();
4230
+ foreach ( $meta['sizes'] as $size => $data ) {
4231
+ ewwwio_debug_message( "processing size: $size" );
4232
+ if ( preg_match( '/webp/', $size ) ) {
4233
+ continue;
4234
+ }
4235
+ if ( ! empty( $disabled_sizes[ $size ] ) ) {
4236
+ continue;
4237
+ }
4238
+ if ( empty( $data['file'] ) ) {
4239
+ continue;
4240
+ }
4241
+ $dup_size = false;
4242
+ // Check through all the sizes we've processed so far.
4243
+ foreach ( $processed as $proc => $scan ) {
4244
+ // If a previous resize had identical dimensions.
4245
+ if ( $scan['height'] == $data['height'] && $scan['width'] == $data['width'] ) {
4246
+ // Found a duplicate resize.
4247
+ $dup_size = true;
4248
+ }
4249
+ }
4250
+ // If this is a unique size.
4251
+ if ( ! $dup_size ) {
4252
+ $resize_path = $base_dir . $data['file'];
4253
+ $resize_url = $base_url . $data['file'];
4254
+ ewwwio_debug_message( "fetching $resize_url to $resize_path" );
4255
+ $temp_file = download_url( $resize_url );
4256
+ if ( ! is_wp_error( $temp_file ) ) {
4257
+ rename( $temp_file, $resize_path );
4258
+ }
4259
+ }
4260
+ // Store info on the sizes we've processed, so we can check the list for duplicate sizes.
4261
+ $processed[ $size ]['width'] = $data['width'];
4262
+ $processed[ $size ]['height'] = $data['height'];
4263
+ }
4264
+ } // End if().
4265
+ } // End if().
4266
+ if ( ! empty( $filename ) && is_file( $filename ) ) {
4267
+ return $filename;
4268
+ } else {
4269
+ return false;
4270
+ }
4271
+ }
4272
+
4273
+ /**
4274
+ * Searches the database for a matching s3 path, and fixes it to use the local path.
4275
+ *
4276
+ * @global object $wpdb
4277
+ *
4278
+ * @param array $meta The attachment metadata.
4279
+ * @param int $id The attachment ID number.
4280
+ * @param string $s3_path The potential s3:// path.
4281
+ */
4282
+ function ewww_image_optimizer_check_table_as3cf( $meta, $id, $s3_path ) {
4283
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4284
+ $local_path = get_attached_file( $id, true );
4285
+ ewwwio_debug_message( "unfiltered local path: $local_path" );
4286
+ if ( $local_path !== $s3_path ) {
4287
+ ewww_image_optimizer_update_table_as3cf( $local_path, $s3_path );
4288
+ }
4289
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
4290
+ ewwwio_debug_message( 'updating s3 resizes' );
4291
+ // Meta sizes don't contain a path, so we calculate one.
4292
+ $local_dir = trailingslashit( dirname( $local_path ) );
4293
+ $s3_dir = trailingslashit( dirname( $s3_path ) );
4294
+ $processed = array();
4295
+ foreach ( $meta['sizes'] as $size => $data ) {
4296
+ if ( strpos( $size, 'webp' ) === 0 ) {
4297
+ continue;
4298
+ }
4299
+ // Check through all the sizes we've processed so far.
4300
+ foreach ( $processed as $proc => $scan ) {
4301
+ // If a previous resize had identical dimensions.
4302
+ if ( $scan['height'] === $data['height'] && $scan['width'] === $data['width'] ) {
4303
+ // Found a duplicate resize.
4304
+ continue;
4305
+ }
4306
+ }
4307
+ // If this is a unique size.
4308
+ $local_resize_path = $local_dir . $data['file'];
4309
+ $s3_resize_path = $s3_dir . $data['file'];
4310
+ if ( $local_resize_path !== $s3_resize_path ) {
4311
+ ewww_image_optimizer_update_table_as3cf( $local_resize_path, $s3_resize_path );
4312
+ }
4313
+ // Store info on the sizes we've processed, so we can check the list for duplicate sizes.
4314
+ $processed[ $size ]['width'] = $data['width'];
4315
+ $processed[ $size ]['height'] = $data['height'];
4316
+ }
4317
+ }
4318
+ global $wpdb;
4319
+ $wpdb->flush();
4320
+ }
4321
+
4322
+ /**
4323
+ * Given an S3 path, replaces it with the local path.
4324
+ *
4325
+ * @global object $wpdb
4326
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
4327
+ *
4328
+ * @param string $local_path The local filesystem path to the image.
4329
+ * @param string $s3_path The remote S3 path to the image.
4330
+ */
4331
+ function ewww_image_optimizer_update_table_as3cf( $local_path, $s3_path ) {
4332
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4333
+ // First we need to see if anything matches the s3 path.
4334
+ $s3_image = ewww_image_optimizer_find_already_optimized( $s3_path );
4335
+ ewwwio_debug_message( "looking for $s3_path" );
4336
+ if ( is_array( $s3_image ) ) {
4337
+ global $wpdb;
4338
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
4339
+ ewww_image_optimizer_db_init();
4340
+ global $ewwwdb;
4341
+ } else {
4342
+ $ewwwdb = $wpdb;
4343
+ }
4344
+ ewwwio_debug_message( "found $s3_path in db" );
4345
+ // When we find a match by the s3 path, we need to find out if there are already records for the local path.
4346
+ $found_local_image = ewww_image_optimizer_find_already_optimized( $local_path );
4347
+ ewwwio_debug_message( "looking for $local_path" );
4348
+ // If we found records for both local and s3 paths, we delete the s3 record, but store the original size in the local record.
4349
+ if ( ! empty( $found_local_image ) && is_array( $found_local_image ) ) {
4350
+ ewwwio_debug_message( "found $local_path in db" );
4351
+ $ewwwdb->delete( $ewwwdb->ewwwio_images,
4352
+ array(
4353
+ 'id' => $s3_image['id'],
4354
+ ),
4355
+ array(
4356
+ '%d'
4357
+ )
4358
+ );
4359
+ if ( $s3_image['orig_size'] > $found_local_image['orig_size'] ) {
4360
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
4361
+ array(
4362
+ 'orig_size' => $s3_image['orig_size'],
4363
+ 'results' => $s3_image['results'],
4364
+ ),
4365
+ array(
4366
+ 'id' => $found_local_image['id'],
4367
+ )
4368
+ );
4369
+ }
4370
+ } else {
4371
+ // If we just found an s3 path and no local match, then we just update the path in the table to the local path.
4372
+ ewwwio_debug_message( 'just updating s3 to local' );
4373
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
4374
+ array(
4375
+ 'path' => ewww_image_optimizer_relative_path_remove( $local_path ),
4376
+ ),
4377
+ array(
4378
+ 'id' => $s3_image['id'],
4379
+ )
4380
+ );
4381
+ }
4382
+ } // End if().
4383
+ }
4384
+
4385
+ /**
4386
+ * If a JPG image is using the EXIF orientiation, correct the rotation, and reset the Orientation.
4387
+ *
4388
+ * @param string $file The file to check for rotation.
4389
+ */
4390
+ function ewww_image_optimizer_autorotate( $file ) {
4391
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4392
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_NO_ROTATE' ) && EWWW_IMAGE_OPTIMIZER_NO_ROTATE ) {
4393
+ return;
4394
+ }
4395
+ $type = ewww_image_optimizer_mimetype( $file, 'i' );
4396
+ if ( 'image/jpeg' != $type ) {
4397
+ ewwwio_debug_message( 'not a JPG, no rotation needed' );
4398
+ return;
4399
+ }
4400
+ $orientation = ewww_image_optimizer_get_orientation( $file, $type );
4401
+ if ( ! $orientation || 1 == $orientation ) {
4402
+ return;
4403
+ }
4404
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) < 20 ) {
4405
+ // Read the exif, if it fails, we won't autorotate.
4406
+ $jpeg = new PelJpeg( $file );
4407
+ $exif = $jpeg->getExif();
4408
+ if ( null == $exif ) {
4409
+ ewwwio_debug_message( 'could not work with PelJpeg object, no rotation happening here' );
4410
+ } elseif ( ewww_image_optimizer_jpegtran_autorotate( $file, $type, $orientation ) ) {
4411
+ // Use PEL to correct the orientation flag when metadata was preserved.
4412
+ $jpeg = new PelJpeg( $file );
4413
+ $exif = $jpeg->getExif();
4414
+ if ( null != $exif ) {
4415
+ $tiff = $exif->getTiff();
4416
+ $ifd0 = $tiff->getIfd();
4417
+ $orientation = $ifd0->getEntry( PelTag::ORIENTATION );
4418
+ if ( null != $orientation ) {
4419
+ ewwwio_debug_message( 'orientation being adjusted' );
4420
+ $orientation->setValue( 1 );
4421
+ }
4422
+ $jpeg->saveFile( $file );
4423
+ }
4424
+ }
4425
+ return;
4426
+ }
4427
+ ewww_image_optimizer_cloud_autorotate( $file, $type );
4428
+ }
4429
+
4430
+ /**
4431
+ * Resizes Media Library uploads based on the maximum dimensions specified by the user.
4432
+ *
4433
+ * @global object $wpdb
4434
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
4435
+ *
4436
+ * @param string $file The file to check for rotation.
4437
+ * @return array|bool The new height and width, or false if no resizing was done.
4438
+ */
4439
+ function ewww_image_optimizer_resize_upload( $file ) {
4440
+ // Parts adapted from Imsanity (THANKS Jason!).
4441
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4442
+ if ( ! $file ) {
4443
+ return false;
4444
+ }
4445
+ if ( ! empty( $_REQUEST['post_id'] ) || ( ! empty( $_REQUEST['action'] ) && 'upload-attachment' === $_REQUEST['action'] ) || ( ! empty( $_SERVER['HTTP_REFERER'] ) && strpos( $_SERVER['HTTP_REFERER'], 'media-new.php' ) ) ) {
4446
+ $maxwidth = ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediawidth' );
4447
+ $maxheight = ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediaheight' );
4448
+ ewwwio_debug_message( 'resizing image from media library or attached to post' );
4449
+ } else {
4450
+ $maxwidth = ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherwidth' );
4451
+ $maxheight = ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherheight' );
4452
+ ewwwio_debug_message( 'resizing images from somewhere else' );
4453
+ }
4454
+
4455
+ /**
4456
+ * Filters the dimensions to used for resizing uploaded images.
4457
+ *
4458
+ * @param array $args {
4459
+ * The dimensions to be used in resizing.
4460
+ *
4461
+ * @type int $maxwidth The maximum width of the image.
4462
+ * @type int $maxheight The maximum height of the image.
4463
+ * }
4464
+ * @param string $file The name of the file being resized.
4465
+ */
4466
+ list( $maxwidth, $maxheight ) = apply_filters( 'ewww_image_optimizer_resize_dimensions', array( $maxwidth, $maxheight ), $file );
4467
+
4468
+ // Check that options are not both set to zero.
4469
+ if ( 0 == $maxwidth && 0 == $maxheight ) {
4470
+ return false;
4471
+ }
4472
+ $maxwidth = $maxwidth ? $maxwidth : 999999;
4473
+ $maxheight = $maxheight ? $maxheight : 999999;
4474
+ // Check the file type.
4475
+ $type = ewww_image_optimizer_mimetype( $file, 'i' );
4476
+ if ( strpos( $type, 'image' ) === false ) {
4477
+ ewwwio_debug_message( 'not an image, cannot resize' );
4478
+ return false;
4479
+ }
4480
+ // Check file size (dimensions).
4481
+ list( $oldwidth, $oldheight ) = getimagesize( $file );
4482
+ if ( $oldwidth <= $maxwidth && $oldheight <= $maxheight ) {
4483
+ ewwwio_debug_message( 'image too small for resizing' );
4484
+ if ( $oldwidth && $oldheight ) {
4485
+ return array( $oldwidth, $oldheight );
4486
+ }
4487
+ return false;
4488
+ }
4489
+ $crop = false;
4490
+ if ( $oldwidth > $maxwidth && $maxwidth && $oldheight > $maxheight && $maxheight && apply_filters( 'ewww_image_optimizer_crop_image', false ) ) {
4491
+ $crop = true;
4492
+ $newwidth = $maxwidth;
4493
+ $newheight = $maxheight;
4494
+ } else {
4495
+ list( $newwidth, $newheight ) = wp_constrain_dimensions( $oldwidth, $oldheight, $maxwidth, $maxheight );
4496
+ }
4497
+ if ( ! function_exists( 'wp_get_image_editor' ) ) {
4498
+ ewwwio_debug_message( 'no image editor function' );
4499
+ return false;
4500
+ }
4501
+ remove_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
4502
+ $editor = wp_get_image_editor( $file );
4503
+ if ( is_wp_error( $editor ) ) {
4504
+ ewwwio_debug_message( 'could not get image editor' );
4505
+ return false;
4506
+ }
4507
+ // Rotation should not happen here anymore, so don't worry about it breaking the dimensions.
4508
+ $orientation = ewww_image_optimizer_get_orientation( $file, $type );
4509
+ $rotated = false;
4510
+ switch ( $orientation ) {
4511
+ case 3:
4512
+ $editor->rotate( 180 );
4513
+ $rotated = true;
4514
+ break;
4515
+ case 6:
4516
+ $editor->rotate( -90 );
4517
+ $new_newwidth = $newwidth;
4518
+ $newwidth = $newheight;
4519
+ $newheight = $new_newwidth;
4520
+ $rotated = true;
4521
+ break;
4522
+ case 8:
4523
+ $editor->rotate( 90 );
4524
+ $new_newwidth = $newwidth;
4525
+ $newwidth = $newheight;
4526
+ $newheight = $new_newwidth;
4527
+ $rotated = true;
4528
+ break;
4529
+ }
4530
+ $resized_image = $editor->resize( $newwidth, $newheight, $crop );
4531
+ if ( is_wp_error( $resized_image ) ) {
4532
+ ewwwio_debug_message( 'error during resizing' );
4533
+ return false;
4534
+ }
4535
+ $new_file = $editor->generate_filename( 'tmp' );
4536
+ $orig_size = filesize( $file );
4537
+ ewwwio_debug_message( "before resizing: $orig_size" );
4538
+ $saved = $editor->save( $new_file );
4539
+ if ( is_wp_error( $saved ) ) {
4540
+ ewwwio_debug_message( 'error saving resized image' );
4541
+ }
4542
+ add_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
4543
+ $new_size = ewww_image_optimizer_filesize( $new_file );
4544
+ if ( $new_size && $new_size < $orig_size ) {
4545
+ // Use this action to perform any operations on the original file before it is overwritten with the new, smaller file.
4546
+ do_action( 'ewww_image_optimizer_image_resized', $file, $new_file );
4547
+ ewwwio_debug_message( "after resizing: $new_size" );
4548
+ // TODO: see if there is a way to just check that meta exists on the new image.
4549
+ // Use PEL to get the exif (if Remove Meta is unchecked) and GD is in use, so we can save it to the new image.
4550
+ if ( 'image/jpeg' === $type && ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_skip_full' ) || ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) ) && ! ewww_image_optimizer_imagick_support() ) {
4551
+ $old_jpeg = new PelJpeg( $file );
4552
+ $old_exif = $old_jpeg->getExif();
4553
+ $new_jpeg = new PelJpeg( $new_file );
4554
+ if ( null != $old_exif ) {
4555
+ if ( $rotated ) {
4556
+ $tiff = $old_exif->getTiff();
4557
+ $ifd0 = $tiff->getIfd();
4558
+ $orientation = $ifd0->getEntry( PelTag::ORIENTATION );
4559
+ if ( null != $orientation ) {
4560
+ $orientation->setValue( 1 );
4561
+ }
4562
+ }
4563
+ $new_jpeg->setExif( $old_exif );
4564
+ }
4565
+ $new_jpeg->saveFile( $new_file );
4566
+ }
4567
+ rename( $new_file, $file );
4568
+ // Store info on the current image for future reference.
4569
+ global $wpdb;
4570
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
4571
+ ewww_image_optimizer_db_init();
4572
+ global $ewwwdb;
4573
+ } else {
4574
+ $ewwwdb = $wpdb;
4575
+ }
4576
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $file );
4577
+ // If the original file has never been optimized, then just update the record that was created with the proper filename (because the resized file has usually been optimized).
4578
+ if ( empty( $already_optimized ) ) {
4579
+ $tmp_exists = $ewwwdb->update( $ewwwdb->ewwwio_images,
4580
+ array(
4581
+ 'path' => ewww_image_optimizer_relative_path_remove( $file ),
4582
+ 'orig_size' => $orig_size,
4583
+ ),
4584
+ array(
4585
+ 'path' => ewww_image_optimizer_relative_path_remove( $new_file ),
4586
+ )
4587
+ );
4588
+ // If the tmp file didn't get optimized (and it shouldn't), then just insert a dummy record to be updated shortly.
4589
+ if ( ! $tmp_exists ) {
4590
+ $ewwwdb->insert( $ewwwdb->ewwwio_images, array(
4591
+ 'path' => ewww_image_optimizer_relative_path_remove( $file ),
4592
+ 'orig_size' => $orig_size,
4593
+ ) );
4594
+ }
4595
+ } else {
4596
+ // Otherwise, we delete the record created from optimizing the resized file.
4597
+ $temp_optimized = ewww_image_optimizer_find_already_optimized( $new_file );
4598
+ if ( is_array( $temp_optimized ) && ! empty( $temp_optimized['id'] ) ) {
4599
+ $ewwwdb->delete( $ewwwdb->ewwwio_images,
4600
+ array(
4601
+ 'id' => $temp_optimized['id'],
4602
+ ),
4603
+ array(
4604
+ '%d',
4605
+ )
4606
+ );
4607
+ }
4608
+ }
4609
+ return array( $newwidth, $newheight );
4610
+ } // End if().
4611
+ if ( is_file( $new_file ) ) {
4612
+ ewwwio_debug_message( "resizing did not create a smaller image: $new_size" );
4613
+ unlink( $new_file );
4614
+ }
4615
+ return false;
4616
+ }
4617
+
4618
+ /**
4619
+ * Gets the orientation/rotation of a JPG image using the EXIF data.
4620
+ *
4621
+ * @param string $file Name of the file.
4622
+ * @param string $type Mime type of the file.
4623
+ * @return int|bool The orientation value or false.
4624
+ */
4625
+ function ewww_image_optimizer_get_orientation( $file, $type ) {
4626
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4627
+ if ( function_exists( 'exif_read_data' ) && 'image/jpeg' === $type ) {
4628
+ $exif = @exif_read_data( $file );
4629
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
4630
+ ewwwio_debug_message( print_r( $exif, true ) );
4631
+ }
4632
+ if ( is_array( $exif ) && array_key_exists( 'Orientation', $exif ) ) {
4633
+ return $exif['Orientation'];
4634
+ }
4635
+ }
4636
+ return false;
4637
+ }
4638
+
4639
+ /**
4640
+ * Searches the images table for a file.
4641
+ *
4642
+ * If more than one record is found, verifies case and calls duplicate removal if needed.
4643
+ *
4644
+ * @global object $wpdb
4645
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
4646
+ *
4647
+ * @param string $attachment The name of the file.
4648
+ * @return array|bool If found, information about the image, false otherwise.
4649
+ */
4650
+ function ewww_image_optimizer_find_already_optimized( $attachment ) {
4651
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4652
+ global $wpdb;
4653
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
4654
+ ewww_image_optimizer_db_init();
4655
+ global $ewwwdb;
4656
+ } else {
4657
+ $ewwwdb = $wpdb;
4658
+ }
4659
+ $maybe_return_image = false;
4660
+ $query = $ewwwdb->prepare( "SELECT * FROM $ewwwdb->ewwwio_images WHERE path = %s", ewww_image_optimizer_relative_path_remove( $attachment ) );
4661
+ $optimized_query = $ewwwdb->get_results( $query, ARRAY_A );
4662
+ if ( ewww_image_optimizer_iterable( $optimized_query ) ) {
4663
+ foreach ( $optimized_query as $image ) {
4664
+ $image['path'] = ewww_image_optimizer_relative_path_replace( $image['path'] );
4665
+ if ( $image['path'] != $attachment ) {
4666
+ ewwwio_debug_message( "{$image['path']} does not match $attachment, continuing our search" );
4667
+ } elseif ( ! $maybe_return_image ) {
4668
+ ewwwio_debug_message( "found a match for $attachment" );
4669
+ $maybe_return_image = $image;
4670
+ } else {
4671
+ if ( empty( $duplicates ) ) {
4672
+ $duplicates = array( $maybe_return_image, $image );
4673
+ } else {
4674
+ $duplicates[] = $image;
4675
+ }
4676
+ }
4677
+ }
4678
+ }
4679
+ // Do something with duplicates.
4680
+ if ( ! empty( $duplicates ) && is_array( $duplicates ) ) {
4681
+ $keeper = ewww_image_optimizer_remove_duplicate_records( $duplicates );
4682
+ if ( ! empty( $keeper ) && is_array( $keeper ) ) {
4683
+ $maybe_return_image = $keeper;
4684
+ }
4685
+ }
4686
+ return $maybe_return_image;
4687
+ }
4688
+
4689
+ /**
4690
+ * Merge duplicate records from the images table and remove any extras.
4691
+ *
4692
+ * @global object $wpdb
4693
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
4694
+ *
4695
+ * @param array $duplicates An array of records referencing the same image.
4696
+ * @return array|bool A single image record or false if something unexpected happens.
4697
+ */
4698
+ function ewww_image_optimizer_remove_duplicate_records( $duplicates ) {
4699
+ if ( empty( $duplicates ) ) {
4700
+ return false;
4701
+ }
4702
+ global $wpdb;
4703
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
4704
+ ewww_image_optimizer_db_init();
4705
+ global $ewwwdb;
4706
+ } else {
4707
+ $ewwwdb = $wpdb;
4708
+ }
4709
+ if ( ! is_array( $duplicates[0] ) ) {
4710
+ // Retrieve records for the ID #s passed.
4711
+ $duplicate_ids = implode( ',', array_map( 'intval', $duplicates ) );
4712
+ $duplicates = $ewwwdb->get_results( "SELECT * FROM $ewwwdb->ewwwio_images WHERE id IN ($duplicate_ids)" ); // WPCS: unprepared SQL ok.
4713
+ }
4714
+ if ( ! is_array( $duplicates ) || ! is_array( $duplicates[0] ) ) {
4715
+ return false;
4716
+ }
4717
+ $image_size = ewww_image_optimizer_filesize( $duplicates[0]['path'] );
4718
+ $discard = array();
4719
+ // First look for an image size match.
4720
+ foreach ( $duplicates as $duplicate ) {
4721
+ if ( empty( $keeper ) && ! empty( $duplicate['image_size'] ) && $image_size == $duplicate['image_size'] ) {
4722
+ $keeper = $duplicate;
4723
+ } else {
4724
+ $discard[] = $duplicate;
4725
+ }
4726
+ }
4727
+ // Then look for the first record with an image_size (that means it has been optimized).
4728
+ if ( empty( $keeper ) ) {
4729
+ $discard = array();
4730
+ foreach ( $duplicates as $duplicate ) {
4731
+ if ( empty( $keeper ) && ! empty( $duplicate['image_size'] ) ) {
4732
+ $keeper = $duplicate;
4733
+ } else {
4734
+ $discard[] = $duplicate;
4735
+ }
4736
+ }
4737
+ }
4738
+ // If we still have nothing, mark the 0 record as the primary and pull it off the stack.
4739
+ if ( empty( $keeper ) ) {
4740
+ $keeper = array_shift( $duplicates );
4741
+ $discard = $duplicates;
4742
+ }
4743
+ if ( is_array( $keeper ) && is_array( $discard ) ) {
4744
+ $delete_ids = array();
4745
+ foreach ( $discard as $record ) {
4746
+ foreach ( $record as $key => $value ) {
4747
+ if ( empty( $keeper[ $key ] ) && ! empty( $value ) ) {
4748
+ $keeper[ $key ] = $value;
4749
+ }
4750
+ }
4751
+ $delete_ids[] = (int) $record['id'];
4752
+ }
4753
+ if ( ! empty( $delete_ids ) && is_array( $delete_ids ) ) {
4754
+ $ewwwdb->query( "DELETE FROM $ewwwdb->ewwwio_images WHERE id IN (" . implode( ',', $delete_ids ) . ')' ); // WPCS: unprepared SQL ok.
4755
+ }
4756
+ return $keeper;
4757
+ }
4758
+ return false;
4759
+ }
4760
+
4761
+ /**
4762
+ * Checks to see if we should use background optimization for an image.
4763
+ *
4764
+ * Uses the mimetype and current configuration to determine if background mode should be used.
4765
+ *
4766
+ * @global bool $ewww_defer True to defer optimization.
4767
+ *
4768
+ * @param string $type Optional. Mime type of image being processed. Default ''.
4769
+ * @return bool True if background mode should be used.
4770
+ */
4771
+ function ewww_image_optimizer_test_background_opt( $type = '' ) {
4772
+ if ( defined( 'EWWW_DISABLE_ASYNC' ) && EWWW_DISABLE_ASYNC ) {
4773
+ return false;
4774
+ }
4775
+ if ( ! ewww_image_optimizer_function_exists( 'sleep' ) ) {
4776
+ return false;
4777
+ }
4778
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_background_optimization' ) ) {
4779
+ return false;
4780
+ }
4781
+ if ( ewww_image_optimizer_detect_wpsf_location_lock() ) {
4782
+ return false;
4783
+ }
4784
+ if ( 'image/type' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' ) ) {
4785
+ return apply_filters( 'ewww_image_optimizer_defer_conversion', false );
4786
+ }
4787
+ if ( 'image/png' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' ) ) {
4788
+ return apply_filters( 'ewww_image_optimizer_defer_conversion', false );
4789
+ }
4790
+ if ( 'image/gif' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_to_png' ) ) {
4791
+ return apply_filters( 'ewww_image_optimizer_defer_conversion', false );
4792
+ }
4793
+ global $ewww_defer;
4794
+ return (bool) apply_filters( 'ewww_image_optimizer_background_optimization', $ewww_defer );
4795
+ }
4796
+
4797
+ /**
4798
+ * Checks to see if we should use parallel optimization for an image.
4799
+ *
4800
+ * Uses the mimetype and current configuration to determine if parallel mode should be used.
4801
+ *
4802
+ * @param string $type Optional. Mime type of image being processed. Default ''.
4803
+ * @param int $id Optional. Attachment ID number. Default 0.
4804
+ * @return bool True if parallel mode should be used.
4805
+ */
4806
+ function ewww_image_optimizer_test_parallel_opt( $type = '', $id = 0 ) {
4807
+ if ( defined( 'EWWW_DISABLE_ASYNC' ) && EWWW_DISABLE_ASYNC ) {
4808
+ return false;
4809
+ }
4810
+ if ( ! ewww_image_optimizer_function_exists( 'sleep' ) ) {
4811
+ return false;
4812
+ }
4813
+ if ( ewww_image_optimizer_detect_wpsf_location_lock() ) {
4814
+ return false;
4815
+ }
4816
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_parallel_optimization' ) ) {
4817
+ return false;
4818
+ }
4819
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_background_optimization' ) ) {
4820
+ return false;
4821
+ }
4822
+ if ( empty( $id ) ) {
4823
+ return true;
4824
+ }
4825
+ if ( ! empty( $_REQUEST['ewww_convert'] ) ) {
4826
+ return false;
4827
+ }
4828
+ if ( empty( $type ) ) {
4829
+ $type = get_post_mime_type( $id );
4830
+ }
4831
+ if ( 'image/jpeg' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' ) ) {
4832
+ return false;
4833
+ }
4834
+ if ( 'image/png' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' ) ) {
4835
+ return false;
4836
+ }
4837
+ if ( 'image/gif' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_to_png' ) ) {
4838
+ return false;
4839
+ }
4840
+ if ( 'application/pdf' == $type ) {
4841
+ return false;
4842
+ }
4843
+ return true;
4844
+ }
4845
+
4846
+ /**
4847
+ * Rebuilds metadata and regenerates thumbs for an attachment.
4848
+ *
4849
+ * @param int $attachment_id The ID number of the attachment.
4850
+ * @return array Attachment metadata, if the rebuild was successful.
4851
+ */
4852
+ function ewww_image_optimizer_rebuild_meta( $attachment_id ) {
4853
+ $file = get_attached_file( $attachment_id );
4854
+ if ( is_file( $file ) ) {
4855
+ remove_filter( 'wp_image_editors', 'ewww_image_optimizer_load_editor', 60 );
4856
+ remove_all_filters( 'wp_generate_attachment_metadata' );
4857
+ ewwwio_debug_message( "generating new meta for $attachment_id" );
4858
+ $meta = wp_generate_attachment_metadata( $attachment_id, $file );
4859
+ ewwwio_debug_message( "generated new meta for $attachment_id" );
4860
+ $updated = update_post_meta( $attachment_id, '_wp_attachment_metadata', $meta );
4861
+ if ( $updated ) {
4862
+ ewwwio_debug_message( "updated meta for $attachment_id" );
4863
+ } else {
4864
+ ewwwio_debug_message( "failed meta update for $attachment_id" );
4865
+ }
4866
+ return $meta;
4867
+ }
4868
+ }
4869
+
4870
+ /**
4871
+ * Find image paths from an attachment's meta data and process each image.
4872
+ *
4873
+ * Called after `wp_generate_attachment_metadata` is completed, it also searches for retina images,
4874
+ * and a few custom theme resizes. When a new image is uploaded, it is added to the queue, if
4875
+ * possible, and then this same function is run in the background. Optionally, it will use parallel
4876
+ * (async) requests to speed up the process.
4877
+ *
4878
+ * @global object $wpdb
4879
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
4880
+ * @global bool $ewww_defer True if optimization should be deferred until later.
4881
+ * @global bool $ewww_new_image True if this is a newly uploaded image.
4882
+ * @global object $ewww_image Contains more information about the image currently being processed.
4883
+ * @global object $ewwwio_media_background;
4884
+ * @global object $ewwwio_async_optimize_media;
4885
+ * @global array $ewww_attachment {
4886
+ * Stores the ID and meta for later use with W3TC.
4887
+ *
4888
+ * @int int $id The attachment ID number.
4889
+ * @array array $meta The attachment metadata from the postmeta table.
4890
+ * }
4891
+ * @global object $as3cf For working with the WP Offload S3 plugin.
4892
+ * @global object $dreamspeed For working with the Dreamspeed CDN plugin.
4893
+ *
4894
+ * @param array $meta The attachment metadata generated by WordPress.
4895
+ * @param int $id Optional. The attachment ID number. Default null. Accepts any non-negative integer.
4896
+ * @param bool $log Optional. True to flush debug info to the log at the end of the function.
4897
+ * @param bool $background_new Optional. True indicates this is a new image processed in the background.
4898
+ * @return array $meta Send the metadata back from whence it came.
4899
+ */
4900
+ function ewww_image_optimizer_resize_from_meta_data( $meta, $id = null, $log = true, $background_new = false ) {
4901
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
4902
+ if ( ! is_array( $meta ) && empty( $meta ) ) {
4903
+ $meta = array();
4904
+ } elseif ( ! is_array( $meta ) ) {
4905
+ if ( is_string( $meta ) && is_numeric( $id ) && 'processing' == $meta ) {
4906
+ ewwwio_debug_message( "attempting to rebuild attachment meta for $id" );
4907
+ $new_meta = ewww_image_optimizer_rebuild_meta( $id );
4908
+ if ( ! is_array( $new_meta ) ) {
4909
+ ewwwio_debug_message( 'attempt to rebuild attachment meta failed' );
4910
+ return $meta;
4911
+ } else {
4912
+ $meta = $new_meta;
4913
+ }
4914
+ } else {
4915
+ ewwwio_debug_message( 'attachment meta is not a usable array' );
4916
+ return $meta;
4917
+ }
4918
+ } elseif ( is_array( $meta ) && ! empty( $meta[0] ) && 'processing' == $meta[0] ) {
4919
+ ewwwio_debug_message( "attempting to rebuild attachment meta for $id" );
4920
+ $new_meta = ewww_image_optimizer_rebuild_meta( $id );
4921
+ if ( ! is_array( $new_meta ) ) {
4922
+ ewwwio_debug_message( 'attempt to rebuild attachment meta failed' );
4923
+ return $meta;
4924
+ } else {
4925
+ $meta = $new_meta;
4926
+ }
4927
+ }
4928
+ global $wpdb;
4929
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
4930
+ ewww_image_optimizer_db_init();
4931
+ global $ewwwdb;
4932
+ } else {
4933
+ $ewwwdb = $wpdb;
4934
+ }
4935
+ global $ewww_defer;
4936
+ global $ewww_new_image;
4937
+ global $ewww_image;
4938
+ $gallery_type = 1;
4939
+ ewwwio_debug_message( "attachment id: $id" );
4940
+
4941
+ session_write_close();
4942
+ if ( ! empty( $ewww_new_image ) ) {
4943
+ ewwwio_debug_message( 'this is a newly uploaded image with no metadata yet' );
4944
+ $new_image = true;
4945
+ } else {
4946
+ ewwwio_debug_message( 'this image already has metadata, so it is not new' );
4947
+ $new_image = false;
4948
+ }
4949
+ list( $file_path, $upload_path ) = ewww_image_optimizer_attachment_path( $meta, $id );
4950
+ // If the attachment has been uploaded via the image store plugin.
4951
+ if ( 'ims_image' == get_post_type( $id ) ) {
4952
+ $gallery_type = 6;
4953
+ }
4954
+ if ( ! $new_image && class_exists( 'Amazon_S3_And_CloudFront' ) && strpos( $file_path, 's3' ) === 0 ) {
4955
+ ewww_image_optimizer_check_table_as3cf( $meta, $id, $file_path );
4956
+ }
4957
+ // If the local file is missing and we have valid metadata, see if we can fetch via CDN.
4958
+ if ( ! is_file( $file_path ) || ( strpos( $file_path, 's3' ) === 0 && ! class_exists( 'S3_Uploads' ) ) ) {
4959
+ $file_path = ewww_image_optimizer_remote_fetch( $id, $meta );
4960
+ if ( ! $file_path ) {
4961
+ ewwwio_debug_message( 'could not retrieve path' );
4962
+ return $meta;
4963
+ }
4964
+ }
4965
+ ewwwio_debug_message( "retrieved file path: $file_path" );
4966
+ $type = ewww_image_optimizer_mimetype( $file_path, 'i' );
4967
+ $supported_types = array(
4968
+ 'image/jpeg',
4969
+ 'image/png',
4970
+ 'image/gif',
4971
+ 'application/pdf',
4972
+ );
4973
+ if ( ! in_array( $type, $supported_types ) ) {
4974
+ ewwwio_debug_message( "mimetype not supported: $id" );
4975
+ return $meta;
4976
+ }
4977
+ $fullsize_size = ewww_image_optimizer_filesize( $file_path );
4978
+ // See if this is a new image and Imsanity resized it (which means it could be already optimized).
4979
+ if ( ! empty( $new_image ) && function_exists( 'imsanity_get_max_width_height' ) && strpos( $type, 'image' ) !== false ) {
4980
+ list( $maxw, $maxh ) = imsanity_get_max_width_height( IMSANITY_SOURCE_LIBRARY );
4981
+ list( $oldw, $oldh ) = getimagesize( $file_path );
4982
+ list( $neww, $newh ) = wp_constrain_dimensions( $oldw, $oldh, $maxw, $maxh );
4983
+ $path_parts = pathinfo( $file_path );
4984
+ $imsanity_path = trailingslashit( $path_parts['dirname'] ) . $path_parts['filename'] . '-' . $neww . 'x' . $newh . '.' . $path_parts['extension'];
4985
+ ewwwio_debug_message( "imsanity path: $imsanity_path" );
4986
+ $image_size = ewww_image_optimizer_filesize( $file_path );
4987
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $imsanity_path );
4988
+ if ( is_array( $already_optimized ) ) {
4989
+ ewwwio_debug_message( "updating existing record, path: $file_path, size: " . $image_size );
4990
+ // Store info on the current image for future reference.
4991
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
4992
+ array(
4993
+ 'path' => ewww_image_optimizer_relative_path_remove( $file_path ),
4994
+ 'attachment_id' => $id,
4995
+ 'resize' => 'full',
4996
+ 'gallery' => 'media',
4997
+ ),
4998
+ array(
4999
+ 'id' => $already_optimized['id'],
5000
+ ));
5001
+ }
5002
+ }
5003
+ // Resize here so long as this is not a new image AND resize existing is enabled, and imsanity isn't enabled with a max size.
5004
+ if ( ( empty( $new_image ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_existing' ) ) && ! function_exists( 'imsanity_get_max_width_height' ) ) {
5005
+ $new_dimensions = ewww_image_optimizer_resize_upload( $file_path );
5006
+ if ( is_array( $new_dimensions ) ) {
5007
+ $meta['width'] = $new_dimensions[0];
5008
+ $meta['height'] = $new_dimensions[1];
5009
+ }
5010
+ }
5011
+ if ( ewww_image_optimizer_test_background_opt( $type ) ) {
5012
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
5013
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_NO_DEFER_S3' ) || ! EWWW_IMAGE_OPTIMIZER_NO_DEFER_S3 ) {
5014
+ ewwwio_debug_message( 's3 upload deferred' );
5015
+ add_filter( 'as3cf_pre_update_attachment_metadata', '__return_true' );
5016
+ }
5017
+ global $ewwwio_media_background;
5018
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
5019
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
5020
+ }
5021
+ if ( ! is_object( $ewwwio_media_background ) ) {
5022
+ $ewwwio_media_background = new EWWWIO_Media_Background_Process();
5023
+ }
5024
+ ewwwio_debug_message( "backgrounding optimization for $id" );
5025
+ $ewwwio_media_background->push_to_queue( array(
5026
+ 'id' => $id,
5027
+ 'new' => $new_image,
5028
+ 'type' => $type,
5029
+ ) );
5030
+ $ewwwio_media_background->save()->dispatch();
5031
+ set_transient( 'ewwwio-background-in-progress-' . $id, true, 24 * HOUR_IN_SECONDS );
5032
+ if ( $log ) {
5033
+ ewww_image_optimizer_debug_log();
5034
+ }
5035
+ return $meta;
5036
+ }
5037
+ if ( $background_new ) {
5038
+ $new_image = true;
5039
+ }
5040
+ // Resize here if the user has used the filter to defer resizing, we have a new image OR resize existing is enabled, and imsanity isn't enabled with a max size.
5041
+ if ( apply_filters( 'ewww_image_optimizer_defer_resizing', false ) && ( ! empty( $new_image ) || ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_existing' ) ) && ! function_exists( 'imsanity_get_max_width_height' ) ) {
5042
+ $new_dimensions = ewww_image_optimizer_resize_upload( $file_path );
5043
+ if ( is_array( $new_dimensions ) ) {
5044
+ $meta['width'] = $new_dimensions[0];
5045
+ $meta['height'] = $new_dimensions[1];
5046
+ }
5047
+ }
5048
+ // This gets a bit long, so here goes:
5049
+ // we run in parallel if we didn't detect breakage (test_parallel_opt), and there are enough resizes to make it worthwhile (or if the API is enabled).
5050
+ if ( ewww_image_optimizer_test_parallel_opt( $type, $id ) && isset( $meta['sizes'] ) && ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) || count( $meta['sizes'] ) > 5 ) ) {
5051
+ ewwwio_debug_message( 'running in parallel' );
5052
+ $parallel_opt = true;
5053
+ } else {
5054
+ ewwwio_debug_message( 'running in sequence' );
5055
+ $parallel_opt = false;
5056
+ }
5057
+ $parallel_sizes = array();
5058
+ if ( $parallel_opt ) {
5059
+ add_filter( 'http_headers_useragent', 'ewww_image_optimizer_cloud_useragent', PHP_INT_MAX );
5060
+ $parallel_sizes['full'] = $file_path;
5061
+ global $ewwwio_async_optimize_media;
5062
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
5063
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
5064
+ }
5065
+ if ( ! is_object( $ewwwio_async_optimize_media ) ) {
5066
+ $ewwwio_async_optimize_media = new EWWWIO_Async_Request();
5067
+ }
5068
+ } else {
5069
+ $ewww_image = new EWWW_Image( $id, 'media', $file_path );
5070
+ $ewww_image->resize = 'full';
5071
+ list( $file, $msg, $conv, $original ) = ewww_image_optimizer( $file_path, $gallery_type, false, $new_image, true );
5072
+
5073
+ if ( false === $file ) {
5074
+ return $meta;
5075
+ }
5076
+ // If the file was converted.
5077
+ if ( false !== $conv ) {
5078
+ if ( $conv ) {
5079
+ $ewww_image->increment = $conv;
5080
+ }
5081
+ $ewww_image->file = $file;
5082
+ $ewww_image->converted = $original;
5083
+ $meta['file'] = trailingslashit( dirname( $meta['file'] ) ) . basename( $file );
5084
+ $ewww_image->update_converted_attachment( $meta );
5085
+ $meta = $ewww_image->convert_sizes( $meta );
5086
+ ewwwio_debug_message( 'image was converted' );
5087
+ } else {
5088
+ remove_filter( 'wp_update_attachment_metadata', 'ewww_image_optimizer_update_attachment', 10 );
5089
+ }
5090
+ ewww_image_optimizer_hidpi_optimize( $file );
5091
+ }
5092
+ // Resized versions, so we can continue.
5093
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
5094
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes_opt' );
5095
+ ewwwio_debug_message( 'processing resizes' );
5096
+ // Meta sizes don't contain a path, so we calculate one.
5097
+ if ( 6 === $gallery_type ) {
5098
+ $base_ims_dir = trailingslashit( dirname( $file_path ) ) . '_resized/';
5099
+ }
5100
+ $base_dir = trailingslashit( dirname( $file_path ) );
5101
+ // Process each resized version.
5102
+ $processed = array();
5103
+ foreach ( $meta['sizes'] as $size => $data ) {
5104
+ ewwwio_debug_message( "processing size: $size" );
5105
+ if ( strpos( $size, 'webp' ) === 0 ) {
5106
+ continue;
5107
+ }
5108
+ if ( ! empty( $disabled_sizes[ $size ] ) ) {
5109
+ continue;
5110
+ }
5111
+ if ( ! empty( $disabled_sizes['pdf-full'] ) && 'full' == $size ) {
5112
+ continue;
5113
+ }
5114
+ if ( empty( $data['file'] ) ) {
5115
+ continue;
5116
+ }
5117
+ if ( 6 === $gallery_type ) {
5118
+ // We reset base_dir, because base_dir potentially gets overwritten with base_ims_dir.
5119
+ $base_dir = trailingslashit( dirname( $file_path ) );
5120
+ $image_path = $base_dir . $data['file'];
5121
+ $ims_path = $base_ims_dir . $data['file'];
5122
+ if ( is_file( $ims_path ) ) {
5123
+ ewwwio_debug_message( 'ims resize already exists, wahoo' );
5124
+ ewwwio_debug_message( "ims path: $ims_path" );
5125
+ $image_size = ewww_image_optimizer_filesize( $ims_path );
5126
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $image_path );
5127
+ if ( is_array( $already_optimized ) ) {
5128
+ ewwwio_debug_message( "updating existing record, path: $ims_path, size: " . $image_size );
5129
+ // Store info on the current image for future reference.
5130
+ $ewwwdb->update( $ewwwdb->ewwwio_images,
5131
+ array(
5132
+ 'path' => ewww_image_optimizer_relative_path_remove( $ims_path ),
5133
+ ),
5134
+ array(
5135
+ 'id' => $already_optimized['id'],
5136
+ ));
5137
+ }
5138
+ $base_dir = $base_ims_dir;
5139
+ }
5140
+ }
5141
+ // Check through all the sizes we've processed so far.
5142
+ foreach ( $processed as $proc => $scan ) {
5143
+ // If a previous resize had identical dimensions.
5144
+ if ( $scan['height'] == $data['height'] && $scan['width'] == $data['width'] ) {
5145
+ // We found a duplicate resize, so...
5146
+ // Point this resize at the same image as the previous one.
5147
+ $meta['sizes'][ $size ]['file'] = $meta['sizes'][ $proc ]['file'];
5148
+ continue( 2 );
5149
+ }
5150
+ }
5151
+ // If this is a unique size.
5152
+ $resize_path = $base_dir . $data['file'];
5153
+ if ( 'application/pdf' == $type && 'full' == $size ) {
5154
+ $size = 'pdf-full';
5155
+ ewwwio_debug_message( 'processing full size pdf preview' );
5156
+ }
5157
+ // Run the optimization and store the results.
5158
+ if ( $parallel_opt && is_file( $resize_path ) ) {
5159
+ $parallel_sizes[ $size ] = $resize_path;
5160
+ } else {
5161
+ $ewww_image = new EWWW_Image( $id, 'media', $resize_path );
5162
+ $ewww_image->resize = $size;
5163
+ list( $optimized_file, $results, $resize_conv, $original ) = ewww_image_optimizer( $resize_path );
5164
+ }
5165
+ // Optimize retina images, if they exist.
5166
+ if ( function_exists( 'wr2x_get_retina' ) ) {
5167
+ $retina_path = wr2x_get_retina( $resize_path );
5168
+ } else {
5169
+ $retina_path = false;
5170
+ }
5171
+ if ( $retina_path && is_file( $retina_path ) ) {
5172
+ if ( $parallel_opt ) {
5173
+ $async_path = str_replace( $upload_path, '', $retina_path );
5174
+ $ewwwio_async_optimize_media->data(
5175
+ array(
5176
+ 'ewwwio_id' => $id,
5177
+ 'ewwwio_path' => $async_path,
5178
+ 'ewwwio_size' => '',
5179
+ 'ewww_force' => $force,
5180
+ )
5181
+ )->dispatch();
5182
+ } else {
5183
+ $ewww_image = new EWWW_Image( $id, 'media', $retina_path );
5184
+ $ewww_image->resize = $size . '-retina';
5185
+ ewww_image_optimizer( $retina_path );
5186
+ }
5187
+ } elseif ( ! $parallel_opt ) {
5188
+ ewww_image_optimizer_hidpi_optimize( $resize_path );
5189
+ }
5190
+ // Store info on the sizes we've processed, so we can check the list for duplicate sizes.
5191
+ $processed[ $size ]['width'] = $data['width'];
5192
+ $processed[ $size ]['height'] = $data['height'];
5193
+ } // End foreach().
5194
+ } // End if().
5195
+
5196
+ // Process size from a custom theme.
5197
+ if ( isset( $meta['image_meta']['resized_images'] ) && ewww_image_optimizer_iterable( $meta['image_meta']['resized_images'] ) ) {
5198
+ $imagemeta_resize_pathinfo = pathinfo( $file_path );
5199
+ $imagemeta_resize_path = '';
5200
+ foreach ( $meta['image_meta']['resized_images'] as $imagemeta_resize ) {
5201
+ $imagemeta_resize_path = $imagemeta_resize_pathinfo['dirname'] . '/' . $imagemeta_resize_pathinfo['filename'] . '-' . $imagemeta_resize . '.' . $imagemeta_resize_pathinfo['extension'];
5202
+ if ( $parallel_opt && is_file( $imagemeta_resize_path ) ) {
5203
+ $async_path = str_replace( $upload_path, '', $imagemeta_resize_path );
5204
+ $ewwwio_async_optimize_media->data(
5205
+ array(
5206
+ 'ewwwio_id' => $id,
5207
+ 'ewwwio_path' => $async_path,
5208
+ 'ewwwio_size' => '',
5209
+ 'ewww_force' => $force,
5210
+ )
5211
+ )->dispatch();
5212
+ } else {
5213
+ $ewww_image = new EWWW_Image( $id, 'media', $imagemeta_resize_path );
5214
+ ewww_image_optimizer( $imagemeta_resize_path );
5215
+ }
5216
+ }
5217
+ }
5218
+
5219
+ // And another custom theme.
5220
+ if ( isset( $meta['custom_sizes'] ) && ewww_image_optimizer_iterable( $meta['custom_sizes'] ) ) {
5221
+ $custom_sizes_pathinfo = pathinfo( $file_path );
5222
+ $custom_size_path = '';
5223
+ foreach ( $meta['custom_sizes'] as $custom_size ) {
5224
+ $custom_size_path = $custom_sizes_pathinfo['dirname'] . '/' . $custom_size['file'];
5225
+ if ( $parallel_opt && file_exists( $custom_size_path ) ) {
5226
+ $async_path = str_replace( $upload_path, '', $custom_size_path );
5227
+ $ewwwio_async_optimize_media->data(
5228
+ array(
5229
+ 'ewwwio_id' => $id,
5230
+ 'ewwwio_path' => $async_path,
5231
+ 'ewwwio_size' => '',
5232
+ 'ewww_force' => $force,
5233
+ )
5234
+ )->dispatch();
5235
+ } else {
5236
+ $ewww_image = new EWWW_Image( $id, 'media', $custom_size_path );
5237
+ ewww_image_optimizer( $custom_size_path );
5238
+ }
5239
+ }
5240
+ }
5241
+
5242
+ if ( $parallel_opt && count( $parallel_sizes ) > 0 ) {
5243
+ $max_threads = (int) apply_filters( 'ewww_image_optimizer_max_parallel_threads', 5 );
5244
+ $processing = true;
5245
+ $timer = (int) apply_filters( 'ewww_image_optimizer_background_timer_init', 1 );
5246
+ $increment = (int) apply_filters( 'ewww_image_optimizer_background_timer_increment', 1 );
5247
+ $timer_max = (int) apply_filters( 'ewww_image_optimizer_background_timer_max', 20 );
5248
+ $processing_sizes = array();
5249
+ if ( ! empty( $_REQUEST['ewww_force'] ) ) {
5250
+ $force = true;
5251
+ } else {
5252
+ $force = false;
5253
+ }
5254
+ global $ewwwio_async_optimize_media;
5255
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
5256
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
5257
+ }
5258
+ if ( ! is_object( $ewwwio_async_optimize_media ) ) {
5259
+ $ewwwio_async_optimize_media = new EWWWIO_Async_Request();
5260
+ }
5261
+ while ( $parallel_opt && count( $parallel_sizes ) > 0 ) {
5262
+ $threads = $max_threads;
5263
+ ewwwio_debug_message( 'sizes left to queue: ' . count( $parallel_sizes ) );
5264
+ // Phase 1, add $max_threads items to the queue and dispatch.
5265
+ foreach ( $parallel_sizes as $size => $filename ) {
5266
+ if ( $threads < 1 ) {
5267
+ continue;
5268
+ }
5269
+ if ( ! file_exists( $filename ) ) {
5270
+ unset( $parallel_sizes[ $size ] );
5271
+ continue;
5272
+ }
5273
+ ewwwio_debug_message( "queueing size $size - $filename" );
5274
+ $processing_sizes[ $size ] = $filename;
5275
+ unset( $parallel_sizes[ $size ] );
5276
+ touch( $filename . '.processing' );
5277
+ $async_path = str_replace( $upload_path, '', $filename );
5278
+ ewwwio_debug_message( "sending off $async_path in folder $upload_path" );
5279
+ $ewwwio_async_optimize_media->data(
5280
+ array(
5281
+ 'ewwwio_id' => $id,
5282
+ 'ewwwio_path' => $async_path,
5283
+ 'ewwwio_size' => $size,
5284
+ 'ewww_force' => $force,
5285
+ )
5286
+ )->dispatch();
5287
+ $threads--;
5288
+ ewwwio_debug_message( 'sizes left to queue: ' . count( $parallel_sizes ) );
5289
+ $processing = true;
5290
+ }
5291
+ // In phase 2, we start checking to see what sizes are done, until they all finish.
5292
+ while ( $parallel_opt && $processing ) {
5293
+ $processing = false;
5294
+ foreach ( $processing_sizes as $size => $filename ) {
5295
+ if ( is_file( $filename . '.processing' ) ) {
5296
+ ewwwio_debug_message( "still processing $size" );
5297
+ $processing = true;
5298
+ continue;
5299
+ }
5300
+ $image = ewww_image_optimizer_find_already_optimized( $filename );
5301
+ unset( $processing_sizes[ $size ] );
5302
+ ewwwio_debug_message( "got results for $size size" );
5303
+ }
5304
+ if ( $processing ) {
5305
+ ewwwio_debug_message( "sleeping for $timer seconds" );
5306
+ sleep( $timer );
5307
+ $timer += $increment;
5308
+ clearstatcache();
5309
+ }
5310
+ if ( $timer > $timer_max ) {
5311
+ break;
5312
+ }
5313
+ if ( $log ) {
5314
+ ewww_image_optimizer_debug_log();
5315
+ }
5316
+ }
5317
+ if ( $timer > $timer_max ) {
5318
+ foreach ( $processing_sizes as $filename ) {
5319
+ if ( is_file( $filename . '.processing' ) ) {
5320
+ unlink( $filename . '.processing' );
5321
+ }
5322
+ }
5323
+ $meta['processing'] = 1;
5324
+ if ( $log ) {
5325
+ ewww_image_optimizer_debug_log();
5326
+ }
5327
+ return $meta;
5328
+ }
5329
+ if ( $log ) {
5330
+ ewww_image_optimizer_debug_log();
5331
+ }
5332
+ } // End while().
5333
+ } // End if().
5334
+ unset( $meta['processing'] );
5335
+
5336
+ global $ewww_attachment;
5337
+ $ewww_attachment['id'] = $id;
5338
+ $ewww_attachment['meta'] = $meta;
5339
+ add_filter( 'w3tc_cdn_update_attachment_metadata', 'ewww_image_optimizer_w3tc_update_files' );
5340
+
5341
+ // In case we used parallel opt, $file might not be set.
5342
+ if ( empty( $file ) ) {
5343
+ $file = $file_path;
5344
+ }
5345
+ $fullsize_opt_size = ewww_image_optimizer_filesize( $file );
5346
+ if ( $fullsize_opt_size && $fullsize_opt_size < $fullsize_size && class_exists( 'Amazon_S3_And_CloudFront' ) ) {
5347
+ global $as3cf;
5348
+ if ( method_exists( $as3cf, 'wp_update_attachment_metadata' ) ) {
5349
+ ewwwio_debug_message( 'deferring to normal S3 hook' );
5350
+ } elseif ( method_exists( $as3cf, 'wp_generate_attachment_metadata' ) ) {
5351
+ $as3cf->wp_generate_attachment_metadata( $meta, $id );
5352
+ ewwwio_debug_message( 'uploading to Amazon S3' );
5353
+ }
5354
+ }
5355
+ if ( $fullsize_opt_size && $fullsize_opt_size < $fullsize_size && class_exists( 'DreamSpeed_Services' ) ) {
5356
+ global $dreamspeed;
5357
+ $dreamspeed->wp_generate_attachment_metadata( $meta, $id );
5358
+ ewwwio_debug_message( 'uploading to Dreamspeed' );
5359
+ }
5360
+ if ( class_exists( 'Cloudinary' ) && Cloudinary::config_get( 'api_secret' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_cloudinary' ) && ! empty( $new_image ) ) {
5361
+ try {
5362
+ $result = CloudinaryUploader::upload( $file, array(
5363
+ 'use_filename' => true,
5364
+ ) );
5365
+ } catch ( Exception $e ) {
5366
+ $error = $e->getMessage();
5367
+ }
5368
+ if ( ! empty( $error ) ) {
5369
+ ewwwio_debug_message( "Cloudinary error: $error" );
5370
+ } else {
5371
+ ewwwio_debug_message( 'successfully uploaded to Cloudinary' );
5372
+ // Register the attachment in the database as a cloudinary attachment.
5373
+ $old_url = wp_get_attachment_url( $id );
5374
+ wp_update_post( array(
5375
+ 'ID' => $id,
5376
+ 'guid' => $result['url'],
5377
+ ) );
5378
+ update_attached_file( $id, $result['url'] );
5379
+ $meta['cloudinary'] = true;
5380
+ $errors = array();
5381
+ // Update the image location for the attachment.
5382
+ CloudinaryPlugin::update_image_src_all( $id, $result, $old_url, $result['url'], true, $errors );
5383
+ if ( count( $errors ) > 0 ) {
5384
+ ewwwio_debug_message( 'Cannot migrate the following posts:' );
5385
+ foreach ( $errors as $error ) {
5386
+ ewwwio_debug_message( $error );
5387
+ }
5388
+ }
5389
+ }
5390
+ }
5391
+ if ( $log ) {
5392
+ ewww_image_optimizer_debug_log();
5393
+ }
5394
+ ewwwio_memory( __FUNCTION__ );
5395
+ // Send back the updated metadata.
5396
+ return $meta;
5397
+ }
5398
+
5399
+ /**
5400
+ * Check to see if Shield's location lock option is enabled.
5401
+ *
5402
+ * @return bool True if the IP location lock is enabled.
5403
+ */
5404
+ function ewww_image_optimizer_detect_wpsf_location_lock() {
5405
+ if ( class_exists( 'ICWP_Wordpress_Simple_Firewall' ) ) {
5406
+ $shield_user_man = ewww_image_optimizer_get_option( 'icwp_wpsf_user_management_options' );
5407
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
5408
+ ewwwio_debug_message( print_r( $shield_user_man, true ) );
5409
+ }
5410
+ if ( 'Y' == $shield_user_man['session_lock_location'] ) {
5411
+ return true;
5412
+ }
5413
+ }
5414
+ return false;
5415
+ }
5416
+
5417
+ /**
5418
+ * Update the attachment's meta data after being converted.
5419
+ *
5420
+ * @global object $wpdb
5421
+ *
5422
+ * @param array $meta Attachment metadata.
5423
+ * @param int $id Attachment ID number.
5424
+ */
5425
+ function ewww_image_optimizer_update_attachment( $meta, $id ) {
5426
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5427
+ global $wpdb;
5428
+ // Update the file location in the post metadata based on the new path stored in the attachment metadata.
5429
+ update_attached_file( $id, $meta['file'] );
5430
+ $guid = wp_get_attachment_url( $id );
5431
+ if ( empty( $meta['real_orig_file'] ) ) {
5432
+ $old_guid = dirname( $guid ) . '/' . basename( $meta['orig_file'] );
5433
+ } else {
5434
+ $old_guid = dirname( $guid ) . '/' . basename( $meta['real_orig_file'] );
5435
+ unset( $meta['real_orig_file'] );
5436
+ }
5437
+ // Construct the new guid based on the filename from the attachment metadata.
5438
+ ewwwio_debug_message( "old guid: $old_guid" );
5439
+ ewwwio_debug_message( "new guid: $guid" );
5440
+ if ( substr( $old_guid, -1 ) == '/' || substr( $guid, -1 ) == '/' ) {
5441
+ ewwwio_debug_message( 'could not obtain full url for current and previous image, bailing' );
5442
+ return $meta;
5443
+ }
5444
+ // Retrieve any posts that link the image.
5445
+ $esql = $wpdb->prepare( "SELECT ID, post_content FROM $wpdb->posts WHERE post_content LIKE '%%%s%%'", $old_guid );
5446
+ ewwwio_debug_message( "using query: $esql" );
5447
+ // While there are posts to process.
5448
+ $rows = $wpdb->get_results( $esql, ARRAY_A ); // WPCS: unprepared SQL ok.
5449
+ if ( ewww_image_optimizer_iterable( $rows ) ) {
5450
+ foreach ( $rows as $row ) {
5451
+ // Replace all occurences of the old guid with the new guid.
5452
+ $post_content = str_replace( $old_guid, $guid, $row['post_content'] );
5453
+ ewwwio_debug_message( "replacing $old_guid with $guid in post " . $row['ID'] );
5454
+ // Send the updated content back to the database.
5455
+ $wpdb->update(
5456
+ $wpdb->posts,
5457
+ array(
5458
+ 'post_content' => $post_content,
5459
+ ),
5460
+ array(
5461
+ 'ID' => $row['ID'],
5462
+ )
5463
+ );
5464
+ }
5465
+ }
5466
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
5467
+ // For each resized version.
5468
+ foreach ( $meta['sizes'] as $size => $data ) {
5469
+ // If the resize was converted.
5470
+ if ( isset( $data['converted'] ) ) {
5471
+ // Generate the url for the old image.
5472
+ if ( empty( $data['real_orig_file'] ) ) {
5473
+ $old_sguid = dirname( $old_guid ) . '/' . basename( $data['orig_file'] );
5474
+ } else {
5475
+ $old_sguid = dirname( $old_guid ) . '/' . basename( $data['real_orig_file'] );
5476
+ unset( $meta['sizes'][ $size ]['real_orig_file'] );
5477
+ }
5478
+ ewwwio_debug_message( "processing: $size" );
5479
+ ewwwio_debug_message( "old sguid: $old_sguid" );
5480
+ // Generate the url for the new image.
5481
+ $sguid = dirname( $old_guid ) . '/' . basename( $data['file'] );
5482
+ ewwwio_debug_message( "new sguid: $sguid" );
5483
+ if ( substr( $old_sguid, -1 ) == '/' || substr( $sguid, -1 ) == '/' ) {
5484
+ ewwwio_debug_message( 'could not obtain full url for current and previous resized image, bailing' );
5485
+ continue;
5486
+ }
5487
+ // Retrieve any posts that link the resize.
5488
+ $rows = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_content FROM $wpdb->posts WHERE post_content LIKE '%%%s%%'", $old_sguid ), ARRAY_A );
5489
+ // While there are posts to process.
5490
+ if ( ewww_image_optimizer_iterable( $rows ) ) {
5491
+ foreach ( $rows as $row ) {
5492
+ // Replace all occurences of the old guid with the new guid.
5493
+ $post_content = str_replace( $old_sguid, $sguid, $row['post_content'] );
5494
+ ewwwio_debug_message( "replacing $old_sguid with $sguid in post " . $row['ID'] );
5495
+ // Send the updated content back to the database.
5496
+ $wpdb->update(
5497
+ $wpdb->posts,
5498
+ array(
5499
+ 'post_content' => $post_content,
5500
+ ),
5501
+ array(
5502
+ 'ID' => $row['ID'],
5503
+ )
5504
+ );
5505
+ }
5506
+ }
5507
+ } // End if().
5508
+ } // End foreach().
5509
+ } // End if().
5510
+ if ( preg_match( '/.jpg$/i', basename( $meta['file'] ) ) ) {
5511
+ $mime = 'image/jpeg';
5512
+ }
5513
+ if ( preg_match( '/.png$/i', basename( $meta['file'] ) ) ) {
5514
+ $mime = 'image/png';
5515
+ }
5516
+ if ( preg_match( '/.gif$/i', basename( $meta['file'] ) ) ) {
5517
+ $mime = 'image/gif';
5518
+ }
5519
+ // Update the attachment post with the new mimetype and id.
5520
+ wp_update_post( array(
5521
+ 'ID' => $id,
5522
+ 'post_mime_type' => $mime,
5523
+ )
5524
+ );
5525
+ ewww_image_optimizer_debug_log();
5526
+ ewwwio_memory( __FUNCTION__ );
5527
+ return $meta;
5528
+ }
5529
+
5530
+ /**
5531
+ * Retrieves the path of an attachment via the $id and the $meta.
5532
+ *
5533
+ * @param array $meta The attachment metadata.
5534
+ * @param int $id The attachment ID number.
5535
+ * @param string $file Optional. Path relative to the uploads folder. Default ''.
5536
+ * @param bool $refresh_cache Optional. True to flush cache prior to fetching path. Default true.
5537
+ * @return array {
5538
+ * Information about the file.
5539
+ *
5540
+ * @type string The full path to the image.
5541
+ * @type string The path to the uploads folder.
5542
+ * }
5543
+ */
5544
+ function ewww_image_optimizer_attachment_path( $meta, $id, $file = '', $refresh_cache = true ) {
5545
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5546
+
5547
+ // Retrieve the location of the wordpress upload folder.
5548
+ $upload_dir = wp_upload_dir( null, false, $refresh_cache );
5549
+ $upload_path = trailingslashit( $upload_dir['basedir'] );
5550
+ if ( ! $file ) {
5551
+ $file = get_post_meta( $id, '_wp_attached_file', true );
5552
+ } else {
5553
+ ewwwio_debug_message( 'using prefetched _wp_attached_file' );
5554
+ }
5555
+ $file_path = ( 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) ? $upload_path . $file : $file );
5556
+ $filtered_file_path = apply_filters( 'get_attached_file', $file_path, $id );
5557
+ ewwwio_debug_message( "WP (filtered) thinks the file is at: $filtered_file_path" );
5558
+ if ( ( strpos( $filtered_file_path, 's3' ) === false || in_array( 's3', stream_get_wrappers() ) ) && is_file( $filtered_file_path ) ) {
5559
+ return array( str_replace( '//_imsgalleries/', '/_imsgalleries/', $filtered_file_path ), $upload_path );
5560
+ }
5561
+ ewwwio_debug_message( "WP (unfiltered) thinks the file is at: $file_path" );
5562
+ if ( ( strpos( $file_path, 's3' ) === false || in_array( 's3', stream_get_wrappers() ) ) && is_file( $file_path ) ) {
5563
+ return array( str_replace( '//_imsgalleries/', '/_imsgalleries/', $file_path ), $upload_path );
5564
+ }
5565
+ if ( 'ims_image' == get_post_type( $id ) && is_array( $meta ) && ! empty( $meta['file'] ) ) {
5566
+ ewwwio_debug_message( "finding path for IMS image: $id " );
5567
+ if ( is_dir( $file_path ) && is_file( $file_path . $meta['file'] ) ) {
5568
+ // Generate the absolute path.
5569
+ $file_path = $file_path . $meta['file'];
5570
+ $upload_path = ewww_image_optimizer_upload_path( $file_path, $upload_path );
5571
+ ewwwio_debug_message( "found path for IMS image: $file_path" );
5572
+ } elseif ( is_file( $meta['file'] ) ) {
5573
+ $file_path = $meta['file'];
5574
+ $upload_path = ewww_image_optimizer_upload_path( $file_path, $upload_path );
5575
+ ewwwio_debug_message( "found path for IMS image: $file_path" );
5576
+ } else {
5577
+ $upload_path = trailingslashit( WP_CONTENT_DIR );
5578
+ $file_path = $upload_path . ltrim( $meta['file'], '/' );
5579
+ ewwwio_debug_message( "checking path for IMS image: $file_path" );
5580
+ if ( ! file_exists( $file_path ) ) {
5581
+ $file_path = '';
5582
+ }
5583
+ }
5584
+ return array( $file_path, $upload_path );
5585
+ }
5586
+ if ( is_array( $meta ) && ! empty( $meta['file'] ) ) {
5587
+ $file_path = $meta['file'];
5588
+ if ( strpos( $file_path, 's3' ) === 0 && ! in_array( 's3', stream_get_wrappers() ) ) {
5589
+ return array( '', $upload_path );
5590
+ }
5591
+ ewwwio_debug_message( "looking for file at $file_path" );
5592
+ if ( is_file( $file_path ) ) {
5593
+ return array( $file_path, $upload_path );
5594
+ }
5595
+ $file_path = trailingslashit( $upload_path ) . $file_path;
5596
+ ewwwio_debug_message( "that did not work, try it with the upload_dir: $file_path" );
5597
+ if ( is_file( $file_path ) ) {
5598
+ return array( $file_path, $upload_path );
5599
+ }
5600
+ $upload_path = trailingslashit( WP_CONTENT_DIR ) . 'uploads/';
5601
+ $file_path = $upload_path . $meta['file'];
5602
+ ewwwio_debug_message( "one last shot, using the wp-content/ constant: $file_path" );
5603
+ if ( is_file( $file_path ) ) {
5604
+ return array( $file_path, $upload_path );
5605
+ }
5606
+ }
5607
+ ewwwio_memory( __FUNCTION__ );
5608
+ return array( '', $upload_path );
5609
+ }
5610
+
5611
+ /**
5612
+ * Removes parent folders to create a relative path.
5613
+ *
5614
+ * Replaces either ABSPATH, WP_CONTENT_DIR, or EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER with the literal
5615
+ * string name of the applicable constant. For example: /var/www/wp-content/uploads/test.jpg becomes
5616
+ * ABSPATHwp-content/uploads/test.jpg.
5617
+ *
5618
+ * @param string $file The filename to mangle.
5619
+ * @return string The filename with parent folders replaced by a constant name.
5620
+ */
5621
+ function ewww_image_optimizer_relative_path_remove( $file ) {
5622
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_RELATIVE' ) || ! EWWW_IMAGE_OPTIMIZER_RELATIVE ) {
5623
+ return $file;
5624
+ }
5625
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5626
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER' ) && EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER && strpos( $file, EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER ) === 0 ) {
5627
+ ewwwio_debug_message( "removing custom relative folder from $file" );
5628
+ return str_replace( EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER, 'EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER', $file );
5629
+ }
5630
+ if ( strpos( $file, ABSPATH ) === 0 ) {
5631
+ ewwwio_debug_message( "removing ABSPATH from $file" );
5632
+ return str_replace( ABSPATH, 'ABSPATH', $file );
5633
+ }
5634
+ if ( defined( 'WP_CONTENT_DIR' ) && WP_CONTENT_DIR && strpos( $file, WP_CONTENT_DIR ) === 0 ) {
5635
+ ewwwio_debug_message( "removing WP_CONTENT_DIR from $file" );
5636
+ return str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $file );
5637
+ }
5638
+ return $file;
5639
+ }
5640
+
5641
+ /**
5642
+ * Replaces constant names with their actual values to recreate an absolute path.
5643
+ *
5644
+ * Replaces the literal strings 'ABSPATH', 'WP_CONTENT_DIR', or
5645
+ * 'EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER' with the actual value of the constant contained within
5646
+ * the file path.string name of the applicable constant.
5647
+ *
5648
+ * @param string $file The filename to parse.
5649
+ * @return string The full filename with parent folders reinserted.
5650
+ */
5651
+ function ewww_image_optimizer_relative_path_replace( $file ) {
5652
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_RELATIVE' ) ) {
5653
+ return $file;
5654
+ }
5655
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5656
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER' ) && EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER && strpos( $file, 'EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER' ) === 0 ) {
5657
+ ewwwio_debug_message( "replacing custom relative folder in $file" );
5658
+ return str_replace( 'EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER', EWWW_IMAGE_OPTIMIZER_RELATIVE_FOLDER, $file );
5659
+ }
5660
+ if ( strpos( $file, 'ABSPATH' ) === 0 ) {
5661
+ ewwwio_debug_message( "replacing ABSPATH in $file" );
5662
+ return str_replace( 'ABSPATH', ABSPATH, $file );
5663
+ }
5664
+ if ( defined( 'WP_CONTENT_DIR' ) && WP_CONTENT_DIR && strpos( $file, 'WP_CONTENT_DIR' ) === 0 ) {
5665
+ ewwwio_debug_message( "replacing WP_CONTENT_DIR in $file" );
5666
+ return str_replace( 'WP_CONTENT_DIR', WP_CONTENT_DIR, $file );
5667
+ }
5668
+ return $file;
5669
+ }
5670
+
5671
+ /**
5672
+ * Takes a file and upload folder, and makes sure that the file is within the folder.
5673
+ *
5674
+ * Used for path replacement with async/parallel processing, since security plugins can block
5675
+ * POSTing of full paths.
5676
+ *
5677
+ * @param string $file Name of the file.
5678
+ * @param string $upload_path Location of the upload directory.
5679
+ * @return string The upload path or an empty string if the file is outside the uploads folder.
5680
+ */
5681
+ function ewww_image_optimizer_upload_path( $file, $upload_path ) {
5682
+ if ( strpos( $file, $upload_path ) === 0 ) {
5683
+ return $upload_path;
5684
+ } else {
5685
+ return '';
5686
+ }
5687
+ }
5688
+
5689
+ /**
5690
+ * Takes a human-readable size, and generates an approximate byte-size.
5691
+ *
5692
+ * @param string $formatted A human-readable file size.
5693
+ * @return int The approximated filesize.
5694
+ */
5695
+ function ewww_image_optimizer_size_unformat( $formatted ) {
5696
+ $size_parts = explode( '&nbsp;', $formatted );
5697
+ switch ( $size_parts[1] ) {
5698
+ case 'B':
5699
+ return intval( $size_parts[0] );
5700
+ case 'kB':
5701
+ return intval( $size_parts[0] * 1024 );
5702
+ case 'MB':
5703
+ return intval( $size_parts[0] * 1048576 );
5704
+ case 'GB':
5705
+ return intval( $size_parts[0] * 1073741824 );
5706
+ case 'TB':
5707
+ return intval( $size_parts[0] * 1099511627776 );
5708
+ default:
5709
+ return 0;
5710
+ }
5711
+ }
5712
+
5713
+ /**
5714
+ * Generate a unique filename for a converted image.
5715
+ *
5716
+ * @param string $file The filename to test for uniqueness.
5717
+ * @param string $fileext An iterator to append to the base filename, starts empty usually.
5718
+ * @return array {
5719
+ * Filename information.
5720
+ *
5721
+ * @type string A unique filename for converting an image.
5722
+ * @type int|string The iterator used for uniqueness.
5723
+ * }
5724
+ */
5725
+ function ewww_image_optimizer_unique_filename( $file, $fileext ) {
5726
+ // Strip the file extension.
5727
+ $filename = preg_replace( '/\.\w+$/', '', $file );
5728
+ if ( ! is_file( $filename . $fileext ) ) {
5729
+ return array( $filename . $fileext, '' );
5730
+ }
5731
+ // Set the increment to 1 (but allow the user to override it).
5732
+ $filenum = apply_filters( 'ewww_image_optimizer_converted_filename_suffix', 1 );
5733
+ // But it must be only letters, numbers, or underscores.
5734
+ $filenum = ( preg_match( '/^[\w\d]*$/', $filenum ) ? $filenum : 1 );
5735
+ $suffix = ( ! empty( $filenum ) ? '-' . $filenum : '' );
5736
+ // While a file exists with the current increment.
5737
+ while ( file_exists( $filename . $suffix . $fileext ) ) {
5738
+ // Increment the increment...
5739
+ $filenum++;
5740
+ $suffix = '-' . $filenum;
5741
+ }
5742
+ // All done, let's reconstruct the filename.
5743
+ ewwwio_memory( __FUNCTION__ );
5744
+ return array( $filename . $suffix . $fileext, $filenum );
5745
+ }
5746
+
5747
+ /**
5748
+ * Get mimetype based on file extension instead of file contents when speed outweighs accuracy.
5749
+ *
5750
+ * @param string $path The name of the file.
5751
+ * @return string|bool The mime type based on the extension or false.
5752
+ */
5753
+ function ewww_image_optimizer_quick_mimetype( $path ) {
5754
+ $pathextension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
5755
+ switch ( $pathextension ) {
5756
+ case 'jpg':
5757
+ case 'jpeg':
5758
+ case 'jpe':
5759
+ return 'image/jpeg';
5760
+ case 'png':
5761
+ return 'image/png';
5762
+ case 'gif':
5763
+ return 'image/gif';
5764
+ case 'pdf':
5765
+ return 'application/pdf';
5766
+ default:
5767
+ return false;
5768
+ }
5769
+ }
5770
+
5771
+ /**
5772
+ * Check a PNG to see if it has transparency.
5773
+ *
5774
+ * @param string $filename The name of the PNG file.
5775
+ * @return bool True if transparency is found.
5776
+ */
5777
+ function ewww_image_optimizer_png_alpha( $filename ) {
5778
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5779
+ if ( ! is_file( $filename ) ) {
5780
+ return false;
5781
+ }
5782
+ // Determine what color type is stored in the file.
5783
+ $color_type = ord( file_get_contents( $filename, null, null, 25, 1 ) );
5784
+ ewwwio_debug_message( "color type: $color_type" );
5785
+ // If it is set to RGB alpha or Grayscale alpha.
5786
+ if ( 4 == $color_type || 6 == $color_type ) {
5787
+ ewwwio_debug_message( 'transparency found' );
5788
+ return true;
5789
+ } elseif ( 3 == $color_type && ewww_image_optimizer_gd_support() ) {
5790
+ $image = imagecreatefrompng( $filename );
5791
+ if ( imagecolortransparent( $image ) >= 0 ) {
5792
+ ewwwio_debug_message( 'transparency found' );
5793
+ return true;
5794
+ }
5795
+ list( $width, $height ) = getimagesize( $filename );
5796
+ ewwwio_debug_message( "image dimensions: $width x $height" );
5797
+ ewwwio_debug_message( 'preparing to scan image' );
5798
+ for ( $y = 0; $y < $height; $y++ ) {
5799
+ for ( $x = 0; $x < $width; $x++ ) {
5800
+ $color = imagecolorat( $image, $x, $y );
5801
+ $rgb = imagecolorsforindex( $image, $color );
5802
+ if ( $rgb['alpha'] > 0 ) {
5803
+ ewwwio_debug_message( 'transparency found' );
5804
+ return true;
5805
+ }
5806
+ }
5807
+ }
5808
+ }
5809
+ ewwwio_debug_message( 'no transparency' );
5810
+ ewwwio_memory( __FUNCTION__ );
5811
+ return false;
5812
+ }
5813
+
5814
+ /**
5815
+ * Check the submitted GIF to see if it is animated
5816
+ *
5817
+ * @param string $filename Name of the GIF to test for animation.
5818
+ * @return bool True if animation found.
5819
+ */
5820
+ function ewww_image_optimizer_is_animated( $filename ) {
5821
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5822
+ if ( ! is_file( $filename ) ) {
5823
+ return false;
5824
+ }
5825
+ // If we can't open the file in read-only buffered mode.
5826
+ $fh = fopen( $filename, 'rb' );
5827
+ if ( ! $fh ) {
5828
+ return false;
5829
+ }
5830
+ $count = 0;
5831
+ // We read through the file til we reach the end of the file, or we've found at least 2 frame headers.
5832
+ while ( ! feof( $fh ) && $count < 2 ) {
5833
+ $chunk = fread( $fh, 1024 * 100 ); // Read 100kb at a time.
5834
+ $count += preg_match_all( '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches );
5835
+ }
5836
+ fclose( $fh );
5837
+ ewwwio_debug_message( "scanned GIF and found $count frames" );
5838
+ ewwwio_memory( __FUNCTION__ );
5839
+ return $count > 1;
5840
+ }
5841
+
5842
+ /**
5843
+ * Count how many sizes are in the metadata, accounting for those with duplicate dimensions.
5844
+ *
5845
+ * @param array $sizes A list of resize information from an attachment.
5846
+ * @return int The number of sizes found.
5847
+ */
5848
+ function ewww_image_optimizer_resize_count( $sizes ) {
5849
+ if ( empty( $sizes ) || ! is_array( $sizes ) ) {
5850
+ return 0;
5851
+ }
5852
+ $size_count = 0;
5853
+ $processed = array();
5854
+ foreach ( $sizes as $size => $data ) {
5855
+ if ( strpos( $size, 'webp' ) === 0 ) {
5856
+ continue;
5857
+ }
5858
+ if ( empty( $data['file'] ) ) {
5859
+ continue;
5860
+ }
5861
+ // Check through all the sizes we've processed so far.
5862
+ foreach ( $processed as $proc => $scan ) {
5863
+ // If a previous resize had identical dimensions.
5864
+ if ( $scan['height'] == $data['height'] && $scan['width'] == $data['width'] ) {
5865
+ continue( 2 );
5866
+ }
5867
+ }
5868
+ // If this is a unique size.
5869
+ $size_count++;
5870
+ // Sore info on the sizes we've processed, so we can check the list for duplicate sizes.
5871
+ $processed[ $size ]['width'] = $data['width'];
5872
+ $processed[ $size ]['height'] = $data['height'];
5873
+ }
5874
+ return $size_count;
5875
+ }
5876
+
5877
+ /**
5878
+ * Add column header for optimizer results in the media library listing.
5879
+ *
5880
+ * @param array $defaults A list of columns in the media library.
5881
+ * @return array The new list of columns.
5882
+ */
5883
+ function ewww_image_optimizer_columns( $defaults ) {
5884
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5885
+ $defaults['ewww-image-optimizer'] = esc_html__( 'Image Optimizer', 'ewww-image-optimizer' );
5886
+ ewwwio_memory( __FUNCTION__ );
5887
+ return $defaults;
5888
+ }
5889
+
5890
+ /**
5891
+ * Print column data for optimizer results in the media library.
5892
+ *
5893
+ * @global object $wpdb
5894
+ *
5895
+ * @param string $column_name The name of the column being displayed.
5896
+ * @param int $id The attachment ID number.
5897
+ * @param array $meta Optional. The attachment metadata. Default null.
5898
+ * @param bool $return_output Optional. True if output should be returned instead of output.
5899
+ * @return string If $return_output, the data that would normally be output directly.
5900
+ */
5901
+ function ewww_image_optimizer_custom_column( $column_name, $id, $meta = null, $return_output = false ) {
5902
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
5903
+ // Once we get to the EWWW IO custom column.
5904
+ if ( 'ewww-image-optimizer' == $column_name ) {
5905
+ $output = '';
5906
+ if ( null == $meta ) {
5907
+ // Retrieve the metadata.
5908
+ $meta = wp_get_attachment_metadata( $id );
5909
+ }
5910
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) && ! $return_output && ewww_image_optimizer_function_exists( 'print_r' ) ) {
5911
+ $print_meta = print_r( $meta, true );
5912
+ $print_meta = preg_replace( array( '/ /', '/\n+/' ), array( '&nbsp;', '<br />' ), $print_meta );
5913
+ $output .= '<div style="background-color:#ffff99;font-size: 10px;padding: 10px;margin:-10px -10px 10px;line-height: 1.1em">' . $print_meta . '</div>';
5914
+ }
5915
+ $output .= "<div id='ewww-media-status-$id'>";
5916
+ $ewww_cdn = false;
5917
+ if ( is_array( $meta ) && ! empty( $meta['cloudinary'] ) ) {
5918
+ $output .= esc_html__( 'Cloudinary image', 'ewww-image-optimizer' ) . '</div>';
5919
+ if ( $return_output ) {
5920
+ return $output;
5921
+ }
5922
+ echo $output;
5923
+ return;
5924
+ }
5925
+ if ( is_array( $meta ) & class_exists( 'WindowsAzureStorageUtil' ) && ! empty( $meta['url'] ) ) {
5926
+ $output .= '<div>' . esc_html__( 'Azure Storage image', 'ewww-image-optimizer' ) . '</div>';
5927
+ $ewww_cdn = true;
5928
+ }
5929
+ if ( is_array( $meta ) && class_exists( 'Amazon_S3_And_CloudFront' ) && preg_match( '/^(http|s3)\w*:/', get_attached_file( $id ) ) ) {
5930
+ $output .= '<div>' . esc_html__( 'Amazon S3 image', 'ewww-image-optimizer' ) . '</div>';
5931
+ $ewww_cdn = true;
5932
+ }
5933
+ if ( is_array( $meta ) && class_exists( 'S3_Uploads' ) && preg_match( '/^(http|s3)\w*:/', get_attached_file( $id ) ) ) {
5934
+ $output .= '<div>' . esc_html__( 'Amazon S3 image', 'ewww-image-optimizer' ) . '</div>';
5935
+ $ewww_cdn = true;
5936
+ }
5937
+ list( $file_path, $upload_path ) = ewww_image_optimizer_attachment_path( $meta, $id );
5938
+ // If the file does not exist.
5939
+ if ( empty( $file_path ) && ! $ewww_cdn ) {
5940
+ $output .= esc_html__( 'Could not retrieve file path.', 'ewww-image-optimizer' ) . '</div>';
5941
+ ewww_image_optimizer_debug_log();
5942
+ if ( $return_output ) {
5943
+ return $output;
5944
+ }
5945
+ echo $output;
5946
+ return;
5947
+ }
5948
+ if ( is_array( $meta ) && ( ! empty( $meta['ewww_image_optimizer'] ) || ! empty( $meta['converted'] ) ) ) {
5949
+ $meta = ewww_image_optimizer_migrate_meta_to_db( $id, $meta );
5950
+ }
5951
+ $msg = '';
5952
+ $convert_desc = '';
5953
+ $convert_link = '';
5954
+ if ( $ewww_cdn ) {
5955
+ $type = get_post_mime_type( $id );
5956
+ } else {
5957
+ // Retrieve the mimetype of the attachment.
5958
+ $type = ewww_image_optimizer_mimetype( $file_path, 'i' );
5959
+ // Get a human readable filesize.
5960
+ $file_size = ewww_image_optimizer_size_format( filesize( $file_path ) );
5961
+ }
5962
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN' ) ) {
5963
+ ewww_image_optimizer_tool_init();
5964
+ ewww_image_optimizer_notice_utils( 'quiet' );
5965
+ }
5966
+ $skip = ewww_image_optimizer_skip_tools();
5967
+ // Run the appropriate code based on the mimetype.
5968
+ switch ( $type ) {
5969
+ case 'image/jpeg':
5970
+ // If jpegtran is missing and should not be skipped.
5971
+ if ( ! EWWW_IMAGE_OPTIMIZER_JPEGTRAN && ! $skip['jpegtran'] ) {
5972
+ $msg = '<div>' . sprintf(
5973
+ /* translators: %s: name of a tool like jpegtran */
5974
+ esc_html__( '%s is missing', 'ewww-image-optimizer' ),
5975
+ '<em>jpegtran</em>'
5976
+ ) . '</div>';
5977
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 0 ) {
5978
+ $msg = '<div>' . sprintf(
5979
+ /* translators: %s: JPG, PNG, GIF, or PDF */
5980
+ esc_html__( '%s compression disabled', 'ewww-image-optimizer' ),
5981
+ 'JPG'
5982
+ ) . '</div>';
5983
+ } else {
5984
+ $convert_link = esc_html__( 'JPG to PNG', 'ewww-image-optimizer' );
5985
+ $convert_desc = esc_attr__( 'WARNING: Removes metadata. Requires GD or ImageMagick. PNG is generally much better than JPG for logos and other images with a limited range of colors.', 'ewww-image-optimizer' );
5986
+ }
5987
+ break;
5988
+ case 'image/png':
5989
+ // If pngout and optipng are missing and should not be skipped.
5990
+ if ( ! EWWW_IMAGE_OPTIMIZER_PNGOUT && ! EWWW_IMAGE_OPTIMIZER_OPTIPNG && ! $skip['optipng'] && ! $skip['pngout'] ) {
5991
+ $msg = '<div>' . sprintf(
5992
+ /* translators: %s: name of a tool like jpegtran */
5993
+ esc_html__( '%s is missing', 'ewww-image-optimizer' ),
5994
+ '<em>optipng/pngout</em>'
5995
+ ) . '</div>';
5996
+ } else {
5997
+ $convert_link = esc_html__( 'PNG to JPG', 'ewww-image-optimizer' );
5998
+ $convert_desc = esc_attr__( 'WARNING: This is not a lossless conversion and requires GD or ImageMagick. JPG is much better than PNG for photographic use because it compresses the image and discards data. Transparent images will only be converted if a background color has been set.', 'ewww-image-optimizer' );
5999
+ }
6000
+ break;
6001
+ case 'image/gif':
6002
+ // If gifsicle is missing and should not be skipped.
6003
+ if ( ! EWWW_IMAGE_OPTIMIZER_GIFSICLE && ! $skip['gifsicle'] ) {
6004
+ $msg = '<div>' . sprintf(
6005
+ /* translators: %s: name of a tool like jpegtran */
6006
+ esc_html__( '%s is missing', 'ewww-image-optimizer' ),
6007
+ '<em>gifsicle</em>'
6008
+ ) . '</div>';
6009
+ } else {
6010
+ $convert_link = esc_html__( 'GIF to PNG', 'ewww-image-optimizer' );
6011
+ $convert_desc = esc_attr__( 'PNG is generally better than GIF, but does not support animation. Animated images will not be converted.', 'ewww-image-optimizer' );
6012
+ }
6013
+ break;
6014
+ case 'application/pdf':
6015
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) == 0 ) {
6016
+ $msg = '<div>' . sprintf(
6017
+ /* translators: %s: JPG, PNG, GIF, or PDF */
6018
+ esc_html__( '%s compression disabled', 'ewww-image-optimizer' ),
6019
+ 'PDF'
6020
+ ) . '</div>';
6021
+ } else {
6022
+ $convert_desc = '';
6023
+ }
6024
+ break;
6025
+ default:
6026
+ // Not a supported mimetype.
6027
+ $msg = '<div>' . esc_html__( 'Unsupported file type', 'ewww-image-optimizer' ) . '</div>';
6028
+ ewww_image_optimizer_debug_log();
6029
+ } // End switch().
6030
+ if ( ! empty( $msg ) ) {
6031
+ if ( $return_output ) {
6032
+ return $msg;
6033
+ }
6034
+ echo $msg;
6035
+ return;
6036
+ }
6037
+ $ewww_manual_nonce = wp_create_nonce( 'ewww-manual' );
6038
+ global $wpdb;
6039
+ $in_progress = false;
6040
+ $migrated = false;
6041
+ $optimized_images = false;
6042
+ $backup_available = false;
6043
+ $file_parts = pathinfo( $file_path );
6044
+ $basename = $file_parts['filename'];
6045
+ // If necessary, use get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); to only use basename for edited attachments, but that requires extra queries, so kiss for now.
6046
+ if ( $ewww_cdn ) {
6047
+ if ( get_transient( 'ewwwio-background-in-progress-' . $id ) ) {
6048
+ $output .= '<div>' . esc_html__( 'In Progress', 'ewww-image-optimizer' ) . '</div>';
6049
+ $in_progress = true;
6050
+ }
6051
+ if ( ! $in_progress ) {
6052
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'media' AND image_size <> 0 AND path LIKE '%%%s%%' ORDER BY orig_size DESC", $id, $basename ), ARRAY_A );
6053
+ if ( ! $optimized_images ) {
6054
+ // Attempt migration, but only if the original image is in the db, $migrated will be metadata on success, false on failure.
6055
+ $migrated = ewww_image_optimizer_migrate_meta_to_db( $id, $meta, true );
6056
+ }
6057
+ if ( $migrated ) {
6058
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'media' AND image_size <> 0 AND path LIKE '%%%s%%' ORDER BY orig_size DESC", $id, $basename ), ARRAY_A );
6059
+ }
6060
+ }
6061
+ // If optimizer data exists in the db.
6062
+ if ( ! empty( $optimized_images ) ) {
6063
+ list( $detail_output, $converted, $backup_available ) = ewww_image_optimizer_custom_column_results( $id, $optimized_images );
6064
+ $output .= $detail_output;
6065
+ // Output the optimizer actions.
6066
+ if ( current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
6067
+ // Display a link to re-optimize manually.
6068
+ $output .= '<div>' . sprintf( "<a class='ewww-manual-optimize' data-id='$id' data-nonce='$ewww_manual_nonce' href=\"admin.php?action=ewww_image_optimizer_manual_optimize&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_force=1&amp;ewww_attachment_ID=%d\">%s</a>",
6069
+ $id,
6070
+ esc_html__( 'Re-optimize', 'ewww-image-optimizer' )
6071
+ ) . '</div>';
6072
+ }
6073
+ if ( $backup_available && current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
6074
+ $output .= '<div>' . sprintf( "<a class='ewww-manual-cloud-restore' data-id='$id' data-nonce='$ewww_manual_nonce' href=\"admin.php?action=ewww_image_optimizer_manual_cloud_restore&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_attachment_ID=%d\">%s</a>",
6075
+ $id,
6076
+ esc_html__( 'Restore original', 'ewww-image-optimizer' )
6077
+ ) . '</div>';
6078
+ }
6079
+ } elseif ( ! $in_progress && current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
6080
+ ewww_image_optimizer_migrate_meta_to_db( $id, $meta );
6081
+ // Give the user the option to optimize the image right now.
6082
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
6083
+ $sizes_to_opt = ewww_image_optimizer_count_unoptimized_sizes( $meta['sizes'] ) + 1;
6084
+ $output .= '<div>' . sprintf( esc_html(
6085
+ /* translators: %d: The number of resize/thumbnail images */
6086
+ _n( '%d size to compress', '%d sizes to compress', $sizes_to_opt, 'ewww-image-optimizer' )
6087
+ ), $sizes_to_opt ) . '</div>';
6088
+ }
6089
+ $output .= '<div>' . sprintf( "<a class='ewww-manual-optimize' data-id='$id' data-nonce='$ewww_manual_nonce' href=\"admin.php?action=ewww_image_optimizer_manual_optimize&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_attachment_ID=%d\">%s</a>", $id, esc_html__( 'Optimize now!', 'ewww-image-optimizer' ) ) . '</div>';
6090
+ }
6091
+ $output .= '</div>';
6092
+ if ( $return_output ) {
6093
+ return $output;
6094
+ }
6095
+ echo $output;
6096
+ return;
6097
+ } // End if().
6098
+ // End of output for CDN images.
6099
+ if ( get_transient( 'ewwwio-background-in-progress-' . $id ) ) {
6100
+ $output .= esc_html__( 'In Progress', 'ewww-image-optimizer' );
6101
+ $in_progress = true;
6102
+ }
6103
+ if ( ! $in_progress ) {
6104
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'media' AND image_size <> 0 AND path LIKE '%%%s%%' ORDER BY orig_size DESC", $id, $basename ), ARRAY_A );
6105
+ if ( ! $optimized_images ) {
6106
+ // Attempt migration, but only if the original image is in the db, $migrated will be metadata on success, false on failure.
6107
+ $migrated = ewww_image_optimizer_migrate_meta_to_db( $id, $meta, true );
6108
+ }
6109
+ if ( $migrated ) {
6110
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT image_size,orig_size,resize,converted,level,backup,updated FROM $wpdb->ewwwio_images WHERE attachment_id = %d AND gallery = 'media' AND image_size <> 0 AND path LIKE '%%%s%%' ORDER BY orig_size DESC", $id, $basename ), ARRAY_A );
6111
+ }
6112
+ }
6113
+ // If optimizer data exists.
6114
+ if ( ! empty( $optimized_images ) ) {
6115
+ list( $detail_output, $converted, $backup_available ) = ewww_image_optimizer_custom_column_results( $id, $optimized_images );
6116
+ $output .= $detail_output;
6117
+
6118
+ // Link to webp upgrade script.
6119
+ $oldwebpfile = preg_replace( '/\.\w+$/', '.webp', $file_path );
6120
+ if ( file_exists( $oldwebpfile ) && current_user_can( apply_filters( 'ewww_image_optimizer_admin_permissions', '' ) ) ) {
6121
+ $output .= "<div><a href='options.php?page=ewww-image-optimizer-webp-migrate'>" . esc_html__( 'Run WebP upgrade', 'ewww-image-optimizer' ) . '</a></div>';
6122
+ }
6123
+
6124
+ // Determine filepath for webp.
6125
+ $webpfile = $file_path . '.webp';
6126
+ $webp_size = ewww_image_optimizer_filesize( $webpfile );
6127
+ if ( $webp_size ) {
6128
+ // Get a human readable filesize.
6129
+ $webp_size = ewww_image_optimizer_size_format( $webp_size );
6130
+ $webpurl = esc_url( wp_get_attachment_url( $id ) . '.webp' );
6131
+ $output .= "<div>WebP: <a href='$webpurl'>$webp_size</a></div>";
6132
+ }
6133
+
6134
+ if ( empty( $msg ) && current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
6135
+ // Output a link to re-optimize manually.
6136
+ $output .= '<div>' . sprintf("<a class='ewww-manual-optimize' data-id='$id' data-nonce='$ewww_manual_nonce' href=\"admin.php?action=ewww_image_optimizer_manual_optimize&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_force=1&amp;ewww_attachment_ID=%d\">%s</a>",
6137
+ $id,
6138
+ esc_html__( 'Re-optimize', 'ewww-image-optimizer' )
6139
+ );
6140
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_convert_links' ) && 'ims_image' != get_post_type( $id ) && ! empty( $convert_desc ) ) {
6141
+ $output .= " | <a class='ewww-manual-convert' data-id='$id' data-nonce='$ewww_manual_nonce' title='$convert_desc' href='admin.php?action=ewww_image_optimizer_manual_optimize&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_attachment_ID=$id&amp;ewww_convert=1&amp;ewww_force=1'>$convert_link</a>";
6142
+ }
6143
+ $output .= '</div>';
6144
+ } else {
6145
+ $output .= $msg;
6146
+ }
6147
+ $restorable = false;
6148
+ if ( $converted && is_file( $converted ) ) {
6149
+ $restorable = true;
6150
+ }
6151
+ if ( $restorable && current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
6152
+ $output .= '<div>' . sprintf( "<a class='ewww-manual-restore' data-id='$id' data-nonce='$ewww_manual_nonce' href=\"admin.php?action=ewww_image_optimizer_manual_restore&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_attachment_ID=%d\">%s</a>",
6153
+ $id,
6154
+ esc_html__( 'Restore original', 'ewww-image-optimizer' )
6155
+ ) . '</div>';
6156
+ } elseif ( $backup_available && current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
6157
+ $output .= '<div>' . sprintf( "<a class='ewww-manual-cloud-restore' data-id='$id' data-nonce='$ewww_manual_nonce' href=\"admin.php?action=ewww_image_optimizer_manual_cloud_restore&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_attachment_ID=%d\">%s</a>",
6158
+ $id,
6159
+ esc_html__( 'Restore original', 'ewww-image-optimizer' )
6160
+ ) . '</div>';
6161
+ }
6162
+ } elseif ( ! $in_progress ) {
6163
+ // Otherwise, this must be an image we haven't processed.
6164
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
6165
+ $sizes_to_opt = ewww_image_optimizer_count_unoptimized_sizes( $meta['sizes'] ) + 1;
6166
+ $output .= '<div>' . sprintf( esc_html(
6167
+ /* translators: %d: The number of resize/thumbnail images */
6168
+ _n( '%d size to compress', '%d sizes to compress', $sizes_to_opt, 'ewww-image-optimizer' )
6169
+ ), $sizes_to_opt ) . '</div>';
6170
+ } else {
6171
+ $output .= '<div>' . esc_html__( 'Not processed', 'ewww-image-optimizer' ) . '</div>';
6172
+ }
6173
+ // Tell them the filesize.
6174
+ $output .= '<div>' . sprintf(
6175
+ /* translators: %s: size of the image */
6176
+ esc_html__( 'Image Size: %s', 'ewww-image-optimizer' ),
6177
+ $file_size
6178
+ ) . '</div>';
6179
+ if ( empty( $msg ) && current_user_can( apply_filters( 'ewww_image_optimizer_manual_permissions', '' ) ) ) {
6180
+ // Give the user the option to optimize the image right now.
6181
+ $output .= sprintf( "<div><a class='ewww-manual-optimize' data-id='$id' data-nonce='$ewww_manual_nonce' href=\"admin.php?action=ewww_image_optimizer_manual_optimize&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_attachment_ID=%d\">%s</a>", $id, esc_html__( 'Optimize now!', 'ewww-image-optimizer' ) );
6182
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_convert_links' ) && 'ims_image' != get_post_type( $id ) && ! empty( $convert_desc ) ) {
6183
+ $output .= " | <a class='ewww-manual-convert' data-id='$id' data-nonce='$ewww_manual_nonce' title='$convert_desc' href='admin.php?action=ewww_image_optimizer_manual_optimize&amp;ewww_manual_nonce=$ewww_manual_nonce&amp;ewww_attachment_ID=$id&amp;ewww_convert=1&amp;ewww_force=1'>$convert_link</a>";
6184
+ }
6185
+ $output .= '</div>';
6186
+ } else {
6187
+ $output .= $msg;
6188
+ }
6189
+ } // End if().
6190
+ $output .= '</div>';
6191
+ if ( $return_output ) {
6192
+ ewww_image_optimizer_debug_log();
6193
+ return $output;
6194
+ }
6195
+ echo $output;
6196
+ } // End if().
6197
+ ewwwio_memory( __FUNCTION__ );
6198
+ ewww_image_optimizer_debug_log();
6199
+ }
6200
+
6201
+ /**
6202
+ * Determine how many sizes need optimization.
6203
+ *
6204
+ * @param array $sizes The 'sizes' portion of the attachment metadata.
6205
+ * @return int The number of unoptimized sizes.
6206
+ */
6207
+ function ewww_image_optimizer_count_unoptimized_sizes( $sizes ) {
6208
+ if ( ! ewww_image_optimizer_iterable( $sizes ) ) {
6209
+ ewwwio_debug_message( 'unoptimized sizes cannot be counted' );
6210
+ return 0;
6211
+ }
6212
+ $sizes_to_opt = 0;
6213
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes_opt' );
6214
+
6215
+ // To keep track of the ones we have already processed.
6216
+ $processed = array();
6217
+ foreach ( $sizes as $size => $data ) {
6218
+ ewwwio_debug_message( "checking for size: $size" );
6219
+ ewww_image_optimizer_debug_log();
6220
+ if ( strpos( $size, 'webp' ) === 0 ) {
6221
+ continue;
6222
+ }
6223
+ if ( ! empty( $disabled_sizes[ $size ] ) ) {
6224
+ continue;
6225
+ }
6226
+ if ( ! empty( $disabled_sizes['pdf-full'] ) && 'full' == $size ) {
6227
+ continue;
6228
+ }
6229
+ if ( empty( $data['file'] ) ) {
6230
+ continue;
6231
+ }
6232
+
6233
+ // Check through all the sizes we've processed so far.
6234
+ foreach ( $processed as $proc => $scan ) {
6235
+ // If a previous resize had identical dimensions...
6236
+ if ( $scan['height'] == $data['height'] && $scan['width'] == $data['width'] ) {
6237
+ // Found a duplicate size, get outta here!
6238
+ continue( 2 );
6239
+ }
6240
+ }
6241
+ $sizes_to_opt++;
6242
+ // Store info on the sizes we've processed, so we can check the list for duplicate sizes.
6243
+ $processed[ $size ]['width'] = $data['width'];
6244
+ $processed[ $size ]['height'] = $data['height'];
6245
+ } // End foreach().
6246
+ return $sizes_to_opt;
6247
+ }
6248
+
6249
+ /**
6250
+ * Display cumulative image compression results with individual images displayed in a modal.
6251
+ *
6252
+ * @param int $id The ID number of the attachment.
6253
+ * @param array $optimized_images A list of image records related to $id.
6254
+ * @return array {
6255
+ * Information compiled from the database records.
6256
+ *
6257
+ * @type string $output The image results plus a table of individual image results in a modal.
6258
+ * @type string|bool $converted The original image if the attachment was converted or false.
6259
+ * @type string|bool $backup_available The backup hash if available or false.
6260
+ * }
6261
+ */
6262
+ function ewww_image_optimizer_custom_column_results( $id, $optimized_images ) {
6263
+ if ( empty( $id ) || empty( $optimized_images ) || ! is_array( $optimized_images ) ) {
6264
+ return array( '', false, false );
6265
+ }
6266
+ $orig_size = 0;
6267
+ $opt_size = 0;
6268
+ $level = 0;
6269
+ $converted = false;
6270
+ $backup_available = false;
6271
+ $sizes_to_opt = 0;
6272
+ $output = '';
6273
+ $detail_output = '<table class="striped"><tr><th>&nbsp;</th><th>' . esc_html__( 'Image Size', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Savings', 'ewww-image-optimizer' ) . '</th></tr>';
6274
+ foreach ( $optimized_images as $optimized_image ) {
6275
+ if ( ! empty( $optimized_image['attachment_id'] ) ) {
6276
+ $id = $optimized_image['attachment_id'];
6277
+ }
6278
+ $orig_size += $optimized_image['orig_size'];
6279
+ $opt_size += $optimized_image['image_size'];
6280
+ if ( 'full' == $optimized_image['resize'] ) {
6281
+ $level = $optimized_image['level'];
6282
+ $updated_time = strtotime( $optimized_image['updated'] );
6283
+ if ( DAY_IN_SECONDS * 30 + $updated_time > time() ) {
6284
+ $backup_available = $optimized_image['backup'];
6285
+ } else {
6286
+ $backup_available = '';
6287
+ }
6288
+ }
6289
+ if ( ! empty( $optimized_image['converted'] ) ) {
6290
+ $converted = $optimized_image['converted'];
6291
+ }
6292
+ $sizes_to_opt++;
6293
+ if ( ! empty( $optimized_image['resize'] ) ) {
6294
+ $display_size = ewww_image_optimizer_size_format( $optimized_image['image_size'] );
6295
+ $detail_output .= '<tr><td><strong>' . ucfirst( $optimized_image['resize'] ) . "</strong></td><td>$display_size</td><td>" . esc_html( ewww_image_optimizer_image_results( $optimized_image['orig_size'], $optimized_image['image_size'] ) ) . '</td></tr>';
6296
+ }
6297
+ }
6298
+ $detail_output .= '</table>';
6299
+
6300
+ $output .= '<div>' . sprintf( esc_html(
6301
+ /* translators: %d: number of resizes/thumbnails compressed */
6302
+ _n( '%d size compressed', '%d sizes compressed', $sizes_to_opt, 'ewww-image-optimizer' )
6303
+ ), $sizes_to_opt );
6304
+ $output .= " <a href='#TB_inline?width=550&height=450&inlineId=ewww-attachment-detail-$id' class='thickbox'>(+)</a></div>";
6305
+ $results_msg = ewww_image_optimizer_image_results( $orig_size, $opt_size );
6306
+ // Output the optimizer results.
6307
+ $output .= '<div>' . esc_html( $results_msg ) . '</div>';
6308
+ $display_size = ewww_image_optimizer_size_format( $opt_size );
6309
+ // Output the total filesize.
6310
+ $detail_output .= '<div><strong>' . sprintf(
6311
+ /* translators: %s: human-readable file size */
6312
+ esc_html__( 'Total Size: %s', 'ewww-image-optimizer' ),
6313
+ $display_size
6314
+ ) . '</strong></div>';
6315
+ $output .= "<div id='ewww-attachment-detail-$id' class='ewww-attachment-detail-container'><div class='ewww-attachment-detail'>$detail_output</div></div>";
6316
+ return array( $output, $converted, $backup_available );
6317
+ }
6318
+
6319
+ /**
6320
+ * Removes optimization from metadata, because we store it all in the images table now.
6321
+ *
6322
+ * @param array $meta The attachment metadata.
6323
+ * @return array The attachment metadata after being cleaned.
6324
+ */
6325
+ function ewww_image_optimizer_clean_meta( $meta ) {
6326
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6327
+ if ( is_array( $meta ) && ! empty( $meta['ewww_image_optimizer'] ) ) {
6328
+ unset( $meta['ewww_image_optimizer'] );
6329
+ }
6330
+ if ( is_array( $meta ) && ! empty( $meta['converted'] ) ) {
6331
+ unset( $meta['converted'] );
6332
+ }
6333
+ if ( is_array( $meta ) && ! empty( $meta['orig_file'] ) ) {
6334
+ unset( $meta['orig_file'] );
6335
+ }
6336
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
6337
+ foreach ( $meta['sizes'] as $size => $data ) {
6338
+ if ( is_array( $data ) && ! empty( $data['ewww_image_optimizer'] ) ) {
6339
+ unset( $meta['sizes'][ $size ]['ewww_image_optimizer'] );
6340
+ }
6341
+ if ( is_array( $data ) && ! empty( $data['converted'] ) ) {
6342
+ unset( $meta['sizes'][ $size ]['converted'] );
6343
+ }
6344
+ if ( is_array( $data ) && ! empty( $data['orig_file'] ) ) {
6345
+ unset( $meta['sizes'][ $size ]['orig_file'] );
6346
+ }
6347
+ }
6348
+ }
6349
+ return $meta;
6350
+ }
6351
+
6352
+ /**
6353
+ * Updates a record in the images table with information from the attachment metadata.
6354
+ *
6355
+ * @global object $wpdb
6356
+ *
6357
+ * @param string $file The name of the file to update.
6358
+ * @param string $gallery Optional. Location of the image, like 'media' or 'nextgen'. Default ''.
6359
+ * @param int $attachment_id Optional. The ID number of the image. Default 0.
6360
+ * @param string $size Optional. The name of the image size like 'medium' or 'large'. Default ''.
6361
+ * @param string $converted Optional. The name of the original file. Default ''.
6362
+ */
6363
+ function ewww_image_optimizer_update_file_from_meta( $file, $gallery = '', $attachment_id = 0, $size = '', $converted = '' ) {
6364
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6365
+ $already_optimized = ewww_image_optimizer_find_already_optimized( $file );
6366
+ if ( is_array( $already_optimized ) && ! empty( $already_optimized['id'] ) ) {
6367
+ $updates = array();
6368
+ if ( $gallery && empty( $already_optimized['gallery'] ) ) {
6369
+ $updates['gallery'] = $gallery;
6370
+ }
6371
+ if ( $attachment_id && empty( $already_optimized['attachment_id'] ) ) {
6372
+ $updates['attachment_id'] = $attachment_id;
6373
+ }
6374
+ if ( $size && empty( $already_optimized['resize'] ) ) {
6375
+ $updates['resize'] = $size;
6376
+ }
6377
+ if ( $converted && empty( $already_optimized['converted'] ) ) {
6378
+ $updates['converted'] = $converted;
6379
+ }
6380
+ if ( $updates ) {
6381
+ ewwwio_debug_message( "running update for $file" );
6382
+ $updates['updated'] = $already_optimized['updated'];
6383
+ global $wpdb;
6384
+ // Update the values given for the record we found.
6385
+ $updated = $wpdb->update(
6386
+ $wpdb->ewwwio_images,
6387
+ $updates,
6388
+ array(
6389
+ 'id' => $already_optimized['id'],
6390
+ )
6391
+ );
6392
+ if ( false === $updated ) {
6393
+ ewwwio_debug_message( "failed to update record for $file" );
6394
+ }
6395
+ if ( ! $updated ) {
6396
+ ewwwio_debug_message( "no records updated for $file and {$already_optimized['id']}" );
6397
+ }
6398
+ return $updated;
6399
+ }
6400
+ } // End if().
6401
+ return false;
6402
+ }
6403
+
6404
+ /**
6405
+ * Compiles information from the metadata to be inserted into the images table.
6406
+ *
6407
+ * @param int $id The attachment ID number.
6408
+ * @param array $meta The attachment metadata.
6409
+ * @param bool $bail_early Optional. True to stop execution if full size didn't need migration.
6410
+ * @return array The attachment metadata, potentially cleaned after migration.
6411
+ */
6412
+ function ewww_image_optimizer_migrate_meta_to_db( $id, $meta, $bail_early = false ) {
6413
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6414
+ if ( empty( $meta ) ) {
6415
+ ewwwio_debug_message( "empty meta for $id" );
6416
+ return $meta;
6417
+ }
6418
+ list( $file_path, $upload_path ) = ewww_image_optimizer_attachment_path( $meta, $id );
6419
+ if ( ! is_file( $file_path ) && ( class_exists( 'WindowsAzureStorageUtil' ) || class_exists( 'Amazon_S3_And_CloudFront' ) ) ) {
6420
+ // Construct a $file_path and proceed IF a supported CDN plugin is installed.
6421
+ $file_path = get_attached_file( $selected_id );
6422
+ if ( ! $file_path ) {
6423
+ ewwwio_debug_message( 'no file found for remote attachment' );
6424
+ // $meta = ewww_image_optimizer_clean_meta( $meta );
6425
+ // TODO: once we've kicked the tires about a million times, and we're convinced this can't happen in error, then let's clean the meta
6426
+ return $meta;
6427
+ }
6428
+ } elseif ( ! $file_path ) {
6429
+ ewwwio_debug_message( 'no file found for attachment' );
6430
+ // $meta = ewww_image_optimizer_clean_meta( $meta );
6431
+ // TODO: ditto.
6432
+ return $meta;
6433
+ }
6434
+ $converted = ( is_array( $meta ) && ! empty( $meta['converted'] ) && ! empty( $meta['orig_file'] ) ? trailingslashit( dirname( $file_path ) ) . basename( $meta['orig_file'] ) : false );
6435
+ $full_size_update = ewww_image_optimizer_update_file_from_meta( $file_path, 'media', $id, 'full', $converted );
6436
+ if ( ! $full_size_update && $bail_early ) {
6437
+ ewwwio_debug_message( "bailing early for migration of $id" );
6438
+ return false;
6439
+ }
6440
+ $retina_path = ewww_image_optimizer_hidpi_optimize( $file_path, true, false );
6441
+ if ( $retina_path ) {
6442
+ ewww_image_optimizer_update_file_from_meta( $retina_path, 'media', $id, 'full-retina' );
6443
+ }
6444
+ $type = ewww_image_optimizer_quick_mimetype( $file_path );
6445
+ // Resized versions, so we can continue.
6446
+ if ( isset( $meta['sizes'] ) && ewww_image_optimizer_iterable( $meta['sizes'] ) ) {
6447
+ // Meta sizes don't contain a path, so we calculate one.
6448
+ if ( 'ims_image' == get_post_type( $id ) ) {
6449
+ $base_dir = trailingslashit( dirname( $file_path ) ) . '_resized/';
6450
+ } else {
6451
+ $base_dir = trailingslashit( dirname( $file_path ) );
6452
+ }
6453
+ foreach ( $meta['sizes'] as $size => $data ) {
6454
+ ewwwio_debug_message( "checking for size: $size" );
6455
+ if ( strpos( $size, 'webp' ) === 0 ) {
6456
+ continue;
6457
+ }
6458
+ if ( empty( $data ) || ! is_array( $data ) || empty( $data['file'] ) ) {
6459
+ continue;
6460
+ }
6461
+ if ( 'full' == $size && 'application/pdf' == $type ) {
6462
+ $size = 'pdf-full';
6463
+ } elseif ( 'full' == $size ) {
6464
+ continue;
6465
+ }
6466
+
6467
+ $resize_path = $base_dir . $data['file'];
6468
+ $converted = ( is_array( $data ) && ! empty( $data['converted'] ) && ! empty( $data['orig_file'] ) ? trailingslashit( dirname( $resize_path ) ) . basename( $data['orig_file'] ) : false );
6469
+ ewww_image_optimizer_update_file_from_meta( $resize_path, 'media', $id, $size, $converted );
6470
+ // Search for retina images.
6471
+ if ( function_exists( 'wr2x_get_retina' ) ) {
6472
+ $retina_path = wr2x_get_retina( $resize_path );
6473
+ } else {
6474
+ $retina_path = ewww_image_optimizer_hidpi_optimize( $resize_path, true, false );
6475
+ }
6476
+ if ( $retina_path ) {
6477
+ ewww_image_optimizer_update_file_from_meta( $retina_path, 'media', $id, $size . '-retina' );
6478
+ }
6479
+ }
6480
+ } // End if().
6481
+
6482
+ // Search sizes from a custom theme...
6483
+ if ( isset( $meta['image_meta']['resized_images'] ) && ewww_image_optimizer_iterable( $meta['image_meta']['resized_images'] ) ) {
6484
+ $imagemeta_resize_pathinfo = pathinfo( $file_path );
6485
+ $imagemeta_resize_path = '';
6486
+ foreach ( $meta['image_meta']['resized_images'] as $index => $imagemeta_resize ) {
6487
+ $imagemeta_resize_path = $imagemeta_resize_pathinfo['dirname'] . '/' . $imagemeta_resize_pathinfo['filename'] . '-' . $imagemeta_resize . '.' . $imagemeta_resize_pathinfo['extension'];
6488
+ ewww_image_optimizer_update_file_from_meta( $imagemeta_resize_path, 'media', $id, 'resized-images-' . $index );
6489
+ }
6490
+ }
6491
+
6492
+ // and another custom theme.
6493
+ if ( isset( $meta['custom_sizes'] ) && ewww_image_optimizer_iterable( $meta['custom_sizes'] ) ) {
6494
+ $custom_sizes_pathinfo = pathinfo( $file_path );
6495
+ $custom_size_path = '';
6496
+ foreach ( $meta['custom_sizes'] as $dimensions => $custom_size ) {
6497
+ $custom_size_path = $custom_sizes_pathinfo['dirname'] . '/' . $custom_size['file'];
6498
+ ewww_image_optimizer_update_file_from_meta( $custom_size_path, 'media', $id, 'custom-size-' . $dimensions );
6499
+ }
6500
+ }
6501
+ $meta = ewww_image_optimizer_clean_meta( $meta );
6502
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
6503
+ ewwwio_debug_message( print_r( $meta, true ) );
6504
+ }
6505
+ update_post_meta( $id, '_wp_attachment_metadata', $meta );
6506
+ return $meta;
6507
+ }
6508
+
6509
+ /**
6510
+ * Load JS in media library footer for bulk actions.
6511
+ */
6512
+ function ewww_image_optimizer_load_admin_js() {
6513
+ add_action( 'admin_print_footer_scripts', 'ewww_image_optimizer_add_bulk_actions_via_javascript' );
6514
+ }
6515
+
6516
+ /**
6517
+ * Adds a bulk optimize action to the drop-down on the media library page.
6518
+ */
6519
+ function ewww_image_optimizer_add_bulk_actions_via_javascript() {
6520
+ // Borrowed from http://www.viper007bond.com/wordpress-plugins/regenerate-thumbnails/ .
6521
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6522
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_bulk_permissions', '' ) ) ) {
6523
+ return;
6524
+ }
6525
+ ?>
6526
+ <script type="text/javascript">
6527
+ jQuery(document).ready(function($){
6528
+ $('select[name^="action"] option:last-child').before('<option value="bulk_optimize"><?php esc_html_e( 'Bulk Optimize', 'ewww-image-optimizer' ); ?></option>');
6529
+ $('.ewww-manual-convert').tooltip();
6530
+ });
6531
+ </script>
6532
+ <?php
6533
+ }
6534
+
6535
+ /**
6536
+ * Handles the bulk actions POST.
6537
+ */
6538
+ function ewww_image_optimizer_bulk_action_handler() {
6539
+ // Borrowed from http://www.viper007bond.com/wordpress-plugins/regenerate-thumbnails/ .
6540
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6541
+ // If the requested action is blank, or not a bulk_optimize, do nothing.
6542
+ if ( ( empty( $_REQUEST['action'] ) || 'bulk_optimize' != $_REQUEST['action'] ) && ( empty( $_REQUEST['action2'] ) || 'bulk_optimize' != $_REQUEST['action2'] ) ) {
6543
+ return;
6544
+ }
6545
+ // If there is no media to optimize, do nothing.
6546
+ if ( empty( $_REQUEST['media'] ) || ! is_array( $_REQUEST['media'] ) ) {
6547
+ return;
6548
+ }
6549
+ // Check the referring page.
6550
+ check_admin_referer( 'bulk-media' );
6551
+ // Prep the attachment IDs for optimization.
6552
+ $ids = implode( ',', array_map( 'intval', $_REQUEST['media'] ) );
6553
+ wp_redirect( add_query_arg(
6554
+ array(
6555
+ 'page' => 'ewww-image-optimizer-bulk',
6556
+ '_wpnonce' => wp_create_nonce( 'ewww-image-optimizer-bulk' ),
6557
+ 'goback' => 1,
6558
+ 'ids' => $ids,
6559
+ ),
6560
+ admin_url( 'upload.php' )
6561
+ ) );
6562
+ ewwwio_memory( __FUNCTION__ );
6563
+ exit();
6564
+ }
6565
+
6566
+ /**
6567
+ * Retrieve option: use 'site' setting if plugin is network activated, otherwise use 'blog' setting.
6568
+ *
6569
+ * @param string $option_name The name of the option to retrieve.
6570
+ * @return mixed The value of the option.
6571
+ */
6572
+ function ewww_image_optimizer_get_option( $option_name ) {
6573
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
6574
+ // Need to include the plugin library for the is_plugin_active function.
6575
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
6576
+ }
6577
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) && ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) {
6578
+ $option_value = get_site_option( $option_name );
6579
+ } else {
6580
+ $option_value = get_option( $option_name );
6581
+ }
6582
+ return $option_value;
6583
+ }
6584
+
6585
+ /**
6586
+ * Set an option: use 'site' setting if plugin is network activated, otherwise use 'blog' setting.
6587
+ *
6588
+ * @param string $option_name The name of the option to save.
6589
+ * @param mixed $option_value The value to save for the option.
6590
+ * @return bool True if the operation was successful.
6591
+ */
6592
+ function ewww_image_optimizer_set_option( $option_name, $option_value ) {
6593
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
6594
+ // Need to include the plugin library for the is_plugin_active function.
6595
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
6596
+ }
6597
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) && ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) {
6598
+ $success = update_site_option( $option_name, $option_value );
6599
+ } else {
6600
+ $success = update_option( $option_name, $option_value );
6601
+ }
6602
+ return $success;
6603
+ }
6604
+
6605
+ /**
6606
+ * Check for a list of attachments for which we do not rebuild meta.
6607
+ *
6608
+ * @return array {
6609
+ * Information regarding attachments with broken metadata that could not be rebuilt.
6610
+ *
6611
+ * @type array A list of all know 'bad' attachments.
6612
+ * @type string The most recent 'bad' attachment.
6613
+ * }
6614
+ */
6615
+ function ewww_image_optimizer_get_bad_attachments() {
6616
+ $bad_attachment = get_transient( 'ewww_image_optimizer_rebuilding_attachment' );
6617
+ if ( $bad_attachment ) {
6618
+ $bad_attachments = (array) ewww_image_optimizer_get_option( 'ewww_image_optimizer_bad_attachments' );
6619
+ $bad_attachments[] = $bad_attachment;
6620
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_bad_attachments', $bad_attachments, false );
6621
+ } else {
6622
+ $bad_attachments = (array) ewww_image_optimizer_get_option( 'ewww_image_optimizer_bad_attachments' );
6623
+ }
6624
+ delete_transient( 'ewww_image_optimizer_rebuilding_attachment' );
6625
+ return array( $bad_attachments, $bad_attachment );
6626
+ }
6627
+
6628
+ /**
6629
+ * JS needed for the settings page.
6630
+ *
6631
+ * @param string $hook The hook name of the page being loaded.
6632
+ */
6633
+ function ewww_image_optimizer_settings_script( $hook ) {
6634
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6635
+ // Make sure we are being called from the settings page.
6636
+ if ( strpos( $hook,'settings_page_ewww-image-optimizer' ) !== 0 ) {
6637
+ return;
6638
+ }
6639
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', __FILE__ ), array( 'jquery' ), EWWW_IMAGE_OPTIMIZER_VERSION );
6640
+ wp_enqueue_script( 'postbox' );
6641
+ wp_enqueue_script( 'dashboard' );
6642
+ wp_localize_script( 'ewwwbulkscript', 'ewww_vars', array(
6643
+ '_wpnonce' => wp_create_nonce( 'ewww-image-optimizer-settings' ),
6644
+ )
6645
+ );
6646
+ ewwwio_memory( __FUNCTION__ );
6647
+ return;
6648
+ }
6649
+
6650
+ /**
6651
+ * Get a total of how much space we have saved so far.
6652
+ *
6653
+ * @global object $wpdb
6654
+ *
6655
+ * @return int The total savings found, in bytes.
6656
+ */
6657
+ function ewww_image_optimizer_savings() {
6658
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6659
+ global $wpdb;
6660
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
6661
+ // Need to include the plugin library for the is_plugin_active function.
6662
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
6663
+ }
6664
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
6665
+ ewwwio_debug_message( 'querying savings for multi-site' );
6666
+
6667
+ if ( get_blog_count() > 1000 ) {
6668
+ // TODO: someday do something more clever than this, maybe.
6669
+ return 0;
6670
+ }
6671
+ if ( function_exists( 'get_sites' ) ) {
6672
+ ewwwio_debug_message( 'retrieving list of sites the easy way (4.6+)' );
6673
+ $blogs = get_sites( array(
6674
+ 'fields' => 'ids',
6675
+ 'number' => 1000,
6676
+ ) );
6677
+
6678
+ } elseif ( function_exists( 'wp_get_sites' ) ) {
6679
+ ewwwio_debug_message( 'retrieving list of sites the easy way (pre 4.6)' );
6680
+ $blogs = wp_get_sites( array(
6681
+ 'network_id' => $wpdb->siteid,
6682
+ 'limit' => 1000,
6683
+ ) );
6684
+ }
6685
+ $total_savings = 0;
6686
+ if ( ewww_image_optimizer_iterable( $blogs ) ) {
6687
+ foreach ( $blogs as $blog ) {
6688
+ if ( is_array( $blog ) ) {
6689
+ $blog_id = $blog['blog_id'];
6690
+ } else {
6691
+ $blog_id = $blog;
6692
+ }
6693
+ switch_to_blog( $blog_id );
6694
+ ewwwio_debug_message( "getting savings for site: $blog_id" );
6695
+ $table_name = $wpdb->prefix . 'ewwwio_images';
6696
+ if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) != $table_name ) {
6697
+ ewww_image_optimizer_install_table();
6698
+ }
6699
+ if ( $wpdb->ewwwio_images ) {
6700
+ $wpdb->query( "DELETE FROM $wpdb->ewwwio_images WHERE image_size > orig_size" );
6701
+ $savings = $wpdb->get_var( "SELECT SUM(orig_size-image_size) FROM $wpdb->ewwwio_images" );
6702
+ ewwwio_debug_message( "savings found: $savings" );
6703
+ $total_savings += $savings;
6704
+ }
6705
+ restore_current_blog();
6706
+ }
6707
+ }
6708
+ } else {
6709
+ ewwwio_debug_message( 'querying savings for single site' );
6710
+ $total_savings = 0;
6711
+ $table_name = $wpdb->ewwwio_images;
6712
+ if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) != $table_name ) {
6713
+ ewww_image_optimizer_install_table();
6714
+ }
6715
+ $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'ewwwio_images WHERE image_size > orig_size' );
6716
+ $total_savings = $wpdb->get_var( "SELECT SUM(orig_size-image_size) FROM $wpdb->ewwwio_images" );
6717
+ ewwwio_debug_message( "savings found: $total_savings" );
6718
+ } // End if().
6719
+ return $total_savings;
6720
+ }
6721
+
6722
+ /**
6723
+ * Figure out where the .htaccess file should live.
6724
+ *
6725
+ * @return string The path to the .htaccess file.
6726
+ */
6727
+ function ewww_image_optimizer_htaccess_path() {
6728
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6729
+ $htpath = get_home_path();
6730
+ if ( get_option( 'siteurl' ) !== get_option( 'home' ) ) {
6731
+ ewwwio_debug_message( 'WordPress Address and Site Address are different, possible subdir install' );
6732
+ $path_diff = str_replace( get_option( 'home' ), '', get_option( 'siteurl' ) );
6733
+ $newhtpath = trailingslashit( $htpath . $path_diff ) . '.htaccess';
6734
+ if ( is_file( $newhtpath ) ) {
6735
+ ewwwio_debug_message( 'subdir install confirmed' );
6736
+ return $newhtpath;
6737
+ }
6738
+ }
6739
+ return $htpath . '.htaccess';
6740
+ }
6741
+
6742
+ /**
6743
+ * Called via AJAX, adds WebP rewrite rules to the .htaccess file.
6744
+ */
6745
+ function ewww_image_optimizer_webp_rewrite() {
6746
+ // Verify that the user is properly authorized.
6747
+ if ( ! wp_verify_nonce( $_REQUEST['ewww_wpnonce'], 'ewww-image-optimizer-settings' ) ) {
6748
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
6749
+ }
6750
+ $ewww_rules = ewww_image_optimizer_webp_rewrite_verify();
6751
+ if ( $ewww_rules ) {
6752
+ if ( insert_with_markers( ewww_image_optimizer_htaccess_path(), 'EWWWIO', $ewww_rules ) && ! ewww_image_optimizer_webp_rewrite_verify() ) {
6753
+ esc_html_e( 'Insertion successful', 'ewww-image-optimizer' );
6754
+ } else {
6755
+ esc_html_e( 'Insertion failed', 'ewww-image-optimizer' );
6756
+ }
6757
+ }
6758
+ die();
6759
+ }
6760
+
6761
+ /**
6762
+ * If rules are present, stay silent, otherwise, gives us some rules to insert!
6763
+ */
6764
+ function ewww_image_optimizer_webp_rewrite_verify() {
6765
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6766
+ $current_rules = extract_from_markers( ewww_image_optimizer_htaccess_path(), 'EWWWIO' );
6767
+ $ewww_rules = array(
6768
+ '<IfModule mod_rewrite.c>',
6769
+ 'RewriteEngine On',
6770
+ 'RewriteCond %{HTTP_ACCEPT} image/webp',
6771
+ 'RewriteCond %{REQUEST_FILENAME} (.*)\.(jpe?g|png)$',
6772
+ 'RewriteCond %{REQUEST_FILENAME}.webp -f',
6773
+ 'RewriteCond %{QUERY_STRING} !type=original',
6774
+ 'RewriteRule (.+)\.(jpe?g|png)$ %{REQUEST_FILENAME}.webp [T=image/webp,E=accept:1,L]',
6775
+ '</IfModule>',
6776
+ '<IfModule mod_headers.c>',
6777
+ 'Header append Vary Accept env=REDIRECT_accept',
6778
+ '</IfModule>',
6779
+ 'AddType image/webp .webp',
6780
+ );
6781
+ ewwwio_debug_message( print_r( $current_rules, true ) );
6782
+ if ( empty( $current_rules ) ||
6783
+ ! ewww_image_optimizer_array_search( '{HTTP_ACCEPT} image/webp', $current_rules ) ||
6784
+ ! ewww_image_optimizer_array_search( '{REQUEST_FILENAME}.webp', $current_rules ) ||
6785
+ ! ewww_image_optimizer_array_search( 'Header append Vary Accept', $current_rules ) ||
6786
+ ! ewww_image_optimizer_array_search( 'AddType image/webp', $current_rules )
6787
+ ) {
6788
+ ewwwio_memory( __FUNCTION__ );
6789
+ return $ewww_rules;
6790
+ } else {
6791
+ ewwwio_memory( __FUNCTION__ );
6792
+ return;
6793
+ }
6794
+ }
6795
+
6796
+ /**
6797
+ * Looks for a certain string within all array elements.
6798
+ *
6799
+ * @param string $needle The searched value.
6800
+ * @param array $haystack The array to search.
6801
+ * @return bool True if the needle is found, false otherwise.
6802
+ */
6803
+ function ewww_image_optimizer_array_search( $needle, $haystack ) {
6804
+ if ( ! is_array( $haystack ) ) {
6805
+ return false;
6806
+ }
6807
+ foreach ( $haystack as $straw ) {
6808
+ if ( ! is_string( $straw ) ) {
6809
+ continue;
6810
+ }
6811
+ if ( strpos( $straw, $needle ) !== false ) {
6812
+ return true;
6813
+ }
6814
+ }
6815
+ return false;
6816
+ }
6817
+
6818
+ /**
6819
+ * Retrieves a list of registered image sizes.
6820
+ *
6821
+ * @global array $_wp_additional_image_sizes
6822
+ *
6823
+ * @return array A list if image sizes.
6824
+ */
6825
+ function ewww_image_optimizer_get_image_sizes() {
6826
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6827
+ global $_wp_additional_image_sizes;
6828
+ $sizes = array();
6829
+ $image_sizes = get_intermediate_image_sizes();
6830
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
6831
+ ewwwio_debug_message( print_r( $image_sizes, true ) );
6832
+ }
6833
+ if ( false ) { // set to true to enable more debugging.
6834
+ ewwwio_debug_message( print_r( $_wp_additional_image_sizes, true ) );
6835
+ }
6836
+ if ( ewww_image_optimizer_iterable( $image_sizes ) ) {
6837
+ foreach ( $image_sizes as $_size ) {
6838
+ if ( in_array( $_size, array( 'thumbnail', 'medium', 'medium_large', 'large' ) ) ) {
6839
+ $sizes[ $_size ]['width'] = get_option( $_size . '_size_w' );
6840
+ $sizes[ $_size ]['height'] = get_option( $_size . '_size_h' );
6841
+ if ( 'medium_large' === $_size && 0 == $sizes[ $_size ]['width'] ) {
6842
+ $sizes[ $_size ]['width'] = '768';
6843
+ }
6844
+ if ( 'medium_large' === $_size && 0 == $sizes[ $_size ]['height'] ) {
6845
+ $sizes[ $_size ]['height'] = '9999';
6846
+ }
6847
+ } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
6848
+ $sizes[ $_size ] = array(
6849
+ 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
6850
+ 'height' => $_wp_additional_image_sizes[ $_size ]['height'],
6851
+ );
6852
+ }
6853
+ }
6854
+ }
6855
+ $sizes['pdf-full'] = array(
6856
+ 'width' => 99999,
6857
+ 'height' => 99999,
6858
+ );
6859
+
6860
+ if ( ewww_image_optimizer_function_exists( 'print_r' ) ) {
6861
+ ewwwio_debug_message( print_r( $sizes, true ) );
6862
+ }
6863
+ return $sizes;
6864
+ }
6865
+
6866
+ /**
6867
+ * Wrapper that displays the EWWW IO options in the multisite network admin.
6868
+ *
6869
+ * @global string $ewww_debug In memory debug log.
6870
+ */
6871
+ function ewww_image_optimizer_network_options() {
6872
+ add_filter( 'ewww_image_optimizer_settings', 'ewww_image_optimizer_filter_network_settings_page', 9 );
6873
+ ewww_image_optimizer_options( 'network-multisite' );
6874
+ }
6875
+
6876
+ /**
6877
+ * Wrapper that displays the EWWW IO options for multisite mode on a single site.
6878
+ *
6879
+ * By default, the only options displayed are the per-site resizes list, but a network admin can
6880
+ * permit site admins to configure their own blog settings.
6881
+ *
6882
+ * @global string $ewww_debug In memory debug log.
6883
+ */
6884
+ function ewww_image_optimizer_network_singlesite_options() {
6885
+ add_filter( 'ewww_image_optimizer_settings', 'ewww_image_optimizer_filter_network_singlesite_settings_page', 9 );
6886
+ ewww_image_optimizer_options( 'network-singlesite' );
6887
+ }
6888
+
6889
+ /**
6890
+ * Displays the EWWW IO options along with status information, and debugging information.
6891
+ *
6892
+ * @global string $ewww_debug In memory debug log.
6893
+ *
6894
+ * @param string $network Indicates which options should be shown in multisite installations.
6895
+ */
6896
+ function ewww_image_optimizer_options( $network = 'singlesite' ) {
6897
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6898
+ ewwwio_debug_message( 'ABSPATH: ' . ABSPATH );
6899
+ ewwwio_debug_message( 'WP_CONTENT_DIR: ' . WP_CONTENT_DIR );
6900
+ ewwwio_debug_message( 'home url: ' . get_home_url() );
6901
+ ewwwio_debug_message( 'site url: ' . get_site_url() );
6902
+ $network_class = $network;
6903
+ if ( empty( $network ) ) {
6904
+ $network_class = 'singlesite';
6905
+ }
6906
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
6907
+ // Need to include the plugin library for the is_plugin_active function.
6908
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
6909
+ }
6910
+ if ( 'debug-silent' !== $network ) {
6911
+ global $ewwwio_hs_beacon;
6912
+ $ewwwio_hs_beacon->admin_notice( $network_class );
6913
+ }
6914
+ if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) && ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) {
6915
+ $network_class = 'network-multisite';
6916
+ }
6917
+ $output = array();
6918
+ if ( isset( $_REQUEST['ewww_pngout'] ) ) {
6919
+ if ( 'success' == $_REQUEST['ewww_pngout'] ) {
6920
+ $output[] = "<div id='ewww-image-optimizer-pngout-success' class='updated fade'>\n";
6921
+ $output[] = '<p>' . esc_html__( 'Pngout was successfully installed, check the Plugin Status area for version information.', 'ewww-image-optimizer' ) . "</p>\n";
6922
+ $output[] = "</div>\n";
6923
+ }
6924
+ if ( 'failed' == $_REQUEST['ewww_pngout'] ) {
6925
+ $output[] = "<div id='ewww-image-optimizer-pngout-failure' class='error'>\n";
6926
+ $output[] = '<p>' . sprintf(
6927
+ /* translators: 1: An error message 2: The folder where pngout should be installed */
6928
+ esc_html__( 'Pngout was not installed: %1$s. Make sure this folder is writable: %2$s', 'ewww-image-optimizer' ),
6929
+ sanitize_text_field( $_REQUEST['ewww_error'] ), EWWW_IMAGE_OPTIMIZER_TOOL_PATH
6930
+ ) . "</p>\n";
6931
+ $output[] = "</div>\n";
6932
+ }
6933
+ }
6934
+ $output[] = "<script type='text/javascript'>\n" .
6935
+ 'jQuery(document).ready(function($) {$(".fade").fadeTo(5000,1).fadeOut(3000);});' . "\n" .
6936
+ "</script>\n";
6937
+ $output[] = "<style>\n" .
6938
+ ".ewww-tab span { font-size: 15px; font-weight: 700; color: #555; text-decoration: none; line-height: 36px; padding: 0 10px; }\n" .
6939
+ ".ewww-tab span:hover { color: #464646; }\n" .
6940
+ ".ewww-tab { margin: 0 0 0 5px; padding: 0px; border-width: 1px 1px 1px; border-style: solid solid none; border-image: none; border-color: #ccc; display: inline-block; background-color: #e4e4e4; cursor: pointer }\n" .
6941
+ ".ewww-tab:hover { background-color: #fff }\n" .
6942
+ ".ewww-selected { background-color: #f1f1f1; margin-bottom: -1px; border-bottom: 1px solid #f1f1f1 }\n" .
6943
+ ".ewww-selected span { color: #000; }\n" .
6944
+ ".ewww-selected:hover { background-color: #f1f1f1; }\n" .
6945
+ ".ewww-tab-nav { list-style: none; margin: 10px 0 0; padding-left: 5px; border-bottom: 1px solid #ccc; }\n" .
6946
+ "</style>\n";
6947
+ $output[] = "<div class='wrap'>\n";
6948
+ $output[] = "<h1>EWWW Image Optimizer</h1>\n";
6949
+ $output[] = "<div id='ewww-container-left' style='float: left; margin-right: 225px;'>\n";
6950
+ $output[] = "<p><a href='https://ewww.io/'>" . esc_html__( 'Plugin Home Page', 'ewww-image-optimizer' ) . '</a> | ' .
6951
+ "<a href='http://docs.ewww.io/'>" . esc_html__( 'Documentation', 'ewww-image-optimizer' ) . '</a> | ' .
6952
+ "<a href='https://ewww.io/contact-us/'>" . esc_html__( 'Plugin Support', 'ewww-image-optimizer' ) . '</a> | ' .
6953
+ "<a href='https://ewww.io/status/'>" . esc_html__( 'Cloud Status', 'ewww-image-optimizer' ) . '</a> | ' .
6954
+ "<a href='https://ewww.io/downloads/s3-image-optimizer/'>" . esc_html__( 'S3 Image Optimizer', 'ewww-image-optimizer' ) . "</a></p>\n";
6955
+ if ( 'network-multisite' == $network ) {
6956
+ $bulk_link = esc_html__( 'Media Library', 'ewww-image-optimizer' ) . ' -> ' . esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' );
6957
+ } else {
6958
+ $bulk_link = '<a href="upload.php?page=ewww-image-optimizer-bulk">' . esc_html__( 'Bulk Optimize', 'ewww-image-optimizer' ) . '</a>';
6959
+ }
6960
+ $s3_link = '<a href="https://ewww.io/downloads/s3-image-optimizer/">' . esc_html__( 'S3 Image Optimizer', 'ewww-image-optimizer' ) . '</a>';
6961
+ $output[] = '<p>' .
6962
+ sprintf(
6963
+ /* translators: %s: Bulk Optimize (link) */
6964
+ esc_html__( 'New images uploaded to the Media Library will be optimized automatically. If you have existing images you would like to optimize, you can use the %s tool.', 'ewww-image-optimizer' ),
6965
+ $bulk_link
6966
+ ) . ' ' .
6967
+ sprintf(
6968
+ /* translators: %s: S3 Image Optimizer (link) */
6969
+ esc_html__( 'Images stored in an Amazon S3 bucket can be optimized using our %s.' ),
6970
+ $s3_link
6971
+ ) .
6972
+ "</p>\n";
6973
+ $status_output = "<div id='ewww-widgets' class='metabox-holder'><div class='meta-box-sortables'><div id='ewww-status' class='postbox'>\n" .
6974
+ "<h2 class='hndle'>" . esc_html__( 'Plugin Status', 'ewww-image-optimizer' ) . "&emsp;STATUS_PLACEHOLDER</h2>\n<div class='inside'>";
6975
+ $total_savings = ewww_image_optimizer_savings();
6976
+ if ( $total_savings ) {
6977
+ $status_output .= '<b>' . esc_html__( 'Total Savings:', 'ewww-image-optimizer' ) . "</b> <span id='ewww-total-savings'>" . ewww_image_optimizer_size_format( $total_savings, 2 ) . '</span><br>';
6978
+ }
6979
+ ewwwio_debug_message( ewww_image_optimizer_aux_images_table_count() . ' images have been optimized' );
6980
+ // If this remains true, then we display an all clear, otherwise something needs fixing. TODO: rename this someday.
6981
+ $collapsible = true;
6982
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
6983
+ $status_output .= '<p><b>' . esc_html__( 'Cloud optimization API Key', 'ewww-image-optimizer' ) . ':</b> ';
6984
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_cloud_exceeded', 0 );
6985
+ $verify_cloud = ewww_image_optimizer_cloud_verify( false );
6986
+ if ( preg_match( '/great/', $verify_cloud ) ) {
6987
+ $status_output .= '<span style="color: green">' . esc_html__( 'Verified,', 'ewww-image-optimizer' ) . ' </span>' . ewww_image_optimizer_cloud_quota();
6988
+ } elseif ( preg_match( '/exceeded/', $verify_cloud ) ) {
6989
+ $status_output .= '<span style="color: orange">' . esc_html__( 'Verified,', 'ewww-image-optimizer' ) . ' </span>' . ewww_image_optimizer_cloud_quota();
6990
+ $collapsible = false;
6991
+ } else {
6992
+ $status_output .= '<span style="color: red">' . esc_html__( 'Not Verified', 'ewww-image-optimizer' ) . '</span>';
6993
+ $collapsible = false;
6994
+ }
6995
+ $status_output .= "</p>\n";
6996
+ $disable_level = '';
6997
+ } else {
6998
+ $disable_level = "disabled='disabled'";
6999
+ }
7000
+ if ( ! ewww_image_optimizer_full_cloud() && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7001
+ list ( $jpegtran_src, $optipng_src, $gifsicle_src, $jpegtran_dst, $optipng_dst, $gifsicle_dst ) = ewww_image_optimizer_install_paths();
7002
+ }
7003
+ $skip = ewww_image_optimizer_skip_tools();
7004
+ $status_output .= "<p>\n";
7005
+ if ( ! $skip['jpegtran'] && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7006
+ $status_output .= '<b>jpegtran:</b> ';
7007
+ if ( EWWW_IMAGE_OPTIMIZER_JPEGTRAN ) {
7008
+ $jpegtran_installed = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_JPEGTRAN, 'j' );
7009
+ if ( ! $jpegtran_installed ) {
7010
+ $jpegtran_installed = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_JPEGTRAN, 'jb' );
7011
+ }
7012
+ }
7013
+ if ( ! empty( $jpegtran_installed ) ) {
7014
+ $status_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;' . esc_html__( 'version', 'ewww-image-optimizer' ) . ': ' . $jpegtran_installed . "<br />\n";
7015
+ } else {
7016
+ $status_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span><br />' . "\n";
7017
+ $collapsible = false;
7018
+ }
7019
+ }
7020
+ if ( ! $skip['optipng'] && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7021
+ $status_output .= '<b>optipng:</b> ';
7022
+ if ( EWWW_IMAGE_OPTIMIZER_OPTIPNG ) {
7023
+ $optipng_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_OPTIPNG, 'o' );
7024
+ if ( ! $optipng_version ) {
7025
+ $optipng_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_OPTIPNG, 'ob' );
7026
+ }
7027
+ }
7028
+ if ( ! empty( $optipng_version ) ) {
7029
+ $status_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;' . esc_html__( 'version', 'ewww-image-optimizer' ) . ': ' . $optipng_version . "<br />\n";
7030
+ } else {
7031
+ $status_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span><br />' . "\n";
7032
+ $collapsible = false;
7033
+ }
7034
+ }
7035
+ if ( ! $skip['pngout'] && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7036
+ $status_output .= '<b>pngout:</b> ';
7037
+ if ( EWWW_IMAGE_OPTIMIZER_PNGOUT ) {
7038
+ $pngout_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_PNGOUT, 'p' );
7039
+ if ( ! $pngout_version ) {
7040
+ $pngout_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_PNGOUT, 'pb' );
7041
+ }
7042
+ }
7043
+ if ( ! empty( $pngout_version ) ) {
7044
+ $status_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;' . esc_html__( 'version', 'ewww-image-optimizer' ) . ': ' . preg_replace( '/PNGOUT \[.*\)\s*?/', '', $pngout_version ) . "<br />\n";
7045
+ } else {
7046
+ $status_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span>&emsp;' . esc_html__( 'Install', 'ewww-image-optimizer' ) . ' <a href="admin.php?action=ewww_image_optimizer_install_pngout">' . esc_html__( 'automatically', 'ewww-image-optimizer' ) . '</a> | <a href="http://advsys.net/ken/utils.htm">' . esc_html__( 'manually', 'ewww-image-optimizer' ) . '</a> - ' . esc_html__( 'Pngout is free closed-source software that can produce drastically reduced filesizes for PNGs, but can be very time consuming to process images', 'ewww-image-optimizer' ) . "<br />\n";
7047
+ $collapsible = false;
7048
+ }
7049
+ }
7050
+ if ( $skip['pngout'] && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 10 ) {
7051
+ $status_output .= '<b>pngout:</b> ' . esc_html__( 'Not installed, enable in Advanced Settings', 'ewww-image-optimizer' ) . "<br />\n";
7052
+ }
7053
+ if ( ! $skip['gifsicle'] && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7054
+ $status_output .= '<b>gifsicle:</b> ';
7055
+ if ( EWWW_IMAGE_OPTIMIZER_GIFSICLE ) {
7056
+ $gifsicle_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_GIFSICLE, 'g' );
7057
+ if ( ! $gifsicle_version ) {
7058
+ $gifsicle_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_GIFSICLE, 'gb' );
7059
+ }
7060
+ }
7061
+ if ( ! empty( $gifsicle_version ) ) {
7062
+ $status_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;' . esc_html__( 'version', 'ewww-image-optimizer' ) . ': ' . $gifsicle_version . "<br />\n";
7063
+ } else {
7064
+ $status_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span><br />' . "\n";
7065
+ $collapsible = false;
7066
+ }
7067
+ }
7068
+ if ( ! $skip['pngquant'] && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7069
+ $status_output .= '<b>pngquant:</b> ';
7070
+ if ( EWWW_IMAGE_OPTIMIZER_PNGQUANT ) {
7071
+ $pngquant_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_PNGQUANT, 'q' );
7072
+ if ( ! $pngquant_version ) {
7073
+ $pngquant_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_PNGQUANT, 'qb' );
7074
+ }
7075
+ }
7076
+ if ( ! empty( $pngquant_version ) ) {
7077
+ $status_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;' . esc_html__( 'version', 'ewww-image-optimizer' ) . ': ' . $pngquant_version . "<br />\n";
7078
+ } else {
7079
+ $status_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span><br />' . "\n";
7080
+ $collapsible = false;
7081
+ }
7082
+ }
7083
+ if ( EWWW_IMAGE_OPTIMIZER_WEBP && ! $skip['webp'] && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7084
+ $status_output .= '<b>webp:</b> ';
7085
+ if ( EWWW_IMAGE_OPTIMIZER_WEBP ) {
7086
+ $webp_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_WEBP, 'w' );
7087
+ if ( ! $webp_version ) {
7088
+ $webp_version = ewww_image_optimizer_tool_found( EWWW_IMAGE_OPTIMIZER_WEBP, 'wb' );
7089
+ }
7090
+ }
7091
+ if ( ! empty( $webp_version ) ) {
7092
+ $status_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;' . esc_html__( 'version', 'ewww-image-optimizer' ) . ': ' . $webp_version . "<br />\n";
7093
+ } else {
7094
+ $status_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span><br />' . "\n";
7095
+ $collapsible = false;
7096
+ }
7097
+ }
7098
+ if ( ! ewww_image_optimizer_full_cloud() ) {
7099
+ if ( ewww_image_optimizer_safemode_check() ) {
7100
+ $status_output .= 'safe mode: <span style="color: red; font-weight: bolder">' . esc_html__( 'On', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7101
+ $collapsible = false;
7102
+ } else {
7103
+ $status_output .= 'safe mode: <span style="color: green; font-weight: bolder">' . esc_html__( 'Off', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7104
+ }
7105
+ if ( ewww_image_optimizer_exec_check() ) {
7106
+ $status_output .= 'exec(): <span style="color: red; font-weight: bolder">' . esc_html__( 'Disabled', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7107
+ $collapsible = false;
7108
+ } else {
7109
+ $status_output .= 'exec(): <span style="color: green; font-weight: bolder">' . esc_html__( 'Enabled', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7110
+ }
7111
+ $status_output .= "<br />\n";
7112
+ }
7113
+ if ( ! ewww_image_optimizer_full_cloud() && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7114
+ $toolkit_output = sprintf(
7115
+ /* translators: %s: Graphics libraries - */
7116
+ esc_html__( '%s only need one, used for conversion, not optimization', 'ewww-image-optimizer' ),
7117
+ '<b>' . __( 'Graphics libraries', 'ewww-image-optimizer' ) . '</b> - '
7118
+ );
7119
+ $toolkit_output .= '<br>';
7120
+ $toolkit_found = false;
7121
+ if ( ewww_image_optimizer_gd_support() ) {
7122
+ $toolkit_output .= 'GD: <span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' );
7123
+ $toolkit_found = true;
7124
+ } else {
7125
+ $toolkit_output .= 'GD: <span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' );
7126
+ }
7127
+ $toolkit_output .= '</span>&emsp;&emsp;' .
7128
+ 'Gmagick: ';
7129
+ if ( ewww_image_optimizer_gmagick_support() ) {
7130
+ $toolkit_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' );
7131
+ $toolkit_found = true;
7132
+ } else {
7133
+ $toolkit_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' );
7134
+ }
7135
+ $toolkit_output .= '</span>&emsp;&emsp;' .
7136
+ 'Imagick: ';
7137
+ if ( ewww_image_optimizer_imagick_support() ) {
7138
+ $toolkit_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' );
7139
+ $toolkit_found = true;
7140
+ } else {
7141
+ $toolkit_output .= '<span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' );
7142
+ }
7143
+ $toolkit_output .= "</span><br />\n";
7144
+ if ( ! $toolkit_found && ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' ) || ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' ) ) ) {
7145
+ $collapsible = false;
7146
+ } else {
7147
+ $toolkit_output = str_replace( 'color: red', 'color: gray', $toolkit_output );
7148
+ }
7149
+ $status_output .= $toolkit_output;
7150
+ } // End if().
7151
+ $mimetype_output = '<b>' . esc_html__( 'Only need one of these:', 'ewww-image-optimizer' ) . ' </b><br>';
7152
+ // Initialize this variable to check for the 'file' command if we don't have any php libraries we can use.
7153
+ $file_command_check = true;
7154
+ $mimetype_tool = '';
7155
+ if ( function_exists( 'finfo_file' ) ) {
7156
+ $mimetype_output .= 'fileinfo: <span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7157
+ $file_command_check = false;
7158
+ $mimetype_tool = 'fileinfo';
7159
+ } else {
7160
+ $mimetype_output .= 'fileinfo: <span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7161
+ }
7162
+ if ( function_exists( 'getimagesize' ) ) {
7163
+ $mimetype_output .= 'getimagesize(): <span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7164
+ if ( ewww_image_optimizer_full_cloud() || EWWW_IMAGE_OPTIMIZER_NOEXEC || PHP_OS == 'WINNT' ) {
7165
+ $file_command_check = false;
7166
+ }
7167
+ $mimetype_tool = empty( $mimetype_tool ) ? 'getimagesize' : $mimetype_tool;
7168
+ } else {
7169
+ $mimetype_output .= 'getimagesize(): <span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span>&emsp;&emsp;';
7170
+ }
7171
+ if ( function_exists( 'mime_content_type' ) ) {
7172
+ $mimetype_output .= 'mime_content_type(): <span style="color: green; font-weight: bolder">' . esc_html__( 'Installed', 'ewww-image-optimizer' ) . '</span><br />' . "\n";
7173
+ $file_command_check = false;
7174
+ $mimetype_tool = empty( $mimetype_tool ) ? 'mime_content_type' : $mimetype_tool;
7175
+ } else {
7176
+ $mimetype_output .= 'mime_content_type(): <span style="color: red; font-weight: bolder">' . esc_html__( 'Missing', 'ewww-image-optimizer' ) . '</span><br />' . "\n";
7177
+ }
7178
+ $extra_tool_output = '';
7179
+ if ( PHP_OS != 'WINNT' && ! ewww_image_optimizer_full_cloud() && ! EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
7180
+ if ( $file_command_check && ! ewww_image_optimizer_find_nix_binary( 'file', 'f' ) ) {
7181
+ $extra_tool_output .= '<span style="color: red; font-weight: bolder">file: ' . esc_html__( 'command not found on your system', 'ewww-image-optimizer' ) . '</span><br />';
7182
+ $collapsible = false;
7183
+ } elseif ( empty( $mimetype_tool ) ) {
7184
+ $mimetype_tool = 'file'; // So that we know a mimetype library was found.
7185
+ }
7186
+ if ( ! ewww_image_optimizer_find_nix_binary( 'nice', 'n' ) ) {
7187
+ $extra_tool_output .= '<span style="color: orange; font-weight: bolder">nice: ' . esc_html__( 'command not found on your system', 'ewww-image-optimizer' ) . ' (' . esc_html__( 'not required', 'ewww-image-optimizer' ) . ')</span><br />';
7188
+ }
7189
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) && ! $skip['pngout'] && PHP_OS != 'SunOS' && ! ewww_image_optimizer_find_nix_binary( 'tar', 't' ) ) {
7190
+ $extra_tool_output .= '<span style="color: red; font-weight: bolder">tar: ' . esc_html__( 'command not found on your system', 'ewww-image-optimizer' ) . ' (' . esc_html__( 'required for automatic pngout installer', 'ewww-image-optimizer' ) . ')</span><br />';
7191
+ $collapsible = false;
7192
+ }
7193
+ } elseif ( $file_command_check ) {
7194
+ $collapsible = false;
7195
+ }
7196
+ if ( 'getimagesize' == $mimetype_tool && ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) ) {
7197
+ $collapsible = false;
7198
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_pdf_level', 0 );
7199
+ } elseif ( $mimetype_tool ) {
7200
+ $mimetype_output = str_replace( 'color: red', 'color: gray', $mimetype_output );
7201
+ }
7202
+ ewwwio_debug_message( "mimetype function in use: $mimetype_tool" );
7203
+ $status_output .= $mimetype_output;
7204
+ $status_output .= $extra_tool_output;
7205
+ $status_output .= '<strong>' . esc_html( 'Background and Parallel optimization (faster uploads):', 'ewww-image-optimizer' ) . '</strong><br>';
7206
+ if ( defined( 'EWWW_DISABLE_ASYNC' ) && EWWW_DISABLE_ASYNC ) {
7207
+ $status_output .= '<span>' . esc_html__( 'Disabled by administrator', 'ewww-image-optimizer' ) . '</span>';
7208
+ } elseif ( ! ewww_image_optimizer_function_exists( 'sleep' ) ) {
7209
+ $status_output .= '<span style="color: orange; font-weight: bolder">' . esc_html__( 'Disabled, sleep function missing', 'ewww-image-optimizer' ) . '</span>';
7210
+ } elseif ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_background_optimization' ) ) {
7211
+ $status_output .= '<span style="color: orange; font-weight: bolder">' . esc_html__( 'Disabled automatically, async requests blocked', 'ewww-image-optimizer' ) . '</span>';
7212
+ } elseif ( ewww_image_optimizer_detect_wpsf_location_lock() ) {
7213
+ $status_output .= '<span style="color: orange; font-weight: bolder">' . esc_html__( "Disabled by Shield's Lock to Location feature", 'ewww-image-optimizer' ) . '</p>';
7214
+ } elseif ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_parallel_optimization' ) ) {
7215
+ $status_output .= '<span>' . esc_html__( 'Enable Parallel Optimization in Advanced Settings', 'ewww-image-optimizer' ) . '</span>';
7216
+ } else {
7217
+ $status_output .= '<span style="color: green; font-weight: bolder">' . esc_html__( 'Fully Enabled', 'ewww-image-optimizer' ) . '</span>';
7218
+ }
7219
+ $status_output .= '</div><!-- end .inside -->';
7220
+ $status_output .= "</div></div></div>\n";
7221
+ if ( $collapsible ) {
7222
+ $output[] = str_replace( 'STATUS_PLACEHOLDER', "<span id='ewww-status-ok' style='color: green;'>" . esc_html__( 'All Clear', 'ewww-image-optimizer' ) . '</span>', $status_output );
7223
+ } else {
7224
+ $output[] = str_replace( 'STATUS_PLACEHOLDER', "<span id='ewww-status-attention' style='color: red;'>" . esc_html__( 'Requires Attention', 'ewww-image-optimizer' ) . '</span>', $status_output );
7225
+ }
7226
+ if ( ( 'network-multisite' != $network || ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) && // Display tabs so long as this isn't the network admin OR single-site override is disabled.
7227
+ ! ( 'network-singlesite' == $network && ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) ) { // Also make sure that this isn't single site without override mode.
7228
+ $output[] = "<ul class='ewww-tab-nav'>\n" .
7229
+ "<li class='ewww-tab ewww-general-nav'><span class='ewww-tab-hidden'>" . esc_html__( 'Basic Settings', 'ewww-image-optimizer' ) . "</span></li>\n" .
7230
+ "<li class='ewww-tab ewww-optimization-nav'><span class='ewww-tab-hidden'>" . esc_html__( 'Advanced Settings', 'ewww-image-optimizer' ) . "</span></li>\n" .
7231
+ "<li class='ewww-tab ewww-conversion-nav'><span class='ewww-tab-hidden'>" . esc_html__( 'Conversion Settings', 'ewww-image-optimizer' ) . "</span></li>\n" .
7232
+ "<li class='ewww-tab ewww-webp-nav'><span class='ewww-tab-hidden'>" . esc_html__( 'WebP Settings', 'ewww-image-optimizer' ) . "</span></li>\n" .
7233
+ "</ul>\n";
7234
+ }
7235
+ if ( 'network-multisite' == $network ) {
7236
+ $output[] = "<form method='post' action=''>\n";
7237
+ } else {
7238
+ $output[] = "<form method='post' action='options.php'>\n";
7239
+ }
7240
+ $output[] = "<input type='hidden' name='option_page' value='ewww_image_optimizer_options' />\n";
7241
+ $output[] = "<input type='hidden' name='action' value='update' />\n";
7242
+ $output[] = wp_nonce_field( 'ewww_image_optimizer_options-options', '_wpnonce', true, false ) . "\n";
7243
+ $output[] = "<div id='ewww-general-settings'>\n";
7244
+ $output[] = "<table class='form-table'>\n";
7245
+ if ( is_multisite() ) {
7246
+ $output[] = "<tr class='network-only'><th><label for='ewww_image_optimizer_allow_multisite_override'>" . esc_html__( 'Allow Single-site Override', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_allow_multisite_override' name='ewww_image_optimizer_allow_multisite_override' value='true' " . ( get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'Allow individual sites to configure their own settings and override all network options.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7247
+ if ( 'network-multisite' == $network && get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) ) {
7248
+ $output[] = "<tr><th><label for='ewww_image_optimizer_allow_tracking'>" . esc_html__( 'Allow Usage Tracking?', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_allow_tracking' name='ewww_image_optimizer_allow_tracking' value='true' " . ( get_site_option( 'ewww_image_optimizer_allow_tracking' ) == true ? "checked='true'" : '' ) . ' /> ' .
7249
+ esc_html__( 'Allow EWWW Image Optimizer to anonymously track how this plugin is used and help us make the plugin better. Opt-in to tracking and receive 500 API image credits free. No sensitive data is tracked.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7250
+ $output[] = "<input type='hidden' id='ewww_image_optimizer_allow_multisite_override_active' name='ewww_image_optimizer_allow_multisite_override_active' value='0'>";
7251
+ foreach ( $output as $line ) {
7252
+ echo $line;
7253
+ }
7254
+ echo '</table></div><!-- end container general settings -->';
7255
+ echo "<p class='submit'><input type='submit' class='button-primary' value='" . esc_attr__( 'Save Changes', 'ewww-image-optimizer' ) . "' /></p>\n";
7256
+ echo '</form></div><!-- end container left --></div><!-- end container wrap -->';
7257
+ return;
7258
+ }
7259
+ }
7260
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
7261
+ $blog_id = get_current_blog_id();
7262
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_cloud_notkey'>" . esc_html__( 'Cloud optimization API Key', 'ewww-image-optimizer' ) . "</label></th><td><input type='text' id='ewww_image_optimizer_cloud_notkey' name='ewww_image_optimizer_cloud_notkey' readonly='readonly' value='****************************" . substr( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ), 28 ) . "' size='32' /> <a href='admin.php?action=ewww_image_optimizer_remove_cloud_key&site=$blog_id'>" . esc_html__( 'Remove API key.', 'ewww-image-optimizer' ) . "</a></td></tr>\n";
7263
+ $output[] = "<input type='hidden' id='ewww_image_optimizer_cloud_key' name='ewww_image_optimizer_cloud_key' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) . "' />\n";
7264
+ } else {
7265
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_cloud_key'>" . esc_html__( 'Cloud optimization API Key', 'ewww-image-optimizer' ) . "</label></th><td><input type='text' id='ewww_image_optimizer_cloud_key' name='ewww_image_optimizer_cloud_key' value='' size='32' /> " . esc_html__( 'API Key will be validated when you save your settings.', 'ewww-image-optimizer' ) . " <a href='https://ewww.io/plans/'>" . esc_html__( 'Purchase an API key.', 'ewww-image-optimizer' ) . "</a></td></tr>\n";
7266
+ }
7267
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_debug'>" . esc_html__( 'Debugging', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_debug' name='ewww_image_optimizer_debug' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'Use this to provide information for support purposes, or if you feel comfortable digging around in the code to fix a problem you are experiencing.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7268
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_jpegtran_copy'>" . esc_html__( 'Remove metadata', 'ewww-image-optimizer' ) . "</label></th>\n" .
7269
+ "<td><input type='checkbox' id='ewww_image_optimizer_jpegtran_copy' name='ewww_image_optimizer_jpegtran_copy' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'This will remove ALL metadata: EXIF, comments, color profiles, and anything else that is not pixel data.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7270
+ ewwwio_debug_message( 'remove metadata: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) == true ? 'on' : 'off' ) );
7271
+ $output[] = "<tr class='$network_class'><th>&nbsp;</th><td>" .
7272
+ "<p class='$network_class description'>" . esc_html__( 'All methods used by the EWWW Image Optimizer are intended to produce visually identical images.', 'ewww-image-optimizer' ) .
7273
+ ' ' . esc_html__( 'Lossless compression is actually identical to the original, while lossy reduces the quality a small amount.', 'ewww-image-optimizer' ) . "</p>\n" .
7274
+ ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ? "<p class='$network_class nocloud'><strong>* <a href='https://ewww.io/plans/' target='_blank'>" . esc_html__( 'Get an API key to achieve up to 80% compression and see the quality for yourself.', 'ewww-image-optimizer' ) . "</a></strong></p>\n" : '' );
7275
+ $output[] = "</td></tr>\n";
7276
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_jpg_level'>" . esc_html__( 'JPG Optimization Level', 'ewww-image-optimizer' ) . "</label></th>\n" .
7277
+ "<td><span><select id='ewww_image_optimizer_jpg_level' name='ewww_image_optimizer_jpg_level'>\n" .
7278
+ "<option value='0'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ), 0, false ) . '>' . esc_html__( 'No Compression', 'ewww-image-optimizer' ) . "</option>\n";
7279
+ if ( 'ewww-image-optimizer' !== 'ewww-image-optimizer-cloud' ) {
7280
+ $output[] = "<option class='$network_class' value='10'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ), 10, false ) . '>' . esc_html__( 'Lossless Compression', 'ewww-image-optimizer' ) . "</option>\n";
7281
+ }
7282
+ $output[] = "<option class='$network_class' $disable_level value='20'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ), 20, false ) . '>' . esc_html__( 'Maximum Lossless Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7283
+ "<option $disable_level value='30'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ), 30, false ) . '>' . esc_html__( 'Lossy Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7284
+ "<option $disable_level value='40'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ), 40, false ) . '>' . esc_html__( 'Maximum Lossy Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7285
+ "</select></td></tr>\n";
7286
+ ewwwio_debug_message( 'jpg level: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) );
7287
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_png_level'>" . esc_html__( 'PNG Optimization Level', 'ewww-image-optimizer' ) . "</label></th>\n" .
7288
+ "<td><span><select id='ewww_image_optimizer_png_level' name='ewww_image_optimizer_png_level'>\n" .
7289
+ "<option value='0'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ), 0, false ) . '>' . esc_html__( 'No Compression', 'ewww-image-optimizer' ) . "</option>\n";
7290
+ if ( 'ewww-image-optimizer' !== 'ewww-image-optimizer-cloud' ) {
7291
+ $output[] = "<option class='$network_class' value='10'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ), 10, false ) . '>' . esc_html__( 'Lossless Compression', 'ewww-image-optimizer' ) . "</option>\n";
7292
+ }
7293
+ $output[] = "<option class='$network_class' $disable_level value='20'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ), 20, false ) . '>' . esc_html__( 'Better Lossless Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7294
+ "<option $disable_level value='30'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ), 30, false ) . '>' . esc_html__( 'Maximum Lossless Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7295
+ "<option value='40'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ), 40, false ) . '>' . esc_html__( 'Lossy Compression', 'ewww-image-optimizer' ) . "</option>\n" .
7296
+ "<option $disable_level value='50'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ), 50, false ) . '>' . esc_html__( 'Maximum Lossy Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7297
+ "</select></td></tr>\n";
7298
+ ewwwio_debug_message( 'png level: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) );
7299
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_gif_level'>" . esc_html__( 'GIF Optimization Level', 'ewww-image-optimizer' ) . "</label></th>\n" .
7300
+ "<td><span><select id='ewww_image_optimizer_gif_level' name='ewww_image_optimizer_gif_level'>\n" .
7301
+ "<option value='0'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ), 0, false ) . '>' . esc_html__( 'No Compression', 'ewww-image-optimizer' ) . "</option>\n" .
7302
+ "<option value='10'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ), 10, false ) . '>' . esc_html__( 'Lossless Compression', 'ewww-image-optimizer' ) . "</option>\n" .
7303
+ "</select></td></tr>\n";
7304
+ ewwwio_debug_message( 'gif level: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) );
7305
+ $disable_pdf_level = $disable_level;
7306
+ if ( 'getimagesize' == $mimetype_tool ) {
7307
+ $disable_pdf_level = "disabled='disabled'";
7308
+ $output[] = "<tr class='$network_class'><th>&nbsp;</th><td>";
7309
+ $output[] = "<p class='$network_class description'>" . esc_html__( '*PDF optimization cannot be enabled because the fileinfo extension is missing.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7310
+ }
7311
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_pdf_level'>" . esc_html__( 'PDF Optimization Level', 'ewww-image-optimizer' ) . "</label></th>\n" .
7312
+ "<td><span><select id='ewww_image_optimizer_pdf_level' name='ewww_image_optimizer_pdf_level'>\n" .
7313
+ "<option value='0'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ), 0, false ) . '>' . esc_html__( 'No Compression', 'ewww-image-optimizer' ) . "</option>\n" .
7314
+ "<option $disable_pdf_level value='10'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ), 10, false ) . '>' . esc_html__( 'Lossless Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7315
+ "<option $disable_pdf_level value='20'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ), 20, false ) . '>' . esc_html__( 'Lossy Compression', 'ewww-image-optimizer' ) . " *</option>\n" .
7316
+ "</select></td></tr>\n";
7317
+ ewwwio_debug_message( 'pdf level: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) );
7318
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_delay'>" . esc_html__( 'Bulk Delay', 'ewww-image-optimizer' ) . "</label></th><td><input type='text' id='ewww_image_optimizer_delay' name='ewww_image_optimizer_delay' size='5' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) . "'> " . esc_html__( 'Choose how long to pause between images (in seconds, 0 = disabled)', 'ewww-image-optimizer' ) . "</td></tr>\n";
7319
+ ewwwio_debug_message( 'bulk delay: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_delay' ) );
7320
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_backup_files'>" . esc_html__( 'Backup Originals', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_backup_files' name='ewww_image_optimizer_backup_files' value='true' " .
7321
+ ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_backup_files' ) ? "checked='true'" : '' ) . " $disable_level > " . esc_html__( 'Store a copy of your original images on our secure server for 30 days. *Requires an active API key.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7322
+ ewwwio_debug_message( 'backup mode: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_backup_files' ) ? 'on' : 'off' ) );
7323
+ if ( class_exists( 'Cloudinary' ) && Cloudinary::config_get( 'api_secret' ) ) {
7324
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_enable_cloudinary'>" . esc_html__( 'Automatic Cloudinary upload', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_enable_cloudinary' name='ewww_image_optimizer_enable_cloudinary' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_cloudinary' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'When enabled, uploads to the Media Library will be transferred to Cloudinary after optimization. Cloudinary generates resizes, so only the full-size image is uploaded.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7325
+ ewwwio_debug_message( 'cloudinary upload: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_cloudinary' ) == true ? 'on' : 'off' ) );
7326
+ }
7327
+ $output[] = "</table>\n</div>\n";
7328
+ $output[] = "<div id='ewww-optimization-settings'>\n";
7329
+ $output[] = "<table class='form-table'>\n";
7330
+ if ( ewww_image_optimizer_full_cloud() ) {
7331
+ $output[] = "<input class='$network_class' id='ewww_image_optimizer_optipng_level' name='ewww_image_optimizer_optipng_level' type='hidden' value='2'>\n" .
7332
+ "<input id='ewww_image_optimizer_pngout_level' name='ewww_image_optimizer_pngout_level' type='hidden' value='2'>\n" .
7333
+ "<input id='ewww_image_optimizer_disable_pngout' name='ewww_image_optimizer_disable_pngout' type='hidden' value='true'/>\n";
7334
+ } else {
7335
+ $optipng_levels = array( 0, 1, 8, 16, 24, 48 );
7336
+ $optipng_levels_output = '';
7337
+ foreach ( $optipng_levels as $level => $trials ) {
7338
+ if ( empty( $level ) ) {
7339
+ continue;
7340
+ }
7341
+ /* translators: %d: a number 0-5 */
7342
+ $optipng_levels_output .= "<option value='$level'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' ), $level, false ) . '>' . sprintf( esc_html__( 'Level %d', 'ewww-image-optimizer' ), $level ) . ': ' .
7343
+ /* translators: %d: a number 1-48 */
7344
+ sprintf( esc_html( _n( '%d trial', '%d trials', $trials, 'ewww-image-optimizer' ) ), $trials ) . "</option>\n";
7345
+ }
7346
+ $output[] = "<tr class='$network_class nocloud'><th><label for='ewww_image_optimizer_optipng_level'>" . esc_html__( 'optipng optimization level', 'ewww-image-optimizer' ) . "</label></th>\n" .
7347
+ "<td><span><select id='ewww_image_optimizer_optipng_level' name='ewww_image_optimizer_optipng_level'>\n" .
7348
+ $optipng_levels_output .
7349
+ '</select> (' . esc_html__( 'default', 'ewww-image-optimizer' ) . "=2)</span>\n" .
7350
+ "<p class='description'>" . esc_html__( 'Levels 4 and above are unlikely to yield any additional savings.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7351
+ ewwwio_debug_message( 'optipng level: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' ) );
7352
+ $output[] = "<tr class='$network_class nocloud'><th><label for='ewww_image_optimizer_disable_pngout'>" . esc_html__( 'disable', 'ewww-image-optimizer' ) . " pngout</label></th><td><input type='checkbox' id='ewww_image_optimizer_disable_pngout' name='ewww_image_optimizer_disable_pngout' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) == true ? "checked='true'" : '' ) . " /></td><tr>\n";
7353
+ ewwwio_debug_message( 'pngout disabled: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) == true ? 'yes' : 'no' ) );
7354
+ $pngout_levels = array(
7355
+ esc_html__( 'Xtreme! (Slowest)', 'ewww-image-optimizer' ),
7356
+ esc_html__( 'Intense (Slow)', 'ewww-image-optimizer' ),
7357
+ esc_html__( 'Longest Match (Fast)', 'ewww-image-optimizer' ),
7358
+ esc_html__( 'Huffman Only (Faster)', 'ewww-image-optimizer' ),
7359
+ );
7360
+ $pngout_levels_output = '';
7361
+ foreach ( $pngout_levels as $level => $description ) {
7362
+ /* translators: %d: a number 0-5 */
7363
+ $pngout_levels_output .= "<option value='$level'" . selected( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' ), $level, false ) . '>' . sprintf( esc_html__( 'Level %d', 'ewww-image-optimizer' ), $level ) . ": $description</option>\n";
7364
+ }
7365
+ $output[] = "<tr class='$network_class nocloud'><th><label for='ewww_image_optimizer_pngout_level'>" . esc_html__( 'pngout optimization level', 'ewww-image-optimizer' ) . "</label></th>\n" .
7366
+ "<td><span><select id='ewww_image_optimizer_pngout_level' name='ewww_image_optimizer_pngout_level'>\n" .
7367
+ $pngout_levels_output .
7368
+ '</select> (' . esc_html__( 'default', 'ewww-image-optimizer' ) . "=2)</span>\n" .
7369
+ "<p class='description'>" . esc_html__( 'If you have CPU cycles to spare, go with level 0', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7370
+ ewwwio_debug_message( 'pngout level: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' ) );
7371
+ } // End if().
7372
+ $output[] = "<tr class='$network_class'><th><span><label for='ewww_image_optimizer_jpg_quality'>" . esc_html__( 'JPG quality level:', 'ewww-image-optimizer' ) . "</label></th><td><input type='text' id='ewww_image_optimizer_jpg_quality' name='ewww_image_optimizer_jpg_quality' class='small-text' value='" . ewww_image_optimizer_jpg_quality() . "' /> " . esc_html__( 'Valid values are 1-100.', 'ewww-image-optimizer' ) . "\n<p class='description'>" . esc_html__( 'Use this to override the default WordPress quality level of 82. Applies to image editing, resizing, PNG to JPG conversion, and JPG to WebP conversion. Does not affect the original uploaded image unless maximum dimensions are set and resizing occurs.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7373
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_parallel_optimization'>" . esc_html__( 'Parallel optimization', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_parallel_optimization' name='ewww_image_optimizer_parallel_optimization' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_parallel_optimization' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'All resizes generated from a single upload are optimized in parallel for faster optimization. If this is causing performance issues, disable parallel optimization to reduce the load on your server.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7374
+ ewwwio_debug_message( 'parallel optimization: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_parallel_optimization' ) == true ? 'on' : 'off' ) );
7375
+ ewwwio_debug_message( 'background optimization: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_background_optimization' ) == true ? 'on' : 'off' ) );
7376
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_auto'>" . esc_html__( 'Scheduled optimization', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_auto' name='ewww_image_optimizer_auto' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_auto' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'This will enable scheduled optimization of unoptimized images for your theme, buddypress, and any additional folders you have configured below. Runs hourly: wp_cron only runs when your site is visited, so it may be even longer between optimizations.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7377
+ ewwwio_debug_message( 'scheduled optimization: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_auto' ) == true ? 'on' : 'off' ) );
7378
+ $media_include_disable = '';
7379
+ if ( get_option( 'ewww_image_optimizer_disable_resizes_opt' ) ) {
7380
+ $media_include_disable = 'disabled="disabled"';
7381
+ $output[] = "<tr class='$network_class'><th>&nbsp;</th><td>" .
7382
+ '<p><span style="color: green">' . esc_html__( '*Include Media Library Folders has been disabled because it will cause the scanner to ignore the disabled resizes.', 'ewww-image-optimizer' ) . "</span></p></td></tr>\n";
7383
+ }
7384
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_include_media_paths'>" . esc_html__( 'Include Media Library Folders', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_include_media_paths' name='ewww_image_optimizer_include_media_paths' $media_include_disable value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_include_media_paths' ) == true && ! get_option( 'ewww_image_optimizer_disable_resizes_opt' ) ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'Scan all images from the latest two folders of the Media Library during the Bulk Optimizer and Scheduled Optimization.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7385
+ ewwwio_debug_message( 'include media library: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_include_media_paths' ) == true ? 'on' : 'off' ) );
7386
+ $aux_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_aux_paths' ) ? esc_html( implode( "\n", ewww_image_optimizer_get_option( 'ewww_image_optimizer_aux_paths' ) ) ) : '';
7387
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_aux_paths'>" . esc_html__( 'Folders to optimize', 'ewww-image-optimizer' ) . '</label></th><td>' .
7388
+ /* translators: %s: the folder where WordPress is installed */
7389
+ sprintf( esc_html__( 'One path per line, must be within %s. Use full paths, not relative paths.', 'ewww-image-optimizer' ), ABSPATH ) . "<br>\n" .
7390
+ "<textarea id='ewww_image_optimizer_aux_paths' name='ewww_image_optimizer_aux_paths' rows='3' cols='60'>$aux_paths</textarea>\n" .
7391
+ "<p class='description'>" . esc_html__( 'Provide paths containing images to be optimized using the Bulk Optimizer and Scheduled Optimization.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7392
+ ewwwio_debug_message( 'folders to optimize:' );
7393
+ ewwwio_debug_message( $aux_paths );
7394
+
7395
+ $exclude_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_exclude_paths' ) ? esc_html( implode( "\n", ewww_image_optimizer_get_option( 'ewww_image_optimizer_exclude_paths' ) ) ) : '';
7396
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_exclude_paths'>" . esc_html__( 'Folders to ignore', 'ewww-image-optimizer' ) . '</label></th><td>' . esc_html__( 'One path per line, partial paths allowed, but no urls.', 'ewww-image-optimizer' ) . "<br>\n" .
7397
+ "<textarea id='ewww_image_optimizer_exclude_paths' name='ewww_image_optimizer_exclude_paths' rows='3' cols='60'>$exclude_paths</textarea>\n" .
7398
+ "<p class='description'>" . esc_html__( 'A file that matches any pattern or path provided will not be optimized.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7399
+ ewwwio_debug_message( 'folders to ignore:' );
7400
+ ewwwio_debug_message( $exclude_paths );
7401
+
7402
+ // $output[] = "<tr><th><label for='ewww_image_optimizer_resize_detection'>" . esc_html__( 'Resize Detection', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_resize_detection' name='ewww_image_optimizer_resize_detection' value='true' " . ( ewww_image_optimizer_get_option('ewww_image_optimizer_resize_detection') == TRUE ? "checked='true'" : "" ) . " /> " . esc_html__( 'Will highlight images that need to be resized because the browser is scaling them down. Only visible for Admin users and adds a button to the admin bar to detect scaled images that have been lazy loaded.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7403
+ // ewwwio_debug_message( 'resize detection: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_detection' ) == TRUE ? 'on' : 'off' ) );
7404
+ if ( function_exists( 'imsanity_get_max_width_height' ) ) {
7405
+ $output[] = "<tr class='$network_class'><th>&nbsp;</th><td>" .
7406
+ '<p><span style="color: green">' . esc_html__( '*Imsanity settings override the EWWW resize dimensions.', 'ewww-image-optimizer' ) . "</span></p></td></tr>\n";
7407
+ }
7408
+ $output[] = "<tr class='$network_class'><th>" . esc_html__( 'Resize Media Images', 'ewww-image-optimizer' ) . "</th><td><label for='ewww_image_optimizer_maxmediawidth'>" . esc_html__( 'Max Width', 'ewww-image-optimizer' ) . "</label> <input type='number' step='1' min='0' class='small-text' id='ewww_image_optimizer_maxmediawidth' name='ewww_image_optimizer_maxmediawidth' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediawidth' ) . ( function_exists( 'imsanity_get_max_width_height' ) ? "' disabled='disabled" : '' ) . "' /> <label for='ewww_image_optimizer_maxmediaheight'>" . esc_html__( 'Max Height', 'ewww-image-optimizer' ) . "</label> <input type='number' step='1' min='0' class='small-text' id='ewww_image_optimizer_maxmediaheight' name='ewww_image_optimizer_maxmediaheight' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediaheight' ) . ( function_exists( 'imsanity_get_max_width_height' ) ? "' disabled='disabled" : '' ) . "' /> " . esc_html__( 'in pixels', 'ewww-image-optimizer' ) . "\n" .
7409
+ "<p class='description'>" . esc_html__( 'Resizes images uploaded directly to the Media Library and those uploaded within a post or page.', 'ewww-image-optimizer' ) .
7410
+ "</td></tr>\n";
7411
+ ewwwio_debug_message( 'max media dimensions: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediawidth' ) . ' x ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxmediaheight' ) );
7412
+ $output[] = "<tr class='$network_class'><th>" . esc_html__( 'Resize Other Images', 'ewww-image-optimizer' ) . "</th><td><label for='ewww_image_optimizer_maxotherwidth'>" . esc_html__( 'Max Width', 'ewww-image-optimizer' ) . "</label> <input type='number' step='1' min='0' class='small-text' id='ewww_image_optimizer_maxotherwidth' name='ewww_image_optimizer_maxotherwidth' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherwidth' ) . ( function_exists( 'imsanity_get_max_width_height' ) ? "' disabled='disabled" : '' ) . "' /> <label for='ewww_image_optimizer_maxotherheight'>" . esc_html__( 'Max Height', 'ewww-image-optimizer' ) . "</label> <input type='number' step='1' min='0' class='small-text' id='ewww_image_optimizer_maxotherheight' name='ewww_image_optimizer_maxotherheight' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherheight' ) . ( function_exists( 'imsanity_get_max_width_height' ) ? "' disabled='disabled" : '' ) . "' /> " . esc_html__( 'in pixels', 'ewww-image-optimizer' ) . "\n" .
7413
+ "<p class='description'>" . esc_html__( 'Resizes images uploaded indirectly to the Media Library, like theme images or front-end uploads.', 'ewww-image-optimizer' ) .
7414
+ "</td></tr>\n";
7415
+ ewwwio_debug_message( 'max other dimensions: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherwidth' ) . ' x ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_maxotherheight' ) );
7416
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_resize_existing'>" . esc_html__( 'Resize Existing Images', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_resize_existing' name='ewww_image_optimizer_resize_existing' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_existing' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'Allow resizing of existing Media Library images.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7417
+ ewwwio_debug_message( 'resize existing images: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_existing' ) == true ? 'on' : 'off' ) );
7418
+
7419
+ $output[] = '<tr class="network-singlesite"><th>' . esc_html__( 'Disable Resizes', 'ewww-image-optimizer' ) . '</th><td><p>' . esc_html__( 'Wordpress, your theme, and other plugins generate various image sizes. You may disable optimization for certain sizes, or completely prevent those sizes from being created.', 'ewww-image-optimizer' ) . "</p>\n";
7420
+ $image_sizes = ewww_image_optimizer_get_image_sizes();
7421
+ $disabled_sizes = get_option( 'ewww_image_optimizer_disable_resizes' );
7422
+ $disabled_sizes_opt = get_option( 'ewww_image_optimizer_disable_resizes_opt' );
7423
+ $output[] = '<table><tr class="network-singlesite"><th>' . esc_html__( 'Disable Optimization', 'ewww-image-optimizer' ) . '</th><th>' . esc_html__( 'Disable Creation', 'ewww-image-optimizer' ) . "</th></tr>\n";
7424
+ ewwwio_debug_message( 'disabled resizes:' );
7425
+ foreach ( $image_sizes as $size => $dimensions ) {
7426
+ if ( 'thumbnail' == $size ) {
7427
+ $output [] = "<tr class='network-singlesite'><td><input type='checkbox' id='ewww_image_optimizer_disable_resizes_opt_$size' name='ewww_image_optimizer_disable_resizes_opt[$size]' value='true' " . ( ! empty( $disabled_sizes_opt[ $size ] ) ? "checked='true'" : '' ) . " /></td><td><input type='checkbox' id='ewww_image_optimizer_disable_resizes_$size' name='ewww_image_optimizer_disable_resizes[$size]' value='true' disabled /></td><td><label for='ewww_image_optimizer_disable_resizes_$size'>$size - {$dimensions['width']}x{$dimensions['height']}</label></td></tr>\n";
7428
+ } elseif ( 'pdf-full' == $size ) {
7429
+ $output [] = "<tr class='network-singlesite'><td><input type='checkbox' id='ewww_image_optimizer_disable_resizes_opt_$size' name='ewww_image_optimizer_disable_resizes_opt[$size]' value='true' " . ( ! empty( $disabled_sizes_opt[ $size ] ) ? "checked='true'" : '' ) . " /></td><td><input type='checkbox' id='ewww_image_optimizer_disable_resizes_$size' name='ewww_image_optimizer_disable_resizes[$size]' value='true' " . ( ! empty( $disabled_sizes[ $size ] ) ? "checked='true'" : '' ) . " /></td><td><label for='ewww_image_optimizer_disable_resizes_$size'>$size - <span class='description'>" . esc_html__( 'Disabling creation of the full-size preview for PDF files will disable all PDF preview sizes', 'ewww-image-optimizer' ) . "</span></label></td></tr>\n";
7430
+ } else {
7431
+ $output [] = "<tr class='network-singlesite'><td><input type='checkbox' id='ewww_image_optimizer_disable_resizes_opt_$size' name='ewww_image_optimizer_disable_resizes_opt[$size]' value='true' " . ( ! empty( $disabled_sizes_opt[ $size ] ) ? "checked='true'" : '' ) . " /></td><td><input type='checkbox' id='ewww_image_optimizer_disable_resizes_$size' name='ewww_image_optimizer_disable_resizes[$size]' value='true' " . ( ! empty( $disabled_sizes[ $size ] ) ? "checked='true'" : '' ) . " /></td><td><label for='ewww_image_optimizer_disable_resizes_$size'>$size - {$dimensions['width']}x{$dimensions['height']}</label></td></tr>\n";
7432
+ }
7433
+ ewwwio_debug_message( $size . ': ' . ( ! empty( $disabled_sizes_opt[ $size ] ) ? 'optimization=disabled ' : 'optimization=enabled ' ) . ( ! empty( $disabled_sizes[ $size ] ) ? 'creation=disabled' : 'creation=enabled' ) );
7434
+ }
7435
+ if ( 'network-multisite' != $network ) {
7436
+ $output[] = "</table>\n";
7437
+ $output[] = "</td></tr>\n";
7438
+ } else {
7439
+ $output[] = '<tr><th>' . esc_html__( 'Disable Resizes', 'ewww-image-optimizer' ) . '</th><td>';
7440
+ $output[] = '<p><span style="color: green">' . esc_html__( '*Settings to disable creation and optimization of individual sizes must be configured for each individual site.', 'ewww-image-optimizer' ) . "</span></p></td></tr>\n";
7441
+ }
7442
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_skip_size'>" . esc_html__( 'Skip Small Images', 'ewww-image-optimizer' ) . "</label></th><td><input type='text' id='ewww_image_optimizer_skip_size' name='ewww_image_optimizer_skip_size' size='8' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) . "'> " . esc_html__( 'Do not optimize images smaller than this (in bytes)', 'ewww-image-optimizer' ) . "</td></tr>\n";
7443
+ ewwwio_debug_message( 'skip images smaller than: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) . ' bytes' );
7444
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_skip_png_size'>" . esc_html__( 'Skip Large PNG Images', 'ewww-image-optimizer' ) . "</label></th><td><input type='text' id='ewww_image_optimizer_skip_png_size' name='ewww_image_optimizer_skip_png_size' size='8' value='" . ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) . "'> " . esc_html__( 'Do not optimize PNG images larger than this (in bytes)', 'ewww-image-optimizer' ) . "</td></tr>\n";
7445
+ ewwwio_debug_message( 'skip PNG images larger than: ' . ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) . ' bytes' );
7446
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_lossy_skip_full'>" . esc_html__( 'Exclude full-size images from lossy optimization', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_lossy_skip_full' name='ewww_image_optimizer_lossy_skip_full' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_skip_full' ) == true ? "checked='true'" : '' ) . " /></td></tr>\n";
7447
+ ewwwio_debug_message( 'exclude originals from lossy: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_skip_full' ) == true ? 'on' : 'off' ) );
7448
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_metadata_skip_full'>" . esc_html__( 'Exclude full-size images from metadata removal', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_metadata_skip_full' name='ewww_image_optimizer_metadata_skip_full' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_skip_full' ) == true ? "checked='true'" : '' ) . " /></td></tr>\n";
7449
+ ewwwio_debug_message( 'exclude originals from metadata removal: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_skip_full' ) == true ? 'on' : 'off' ) );
7450
+ $output[] = "<tr class='$network_class nocloud'><th><label for='ewww_image_optimizer_skip_bundle'>" . esc_html__( 'Use System Paths', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_skip_bundle' name='ewww_image_optimizer_skip_bundle' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_bundle' ) == true ? "checked='true'" : '' ) . ' /> ' .
7451
+ /* translators: 1: an example folder where system binaries/executables are installed 2: another example folder */
7452
+ sprintf( esc_html__( 'If you have already installed the utilities in a system location, such as %1$s or %2$s, use this to force the plugin to use those versions and skip the auto-installers.', 'ewww-image-optimizer' ), '/usr/local/bin', '/usr/bin' ) . "</td></tr>\n";
7453
+ ewwwio_debug_message( 'use system binaries: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_bundle' ) == true ? 'yes' : 'no' ) );
7454
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_enable_help'>" . esc_html__( 'Enable Embedded Help', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_enable_help' name='ewww_image_optimizer_enable_help' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help' ) == true ? "checked='true'" : '' ) . ' /> ' .
7455
+ esc_html__( 'Enable the support beacon, which gives you access to documentation and our support team right from your WordPress dashboard. To assist you more efficiently, we may collect the current url, IP address, browser/device information, and debugging information.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7456
+ ewwwio_debug_message( 'enable help beacon: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help' ) == true ? 'yes' : 'no' ) );
7457
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_allow_tracking'>" . esc_html__( 'Allow Usage Tracking?', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_allow_tracking' name='ewww_image_optimizer_allow_tracking' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_allow_tracking' ) == true ? "checked='true'" : '' ) . ' /> ' .
7458
+ esc_html__( 'Allow EWWW Image Optimizer to anonymously track how this plugin is used and help us make the plugin better. Opt-in to tracking and receive 500 API image credits free. No sensitive data is tracked.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7459
+ $output[] = "</table>\n</div>\n";
7460
+ $output[] = "<div id='ewww-conversion-settings'>\n";
7461
+ $output[] = '<p>' . esc_html__( 'Conversion is only available for images in the Media Library (except WebP). By default, all images have a link available in the Media Library for one-time conversion. Turning on individual conversion operations below will enable conversion filters any time an image is uploaded or modified.', 'ewww-image-optimizer' ) . "<br />\n" .
7462
+ '<b>' . esc_html__( 'NOTE:', 'ewww-image-optimizer' ) . '</b> ' . esc_html__( 'The plugin will attempt to update image locations for any posts that contain the images. You may still need to manually update locations/urls for converted images.', 'ewww-image-optimizer' ) . "\n" .
7463
+ "</p>\n";
7464
+ $output[] = "<table class='form-table'>\n";
7465
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_disable_convert_links'>" . esc_html__( 'Hide Conversion Links', 'ewww-image-optimizer' ) . "</label</th><td><input type='checkbox' id='ewww_image_optimizer_disable_convert_links' name='ewww_image_optimizer_disable_convert_links' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_convert_links' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'Site or Network admins can use this to prevent other users from using the conversion links in the Media Library which bypass the settings below.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7466
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_delete_originals'>" . esc_html__( 'Delete originals', 'ewww-image-optimizer' ) . "</label></th><td><input type='checkbox' id='ewww_image_optimizer_delete_originals' name='ewww_image_optimizer_delete_originals' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'This will remove the original image from the server after a successful conversion.', 'ewww-image-optimizer' ) . "</td></tr>\n";
7467
+ ewwwio_debug_message( 'delete originals: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ? 'on' : 'off' ) );
7468
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_jpg_to_png'>" .
7469
+ /* translators: 1: JPG, GIF or PNG 2: JPG or PNG */
7470
+ sprintf( esc_html__( 'enable %1$s to %2$s conversion', 'ewww-image-optimizer' ), 'JPG', 'PNG' ) .
7471
+ "</label></th><td><span><input type='checkbox' id='ewww_image_optimizer_jpg_to_png' name='ewww_image_optimizer_jpg_to_png' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' ) == true ? "checked='true'" : '' ) . ' /> <b>' . esc_html__( 'WARNING:', 'ewww-image-optimizer' ) . '</b> ' . esc_html__( 'Removes metadata and increases cpu usage dramatically.', 'ewww-image-optimizer' ) . "</span>\n" .
7472
+ "<p class='description'>" . esc_html__( 'PNG is generally much better than JPG for logos and other images with a limited range of colors. Checking this option will slow down JPG processing significantly, and you may want to enable it only temporarily.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7473
+ ewwwio_debug_message( 'jpg2png: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' ) == true ? 'on' : 'off' ) );
7474
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_png_to_jpg'>" .
7475
+ /* translators: 1: JPG, GIF or PNG 2: JPG or PNG */
7476
+ sprintf( esc_html__( 'enable %1$s to %2$s conversion', 'ewww-image-optimizer' ), 'PNG', 'JPG' ) .
7477
+ "</label></th><td><span><input type='checkbox' id='ewww_image_optimizer_png_to_jpg' name='ewww_image_optimizer_png_to_jpg' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' ) == true ? "checked='true'" : '' ) . ' /> <b>' . esc_html__( 'WARNING:', 'ewww-image-optimizer' ) . '</b> ' . esc_html__( 'This is not a lossless conversion.', 'ewww-image-optimizer' ) . "</span>\n" .
7478
+ "<p class='description'>" . esc_html__( 'JPG is generally much better than PNG for photographic use because it compresses the image and discards data. PNGs with transparency are not converted by default.', 'ewww-image-optimizer' ) . "</p>\n" .
7479
+ "<span><label for='ewww_image_optimizer_jpg_background'> " . esc_html__( 'JPG background color:', 'ewww-image-optimizer' ) . "</label> #<input type='text' id='ewww_image_optimizer_jpg_background' name='ewww_image_optimizer_jpg_background' size='6' value='" . ewww_image_optimizer_jpg_background() . "' /> <span style='padding-left: 12px; font-size: 12px; border: solid 1px #555555; background-color: #" . ewww_image_optimizer_jpg_background() . "'>&nbsp;</span> " . esc_html__( 'HEX format (#123def)', 'ewww-image-optimizer' ) . ".</span>\n" .
7480
+ "<p class='description'>" . esc_html__( 'Background color is used only if the PNG has transparency. Leave this value blank to skip PNGs with transparency.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7481
+ ewwwio_debug_message( 'png2jpg: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' ) == true ? 'on' : 'off' ) );
7482
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_gif_to_png'>" .
7483
+ /* translators: 1: JPG, GIF or PNG 2: JPG or PNG */
7484
+ sprintf( esc_html__( 'enable %1$s to %2$s conversion', 'ewww-image-optimizer' ), 'GIF', 'PNG' ) .
7485
+ "</label></th><td><span><input type='checkbox' id='ewww_image_optimizer_gif_to_png' name='ewww_image_optimizer_gif_to_png' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_to_png' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'No warnings here, just do it.', 'ewww-image-optimizer' ) . "</span>\n" .
7486
+ "<p class='description'> " . esc_html__( 'PNG is generally better than GIF, but animated images cannot be converted.', 'ewww-image-optimizer' ) . "</p></td></tr>\n";
7487
+ ewwwio_debug_message( 'gif2png: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_to_png' ) == true ? 'on' : 'off' ) );
7488
+ $output[] = "</table>\n</div>\n";
7489
+ $output[] = "<div id='ewww-webp-settings'>\n";
7490
+ $output[] = "<table class='form-table'>\n";
7491
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_webp'>" . esc_html__( 'JPG/PNG to WebP', 'ewww-image-optimizer' ) . "</label></th><td><span><input type='checkbox' id='ewww_image_optimizer_webp' name='ewww_image_optimizer_webp' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) == true ? "checked='true'" : '' ) . ' /> <b>' . esc_html__( 'WARNING:', 'ewww-image-optimizer' ) . '</b> ' . esc_html__( 'JPG to WebP conversion is lossy, but quality loss is minimal. PNG to WebP conversion is lossless.', 'ewww-image-optimizer' ) . "</span>\n" .
7492
+ "<p class='description'>" . esc_html__( 'Originals are never deleted, and WebP images should only be served to supported browsers.', 'ewww-image-optimizer' ) . " <a href='#webp-rewrite'>" . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) ? esc_html__( 'You can use the rewrite rules below to serve WebP images with Apache.', 'ewww-image-optimizer' ) : '' ) . "</a></td></tr>\n";
7493
+ ewwwio_debug_message( 'webp conversion: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) == true ? 'on' : 'off' ) );
7494
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_webp_force'>" . esc_html__( 'Force WebP', 'ewww-image-optimizer' ) . "</label></th><td><span><input type='checkbox' id='ewww_image_optimizer_webp_force' name='ewww_image_optimizer_webp_force' value='true' " . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) == true ? "checked='true'" : '' ) . ' /> ' . esc_html__( 'WebP images will be generated and saved for all JPG/PNG images regardless of their size. The Alternative WebP Rewriting will not check if a file exists, only that the domain matches the home url.', 'ewww-image-optimizer' ) . "</span></td></tr>\n";
7495
+ ewwwio_debug_message( 'forced webp: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) == true ? 'on' : 'off' ) );
7496
+ if ( ! ewww_image_optimizer_ce_webp_enabled() ) {
7497
+ $webp_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_paths' ) ? esc_html( implode( "\n", ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_paths' ) ) ) : '';
7498
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_webp_paths'>" . esc_html__( 'WebP URLs', 'ewww-image-optimizer' ) . '</label></th><td>' . esc_html__( 'If Force WebP is enabled, enter URL patterns that should be permitted for Alternative WebP Rewriting. One pattern per line, may be partial URLs, but must include the domain name.', 'ewww-image-optimizer' ) . '<br>' .
7499
+ "<textarea id='ewww_image_optimizer_webp_paths' name='ewww_image_optimizer_webp_paths' rows='3' cols='60'>$webp_paths</textarea></td></tr>\n";
7500
+ ewwwio_debug_message( 'webp paths:' );
7501
+ ewwwio_debug_message( $webp_paths );
7502
+ $output[] = "<tr class='$network_class'><th><label for='ewww_image_optimizer_webp_for_cdn'>" .
7503
+ esc_html__( 'Alternative WebP Rewriting', 'ewww-image-optimizer' ) .
7504
+ "</label></th><td><span><input type='checkbox' id='ewww_image_optimizer_webp_for_cdn' name='ewww_image_optimizer_webp_for_cdn' value='true' " .
7505
+ ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) == true ? "checked='true'" : '' ) . ' /> ' .
7506
+ esc_html__( 'Uses output buffering and libxml functionality from PHP. Use this if the Apache rewrite rules do not work, or if your images are served from a CDN.', 'ewww-image-optimizer' ) . ' ' .
7507
+ /* translators: %s: Cache Enabler (link) */
7508
+ sprintf( esc_html__( 'Sites using a CDN may also use the WebP option in the %s plugin.', 'ewww-image-optimizer' ), '<a href="https://wordpress.org/plugins/cache-enabler/">Cache Enabler</a>' ) . '</span></td></tr>';
7509
+ }
7510
+ ewwwio_debug_message( 'alt webp rewriting: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) == true ? 'on' : 'off' ) );
7511
+ $output[] = "</table>\n</div>\n";
7512
+ $output[] = "<p class='submit'><input type='submit' class='button-primary' value='" . esc_attr__( 'Save Changes', 'ewww-image-optimizer' ) . "' /></p>\n";
7513
+ $output[] = "</form>\n";
7514
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) && ! ewww_image_optimizer_ce_webp_enabled() ) {
7515
+ $output[] = "<form id='ewww-webp-rewrite'>\n";
7516
+ $output[] = '<p>' . esc_html__( 'There are many ways to serve WebP images to visitors with supported browsers. You may choose any you wish, but it is recommended to serve them with an .htaccess file using mod_rewrite and mod_headers. The plugin can insert the rules for you if the file is writable, or you can edit .htaccess yourself.', 'ewww-image-optimizer' ) . "</p>\n";
7517
+ if ( ! ewww_image_optimizer_webp_rewrite_verify() ) {
7518
+ $output[] = "<img id='webp-image' src='" . plugins_url( '/images/test.png', __FILE__ ) . "' style='float: right; padding: 0 0 10px 10px;'>\n" .
7519
+ "<p id='ewww-webp-rewrite-status'><b>" . esc_html__( 'Rules verified successfully', 'ewww-image-optimizer' ) . "</b></p>\n";
7520
+ ewwwio_debug_message( 'webp .htaccess rewriting enabled' );
7521
+ } else {
7522
+ $output[] = "<pre id='webp-rewrite-rules' style='background: white; font-color: black; border: 1px solid black; clear: both; padding: 10px;'>\n" .
7523
+ "&lt;IfModule mod_rewrite.c&gt;\n" .
7524
+ "RewriteEngine On\n" .
7525
+ "RewriteCond %{HTTP_ACCEPT} image/webp\n" .
7526
+ "RewriteCond %{REQUEST_FILENAME} (.*)\.(jpe?g|png)$\n" .
7527
+ "RewriteCond %{REQUEST_FILENAME}\.webp -f\n" .
7528
+ "RewriteCond %{QUERY_STRING} !type=original\n" .
7529
+ "RewriteRule (.+)\.(jpe?g|png)$ %{REQUEST_FILENAME}.webp [T=image/webp,E=accept:1,L]\n" .
7530
+ "&lt;/IfModule&gt;\n" .
7531
+ "&lt;IfModule mod_headers.c&gt;\n" .
7532
+ "Header append Vary Accept env=REDIRECT_accept\n" .
7533
+ "&lt;/IfModule&gt;\n" .
7534
+ "AddType image/webp .webp</pre>\n" .
7535
+ "<img id='webp-image' src='" . plugins_url( '/images/test.png', __FILE__ ) . "' style='float: right; padding-left: 10px;'>\n" .
7536
+ "<p id='ewww-webp-rewrite-status'>" . esc_html__( 'The image to the right will display a WebP image with WEBP in white text, if your site is serving WebP images and your browser supports WebP.', 'ewww-image-optimizer' ) . "</p>\n" .
7537
+ "<button type='submit' class='button-secondary action'>" . esc_html__( 'Insert Rewrite Rules', 'ewww-image-optimizer' ) . "</button>\n";
7538
+ ewwwio_debug_message( 'webp .htaccess rules not detected' );
7539
+ }
7540
+ $output[] = "</form>\n";
7541
+ } elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) && ! ewww_image_optimizer_ce_webp_enabled() ) {
7542
+ $test_webp_image = plugins_url( '/images/test.png.webp', __FILE__ );
7543
+ $test_png_image = plugins_url( '/images/test.png', __FILE__ );
7544
+ $output[] = "<noscript data-img='$test_png_image' data-webp='$test_webp_image' data-style='float: right; padding: 0 0 10px 10px;' class='ewww_webp'><img src='$test_png_image' style='float: right; padding: 0 0 10px 10px;'></noscript>\n";
7545
+ }
7546
+ $output[] = "</div><!-- end container left -->\n";
7547
+ $output[] = "<div id='ewww-container-right' style='border: 1px solid #e5e5e5; float: right; margin-left: -215px; padding: 0em 1.5em 1em; background-color: #fff; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); display: inline-block; width: 174px;'>\n" .
7548
+ '<h2>' . esc_html__( 'Support EWWW I.O.', 'ewww-image-optimizer' ) . "</h2>\n" .
7549
+ '<p>' . esc_html__( 'Would you like to help support development of this plugin?', 'ewww-image-optimizer' ) . "</p>\n" .
7550
+ "<p><a href='https://translate.wordpress.org/projects/wp-plugins/ewww-image-optimizer/'>" . esc_html__( 'Help translate EWWW I.O.', 'ewww-image-optimizer' ) . "</a></p>\n" .
7551
+ "<p><a href='https://wordpress.org/support/view/plugin-reviews/ewww-image-optimizer#postform'>" . esc_html__( 'Write a review.', 'ewww-image-optimizer' ) . "</a></p>\n" .
7552
+ /* translators: %s: Paypal (link) */
7553
+ '<p>' . sprintf( esc_html__( 'Contribute directly via %s.', 'ewww-image-optimizer' ), "<a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=MKMQKCBFFG3WW'>Paypal</a>" ) . "</p>\n" .
7554
+ '<p>' . esc_html__( 'Use any of these referral links to show your appreciation:', 'ewww-image-optimizer' ) . "</p>\n" .
7555
+ '<p><b>' . esc_html__( 'Web Hosting:', 'ewww-image-optimizer' ) . "</b><br>\n" .
7556
+ "<a href='http://www.a2hosting.com/?aid=b6322137'>A2 Hosting:</a> " . esc_html_x( 'with automatic EWWW IO setup', 'A2 Hosting:', 'ewww-image-optimizer' ) . "<br>\n" .
7557
+ "<a href='http://www.shareasale.com/r.cfm?b=394686&u=1481701&m=41388&urllink='>WP Engine</a><br>\n" .
7558
+ "</p>\n" .
7559
+ '<p><b>' . esc_html_x( 'VPS:', 'abbreviation for Virtual Private Server', 'ewww-image-optimizer' ) . "</b><br>\n" .
7560
+ "<a href='https://www.digitalocean.com/?refcode=89ef0197ec7e'>DigitalOcean</a><br>\n" .
7561
+ "</p>\n" .
7562
+ '<p><b>' . esc_html_x( 'CDN:', 'abbreviation for Content Delivery Network', 'ewww-image-optimizer' ) . "</b><br><a target='_blank' href='http://tracking.maxcdn.com/c/91625/36539/378'>" . esc_html__( 'Add MaxCDN to increase website speeds dramatically! Sign Up Now and Save 25%.', 'ewww-image-optimizer' ) . "</a></p>\n" .
7563
+ "</div>\n" .
7564
+ "</div>\n";
7565
+ ewwwio_debug_message( 'max_execution_time: ' . ini_get( 'max_execution_time' ) );
7566
+ ewww_image_optimizer_stl_check();
7567
+ if ( ! ewww_image_optimizer_function_exists( 'sleep' ) ) {
7568
+ ewwwio_debug_message( 'sleep disabled' );
7569
+ }
7570
+ if ( ! ewww_image_optimizer_function_exists( 'print_r' ) ) {
7571
+ ewwwio_debug_message( 'print_r disabled' );
7572
+ }
7573
+ ewwwio_check_memory_available();
7574
+ if ( 'debug-silent' === $network ) {
7575
+ return;
7576
+ }
7577
+ echo apply_filters( 'ewww_image_optimizer_settings', $output );
7578
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) && ! ewww_image_optimizer_ce_webp_enabled() ) {
7579
+ ewww_image_optimizer_webp_inline_script();
7580
+ }
7581
+
7582
+ /* $help_instructions = esc_js( esc_html__( 'Enable the Debugging option and refresh this page to include debugging information with your question. This will allow us to assist you more quickly.', 'ewww-image-optimizer' ) ); */
7583
+
7584
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
7585
+ ?>
7586
+ <script type="text/javascript">
7587
+ function selectText(containerid) {
7588
+ if (document.selection) {
7589
+ var range = document.body.createTextRange();
7590
+ range.moveToElementText(document.getElementById(containerid));
7591
+ range.select();
7592
+ } else if (window.getSelection) {
7593
+ var range = document.createRange();
7594
+ range.selectNode(document.getElementById(containerid));
7595
+ window.getSelection().addRange(range);
7596
+ }
7597
+ }
7598
+ </script>
7599
+ <?php
7600
+ global $ewww_debug;
7601
+ $debug_log_url = plugins_url( '/debug.log', __FILE__ );
7602
+ echo '<p style="clear:both"><b>' . esc_html__( 'Debugging Information', 'ewww-image-optimizer' ) . ':</b> <button onclick="selectText(' . "'ewww-debug-info'" . ')">' . esc_html__( 'Select All', 'ewww-image-optimizer' ) . '</button>';
7603
+ if ( is_file( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' ) ) {
7604
+ echo "&emsp;<a href='$debug_log_url'>" . esc_html( 'View Debug Log', 'ewww-image-optimizer' ) . "</a> - <a href='admin.php?action=ewww_image_optimizer_delete_debug_log'>" . esc_html( 'Remove Debug Log', 'ewww-image-optimizer' ) . '</a>';
7605
+ }
7606
+ echo '</p>';
7607
+ echo '<div id="ewww-debug-info" style="background:#ffff99;margin-left:-20px;padding:10px" contenteditable="true">' . $ewww_debug . '</div>';
7608
+ /* $help_instructions = esc_js( esc_html__( 'Debugging information will be included with your message automatically. This will allow us to assist you more quickly.', 'ewww-image-optimizer' ) ); */
7609
+ }
7610
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help' ) ) {
7611
+ $help_instructions = esc_html__( 'Please turn on the Debugging option. Then copy and paste the Debug Information from the bottom of the settings page. This will allow us to assist you more quickly.', 'ewww-image-optimizer' );
7612
+ $current_user = wp_get_current_user();
7613
+ $help_email = $current_user->user_email;
7614
+ $hs_config = array(
7615
+ 'color' => '#3eadc9',
7616
+ 'icon' => 'buoy',
7617
+ 'instructions' => $help_instructions,
7618
+ 'poweredBy' => false,
7619
+ 'showContactFields' => true,
7620
+ 'showSubject' => true,
7621
+ 'topArticles' => true,
7622
+ 'zIndex' => 100000,
7623
+ );
7624
+ $hs_identify = array(
7625
+ 'email' => $help_email,
7626
+ 'debug_info' => $ewww_debug,
7627
+ );
7628
+ ?>
7629
+ <script type='text/javascript'>
7630
+ !function(e,o,n){window.HSCW=o,window.HS=n,n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={},t.readyQueue=[],t.config=function(e){this.userConfig=e},t.ready=function(e){this.readyQueue.push(e)},o.config={docs:{enabled:!0,baseUrl:"//ewwwio.helpscoutdocs.com/"},contact:{enabled:!0,formId:"af75cf17-310a-11e7-9841-0ab63ef01522"}};var r=e.getElementsByTagName("script")[0],c=e.createElement("script");c.type="text/javascript",c.async=!0,c.src="https://djtflbt20bdde.cloudfront.net/",r.parentNode.insertBefore(c,r)}(document,window.HSCW||{},window.HS||{});
7631
+ HS.beacon.config(<?php echo json_encode( $hs_config ); ?>);
7632
+ HS.beacon.ready(function() {
7633
+ HS.beacon.identify(
7634
+ <?php echo json_encode( $hs_identify ); ?>
7635
+ );
7636
+ });
7637
+ </script>
7638
+ <?php
7639
+ }
7640
+ ewwwio_memory( __FUNCTION__ );
7641
+ ewww_image_optimizer_debug_log();
7642
+ }
7643
+
7644
+ /**
7645
+ * Filters through the settings page to remove unneeded settings for the -cloud plugin.
7646
+ *
7647
+ * @param array $input The output of the settings page, broken up into an array.
7648
+ * @return string The filtered output for the settings page.
7649
+ */
7650
+ function ewww_image_optimizer_filter_settings_page( $input ) {
7651
+ $output = '';
7652
+ foreach ( $input as $line ) {
7653
+ if ( ewww_image_optimizer_full_cloud() && preg_match( "/class='nocloud'/", $line ) ) {
7654
+ continue;
7655
+ } else {
7656
+ $output .= $line;
7657
+ }
7658
+ }
7659
+ ewwwio_memory( __FUNCTION__ );
7660
+ return $output;
7661
+ }
7662
+
7663
+ /**
7664
+ * Filters through the multisite settings page to remove unneeded settings in multisite mode.
7665
+ *
7666
+ * @param array $input The output of the settings page, broken up into an array.
7667
+ * @return string The filtered output for the settings page.
7668
+ */
7669
+ function ewww_image_optimizer_filter_network_settings_page( $input ) {
7670
+ $output = array();
7671
+ foreach ( $input as $line ) {
7672
+ if ( strpos( $line, 'network-singlesite' ) ) {
7673
+ continue;
7674
+ } else {
7675
+ $output[] = $line;
7676
+ }
7677
+ }
7678
+ ewwwio_memory( __FUNCTION__ );
7679
+ return $output;
7680
+ }
7681
+
7682
+ /**
7683
+ * Filters through the single-site settings page to remove unneeded settings in multisite mode.
7684
+ *
7685
+ * @param array $input The output of the settings page, broken up into an array.
7686
+ * @return string The filtered output for the settings page.
7687
+ */
7688
+ function ewww_image_optimizer_filter_network_singlesite_settings_page( $input ) {
7689
+ $output = array();
7690
+ foreach ( $input as $line ) {
7691
+ if ( ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' ) && strpos( $line, 'network-multisite' ) ) {
7692
+ continue;
7693
+ } elseif ( strpos( $line, 'network-only' ) ) {
7694
+ continue;
7695
+ } else {
7696
+ $output[] = $line;
7697
+ }
7698
+ }
7699
+ ewwwio_memory( __FUNCTION__ );
7700
+ return $output;
7701
+ }
7702
+
7703
+ /**
7704
+ * Removes the API key currently installed.
7705
+ */
7706
+ function ewww_image_optimizer_remove_cloud_key() {
7707
+ $permissions = apply_filters( 'ewww_image_optimizer_admin_permissions', 'manage_options' );
7708
+ if ( false === current_user_can( $permissions ) ) {
7709
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
7710
+ }
7711
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_cloud_key', '' );
7712
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) > 10 ) {
7713
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_jpg_level', 10 );
7714
+ }
7715
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) != 40 ) {
7716
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_png_level', 10 );
7717
+ }
7718
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) > 0 ) {
7719
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_pdf_level', 0 );
7720
+ }
7721
+ ewww_image_optimizer_set_option( 'ewww_image_optimizer_backup_files', '' );
7722
+ $sendback = wp_get_referer();
7723
+ wp_redirect( esc_url_raw( $sendback ) );
7724
+ exit;
7725
+ }
7726
+
7727
+ /**
7728
+ * Loads script to detect scaled images within the page, only enabled for admins.
7729
+ */
7730
+ function ewww_image_optimizer_resize_detection_script() {
7731
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_admin_permissions', 'edit_others_posts' ) ) || 'wp-login.php' == basename( $_SERVER['SCRIPT_NAME'] ) ) {
7732
+ return;
7733
+ }
7734
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_detection' ) ) {
7735
+ wp_enqueue_script( 'ewww-resize-detection', plugins_url( '/includes/resize_detection.js', __FILE__ ), array(), EWWW_IMAGE_OPTIMIZER_VERSION );
7736
+ }
7737
+ }
7738
+
7739
+ /**
7740
+ * Checks if admin bar is visible, and then adds the admin_bar_menu action.
7741
+ */
7742
+ function ewww_image_optimizer_admin_bar_init() {
7743
+ if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_admin_permissions', 'edit_others_posts' ) ) || ! is_admin_bar_showing() || 'wp-login.php' == basename( $_SERVER['SCRIPT_NAME'] ) || is_admin() ) {
7744
+ return;
7745
+ }
7746
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_resize_detection' ) ) {
7747
+ add_action( 'admin_bar_menu', 'ewww_image_optimizer_admin_bar_menu', 99 );
7748
+ }
7749
+ }
7750
+
7751
+ /**
7752
+ * Adds a resize detection button to the wp admin bar.
7753
+ */
7754
+ function ewww_image_optimizer_admin_bar_menu() {
7755
+ global $wp_admin_bar;
7756
+ $wp_admin_bar->add_menu( array(
7757
+ 'id' => 'resize-detection',
7758
+ 'parent' => 'top-secondary',
7759
+ 'title' => __( 'Detect Scaled Images', 'ewww-image-optimizer' ),
7760
+ ) );
7761
+ }
7762
+
7763
+ /**
7764
+ * Adds information to the in-memory debug log.
7765
+ *
7766
+ * @global string $ewww_debug The in-memory debug log.
7767
+ *
7768
+ * @param string $message Debug information to add to the log.
7769
+ */
7770
+ function ewwwio_debug_message( $message ) {
7771
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
7772
+ WP_CLI::debug( $message );
7773
+ return;
7774
+ }
7775
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
7776
+ $memory_limit = ewwwio_memory_limit();
7777
+ if ( strlen( $message ) + 4000000 + memory_get_usage( true ) <= $memory_limit ) {
7778
+ global $ewww_debug;
7779
+ global $ewww_version_dumped;
7780
+ if ( empty( $ewww_debug ) && empty( $ewww_version_dumped ) ) {
7781
+ ewwwio_debug_version_info();
7782
+ $ewww_version_dumped = true;
7783
+ }
7784
+ $message = str_replace( "\n\n\n", '</br>', $message );
7785
+ $message = str_replace( "\n\n", '</br>', $message );
7786
+ $message = str_replace( "\n", '</br>', $message );
7787
+ $ewww_debug .= "$message</br>";
7788
+ } else {
7789
+ global $ewww_debug;
7790
+ $ewww_debug = "not logging message, memory limit is $memory_limit";
7791
+ }
7792
+ }
7793
+ }
7794
+
7795
+ /**
7796
+ * Saves the in-memory debug log to a logfile in the plugin folder.
7797
+ *
7798
+ * @global string $ewww_debug The in-memory debug log.
7799
+ */
7800
+ function ewww_image_optimizer_debug_log() {
7801
+ global $ewww_debug;
7802
+ if ( ! empty( $ewww_debug ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
7803
+ $memory_limit = ewwwio_memory_limit();
7804
+ clearstatcache();
7805
+ $timestamp = date( 'y-m-d h:i:s.u' ) . "\n";
7806
+ if ( ! file_exists( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' ) ) {
7807
+ touch( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' );
7808
+ } else {
7809
+ if ( filesize( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' ) + 4000000 + memory_get_usage( true ) > $memory_limit ) {
7810
+ unlink( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' );
7811
+ touch( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' );
7812
+ }
7813
+ }
7814
+ if ( filesize( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' ) + strlen( $ewww_debug ) + 4000000 + memory_get_usage( true ) <= $memory_limit ) {
7815
+ $ewww_debug_log = str_replace( '</br>', "\n", $ewww_debug );
7816
+ file_put_contents( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log', $timestamp . $ewww_debug_log, FILE_APPEND );
7817
+ }
7818
+ }
7819
+ $ewww_debug = '';
7820
+ ewwwio_memory( __FUNCTION__ );
7821
+ }
7822
+
7823
+ /**
7824
+ * Removes the debug.log file from the plugin folder.
7825
+ */
7826
+ function ewww_image_optimizer_delete_debug_log() {
7827
+ $permissions = apply_filters( 'ewww_image_optimizer_admin_permissions', 'manage_options' );
7828
+ if ( false === current_user_can( $permissions ) ) {
7829
+ wp_die( esc_html__( 'Access denied.', 'ewww-image-optimizer' ) );
7830
+ }
7831
+ if ( is_file( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' ) ) {
7832
+ unlink( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'debug.log' );
7833
+ }
7834
+ $sendback = wp_get_referer();
7835
+ wp_redirect( esc_url_raw( $sendback ) );
7836
+ exit;
7837
+ }
7838
+
7839
+ /**
7840
+ * Adds version information to the in-memory debug log.
7841
+ *
7842
+ * @global string $ewww_debug The in-memory debug log.
7843
+ * @global int $wp_version
7844
+ */
7845
+ function ewwwio_debug_version_info() {
7846
+ global $ewww_debug;
7847
+ if ( ! extension_loaded( 'suhosin' ) && function_exists( 'get_current_user' ) ) {
7848
+ $ewww_debug .= get_current_user() . '</br>';
7849
+ }
7850
+
7851
+ $ewww_debug .= 'EWWW IO version: ' . EWWW_IMAGE_OPTIMIZER_VERSION . '</br>';
7852
+
7853
+ // Check the WP version.
7854
+ global $wp_version;
7855
+ $my_version = substr( $wp_version, 0, 3 );
7856
+ $ewww_debug .= "WP version: $wp_version</br>" ;
7857
+
7858
+ if ( defined( 'PHP_VERSION_ID' ) ) {
7859
+ $ewww_debug .= 'PHP version: ' . PHP_VERSION_ID . '</br>';
7860
+ }
7861
+ if ( defined( 'LIBXML_VERSION' ) ) {
7862
+ $ewww_debug .= 'libxml version: ' . LIBXML_VERSION . '</br>';
7863
+ }
7864
+ if ( ! empty( $_ENV['PANTHEON_ENVIRONMENT'] ) && in_array( $_ENV['PANTHEON_ENVIRONMENT'], array( 'test', 'live', 'dev' ) ) ) {
7865
+ $ewww_debug .= "detected pantheon env: {$_ENV['PANTHEON_ENVIRONMENT']}</br>";
7866
+ }
7867
+ }
7868
+
7869
+ /**
7870
+ * Generate and cleanup a PHP backtrace.
7871
+ *
7872
+ * @return string A serialized backtrace, suitable for database storage.
7873
+ */
7874
+ function ewwwio_debug_backtrace() {
7875
+ if ( defined( 'DEBUG_BACKTRACE_IGNORE_ARGS' ) ) {
7876
+ $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS );
7877
+ } else {
7878
+ $backtrace = debug_backtrace( false );
7879
+ }
7880
+ array_shift( $backtrace );
7881
+ array_shift( $backtrace );
7882
+ return maybe_serialize( $backtrace );
7883
+ }
7884
+
7885
+ /**
7886
+ * Displays backtraces and information on images that have been optimized multiple times.
7887
+ *
7888
+ * @global object $wpdb
7889
+ * @global object $ewwwdb A clone of $wpdb unless it is lacking utf8 connectivity.
7890
+ */
7891
+ function ewww_image_optimizer_dynamic_image_debug() {
7892
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
7893
+ echo '<div class="wrap"><h1>' . esc_html__( 'Dynamic Image Debugging', 'ewww-image-optimizer' ) . '</h1>';
7894
+ global $wpdb;
7895
+ if ( strpos( $wpdb->charset, 'utf8' ) === false ) {
7896
+ ewww_image_optimizer_db_init();
7897
+ global $ewwwdb;
7898
+ } else {
7899
+ $ewwwdb = $wpdb;
7900
+ }
7901
+ $debug_images = $ewwwdb->get_results( "SELECT path,updates,updated,trace FROM $ewwwdb->ewwwio_images WHERE trace IS NOT NULL ORDER BY updates DESC LIMIT 100" );
7902
+ if ( count( $debug_images ) != 0 ) {
7903
+ foreach ( $debug_images as $image ) {
7904
+ $trace = unserialize( $image->trace );
7905
+ echo '<p><b>' . esc_html__( 'File path', 'ewww-image-optimizer' ) . ': ' . $image->path . '</b><br>';
7906
+ echo esc_html__( 'Number of attempted optimizations', 'ewww-image-optimizer' ) . ': ' . $image->updates . '<br>';
7907
+ echo esc_html__( 'Last attempted', 'ewww-image-optimizer' ) . ': ' . $image->updated . '<br>';
7908
+ echo esc_html__( 'PHP trace', 'ewww-image-optimizer' ) . ':<br>';
7909
+ $i = 0;
7910
+ if ( is_array( $trace ) ) {
7911
+ foreach ( $trace as $function ) {
7912
+ if ( ! empty( $function['file'] ) && ! empty( $function['line'] ) ) {
7913
+ echo "#$i {$function['function']}() called at {$function['file']}:{$function['line']}<br>";
7914
+ } else {
7915
+ echo "#$i {$function['function']}() called<br>";
7916
+ }
7917
+ $i++;
7918
+ }
7919
+ } else {
7920
+ esc_html_e( 'Cannot display trace', 'ewww-image-optimizer' );
7921
+ }
7922
+ echo '</p>';
7923
+ }
7924
+ }
7925
+ echo '</div>';
7926
+ }
7927
+
7928
+ /**
7929
+ * Displays images that are in the optimization queue.
7930
+ *
7931
+ * Allows viewing the image queues for debugging, and lets the user clear all queues.
7932
+ *
7933
+ * @global object $wpdb
7934
+ * @global object $ewwwio_media_background Background optimization class object.
7935
+ */
7936
+ function ewww_image_optimizer_image_queue_debug() {
7937
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
7938
+ // Let user clear a queue, or all queues.
7939
+ if ( isset( $_POST['ewww_image_optimizer_clear_queue'] ) && current_user_can( 'manage_options' ) && wp_verify_nonce( $_POST['ewww_nonce'], 'ewww_image_optimizer_clear_queue' ) ) {
7940
+ if ( is_numeric( $_POST['ewww_image_optimizer_clear_queue'] ) ) {
7941
+ global $ewwwio_media_background;
7942
+ if ( ! class_exists( 'WP_Background_Process' ) ) {
7943
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
7944
+ }
7945
+ if ( ! is_object( $ewwwio_media_background ) ) {
7946
+ $ewwwio_media_background = new EWWWIO_Media_Background_Process();
7947
+ }
7948
+ $queues = (int) $_POST['ewww_image_optimizer_clear_queue'];
7949
+ while ( $queues ) {
7950
+ $ewwwio_media_background->cancel_process();
7951
+ $queues--;
7952
+ }
7953
+ if ( ! empty( $_POST['ids'] ) && preg_match( '/^[\d,]+$/', $_POST['ids'], $request_ids ) ) {
7954
+ $ids = explode( ',', $request_ids[0] );
7955
+ foreach ( $ids as $id ) {
7956
+ delete_transient( 'ewwwio-background-in-progress-' . $id );
7957
+ }
7958
+ }
7959
+ } else {
7960
+ delete_site_option( sanitize_text_field( $_POST['ewww_image_optimizer_clear_queue'] ) );
7961
+ if ( ! empty( $_POST['ids'] ) && preg_match( '/^[\d,]+$/', $_POST['ids'], $request_ids ) ) {
7962
+ $ids = explode( ',', $request_ids[0] );
7963
+ foreach ( $ids as $id ) {
7964
+ delete_transient( 'ewwwio-background-in-progress-' . $id );
7965
+ }
7966
+ }
7967
+ }
7968
+ }
7969
+ echo "<div class='wrap'><h1>" . esc_html__( 'Image Queue Debugging', 'ewww-image-optimizer' ) . '</h1>';
7970
+ global $wpdb;
7971
+
7972
+ $table = $wpdb->options;
7973
+
7974
+ $key = 'wp_ewwwio_media_optimize_batch_%';
7975
+ $queues = $wpdb->get_results(
7976
+ $wpdb->prepare( "
7977
+ SELECT *
7978
+ FROM $wpdb->options
7979
+ WHERE option_name LIKE %s AND option_value != ''
7980
+ ORDER BY option_id ASC
7981
+ ", $key ),
7982
+ ARRAY_A
7983
+ );
7984
+
7985
+ $nonce = wp_create_nonce( 'ewww_image_optimizer_clear_queue' );
7986
+ if ( empty( $queues ) ) {
7987
+ esc_html_e( 'Nothing to see here, go upload some images!', 'ewww-image-optimizer' );
7988
+ } else {
7989
+ $all_ids = array();
7990
+ foreach ( $queues as $queue ) {
7991
+ $ids = array();
7992
+ echo "<strong>{$queue['option_id']}</strong> - {$queue['option_name']}<br>";
7993
+ $items = maybe_unserialize( $queue['option_value'] );
7994
+ foreach ( $items as $item ) {
7995
+ echo "{$item['id']} - {$item['type']}<br>";
7996
+ $all_ids[] = $item['id'];
7997
+ $ids[] = $item['id'];
7998
+ }
7999
+ $ids = implode( ',', $ids );
8000
+ ?> <form id="ewww-queue-clear-<?php echo $queue['option_id']; ?>" method="post" style="margin-bottom: 1.5em;" action="">
8001
+ <input type="hidden" id="ewww_nonce" name="ewww_nonce" value="<?php echo $nonce; ?>">
8002
+ <input type="hidden" name="ewww_image_optimizer_clear_queue" value="<?php echo $queue['option_name']; ?>">
8003
+ <input type="hidden" name="ids" value="<?php echo $ids; ?>">
8004
+ <button type="submit" class="button-secondary action"><?php esc_html_e( 'Clear this queue', 'ewww-image-optimizer' ); ?></button>
8005
+ </form>
8006
+ <?php }
8007
+ $all_ids = implode( ',', $all_ids );
8008
+ ?> <form id="ewww-queue-clear-all" method="post" style="margin: 2em 0;" action="">
8009
+ <input type="hidden" id="ewww_nonce" name="ewww_nonce" value="<?php echo $nonce; ?>">
8010
+ <input type="hidden" name="ewww_image_optimizer_clear_queue" value="<?php echo count( $queues ); ?>">
8011
+ <input type="hidden" name="ids" value="<?php echo $all_ids; ?>">
8012
+ <button type="submit" class="button-secondary action"><?php esc_html_e( 'Clear all queues', 'ewww-image-optimizer' ); ?></button>
8013
+ </form>
8014
+ <?php }
8015
+ }
8016
+
8017
+ /**
8018
+ * Finds the current PHP memory limit or a reasonable default.
8019
+ *
8020
+ * @return int The memory limit in bytes.
8021
+ */
8022
+ function ewwwio_memory_limit() {
8023
+ if ( defined( 'EWWW_MEMORY_LIMIT' ) && EWWW_MEMORY_LIMIT ) {
8024
+ $memory_limit = EWWW_MEMORY_LIMIT;
8025
+ } elseif ( function_exists( 'ini_get' ) ) {
8026
+ $memory_limit = ini_get( 'memory_limit' );
8027
+ } else {
8028
+ if ( ! defined( 'EWWW_MEMORY_LIMIT' ) ) {
8029
+ // Conservative default, current usage + 16M.
8030
+ $current_memory = memory_get_usage( true );
8031
+ $memory_limit = round( $current_memory / ( 1024 * 1024 ) ) + 16;
8032
+ define( 'EWWW_MEMORY_LIMIT', $memory_limit );
8033
+ }
8034
+ }
8035
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
8036
+ WP_CLI::debug( "memory limit is set at $memory_limit" );
8037
+ }
8038
+ if ( ! $memory_limit || -1 === intval( $memory_limit ) ) {
8039
+ // Unlimited, set to 32GB.
8040
+ $memory_limit = '32000M';
8041
+ }
8042
+ if ( strpos( $memory_limit, 'G' ) ) {
8043
+ $memory_limit = intval( $memory_limit ) * 1024 * 1024 * 1024;
8044
+ } else {
8045
+ $memory_limit = intval( $memory_limit ) * 1024 * 1024;
8046
+ }
8047
+ return $memory_limit;
8048
+ }
8049
+
8050
+ /**
8051
+ * Checks if there is enough memory still available.
8052
+ *
8053
+ * Looks to see if the current usage + padding will fit within the memory_limit defined by PHP.
8054
+ *
8055
+ * @param int $padding Optional. The amount of memory needed to continue. Default 1050000.
8056
+ * @return True to proceed, false if there is not enough memory.
8057
+ */
8058
+ function ewwwio_check_memory_available( $padding = 1050000 ) {
8059
+ $memory_limit = ewwwio_memory_limit();
8060
+
8061
+ $current_memory = memory_get_usage( true ) + $padding;
8062
+ if ( $current_memory >= $memory_limit ) {
8063
+ ewwwio_debug_message( "detected memory limit is not enough: $memory_limit" );
8064
+ return false;
8065
+ }
8066
+ ewwwio_debug_message( "detected memory limit is: $memory_limit" );
8067
+ return true;
8068
+ }
8069
+
8070
+ /**
8071
+ * Logs memory usage stats. Disabled normally.
8072
+ *
8073
+ * @global string $ewww_memory An buffer of memory stat messages.
8074
+ *
8075
+ * @param string $function The name of the function or descriptive label.
8076
+ */
8077
+ function ewwwio_memory( $function ) {
8078
+ return;
8079
+ if ( WP_DEBUG ) {
8080
+ global $ewww_memory;
8081
+ $ewww_memory .= $function . ': ' . memory_get_usage( true ) . "\n";
8082
+ ewwwio_memory_output();
8083
+ }
8084
+ }
8085
+
8086
+ /**
8087
+ * Saves the memory stats to a log file.
8088
+ *
8089
+ * @global string $ewww_memory An buffer of memory stat messages.
8090
+ */
8091
+ function ewwwio_memory_output() {
8092
+ if ( WP_DEBUG ) {
8093
+ global $ewww_memory;
8094
+ $timestamp = date( 'y-m-d h:i:s.u' ) . ' ';
8095
+ if ( ! file_exists( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'memory.log' ) ) {
8096
+ touch( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'memory.log' );
8097
+ }
8098
+ file_put_contents( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'memory.log', $timestamp . $ewww_memory, FILE_APPEND );
8099
+ $ewww_memory = '';
8100
+ }
8101
+ }
8102
+ ?>
ewww-image-optimizer.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Loader for Standard EWWW I.O. plugin.
4
+ *
5
+ * This file bootstraps the rest of the EWWW IO plugin after some basic checks.
6
+ *
7
+ * @link https://ewww.io
8
+ * @package EWWW_Image_Optimizer
9
+ */
10
+
11
+ /*
12
+ Plugin Name: EWWW Image Optimizer
13
+ Plugin URI: https://wordpress.org/plugins/ewww-image-optimizer/
14
+ Description: Reduce file sizes for images within WordPress including NextGEN Gallery and GRAND FlAGallery. Uses jpegtran, optipng/pngout, and gifsicle.
15
+ Author: Shane Bishop
16
+ Text Domain: ewww-image-optimizer
17
+ Version: 3.5.1
18
+ Author URI: https://ewww.io/
19
+ License: GPLv3
20
+ */
21
+
22
+ if ( ! defined( 'ABSPATH' ) ) {
23
+ exit;
24
+ }
25
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_TOOL_PATH' ) ) {
26
+ /**
27
+ * The folder where we install optimization tools - MUST have a trailing slash.
28
+ *
29
+ * @var string EWWW_IMAGE_OPTIMIZER_TOOL_PATH
30
+ */
31
+ define( 'EWWW_IMAGE_OPTIMIZER_TOOL_PATH', WP_CONTENT_DIR . '/ewww/' );
32
+ }
33
+
34
+ // Check the PHP version.
35
+ if ( ! defined( 'PHP_VERSION_ID' ) || PHP_VERSION_ID < 50300 ) {
36
+ /**
37
+ * This is the full system path to the plugin folder.
38
+ *
39
+ * @var string EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH
40
+ */
41
+ define( 'EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
42
+ add_action( 'network_admin_notices', 'ewww_image_optimizer_unsupported_php' );
43
+ add_action( 'admin_notices', 'ewww_image_optimizer_unsupported_php' );
44
+ // Loads the plugin translations.
45
+ add_action( 'plugins_loaded', 'ewww_image_optimizer_false_init' );
46
+ } elseif ( defined( 'EWWW_IMAGE_OPTIMIZER_VERSION' ) ) {
47
+ // Prevent loading both EWWW IO plugins.
48
+ add_action( 'network_admin_notices', 'ewww_image_optimizer_dual_plugin' );
49
+ add_action( 'admin_notices', 'ewww_image_optimizer_dual_plugin' );
50
+ // Loads the plugin translations.
51
+ add_action( 'plugins_loaded', 'ewww_image_optimizer_false_init' );
52
+ } else {
53
+ /**
54
+ * The full path of the plugin file (this file).
55
+ *
56
+ * @var string EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE
57
+ */
58
+ define( 'EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE', __FILE__ );
59
+ /**
60
+ * The path of the plugin file relative to the plugins/ folder.
61
+ *
62
+ * @var string EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL
63
+ */
64
+ define( 'EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL', 'ewww-image-optimizer/ewww-image-optimizer.php' );
65
+ /**
66
+ * This is the full system path to the plugin folder.
67
+ *
68
+ * @var string EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH
69
+ */
70
+ define( 'EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
71
+ /**
72
+ * This is the full system path to the bundled binaries.
73
+ *
74
+ * @var string EWWW_IMAGE_OPTIMIZER_BINARY_PATH
75
+ */
76
+ define( 'EWWW_IMAGE_OPTIMIZER_BINARY_PATH', plugin_dir_path( __FILE__ ) . 'binaries/' );
77
+ /**
78
+ * This is the full system path to the plugin images for testing.
79
+ *
80
+ * @var string EWWW_IMAGE_OPTIMIZER_IMAGES_PATH
81
+ */
82
+ define( 'EWWW_IMAGE_OPTIMIZER_IMAGES_PATH', plugin_dir_path( __FILE__ ) . 'images/' );
83
+
84
+ /**
85
+ * All the 'unique' functions for the core EWWW I.O. plugin.
86
+ */
87
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'unique.php' );
88
+ /**
89
+ * All the 'common' functions for both EWWW I.O. functions.
90
+ */
91
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'common.php' );
92
+ /**
93
+ * The various class extensions for parallel and background optimization.
94
+ */
95
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'background.php' );
96
+ /**
97
+ * EWWW_Image class for working with queued images and image records from the database.
98
+ */
99
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewww-image.php' );
100
+ /**
101
+ * EWWWIO_Tracking class for reporting anonymous site data.
102
+ */
103
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewwwio-tracking.php' );
104
+ /**
105
+ * EWWWIO_HS_Beacon class for embedding the HelpScout Beacon.
106
+ */
107
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewwwio-hs-beacon.php' );
108
+ } // End if().
109
+
110
+ if ( ! function_exists( 'ewww_image_optimizer_unsupported_php' ) ) {
111
+ /**
112
+ * Display a notice that the PHP version is too old.
113
+ */
114
+ function ewww_image_optimizer_unsupported_php() {
115
+ echo "<div id='ewww-image-optimizer-warning-php' class='error'><p><strong>" . esc_html__( 'EWWW Image Optimizer requires PHP 5.3 or greater. Newer versions of PHP, like 5.6, 7.0 and 7.1, are significantly faster and much more secure, as PHP 5.2 has been unsupported for several years. If you are unsure how to upgrade to a supported version, ask your webhost for instructions.', 'ewww-image-optimizer' ) . '</strong></p></div>';
116
+ }
117
+
118
+ /**
119
+ * Display a notice when both the standard and cloud plugins are active.
120
+ */
121
+ function ewww_image_optimizer_dual_plugin() {
122
+ echo "<div id='ewww-image-optimizer-warning-double-plugin' class='error'><p><strong>" . esc_html__( 'Only one version of the EWWW Image Optimizer can be active at a time. Please deactivate other copies of the plugin.', 'ewww-image-optimizer' ) . '</strong></p></div>';
123
+ }
124
+
125
+ /**
126
+ * Runs on 'plugins_loaded' to load the language files when EWWW is not loading.
127
+ */
128
+ function ewww_image_optimizer_false_init() {
129
+ load_plugin_textdomain( 'ewww-image-optimizer', false, EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'languages/' );
130
+ }
131
+ }
images/sample.jpg ADDED
Binary file
images/spinner.gif ADDED
Binary file
images/test.png ADDED
Binary file
images/test.png.webp ADDED
Binary file
images/testorig.gif ADDED
Binary file
images/testorig.jpg ADDED
Binary file
images/testorig.png ADDED
Binary file
images/wpspin.gif ADDED
Binary file
includes/eio.js ADDED
@@ -0,0 +1,604 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ var ewww_error_counter = 30;
3
+ if (!ewww_vars.scan_fail) {
4
+ $('#ewww-webp-rewrite').submit(function() {
5
+ var ewww_webp_rewrite_action = 'ewww_webp_rewrite';
6
+ var ewww_webp_rewrite_data = {
7
+ action: ewww_webp_rewrite_action,
8
+ ewww_wpnonce: ewww_vars._wpnonce,
9
+ };
10
+ $.post(ajaxurl, ewww_webp_rewrite_data, function(response) {
11
+ $('#ewww-webp-rewrite-status').html('<b>' + response + '</b>');
12
+ ewww_webp_image = document.getElementById("webp-image").src;
13
+ document.getElementById("webp-image").src = ewww_webp_image + '#' + new Date().getTime();
14
+ });
15
+ return false;
16
+ });
17
+ $('#ewww-status-expand').click(function() {
18
+ $('#ewww-collapsible-status').show();
19
+ $('#ewww-status-expand').hide();
20
+ $('#ewww-status-collapse').show();
21
+ });
22
+ $('#ewww-status-collapse').click(function() {
23
+ $('#ewww-collapsible-status').hide();
24
+ $('#ewww-status-expand').show();
25
+ $('#ewww-status-collapse').hide();
26
+ });
27
+ $('#ewww-webp-settings').hide();
28
+ $('#ewww-general-settings').show();
29
+ $('li.ewww-general-nav').addClass('ewww-selected');
30
+ if($('#ewww_image_optimizer_cloud_key').length){
31
+ $('#ewww-optimization-settings').hide();
32
+ console.log($('#ewww-general-settings').length);
33
+ }
34
+ $('#ewww-conversion-settings').hide();
35
+ $('.ewww-webp-nav').click(function() {
36
+ $('.ewww-tab-nav li').removeClass('ewww-selected');
37
+ $('li.ewww-webp-nav').addClass('ewww-selected');
38
+ $('.ewww-tab a').blur();
39
+ $('#ewww-webp-settings').show();
40
+ $('#ewww-general-settings').hide();
41
+ $('#ewww-optimization-settings').hide();
42
+ $('#ewww-conversion-settings').hide();
43
+ });
44
+ $('.ewww-general-nav').click(function() {
45
+ $('.ewww-tab-nav li').removeClass('ewww-selected');
46
+ $('li.ewww-general-nav').addClass('ewww-selected');
47
+ $('.ewww-tab a').blur();
48
+ $('#ewww-webp-settings').hide();
49
+ $('#ewww-general-settings').show();
50
+ $('#ewww-optimization-settings').hide();
51
+ $('#ewww-conversion-settings').hide();
52
+ });
53
+ $('.ewww-optimization-nav').click(function() {
54
+ $('.ewww-tab-nav li').removeClass('ewww-selected');
55
+ $('li.ewww-optimization-nav').addClass('ewww-selected');
56
+ $('.ewww-tab a').blur();
57
+ $('#ewww-webp-settings').hide();
58
+ $('#ewww-general-settings').hide();
59
+ $('#ewww-optimization-settings').show();
60
+ $('#ewww-conversion-settings').hide();
61
+ });
62
+ $('.ewww-conversion-nav').click(function() {
63
+ $('.ewww-tab-nav li').removeClass('ewww-selected');
64
+ $('li.ewww-conversion-nav').addClass('ewww-selected');
65
+ $('.ewww-tab a').blur();
66
+ $('#ewww-webp-settings').hide();
67
+ $('#ewww-general-settings').hide();
68
+ $('#ewww-optimization-settings').hide();
69
+ $('#ewww-conversion-settings').show();
70
+ });
71
+ return false;
72
+ } else {
73
+ $(function() {
74
+ $("#ewww-delay-slider").slider({
75
+ min: 0,
76
+ max: 30,
77
+ value: $("#ewww-delay").val(),
78
+ slide: function(event, ui) {
79
+ $("#ewww-delay").val(ui.value);
80
+ }
81
+ });
82
+ });
83
+ var ewww_attachments = ewww_vars.attachments;
84
+ var ewww_i = 0;
85
+ var ewww_k = 0;
86
+ var ewww_import_total = 0;
87
+ var ewww_force = 0;
88
+ var ewww_delay = 0;
89
+ var ewww_batch_limit = 0;
90
+ var ewww_aux = false;
91
+ var ewww_main = false;
92
+ var ewww_quota_update = 0;
93
+ var ewww_scan_failures = 0;
94
+ var ewww_bulk_start_time = 0;
95
+ var ewww_bulk_elapsed_time = 0;
96
+ var ewww_time_per_image = 0;
97
+ var ewww_time_remaining = 0;
98
+ var ewww_days_remaining = 0;
99
+ var ewww_hours_remaining = 0;
100
+ var ewww_minutes_remaining = 0;
101
+ var ewww_seconds_remaining = 0;
102
+ var ewww_countdown = false;
103
+ // initialize the ajax actions for the appropriate bulk page
104
+ var ewww_quota_update_data = {
105
+ action: 'bulk_quota_update',
106
+ ewww_wpnonce: ewww_vars._wpnonce,
107
+ };
108
+ if (ewww_vars.gallery == 'flag') {
109
+ var ewww_init_action = 'bulk_flag_init';
110
+ var ewww_loop_action = 'bulk_flag_loop';
111
+ var ewww_cleanup_action = 'bulk_flag_cleanup';
112
+ } else if (ewww_vars.gallery == 'nextgen') {
113
+ var ewww_preview_action = 'bulk_ngg_preview';
114
+ var ewww_init_action = 'bulk_ngg_init';
115
+ var ewww_loop_action = 'bulk_ngg_loop';
116
+ var ewww_cleanup_action = 'bulk_ngg_cleanup';
117
+ // this loads inline on the nextgen gallery management pages
118
+ if (!document.getElementById('ewww-bulk-loading')) {
119
+ var ewww_preview_data = {
120
+ action: ewww_preview_action,
121
+ ewww_inline: 1,
122
+ };
123
+ $.post(ajaxurl, ewww_preview_data, function(response) {
124
+ $('.wrap').prepend(response);
125
+ $(function() {
126
+ $("#ewww-delay-slider").slider({
127
+ min: 0,
128
+ max: 30,
129
+ value: $("#ewww-delay").val(),
130
+ slide: function(event, ui) {
131
+ $("#ewww-delay").val(ui.value);
132
+ }
133
+ });
134
+ });
135
+ $('#ewww-bulk-start').submit(function() {
136
+ ewwwStartOpt();
137
+ return false;
138
+ });
139
+ });
140
+ }
141
+ } else {
142
+ var ewww_scan_action = 'bulk_scan';
143
+ var ewww_init_action = 'bulk_init';
144
+ var ewww_loop_action = 'bulk_loop';
145
+ var ewww_cleanup_action = 'bulk_cleanup';
146
+ ewww_main = true;
147
+ }
148
+ var ewww_init_data = {
149
+ action: ewww_init_action,
150
+ ewww_wpnonce: ewww_vars._wpnonce,
151
+ };
152
+ var ewww_table_action = 'bulk_aux_images_table';
153
+ var ewww_table_count_action = 'bulk_aux_images_table_count';
154
+ var ewww_import_init_action = 'bulk_import_init';
155
+ var ewww_import_loop_action = 'bulk_import_loop';
156
+ $('#ewww-aux-start').submit(function() {
157
+ ewww_aux = true;
158
+ //ewww_init_action = 'bulk_init';
159
+ //ewww_loop_action = 'bulk_aux_images_loop';
160
+ //ewww_cleanup_action = 'bulk_aux_images_cleanup';
161
+ if ($('#ewww-force:checkbox:checked').val()) {
162
+ ewww_force = 1;
163
+ }
164
+ $('#ewww-aux-start').hide();
165
+ $('.ewww-bulk-info').hide();
166
+ $('.ewww-aux-table').hide();
167
+ $('#ewww-show-table').hide();
168
+ $('#ewww-scanning').show();
169
+ //ewww_vars.scanning = $('#ewww-scanning').html();
170
+ ewwwStartScan();
171
+ return false;
172
+ });
173
+ function ewwwStartScan() {
174
+ var ewww_scan_data = {
175
+ action: ewww_scan_action,
176
+ ewww_force: ewww_force,
177
+ ewww_scan: true,
178
+ ewww_wpnonce: ewww_vars._wpnonce,
179
+ };
180
+ $.post(ajaxurl, ewww_scan_data, function(response) {
181
+ var is_json = true;
182
+ try {
183
+ var ewww_response = $.parseJSON(response);
184
+ } catch (err) {
185
+ is_json = false;
186
+ }
187
+ if ( ! is_json ) {
188
+ $('#ewww-scanning').html('<span style="color: red"><b>' + ewww_vars.invalid_response + '</b></span>');
189
+ console.log( response );
190
+ return false;
191
+ }
192
+ ewww_init_data = {
193
+ action: ewww_init_action,
194
+ ewww_wpnonce: ewww_vars._wpnonce,
195
+ };
196
+ if ( ewww_response.error ) {
197
+ $('#ewww-scanning').html('<span style="color: red"><b>' + ewww_response.error + '</b></span>');
198
+ } else if ( ewww_response.remaining ) {
199
+ $('.ewww-aux-table').hide();
200
+ $('#ewww-show-table').hide();
201
+ //if ( ! ewww_response.notice ) {
202
+ // ewww_response.notice = '';
203
+ //}
204
+ $('#ewww-scanning').html( ewww_response.remaining );
205
+ if ( ewww_response.notice ) {
206
+ $('#ewww-scanning').append( '<br>' + ewww_response.notice );
207
+ }
208
+ if ( ewww_response.bad_attachment ) {
209
+ $('#ewww-scanning').append( '<br>' + ewww_vars.bad_attachment + ' ' + ewww_response.bad_attachment );
210
+ }
211
+ ewww_scan_failures = 0;
212
+ ewwwStartScan();
213
+ } else if ( ewww_response.ready ) {
214
+ ewww_attachments = ewww_response.ready;
215
+ if (ewww_attachments == 0) {
216
+ // $('#ewww-bulk-loading').hide();
217
+ $('#ewww-scanning').hide();
218
+ $('#ewww-nothing').show();
219
+ //$('#ewww-aux-start').show();
220
+ //$('#ewww-aux-again').show();
221
+ //$('#ewww-aux-first').hide();
222
+ } else {
223
+ $('#ewww-scanning').html(ewww_response.message);
224
+ $('#ewww-bulk-start').show();
225
+ // ewwwStartOpt();
226
+ }
227
+ }
228
+ })
229
+ .fail(function() {
230
+ ewww_scan_failures++;
231
+ if (ewww_scan_failures > 10) {
232
+ $('#ewww-scanning').html('<span style="color: red"><b>' + ewww_vars.scan_fail + '</b></span>');
233
+ } else {
234
+ $('#ewww-scanning').html('<span style="color: red"><b>' + ewww_vars.scan_incomplete + '</b></span>');
235
+ setTimeout(function() {
236
+ ewwwStartScan();
237
+ }, 1000);
238
+ }
239
+ });
240
+ }
241
+ $('#ewww-show-table').submit(function() {
242
+ var ewww_pointer = 0;
243
+ var ewww_total_pages = Math.ceil(ewww_vars.image_count / 50);
244
+ $('.ewww-aux-table').show();
245
+ $('#ewww-show-table').hide();
246
+ if (ewww_vars.image_count >= 50) {
247
+ $('.tablenav').show();
248
+ $('#next-images').show();
249
+ $('.last-page').show();
250
+ }
251
+ var ewww_table_data = {
252
+ action: ewww_table_action,
253
+ ewww_wpnonce: ewww_vars._wpnonce,
254
+ ewww_offset: ewww_pointer,
255
+ };
256
+ $('.displaying-num').text(ewww_vars.count_string);
257
+ $.post(ajaxurl, ewww_table_data, function(response) {
258
+ $('#ewww-bulk-table').html(response);
259
+ });
260
+ $('.current-page').text(ewww_pointer + 1);
261
+ $('.total-pages').text(ewww_total_pages);
262
+ $('#ewww-pointer').text(ewww_pointer);
263
+ return false;
264
+ });
265
+ $('#next-images').click(function() {
266
+ var ewww_pointer = $('#ewww-pointer').text();
267
+ ewww_pointer++;
268
+ var ewww_table_data = {
269
+ action: ewww_table_action,
270
+ ewww_wpnonce: ewww_vars._wpnonce,
271
+ ewww_offset: ewww_pointer,
272
+ };
273
+ $.post(ajaxurl, ewww_table_data, function(response) {
274
+ $('#ewww-bulk-table').html(response);
275
+ });
276
+ if (ewww_vars.image_count <= ((ewww_pointer + 1) * 50)) {
277
+ $('#next-images').hide();
278
+ $('.last-page').hide();
279
+ }
280
+ $('.current-page').text(ewww_pointer + 1);
281
+ $('#ewww-pointer').text(ewww_pointer);
282
+ $('#prev-images').show();
283
+ $('.first-page').show();
284
+ return false;
285
+ });
286
+ $('#prev-images').click(function() {
287
+ var ewww_pointer = $('#ewww-pointer').text();
288
+ ewww_pointer--;
289
+ var ewww_table_data = {
290
+ action: ewww_table_action,
291
+ ewww_wpnonce: ewww_vars._wpnonce,
292
+ ewww_offset: ewww_pointer,
293
+ };
294
+ $.post(ajaxurl, ewww_table_data, function(response) {
295
+ $('#ewww-bulk-table').html(response);
296
+ });
297
+ if (!ewww_pointer) {
298
+ $('#prev-images').hide();
299
+ $('.first-page').hide();
300
+ }
301
+ $('.current-page').text(ewww_pointer + 1);
302
+ $('#ewww-pointer').text(ewww_pointer);
303
+ $('#next-images').show();
304
+ $('.last-page').show();
305
+ return false;
306
+ });
307
+ $('.last-page').click(function() {
308
+ var ewww_pointer = $('.total-pages').text();
309
+ ewww_pointer--;
310
+ var ewww_table_data = {
311
+ action: ewww_table_action,
312
+ ewww_wpnonce: ewww_vars._wpnonce,
313
+ ewww_offset: ewww_pointer,
314
+ };
315
+ $.post(ajaxurl, ewww_table_data, function(response) {
316
+ $('#ewww-bulk-table').html(response);
317
+ });
318
+ $('#next-images').hide();
319
+ $('.last-page').hide();
320
+ $('.current-page').text(ewww_pointer + 1);
321
+ $('#ewww-pointer').text(ewww_pointer);
322
+ $('#prev-images').show();
323
+ $('.first-page').show();
324
+ return false;
325
+ });
326
+ $('.first-page').click(function() {
327
+ var ewww_pointer = 0;
328
+ var ewww_table_data = {
329
+ action: ewww_table_action,
330
+ ewww_wpnonce: ewww_vars._wpnonce,
331
+ ewww_offset: ewww_pointer,
332
+ };
333
+ $.post(ajaxurl, ewww_table_data, function(response) {
334
+ $('#ewww-bulk-table').html(response);
335
+ });
336
+ $('#prev-images').hide();
337
+ $('.first-page').hide();
338
+ $('.current-page').text(ewww_pointer + 1);
339
+ $('#ewww-pointer').text(ewww_pointer);
340
+ $('#next-images').show();
341
+ $('.last-page').show();
342
+ return false;
343
+ });
344
+ $('#ewww-bulk-start').submit(function() {
345
+ ewwwStartOpt();
346
+ return false;
347
+ });
348
+ }
349
+ function ewwwUpdateQuota() {
350
+ ewww_quota_update_data.ewww_wpnonce = ewww_vars._wpnonce;
351
+ $.post(ajaxurl, ewww_quota_update_data, function(response) {
352
+ $('#ewww-bulk-credits-available').html(response);
353
+ });
354
+ }
355
+ function ewwwStartOpt () {
356
+ ewww_k = 0;
357
+ ewww_quota_update = setInterval( ewwwUpdateQuota, 60000 );
358
+ $('#ewww-bulk-stop').submit(function() {
359
+ ewww_k = 9;
360
+ $('#ewww-bulk-stop').hide();
361
+ return false;
362
+ });
363
+ if ( ! $('#ewww-delay').val().match( /^[1-9][0-9]*$/) ) {
364
+ ewww_delay = 0;
365
+ } else {
366
+ ewww_delay = $('#ewww-delay').val();
367
+ }
368
+ if (ewww_delay) {
369
+ ewww_batch_limit = 1;
370
+ $('#ewww-bulk-last h2').html( ewww_vars.last_image_header );
371
+ }
372
+ $('.ewww-aux-table').hide();
373
+ $('#ewww-bulk-stop').show();
374
+ $('.ewww-bulk-form').hide();
375
+ $('.ewww-bulk-info').hide();
376
+ $('h2').hide();
377
+ $.post(ajaxurl, ewww_init_data, function(response) {
378
+ var is_json = true;
379
+ try {
380
+ var ewww_init_response = $.parseJSON(response);
381
+ } catch (err) {
382
+ is_json = false;
383
+ }
384
+ if ( ! is_json ) {
385
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_vars.invalid_response + '</b></p>');
386
+ console.log( response );
387
+ return false;
388
+ }
389
+ if ( ewww_init_response.error ) {
390
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_init_response.error + '</b></p>');
391
+ if ( ewww_init_response.data ) {
392
+ console.log( ewww_init_response.data );
393
+ }
394
+ } else {
395
+ if ( ewww_init_response.start_time ) {
396
+ ewww_bulk_start_time = ewww_init_response.start_time;
397
+ }
398
+ $('#ewww-bulk-loading').html(ewww_init_response.results);
399
+ $('#ewww-bulk-progressbar').progressbar({ max: ewww_attachments });
400
+ $('#ewww-bulk-counter').html( ewww_vars.optimized + ' 0/' + ewww_attachments);
401
+ ewwwProcessImage();
402
+ }
403
+ });
404
+ }
405
+ function ewwwProcessImage() {
406
+ if ($('#ewww-force:checkbox:checked').val()) {
407
+ ewww_force = 1;
408
+ }
409
+ var ewww_loop_data = {
410
+ action: ewww_loop_action,
411
+ ewww_wpnonce: ewww_vars._wpnonce,
412
+ ewww_force: ewww_force,
413
+ ewww_batch_limit: ewww_batch_limit,
414
+ };
415
+ var ewww_jqxhr = $.post(ajaxurl, ewww_loop_data, function(response) {
416
+ var is_json = true;
417
+ try {
418
+ var ewww_response = $.parseJSON(response);
419
+ } catch (err) {
420
+ is_json = false;
421
+ }
422
+ if ( ! is_json ) {
423
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_vars.invalid_response + '</b></p>');
424
+ console.log( response );
425
+ return false;
426
+ }
427
+ ewww_i += ewww_response.completed;
428
+ $('#ewww-bulk-progressbar').progressbar( "option", "value", ewww_i );
429
+ $('#ewww-bulk-counter').html(ewww_vars.optimized + ' ' + ewww_i + '/' + ewww_attachments);
430
+ if ( ewww_response.error ) {
431
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_response.error + '</b></p>');
432
+ clearInterval(ewww_quota_update);
433
+ clearInterval(ewww_countdown);
434
+ }
435
+ else if (ewww_k == 9) {
436
+ if ( ewww_response.results ) {
437
+ $('#ewww-bulk-last .inside').html( ewww_response.results );
438
+ $('#ewww-bulk-status .inside').append( ewww_response.results );
439
+ }
440
+ clearInterval(ewww_quota_update);
441
+ clearInterval(ewww_countdown);
442
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_vars.operation_stopped + '</b></p>');
443
+ }
444
+ else if ( response == 0 ) {
445
+ clearInterval(ewww_quota_update);
446
+ clearInterval(ewww_countdown);
447
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_vars.operation_stopped + '</b></p>');
448
+ }
449
+ else if ( ewww_i < ewww_attachments && ! ewww_response.done ) {
450
+ if ( ewww_bulk_start_time && ewww_response.current_time ) {
451
+ ewww_bulk_elapsed_time = ewww_response.current_time - ewww_bulk_start_time;
452
+ ewww_time_per_image = ewww_bulk_elapsed_time / ewww_i;
453
+ ewww_time_remaining = Math.floor((ewww_attachments - ewww_i) * ewww_time_per_image);
454
+ ewwwTimeIncrementsUpdate();
455
+ if ( ! ewww_countdown) {
456
+ $('#ewww-bulk-timer').html(ewww_days_remaining + ':' + ewww_hours_remaining + ':' + ewww_minutes_remaining + ':' + ewww_seconds_remaining + ' ' + ewww_vars.time_remaining);
457
+ ewww_countdown = setInterval( ewwwCountDown, 1000 );
458
+ }
459
+ }
460
+ $('#ewww-bulk-widgets').show();
461
+ $('#ewww-bulk-status h2').show();
462
+ $('#ewww-bulk-last h2').show();
463
+ if ( ewww_response.results ) {
464
+ $('#ewww-bulk-last .inside').html( ewww_response.results );
465
+ $('#ewww-bulk-status .inside').append( ewww_response.results );
466
+ }
467
+ if ( ewww_response.next_file ) {
468
+ $('#ewww-bulk-loading').html(ewww_response.next_file);
469
+ }
470
+ if ( ewww_response.new_nonce ) {
471
+ ewww_vars._wpnonce = ewww_response.new_nonce;
472
+ }
473
+ ewww_error_counter = 30;
474
+ setTimeout(ewwwProcessImage, ewww_delay * 1000);
475
+ }
476
+ else {
477
+ if ( ewww_response.results ) {
478
+ $('#ewww-bulk-widgets').show();
479
+ $('#ewww-bulk-status h2').show();
480
+ $('#ewww-bulk-status .inside').append( ewww_response.results );
481
+ }
482
+ var ewww_cleanup_data = {
483
+ action: ewww_cleanup_action,
484
+ ewww_wpnonce: ewww_vars._wpnonce,
485
+ };
486
+ $.post(ajaxurl, ewww_cleanup_data, function(response) {
487
+ $('#ewww-bulk-loading').html(response);
488
+ $('#ewww-bulk-stop').hide();
489
+ $('#ewww-bulk-last').hide();
490
+ ewwwAuxCleanup();
491
+ });
492
+ }
493
+ })
494
+ .fail(function() {
495
+ if (ewww_error_counter == 0) {
496
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_vars.operation_interrupted + '</b></p>');
497
+ } else {
498
+ $('#ewww-bulk-loading').html('<p style="color: red"><b>' + ewww_vars.temporary_failure + ' ' + ewww_error_counter + '</b></p>');
499
+ ewww_error_counter--;
500
+ setTimeout(function() {
501
+ ewwwProcessImage();
502
+ }, 1000);
503
+ }
504
+ });
505
+ }
506
+ function ewwwAuxCleanup() {
507
+ if (ewww_main == true) {
508
+ clearInterval(ewww_quota_update);
509
+ clearInterval(ewww_countdown);
510
+ var ewww_table_count_data = {
511
+ action: ewww_table_count_action,
512
+ ewww_inline: 1,
513
+ };
514
+ $.post(ajaxurl, ewww_table_count_data, function(response) {
515
+ ewww_vars.image_count = response;
516
+ });
517
+ $('#ewww-show-table').show();
518
+ $('#ewww-table-info').show();
519
+ $('#ewww-bulk-timer').hide();
520
+ if (ewww_aux == true) {
521
+ $('#ewww-aux-first').hide();
522
+ } else {
523
+ $('#ewww-bulk-first').hide();
524
+ }
525
+ ewww_attachments = ewww_vars.attachments;
526
+ ewww_init_action = 'bulk_init';
527
+ ewww_filename_action = 'bulk_filename';
528
+ ewww_loop_action = 'bulk_loop';
529
+ ewww_cleanup_action = 'bulk_cleanup';
530
+ ewww_init_data = {
531
+ action: ewww_init_action,
532
+ ewww_wpnonce: ewww_vars._wpnonce,
533
+ };
534
+ ewww_aux = false;
535
+ ewww_i = 0;
536
+ ewww_force = 0;
537
+ }
538
+ }
539
+ function ewwwCountDown() {
540
+ if (ewww_time_remaining > 1) {
541
+ ewww_time_remaining--;
542
+ }
543
+ ewwwTimeIncrementsUpdate();
544
+ $('#ewww-bulk-timer').html(ewww_days_remaining + ':' + ewww_hours_remaining + ':' + ewww_minutes_remaining + ':' + ewww_seconds_remaining + ' ' + ewww_vars.time_remaining);
545
+ }
546
+ function ewwwTimeIncrementsUpdate() {
547
+ ewww_days_remaining = Math.floor(ewww_time_remaining / 86400);
548
+ ewww_hours_remaining = Math.floor((ewww_time_remaining - (ewww_days_remaining * 86400)) / 3600);
549
+ ewww_minutes_remaining = Math.floor((ewww_time_remaining - (ewww_days_remaining * 86400) - (ewww_hours_remaining * 3600)) / 60);
550
+ ewww_seconds_remaining = ewww_time_remaining - (ewww_days_remaining * 86400) - (ewww_hours_remaining * 3600) - (ewww_minutes_remaining * 60);
551
+ if (ewww_days_remaining < 10) { ewww_days_remaining = '0'+ewww_days_remaining; }
552
+ if (ewww_hours_remaining < 10) { ewww_hours_remaining = '0'+ewww_hours_remaining; }
553
+ if (ewww_minutes_remaining < 10) { ewww_minutes_remaining = '0'+ewww_minutes_remaining; }
554
+ if (ewww_seconds_remaining < 10) { ewww_seconds_remaining = '0'+ewww_seconds_remaining; }
555
+ }
556
+ });
557
+ function ewwwRemoveImage(imageID) {
558
+ var ewww_image_removal = {
559
+ action: 'bulk_aux_images_remove',
560
+ ewww_wpnonce: ewww_vars._wpnonce,
561
+ ewww_image_id: imageID,
562
+ };
563
+ jQuery.post(ajaxurl, ewww_image_removal, function(response) {
564
+ if(response == '1') {
565
+ jQuery('#ewww-image-' + imageID).remove();
566
+ var ewww_prev_count = ewww_vars.image_count;
567
+ ewww_vars.image_count--;
568
+ ewww_vars.count_string = ewww_vars.count_string.replace( ewww_prev_count, ewww_vars.image_count );
569
+ jQuery('.displaying-num').text(ewww_vars.count_string);
570
+ } else {
571
+ alert(ewww_vars.remove_failed);
572
+ }
573
+ });
574
+ }
575
+ function ewwwRestoreImage(imageID) {
576
+ var ewww_image_restore = {
577
+ action: 'ewww_manual_cloud_restore_single',
578
+ ewww_wpnonce: ewww_vars._wpnonce,
579
+ ewww_image_id: imageID,
580
+ };
581
+ var original_html = jQuery('#ewww-image-' + imageID + ' td:last-child').html();
582
+ jQuery('#ewww-image-' + imageID + ' td:last-child').html(ewww_vars.restoring);
583
+ jQuery.post(ajaxurl, ewww_image_restore, function(response) {
584
+ var is_json = true;
585
+ try {
586
+ var ewww_response = jQuery.parseJSON(response);
587
+ } catch (err) {
588
+ is_json = false;
589
+ }
590
+ if ( ! is_json ) {
591
+ alert( ewww_vars.invalid_response );
592
+ console.log( response );
593
+ return false;
594
+ }
595
+ if ( ewww_response.success == '1') {
596
+ jQuery('#ewww-image-' + imageID + ' td:last-child').html(ewww_vars.original_restored);
597
+ return false;
598
+ } else if (ewww_response.error) {
599
+ jQuery('#ewww-image-' + imageID + ' td:last-child').html(original_html);
600
+ alert(ewww_response.error);
601
+ return false;
602
+ }
603
+ });
604
+ }
includes/flag.js ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).on( 'click', '.ewww-manual-optimize', function() {
2
+ var post_id = jQuery(this).data('id');
3
+ var ewww_nonce = jQuery(this).data('nonce');
4
+ var ewww_manual_optimize_data = {
5
+ action: 'ewww_flag_manual',
6
+ ewww_manual_nonce: ewww_nonce,
7
+ ewww_force: 1,
8
+ ewww_attachment_ID: post_id,
9
+ };
10
+ jQuery('#ewww-flag-status-' + post_id ).html( ewww_vars.optimizing );
11
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
12
+ var ewww_manual_response = jQuery.parseJSON(response);
13
+ if (ewww_manual_response.error) {
14
+ jQuery('#ewww-flag-status-' + post_id ).html( ewww_manual_response.error );
15
+ } else if (ewww_manual_response.success) {
16
+ jQuery('#ewww-flag-status-' + post_id ).html( ewww_manual_response.success );
17
+ }
18
+ });
19
+ return false;
20
+ });
21
+ /*jQuery(document).on( 'click', '.ewww-manual-convert', function() {
22
+ var post_id = jQuery(this).data('id');
23
+ var ewww_nonce = jQuery(this).data('nonce');
24
+ var ewww_manual_optimize_data = {
25
+ action: 'ewww_manual_optimize',
26
+ ewww_manual_nonce: ewww_nonce,
27
+ ewww_force: 1,
28
+ ewww_convert: 1,
29
+ ewww_attachment_ID: post_id,
30
+ };
31
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.optimizing );
32
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
33
+ var ewww_manual_response = jQuery.parseJSON(response);
34
+ if (ewww_manual_response.error) {
35
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
36
+ } else if (ewww_manual_response.success) {
37
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
38
+ }
39
+ if (ewww_manual_response.basename) {
40
+ var attachment_span = jQuery('#post-' + post_id + ' .column-title .filename .screen-reader-text').html();
41
+ jQuery('#post-' + post_id + ' .column-title .filename').html('<span class="screen-reader-text">' + attachment_span + '</span>' + ewww_manual_response.basename);
42
+ }
43
+ });
44
+ return false;
45
+ });
46
+ jQuery(document).on( 'click', '.ewww-manual-restore', function() {
47
+ var post_id = jQuery(this).data('id');
48
+ var ewww_nonce = jQuery(this).data('nonce');
49
+ var ewww_manual_optimize_data = {
50
+ action: 'ewww_manual_restore',
51
+ ewww_manual_nonce: ewww_nonce,
52
+ ewww_attachment_ID: post_id,
53
+ };
54
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.restoring );
55
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
56
+ var ewww_manual_response = jQuery.parseJSON(response);
57
+ if (ewww_manual_response.error) {
58
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
59
+ } else if (ewww_manual_response.success) {
60
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
61
+ }
62
+ if (ewww_manual_response.basename) {
63
+ var attachment_span = jQuery('#post-' + post_id + ' .column-title .filename .screen-reader-text').html();
64
+ jQuery('#post-' + post_id + ' .column-title .filename').html('<span class="screen-reader-text">' + attachment_span + '</span>' + ewww_manual_response.basename);
65
+ }
66
+ });
67
+ return false;
68
+ });*/
69
+ jQuery(document).on( 'click', '.ewww-manual-cloud-restore', function() {
70
+ var post_id = jQuery(this).data('id');
71
+ var ewww_nonce = jQuery(this).data('nonce');
72
+ var ewww_manual_optimize_data = {
73
+ action: 'ewww_flag_cloud_restore',
74
+ ewww_manual_nonce: ewww_nonce,
75
+ ewww_attachment_ID: post_id,
76
+ };
77
+ jQuery('#ewww-flag-status-' + post_id ).html( ewww_vars.restoring );
78
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
79
+ var ewww_manual_response = jQuery.parseJSON(response);
80
+ if (ewww_manual_response.error) {
81
+ jQuery('#ewww-flag-status-' + post_id ).html( ewww_manual_response.error );
82
+ } else if (ewww_manual_response.success) {
83
+ jQuery('#ewww-flag-status-' + post_id ).html( ewww_manual_response.success );
84
+ }
85
+ });
86
+ return false;
87
+ });
includes/jquery-ui-1.10.1.custom.css ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! jQuery UI - v1.10.1 - 2013-03-14
2
+ * Copyright 2013 jQuery Foundation and other contributors Licensed MIT */
3
+ /* includes styling for progressbar and tooltip elements */
4
+ /* Layout helpers
5
+ ----------------------------------*/
6
+ .ui-helper-hidden {
7
+ display: none;
8
+ }
9
+ .ui-helper-hidden-accessible {
10
+ border: 0;
11
+ clip: rect(0 0 0 0);
12
+ height: 1px;
13
+ margin: -1px;
14
+ overflow: hidden;
15
+ padding: 0;
16
+ position: absolute;
17
+ width: 1px;
18
+ }
19
+ .ui-helper-reset {
20
+ margin: 0;
21
+ padding: 0;
22
+ border: 0;
23
+ outline: 0;
24
+ line-height: 1.3;
25
+ text-decoration: none;
26
+ font-size: 100%;
27
+ list-style: none;
28
+ }
29
+ .ui-helper-clearfix:before,
30
+ .ui-helper-clearfix:after {
31
+ content: "";
32
+ display: table;
33
+ border-collapse: collapse;
34
+ }
35
+ .ui-helper-clearfix:after {
36
+ clear: both;
37
+ }
38
+ .ui-helper-clearfix {
39
+ min-height: 0; /* support: IE7 */
40
+ }
41
+ .ui-helper-zfix {
42
+ width: 100%;
43
+ height: 100%;
44
+ top: 0;
45
+ left: 0;
46
+ position: absolute;
47
+ opacity: 0;
48
+ filter:Alpha(Opacity=0);
49
+ }
50
+ .ui-front {
51
+ z-index: 100;
52
+ }
53
+ /* Interaction Cues
54
+ ----------------------------------*/
55
+ .ui-state-disabled {
56
+ cursor: default !important;
57
+ }
58
+
59
+ /* Misc visuals
60
+ ----------------------------------*/
61
+ /* Corner radius */
62
+ .ui-corner-all,
63
+ .ui-corner-top,
64
+ .ui-corner-left,
65
+ .ui-corner-tl {
66
+ border-top-left-radius: 22px;
67
+ }
68
+ .ui-corner-all,
69
+ .ui-corner-top,
70
+ .ui-corner-right,
71
+ .ui-corner-tr {
72
+ border-top-right-radius: 22px;
73
+ }
74
+ .ui-corner-all,
75
+ .ui-corner-bottom,
76
+ .ui-corner-left,
77
+ .ui-corner-bl {
78
+ border-bottom-left-radius: 22px;
79
+ }
80
+ .ui-corner-all,
81
+ .ui-corner-bottom,
82
+ .ui-corner-right,
83
+ .ui-corner-br {
84
+ border-bottom-right-radius: 22px;
85
+ }
86
+ /* Overlays */
87
+ .ui-widget-overlay {
88
+ position: fixed;
89
+ top: 0;
90
+ left: 0;
91
+ width: 100%;
92
+ height: 100%;
93
+ }
94
+ .ui-progressbar {
95
+ height: 22px;
96
+ text-align: left;
97
+ overflow: hidden;
98
+ }
99
+ .ui-progressbar .ui-progressbar-value {
100
+ margin: 0px;
101
+ height: 100%;
102
+
103
+ }
104
+ .ui-accordion .ui-accordion-header {
105
+ display: block;
106
+ cursor: pointer;
107
+ position: relative;
108
+ margin-top: 2px;
109
+ padding: .5em .5em .5em .7em;
110
+ min-height: 0; /* support: IE7 */
111
+ }
112
+ .ui-accordion .ui-accordion-icons {
113
+ padding-left: 2.2em;
114
+ }
115
+ .ui-accordion .ui-accordion-noicons {
116
+ padding-left: .7em;
117
+ }
118
+ .ui-accordion .ui-accordion-icons .ui-accordion-icons {
119
+ padding-left: 2.2em;
120
+ }
121
+ .ui-accordion .ui-accordion-header .ui-accordion-header-icon {
122
+ position: absolute;
123
+ left: .5em;
124
+ top: 50%;
125
+ margin-top: -8px;
126
+ }
127
+ .ui-accordion .ui-accordion-content {
128
+ padding: 1em 2.2em;
129
+ border-top: 0;
130
+ overflow: auto;
131
+ border-top-left-radius: 0px;
132
+ border-top-right-radius: 0px;
133
+ }
134
+ .ui-slider {
135
+ position: relative;
136
+ text-align: left;
137
+ }
138
+ .ui-slider .ui-slider-handle {
139
+ position: absolute;
140
+ z-index: 2;
141
+ width: 1.2em;
142
+ height: 1.2em;
143
+ cursor: default;
144
+ }
145
+ .ui-slider .ui-slider-range {
146
+ position: absolute;
147
+ z-index: 1;
148
+ font-size: .7em;
149
+ display: block;
150
+ border: 0;
151
+ background-position: 0 0;
152
+ }
153
+
154
+ /* For IE8 - See #6727 */
155
+ .ui-slider.ui-state-disabled .ui-slider-handle,
156
+ .ui-slider.ui-state-disabled .ui-slider-range {
157
+ filter: inherit;
158
+ }
159
+
160
+ .ui-slider-horizontal {
161
+ height: .8em;
162
+ }
163
+ .ui-slider-horizontal .ui-slider-handle {
164
+ top: -.3em;
165
+ margin-left: -.6em;
166
+ }
167
+ .ui-slider-horizontal .ui-slider-range {
168
+ top: 0;
169
+ height: 100%;
170
+ }
171
+ .ui-slider-horizontal .ui-slider-range-min {
172
+ left: 0;
173
+ }
174
+ .ui-slider-horizontal .ui-slider-range-max {
175
+ right: 0;
176
+ }
177
+
178
+ .ui-slider-vertical {
179
+ width: .8em;
180
+ height: 100px;
181
+ }
182
+ .ui-slider-vertical .ui-slider-handle {
183
+ left: -.3em;
184
+ margin-left: 0;
185
+ margin-bottom: -.6em;
186
+ }
187
+ .ui-slider-vertical .ui-slider-range {
188
+ left: 0;
189
+ width: 100%;
190
+ }
191
+ .ui-slider-vertical .ui-slider-range-min {
192
+ bottom: 0;
193
+ }
194
+ .ui-slider-vertical .ui-slider-range-max {
195
+ top: 0;
196
+ }
197
+ /* Component containers
198
+ ----------------------------------*/
199
+ .ui-widget-content {
200
+ background: none repeat scroll 0% 0% #dddddd;
201
+ -webkit-border-radius: 22px;
202
+ border-radius: 22px;
203
+ -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
204
+ box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
205
+ }
206
+ .ui-widget-content a {
207
+ color: #222222;
208
+ }
209
+ .ui-widget-header {
210
+ z-index: 9;
211
+ background-color: #0074a2;
212
+ -webkit-border-radius: 22px;
213
+ border-radius: 22px;
214
+ }
215
+ .ui-widget-header a {
216
+ color: #ffffff;
217
+ }
218
+ /* Interaction states
219
+ ----------------------------------*/
220
+ .ui-state-default,
221
+ .ui-widget-content .ui-state-default,
222
+ .ui-widget-header .ui-state-default {
223
+ border: 1px solid #d3d3d3;
224
+ background: #e6e6e6 /*url(images/ui-bg_glass_75_e6e6e6_1x400.png)*/ 50% 50% repeat-x;
225
+ font-weight: normal;
226
+ color: #555555;
227
+ }
228
+ .ui-state-default a,
229
+ .ui-state-default a:link,
230
+ .ui-state-default a:visited {
231
+ color: #555555;
232
+ text-decoration: none;
233
+ }
234
+ .ui-state-hover,
235
+ .ui-widget-content .ui-state-hover,
236
+ .ui-widget-header .ui-state-hover,
237
+ .ui-state-focus,
238
+ .ui-widget-content .ui-state-focus,
239
+ .ui-widget-header .ui-state-focus {
240
+ border: 1px solid #999999;
241
+ background: #dadada /*url(images/ui-bg_glass_75_dadada_1x400.png)*/ 50% 50% repeat-x;
242
+ font-weight: normal;
243
+ color: #212121;
244
+ }
245
+ .ui-state-hover a,
246
+ .ui-state-hover a:hover,
247
+ .ui-state-hover a:link,
248
+ .ui-state-hover a:visited {
249
+ color: #212121;
250
+ text-decoration: none;
251
+ }
252
+ .ui-state-active,
253
+ .ui-widget-content .ui-state-active,
254
+ .ui-widget-header .ui-state-active {
255
+ border: 1px solid #aaaaaa;
256
+ background: #ffffff /*url(images/ui-bg_glass_65_ffffff_1x400.png)*/ 50% 50% repeat-x;
257
+ font-weight: normal;
258
+ color: #212121;
259
+ }
260
+ .ui-state-active a,
261
+ .ui-state-active a:link,
262
+ .ui-state-active a:visited {
263
+ color: #212121;
264
+ text-decoration: none;
265
+ }
266
+
267
+ /* Interaction Cues
268
+ ----------------------------------*/
269
+ .ui-state-highlight,
270
+ .ui-widget-content .ui-state-highlight,
271
+ .ui-widget-header .ui-state-highlight {
272
+ border: 1px solid #fcefa1;
273
+ color: #363636;
274
+ }
275
+ .ui-state-highlight a,
276
+ .ui-widget-content .ui-state-highlight a,
277
+ .ui-widget-header .ui-state-highlight a {
278
+ color: #363636;
279
+ }
280
+ .ui-state-error,
281
+ .ui-widget-content .ui-state-error,
282
+ .ui-widget-header .ui-state-error {
283
+ border: 1px solid #cd0a0a;
284
+ color: #cd0a0a;
285
+ }
286
+ .ui-state-error a,
287
+ .ui-widget-content .ui-state-error a,
288
+ .ui-widget-header .ui-state-error a {
289
+ color: #cd0a0a;
290
+ }
291
+ .ui-state-error-text,
292
+ .ui-widget-content .ui-state-error-text,
293
+ .ui-widget-header .ui-state-error-text {
294
+ color: #cd0a0a;
295
+ }
296
+ .ui-priority-primary,
297
+ .ui-widget-content .ui-priority-primary,
298
+ .ui-widget-header .ui-priority-primary {
299
+ font-weight: bold;
300
+ }
301
+ .ui-priority-secondary,
302
+ .ui-widget-content .ui-priority-secondary,
303
+ .ui-widget-header .ui-priority-secondary {
304
+ opacity: .7;
305
+ filter:Alpha(Opacity=70);
306
+ font-weight: normal;
307
+ }
308
+ .ui-state-disabled,
309
+ .ui-widget-content .ui-state-disabled,
310
+ .ui-widget-header .ui-state-disabled {
311
+ opacity: .35;
312
+ filter:Alpha(Opacity=35);
313
+ background-image: none;
314
+ }
315
+ .ui-state-disabled .ui-icon {
316
+ filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
317
+ }
318
+ /* jQuery UI Tooltip 1.10.1 */
319
+
320
+ .ui-tooltip {
321
+ padding: 8px;
322
+ position: absolute;
323
+ z-index: 9999;
324
+ max-width: 300px;
325
+ min-width: 130px;
326
+ }
327
+
328
+ body .ui-tooltip {
329
+ border-width: 1px;
330
+ }
331
+
332
+ .ui-tooltip, .arrow:after {
333
+ border: 1px solid #d7d7d7;
334
+ }
335
+
336
+ .ui-tooltip {
337
+ padding: 5px 10px;
338
+ }
339
+
340
+ .arrow {
341
+ width: 70px;
342
+ height: 16px;
343
+ overflow: hidden;
344
+ position: absolute;
345
+ left: 50%;
346
+ margin-left: -35px;
347
+ bottom: -16px;
348
+ z-index: 99999;
349
+ }
350
+
351
+ .arrow.top {
352
+ top: -16px;
353
+ bottom: auto;
354
+ }
355
+
356
+ .arrow.left {
357
+ left: 20%;
358
+ }
359
+
360
+ .arrow:after {
361
+ content: "";
362
+ position: absolute;
363
+ left: 20px;
364
+ top: -20px;
365
+ width: 25px;
366
+ height: 25px;
367
+ background-color: #FFF;
368
+ -webkit-transform: rotate(45deg);
369
+ -moz-transform: rotate(45deg);
370
+ -ms-transform: rotate(45deg);
371
+ -o-transform: rotate(45deg);
372
+ tranform: rotate(45deg);
373
+ }
374
+
375
+ .arrow.top:after {
376
+ bottom: -20px;
377
+ top: auto;
378
+ }
379
+
380
+ a.removeimage, a.restoreimage {
381
+ cursor: pointer;
382
+ }
383
+ .ewww-attachment-detail-container {
384
+ display: none;
385
+ }
386
+ .ewww-attachment-detail table td, .ewww-attachment-detail table th {
387
+ padding: 8px 10px;
388
+ }
389
+ .ewww-attachment-detail table th {
390
+ border-bottom: 1px solid #e5e5e5;
391
+ }
392
+ .ewww-attachment-detail table {
393
+ width: 100%;
394
+ border: 1px solid #e5e5e5;
395
+ border-collapse: collapse;
396
+ margin-bottom: 8px;
397
+ }
398
+ .ewww-attachment-detail {
399
+ padding: 13px 0px 0px;
400
+ }
401
+ .ewww-toggle {
402
+ color: #0073aa;
403
+ cursor:pointer;
404
+ }
includes/load_webp.js ADDED
@@ -0,0 +1,641 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * arrive.js
3
+ * v2.3.1
4
+ * https://github.com/uzairfarooq/arrive
5
+ * MIT licensed - https://github.com/uzairfarooq/arrive/blob/master/LICENSE
6
+ *
7
+ * Copyright (c) 2014-2016 Uzair Farooq
8
+ */
9
+
10
+ var Arrive = (function(window, $, undefined) {
11
+
12
+ "use strict";
13
+
14
+ if(!window.MutationObserver || typeof HTMLElement === 'undefined'){
15
+ return; //for unsupported browsers
16
+ }
17
+
18
+ var arriveUniqueId = 0;
19
+
20
+ var utils = (function() {
21
+ var matches = HTMLElement.prototype.matches || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector
22
+ || HTMLElement.prototype.msMatchesSelector;
23
+
24
+ return {
25
+ matchesSelector: function(elem, selector) {
26
+ return elem instanceof HTMLElement && matches.call(elem, selector);
27
+ },
28
+ // to enable function overloading - By John Resig (MIT Licensed)
29
+ addMethod: function (object, name, fn) {
30
+ var old = object[ name ];
31
+ object[ name ] = function(){
32
+ if ( fn.length == arguments.length )
33
+ return fn.apply( this, arguments );
34
+ else if ( typeof old == 'function' )
35
+ return old.apply( this, arguments );
36
+ };
37
+ },
38
+ callCallbacks: function(callbacksToBeCalled) {
39
+ for (var i = 0, cb; cb = callbacksToBeCalled[i]; i++) {
40
+ cb.callback.call(cb.elem);
41
+ }
42
+ },
43
+ // traverse through all descendants of a node to check if event should be fired for any descendant
44
+ checkChildNodesRecursively: function(nodes, registrationData, matchFunc, callbacksToBeCalled) {
45
+ // check each new node if it matches the selector
46
+ for (var i=0, node; node = nodes[i]; i++) {
47
+ if (matchFunc(node, registrationData, callbacksToBeCalled)) {
48
+ callbacksToBeCalled.push({ callback: registrationData.callback, elem: node });
49
+ }
50
+
51
+ if (node.childNodes.length > 0) {
52
+ utils.checkChildNodesRecursively(node.childNodes, registrationData, matchFunc, callbacksToBeCalled);
53
+ }
54
+ }
55
+ },
56
+ mergeArrays: function(firstArr, secondArr){
57
+ // Overwrites default options with user-defined options.
58
+ var options = {},
59
+ attrName;
60
+ for (attrName in firstArr) {
61
+ options[attrName] = firstArr[attrName];
62
+ }
63
+ for (attrName in secondArr) {
64
+ options[attrName] = secondArr[attrName];
65
+ }
66
+ return options;
67
+ },
68
+ toElementsArray: function (elements) {
69
+ // check if object is an array (or array like object)
70
+ // Note: window object has .length property but it's not array of elements so don't consider it an array
71
+ if (typeof elements !== "undefined" && (typeof elements.length !== "number" || elements === window)) {
72
+ elements = [elements];
73
+ }
74
+ return elements;
75
+ }
76
+ };
77
+ })();
78
+
79
+
80
+ // Class to maintain state of all registered events of a single type
81
+ var EventsBucket = (function() {
82
+ var EventsBucket = function() {
83
+ // holds all the events
84
+
85
+ this._eventsBucket = [];
86
+ // function to be called while adding an event, the function should do the event initialization/registration
87
+ this._beforeAdding = null;
88
+ // function to be called while removing an event, the function should do the event destruction
89
+ this._beforeRemoving = null;
90
+ };
91
+
92
+ EventsBucket.prototype.addEvent = function(target, selector, options, callback) {
93
+ var newEvent = {
94
+ target: target,
95
+ selector: selector,
96
+ options: options,
97
+ callback: callback,
98
+ firedElems: []
99
+ };
100
+
101
+ if (this._beforeAdding) {
102
+ this._beforeAdding(newEvent);
103
+ }
104
+
105
+ this._eventsBucket.push(newEvent);
106
+ return newEvent;
107
+ };
108
+
109
+ EventsBucket.prototype.removeEvent = function(compareFunction) {
110
+ for (var i=this._eventsBucket.length - 1, registeredEvent; registeredEvent = this._eventsBucket[i]; i--) {
111
+ if (compareFunction(registeredEvent)) {
112
+ if (this._beforeRemoving) {
113
+ this._beforeRemoving(registeredEvent);
114
+ }
115
+ this._eventsBucket.splice(i, 1);
116
+ }
117
+ }
118
+ };
119
+
120
+ EventsBucket.prototype.beforeAdding = function(beforeAdding) {
121
+ this._beforeAdding = beforeAdding;
122
+ };
123
+
124
+ EventsBucket.prototype.beforeRemoving = function(beforeRemoving) {
125
+ this._beforeRemoving = beforeRemoving;
126
+ };
127
+
128
+ return EventsBucket;
129
+ })();
130
+
131
+
132
+ /**
133
+ * @constructor
134
+ * General class for binding/unbinding arrive and leave events
135
+ */
136
+ var MutationEvents = function(getObserverConfig, onMutation) {
137
+ var eventsBucket = new EventsBucket(),
138
+ me = this;
139
+
140
+ var defaultOptions = {
141
+ fireOnAttributesModification: false
142
+ };
143
+
144
+ // actual event registration before adding it to bucket
145
+ eventsBucket.beforeAdding(function(registrationData) {
146
+ var
147
+ target = registrationData.target,
148
+ selector = registrationData.selector,
149
+ callback = registrationData.callback,
150
+ observer;
151
+
152
+ // mutation observer does not work on window or document
153
+ if (target === window.document || target === window)
154
+ target = document.getElementsByTagName("html")[0];
155
+
156
+ // Create an observer instance
157
+ observer = new MutationObserver(function(e) {
158
+ onMutation.call(this, e, registrationData);
159
+ });
160
+
161
+ var config = getObserverConfig(registrationData.options);
162
+
163
+ observer.observe(target, config);
164
+
165
+ registrationData.observer = observer;
166
+ registrationData.me = me;
167
+ });
168
+
169
+ // cleanup/unregister before removing an event
170
+ eventsBucket.beforeRemoving(function (eventData) {
171
+ eventData.observer.disconnect();
172
+ });
173
+
174
+ this.bindEvent = function(selector, options, callback) {
175
+ options = utils.mergeArrays(defaultOptions, options);
176
+
177
+ var elements = utils.toElementsArray(this);
178
+
179
+ for (var i = 0; i < elements.length; i++) {
180
+ eventsBucket.addEvent(elements[i], selector, options, callback);
181
+ }
182
+ };
183
+
184
+ this.unbindEvent = function() {
185
+ var elements = utils.toElementsArray(this);
186
+ eventsBucket.removeEvent(function(eventObj) {
187
+ for (var i = 0; i < elements.length; i++) {
188
+ if (this === undefined || eventObj.target === elements[i]) {
189
+ return true;
190
+ }
191
+ }
192
+ return false;
193
+ });
194
+ };
195
+
196
+ this.unbindEventWithSelectorOrCallback = function(selector) {
197
+ var elements = utils.toElementsArray(this),
198
+ callback = selector,
199
+ compareFunction;
200
+
201
+ if (typeof selector === "function") {
202
+ compareFunction = function(eventObj) {
203
+ for (var i = 0; i < elements.length; i++) {
204
+ if ((this === undefined || eventObj.target === elements[i]) && eventObj.callback === callback) {
205
+ return true;
206
+ }
207
+ }
208
+ return false;
209
+ };
210
+ }
211
+ else {
212
+ compareFunction = function(eventObj) {
213
+ for (var i = 0; i < elements.length; i++) {
214
+ if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector) {
215
+ return true;
216
+ }
217
+ }
218
+ return false;
219
+ };
220
+ }
221
+ eventsBucket.removeEvent(compareFunction);
222
+ };
223
+
224
+ this.unbindEventWithSelectorAndCallback = function(selector, callback) {
225
+ var elements = utils.toElementsArray(this);
226
+ eventsBucket.removeEvent(function(eventObj) {
227
+ for (var i = 0; i < elements.length; i++) {
228
+ if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector && eventObj.callback === callback) {
229
+ return true;
230
+ }
231
+ }
232
+ return false;
233
+ });
234
+ };
235
+
236
+ return this;
237
+ };
238
+
239
+
240
+ /**
241
+ * @constructor
242
+ * Processes 'arrive' events
243
+ */
244
+ var ArriveEvents = function() {
245
+ var mutationEvents,
246
+ me = this;
247
+
248
+ // Default options for 'arrive' event
249
+ var arriveDefaultOptions = {
250
+ fireOnAttributesModification: false,
251
+ onceOnly: false,
252
+ existing: false
253
+ };
254
+
255
+ function getArriveObserverConfig(options) {
256
+ var config = {
257
+ attributes: false,
258
+ childList: true,
259
+ subtree: true
260
+ };
261
+
262
+ if (options.fireOnAttributesModification) {
263
+ config.attributes = true;
264
+ }
265
+
266
+ return config;
267
+ }
268
+
269
+ function onArriveMutation(mutations, registrationData) {
270
+ mutations.forEach(function( mutation ) {
271
+ var newNodes = mutation.addedNodes,
272
+ targetNode = mutation.target,
273
+ callbacksToBeCalled = [];
274
+
275
+ // If new nodes are added
276
+ if( newNodes !== null && newNodes.length > 0 ) {
277
+ utils.checkChildNodesRecursively(newNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
278
+ }
279
+ else if (mutation.type === "attributes") {
280
+ if (nodeMatchFunc(targetNode, registrationData, callbacksToBeCalled)) {
281
+ callbacksToBeCalled.push({ callback: registrationData.callback, elem: node });
282
+ }
283
+ }
284
+
285
+ utils.callCallbacks(callbacksToBeCalled);
286
+ });
287
+ }
288
+
289
+ function nodeMatchFunc(node, registrationData, callbacksToBeCalled) {
290
+ // check a single node to see if it matches the selector
291
+ if (utils.matchesSelector(node, registrationData.selector)) {
292
+ if(node._id === undefined) {
293
+ node._id = arriveUniqueId++;
294
+ }
295
+ // make sure the arrive event is not already fired for the element
296
+ if (registrationData.firedElems.indexOf(node._id) == -1) {
297
+
298
+ if (registrationData.options.onceOnly) {
299
+ if (registrationData.firedElems.length === 0) {
300
+ // On first callback, unbind event.
301
+ registrationData.me.unbindEventWithSelectorAndCallback.call(
302
+ registrationData.target, registrationData.selector, registrationData.callback);
303
+ } else {
304
+ // Ignore multiple mutations which may have been queued before the event was unbound.
305
+ return;
306
+ }
307
+ }
308
+
309
+ registrationData.firedElems.push(node._id);
310
+ callbacksToBeCalled.push({ callback: registrationData.callback, elem: node });
311
+ }
312
+ }
313
+ }
314
+
315
+ arriveEvents = new MutationEvents(getArriveObserverConfig, onArriveMutation);
316
+
317
+ var mutationBindEvent = arriveEvents.bindEvent;
318
+
319
+ // override bindEvent function
320
+ arriveEvents.bindEvent = function(selector, options, callback) {
321
+
322
+ if (typeof callback === "undefined") {
323
+ callback = options;
324
+ options = arriveDefaultOptions;
325
+ } else {
326
+ options = utils.mergeArrays(arriveDefaultOptions, options);
327
+ }
328
+
329
+ var elements = utils.toElementsArray(this);
330
+
331
+ if (options.existing) {
332
+ var existing = [];
333
+
334
+ for (var i = 0; i < elements.length; i++) {
335
+ var nodes = elements[i].querySelectorAll(selector);
336
+ for (var j = 0; j < nodes.length; j++) {
337
+ existing.push({ callback: callback, elem: nodes[j] });
338
+ }
339
+ }
340
+
341
+ // no need to bind event if the callback has to be fired only once and we have already found the element
342
+ if (options.onceOnly && existing.length) {
343
+ return callback.call(existing[0].elem);
344
+ }
345
+
346
+ setTimeout(utils.callCallbacks, 1, existing);
347
+ }
348
+
349
+ mutationBindEvent.call(this, selector, options, callback);
350
+ };
351
+
352
+ return arriveEvents;
353
+ };
354
+
355
+
356
+ /**
357
+ * @constructor
358
+ * Processes 'leave' events
359
+ */
360
+ var LeaveEvents = function() {
361
+ var mutationEvents,
362
+ me = this;
363
+
364
+ // Default options for 'leave' event
365
+ var leaveDefaultOptions = {};
366
+
367
+ function getLeaveObserverConfig(options) {
368
+ var config = {
369
+ childList: true,
370
+ subtree: true
371
+ };
372
+
373
+ return config;
374
+ }
375
+
376
+ function onLeaveMutation(mutations, registrationData) {
377
+ mutations.forEach(function( mutation ) {
378
+ var removedNodes = mutation.removedNodes,
379
+ targetNode = mutation.target,
380
+ callbacksToBeCalled = [];
381
+
382
+ if( removedNodes !== null && removedNodes.length > 0 ) {
383
+ utils.checkChildNodesRecursively(removedNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
384
+ }
385
+
386
+ utils.callCallbacks(callbacksToBeCalled);
387
+ });
388
+ }
389
+
390
+ function nodeMatchFunc(node, registrationData) {
391
+ return utils.matchesSelector(node, registrationData.selector);
392
+ }
393
+
394
+ leaveEvents = new MutationEvents(getLeaveObserverConfig, onLeaveMutation);
395
+
396
+ var mutationBindEvent = leaveEvents.bindEvent;
397
+
398
+ // override bindEvent function
399
+ leaveEvents.bindEvent = function(selector, options, callback) {
400
+
401
+ if (typeof callback === "undefined") {
402
+ callback = options;
403
+ options = leaveDefaultOptions;
404
+ } else {
405
+ options = utils.mergeArrays(leaveDefaultOptions, options);
406
+ }
407
+
408
+ mutationBindEvent.call(this, selector, options, callback);
409
+ };
410
+
411
+ return leaveEvents;
412
+ };
413
+
414
+
415
+ var arriveEvents = new ArriveEvents(),
416
+ leaveEvents = new LeaveEvents();
417
+
418
+ function exposeUnbindApi(eventObj, exposeTo, funcName) {
419
+ // expose unbind function with function overriding
420
+ utils.addMethod(exposeTo, funcName, eventObj.unbindEvent);
421
+ utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorOrCallback);
422
+ utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorAndCallback);
423
+ }
424
+
425
+ /*** expose APIs ***/
426
+ function exposeApi(exposeTo) {
427
+ exposeTo.arrive = arriveEvents.bindEvent;
428
+ exposeUnbindApi(arriveEvents, exposeTo, "unbindArrive");
429
+
430
+ exposeTo.leave = leaveEvents.bindEvent;
431
+ exposeUnbindApi(leaveEvents, exposeTo, "unbindLeave");
432
+ }
433
+
434
+ if ($) {
435
+ exposeApi($.fn);
436
+ }
437
+ exposeApi(HTMLElement.prototype);
438
+ exposeApi(NodeList.prototype);
439
+ exposeApi(HTMLCollection.prototype);
440
+ exposeApi(HTMLDocument.prototype);
441
+ exposeApi(Window.prototype);
442
+
443
+ var Arrive = {};
444
+ // expose functions to unbind all arrive/leave events
445
+ exposeUnbindApi(arriveEvents, Arrive, "unbindAllArrive");
446
+ exposeUnbindApi(leaveEvents, Arrive, "unbindAllLeave");
447
+
448
+ return Arrive;
449
+
450
+ })(window, typeof jQuery === 'undefined' ? null : jQuery, undefined);
451
+
452
+ // webp detection borrowed from https://developers.google.com/speed/webp/faq#how_can_i_detect_browser_support_using_javascript
453
+ function check_webp_feature(feature, callback) {
454
+ var kTestImages = {
455
+ alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
456
+ animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"
457
+ };
458
+ var webp_supported = false;
459
+ var img = new Image();
460
+ img.onload = function () {
461
+ var result = (img.width > 0) && (img.height > 0);
462
+ webp_supported = true;
463
+ callback(result);
464
+ };
465
+ img.onerror = function () {
466
+ webp_supported = false;
467
+ callback(false);
468
+ };
469
+ img.src = "data:image/webp;base64," + kTestImages[feature];
470
+ }
471
+ function ewww_load_images(ewww_webp_supported) {
472
+ jQuery(document).arrive('.ewww_webp', function() {
473
+ ewww_load_images(ewww_webp_supported);
474
+ });
475
+ (function($) {
476
+ var attr_prefix = 'data-';
477
+ function ewww_copy_attrs(ewww_nscript, ewww_img) {
478
+ var attrs = ['align','alt','border','crossorigin','height','hspace','ismap','longdesc','usemap','vspace','width','accesskey','class','contenteditable','contextmenu','dir','draggable','dropzone','hidden','id','lang','spellcheck','style','tabindex','title','translate','sizes','data-attachment-id','data-permalink','data-orig-size','data-comments-opened','data-image-meta','data-image-title','data-image-description'];
479
+ for (var i = 0, len = attrs.length; i < len; i++) {
480
+ var ewww_attr = $(ewww_nscript).attr(attr_prefix + attrs[i]);
481
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
482
+ $(ewww_img).attr(attrs[i], ewww_attr);
483
+ }
484
+ }
485
+ return ewww_img;
486
+ }
487
+ if (ewww_webp_supported) {
488
+ $('.batch-image img, .image-wrapper a, .ngg-pro-masonry-item a').each(function() {
489
+ var ewww_attr = $(this).attr('data-webp');
490
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
491
+ $(this).attr('data-src', ewww_attr);
492
+ }
493
+ var ewww_attr = $(this).attr('data-webp-thumbnail');
494
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
495
+ $(this).attr('data-thumbnail', ewww_attr);
496
+ }
497
+ });
498
+ $('.image-wrapper a, .ngg-pro-masonry-item a').each(function() {
499
+ var ewww_attr = $(this).attr('data-webp');
500
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
501
+ $(this).attr('href', ewww_attr);
502
+ }
503
+ });
504
+ $('.rev_slider ul li').each(function() {
505
+ var ewww_attr = $(this).attr('data-webp-thumb');
506
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
507
+ $(this).attr('data-thumb', ewww_attr);
508
+ }
509
+ var param_num = 1;
510
+ while ( param_num < 11 ) {
511
+ var ewww_attr = $(this).attr('data-webp-param' + param_num);
512
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
513
+ $(this).attr('data-param' + param_num, ewww_attr);
514
+ }
515
+ param_num++;
516
+ }
517
+ });
518
+ $('.rev_slider img').each(function() {
519
+ var ewww_attr = $(this).attr('data-webp-lazyload');
520
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
521
+ $(this).attr('data-lazyload', ewww_attr);
522
+ }
523
+ });
524
+ }
525
+ $('img.ewww_webp_lazy_retina').each(function() {
526
+ if (ewww_webp_supported) {
527
+ var ewww_attr = $(this).attr('data-srcset-webp');
528
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
529
+ $(this).attr('data-srcset', ewww_attr);
530
+ }
531
+ } else {
532
+ var ewww_attr = $(this).attr('data-srcset-img');
533
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
534
+ $(this).attr('data-srcset', ewww_attr);
535
+ }
536
+ }
537
+ $(this).removeClass('ewww_webp_lazy_retina');
538
+ });
539
+ $('video').each(function() {
540
+ if (ewww_webp_supported) {
541
+ var ewww_attr = $(this).attr('data-poster-webp');
542
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
543
+ $(this).attr('poster', ewww_attr);
544
+ }
545
+ } else {
546
+ var ewww_attr = $(this).attr('data-poster-image');
547
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
548
+ $(this).attr('poster', ewww_attr);
549
+ }
550
+ }
551
+ });
552
+ $('img.ewww_webp_lazy_load').each(function() {
553
+ if (ewww_webp_supported) {
554
+ $(this).attr('data-lazy-src', $(this).attr('data-lazy-webp-src'));
555
+ var ewww_attr = $(this).attr('data-srcset-webp');
556
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
557
+ $(this).attr('srcset', ewww_attr);
558
+ }
559
+ var ewww_attr = $(this).attr('data-lazy-srcset-webp');
560
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
561
+ $(this).attr('data-lazy-srcset', ewww_attr);
562
+ }
563
+ } else {
564
+ $(this).attr('data-lazy-src', $(this).attr('data-lazy-img-src'));
565
+ var ewww_attr = $(this).attr('data-srcset');
566
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
567
+ $(this).attr('srcset', ewww_attr);
568
+ }
569
+ var ewww_attr = $(this).attr('data-lazy-srcset-img');
570
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
571
+ $(ewww_img).attr('data-lazy-srcset', ewww_attr);
572
+ }
573
+ }
574
+ $(this).removeClass('ewww_webp_lazy_load');
575
+ });
576
+ $('.ewww_webp_lazy_hueman').each(function() {
577
+ var ewww_img = document.createElement('img');
578
+ $(ewww_img).attr('src', $(this).attr('data-src'));
579
+ if (ewww_webp_supported) {
580
+ $(ewww_img).attr('data-src', $(this).attr('data-webp-src'));
581
+ var ewww_attr = $(this).attr('data-srcset-webp');
582
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
583
+ $(ewww_img).attr('data-srcset', ewww_attr);
584
+ }
585
+ } else {
586
+ $(ewww_img).attr('data-src', $(this).attr('data-img'));
587
+ var ewww_attr = $(this).attr('data-srcset-img');
588
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
589
+ $(ewww_img).attr('data-srcset', ewww_attr);
590
+ }
591
+ }
592
+ ewww_img = ewww_copy_attrs(this, ewww_img);
593
+ $(this).after(ewww_img);
594
+ $(this).removeClass('ewww_webp_lazy_hueman');
595
+ });
596
+ $('.ewww_webp').each(function() {
597
+ var ewww_img = document.createElement('img');
598
+ if (ewww_webp_supported) {
599
+ $(ewww_img).attr('src', $(this).attr('data-webp'));
600
+ var ewww_attr = $(this).attr('data-srcset-webp');
601
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
602
+ $(ewww_img).attr('srcset', ewww_attr);
603
+ }
604
+ var ewww_attr = $(this).attr('data-webp-orig-file');
605
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
606
+ $(ewww_img).attr('data-orig-file', ewww_attr);
607
+ }
608
+ var ewww_attr = $(this).attr('data-webp-medium-file');
609
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
610
+ $(ewww_img).attr('data-medium-file', ewww_attr);
611
+ }
612
+ var ewww_attr = $(this).attr('data-webp-large-file');
613
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
614
+ $(ewww_img).attr('data-large-file', ewww_attr);
615
+ }
616
+ } else {
617
+ $(ewww_img).attr('src', $(this).attr('data-img'));
618
+ var ewww_attr = $(this).attr('data-srcset-img');
619
+ if (typeof ewww_attr !== typeof undefined && ewww_attr !== false) {
620
+ $(ewww_img).attr('srcset', ewww_attr);
621
+ }
622
+ }
623
+ ewww_img = ewww_copy_attrs(this, ewww_img);
624
+ $(this).after(ewww_img);
625
+ $(this).removeClass('ewww_webp');
626
+ });
627
+ })(jQuery);
628
+ if ( jQuery.fn.isotope && jQuery.fn.imagesLoaded ) {
629
+ jQuery('.fusion-posts-container-infinite').imagesLoaded( function() {
630
+ if ( jQuery( '.fusion-posts-container-infinite' ).hasClass( 'isotope' ) ) {
631
+ jQuery( '.fusion-posts-container-infinite' ).isotope();
632
+ }
633
+ });
634
+ jQuery('.fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper').imagesLoaded( function() {
635
+ jQuery( '.fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper' ).isotope();
636
+ });
637
+ }
638
+ }
639
+ if ( typeof jQuery !== 'undefined' ) {
640
+ check_webp_feature('alpha', ewww_load_images);
641
+ }
includes/load_webp.min.js ADDED
@@ -0,0 +1 @@
 
1
+ function check_webp_feature(a,b){var c={alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",animation:"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"},d=!1,e=new Image;e.onload=function(){var a=e.width>0&&e.height>0;d=!0,b(a)},e.onerror=function(){d=!1,b(!1)},e.src="data:image/webp;base64,"+c[a]}function ewww_load_images(a){jQuery(document).arrive(".ewww_webp",function(){ewww_load_images(a)}),function(b){function d(a,d){for(var e=["align","alt","border","crossorigin","height","hspace","ismap","longdesc","usemap","vspace","width","accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","lang","spellcheck","style","tabindex","title","translate","sizes","data-attachment-id","data-permalink","data-orig-size","data-comments-opened","data-image-meta","data-image-title","data-image-description"],f=0,g=e.length;f<g;f++){var h=b(a).attr(c+e[f]);void 0!==h&&!1!==h&&b(d).attr(e[f],h)}return d}var c="data-";a&&(b(".batch-image img, .image-wrapper a, .ngg-pro-masonry-item a").each(function(){var a=b(this).attr("data-webp");void 0!==a&&!1!==a&&b(this).attr("data-src",a);var a=b(this).attr("data-webp-thumbnail");void 0!==a&&!1!==a&&b(this).attr("data-thumbnail",a)}),b(".image-wrapper a, .ngg-pro-masonry-item a").each(function(){var a=b(this).attr("data-webp");void 0!==a&&!1!==a&&b(this).attr("href",a)}),b(".rev_slider ul li").each(function(){var a=b(this).attr("data-webp-thumb");void 0!==a&&!1!==a&&b(this).attr("data-thumb",a);for(var c=1;c<11;){var a=b(this).attr("data-webp-param"+c);void 0!==a&&!1!==a&&b(this).attr("data-param"+c,a),c++}}),b(".rev_slider img").each(function(){var a=b(this).attr("data-webp-lazyload");void 0!==a&&!1!==a&&b(this).attr("data-lazyload",a)})),b("img.ewww_webp_lazy_retina").each(function(){if(a){var c=b(this).attr("data-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("data-srcset",c)}else{var c=b(this).attr("data-srcset-img");void 0!==c&&!1!==c&&b(this).attr("data-srcset",c)}b(this).removeClass("ewww_webp_lazy_retina")}),b("video").each(function(){if(a){var c=b(this).attr("data-poster-webp");void 0!==c&&!1!==c&&b(this).attr("poster",c)}else{var c=b(this).attr("data-poster-image");void 0!==c&&!1!==c&&b(this).attr("poster",c)}}),b("img.ewww_webp_lazy_load").each(function(){if(a){b(this).attr("data-lazy-src",b(this).attr("data-lazy-webp-src"));var c=b(this).attr("data-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("srcset",c);var c=b(this).attr("data-lazy-srcset-webp");void 0!==c&&!1!==c&&b(this).attr("data-lazy-srcset",c)}else{b(this).attr("data-lazy-src",b(this).attr("data-lazy-img-src"));var c=b(this).attr("data-srcset");void 0!==c&&!1!==c&&b(this).attr("srcset",c);var c=b(this).attr("data-lazy-srcset-img");void 0!==c&&!1!==c&&b(ewww_img).attr("data-lazy-srcset",c)}b(this).removeClass("ewww_webp_lazy_load")}),b(".ewww_webp_lazy_hueman").each(function(){var c=document.createElement("img");if(b(c).attr("src",b(this).attr("data-src")),a){b(c).attr("data-src",b(this).attr("data-webp-src"));var e=b(this).attr("data-srcset-webp");void 0!==e&&!1!==e&&b(c).attr("data-srcset",e)}else{b(c).attr("data-src",b(this).attr("data-img"));var e=b(this).attr("data-srcset-img");void 0!==e&&!1!==e&&b(c).attr("data-srcset",e)}c=d(this,c),b(this).after(c),b(this).removeClass("ewww_webp_lazy_hueman")}),b(".ewww_webp").each(function(){var c=document.createElement("img");if(a){b(c).attr("src",b(this).attr("data-webp"));var e=b(this).attr("data-srcset-webp");void 0!==e&&!1!==e&&b(c).attr("srcset",e);var e=b(this).attr("data-webp-orig-file");void 0!==e&&!1!==e&&b(c).attr("data-orig-file",e);var e=b(this).attr("data-webp-medium-file");void 0!==e&&!1!==e&&b(c).attr("data-medium-file",e);var e=b(this).attr("data-webp-large-file");void 0!==e&&!1!==e&&b(c).attr("data-large-file",e)}else{b(c).attr("src",b(this).attr("data-img"));var e=b(this).attr("data-srcset-img");void 0!==e&&!1!==e&&b(c).attr("srcset",e)}c=d(this,c),b(this).after(c),b(this).removeClass("ewww_webp")})}(jQuery),jQuery.fn.isotope&&jQuery.fn.imagesLoaded&&(jQuery(".fusion-posts-container-infinite").imagesLoaded(function(){jQuery(".fusion-posts-container-infinite").hasClass("isotope")&&jQuery(".fusion-posts-container-infinite").isotope()}),jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").imagesLoaded(function(){jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").isotope()}))}var Arrive=function(a,b,c){"use strict";function l(a,b,c){e.addMethod(b,c,a.unbindEvent),e.addMethod(b,c,a.unbindEventWithSelectorOrCallback),e.addMethod(b,c,a.unbindEventWithSelectorAndCallback)}function m(a){a.arrive=j.bindEvent,l(j,a,"unbindArrive"),a.leave=k.bindEvent,l(k,a,"unbindLeave")}if(a.MutationObserver&&"undefined"!=typeof HTMLElement){var d=0,e=function(){var b=HTMLElement.prototype.matches||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector;return{matchesSelector:function(a,c){return a instanceof HTMLElement&&b.call(a,c)},addMethod:function(a,b,c){var d=a[b];a[b]=function(){return c.length==arguments.length?c.apply(this,arguments):"function"==typeof d?d.apply(this,arguments):void 0}},callCallbacks:function(a){for(var c,b=0;c=a[b];b++)c.callback.call(c.elem)},checkChildNodesRecursively:function(a,b,c,d){for(var g,f=0;g=a[f];f++)c(g,b,d)&&d.push({callback:b.callback,elem:g}),g.childNodes.length>0&&e.checkChildNodesRecursively(g.childNodes,b,c,d)},mergeArrays:function(a,b){var d,c={};for(d in a)c[d]=a[d];for(d in b)c[d]=b[d];return c},toElementsArray:function(b){return void 0===b||"number"==typeof b.length&&b!==a||(b=[b]),b}}}(),f=function(){var a=function(){this._eventsBucket=[],this._beforeAdding=null,this._beforeRemoving=null};return a.prototype.addEvent=function(a,b,c,d){var e={target:a,selector:b,options:c,callback:d,firedElems:[]};return this._beforeAdding&&this._beforeAdding(e),this._eventsBucket.push(e),e},a.prototype.removeEvent=function(a){for(var c,b=this._eventsBucket.length-1;c=this._eventsBucket[b];b--)a(c)&&(this._beforeRemoving&&this._beforeRemoving(c),this._eventsBucket.splice(b,1))},a.prototype.beforeAdding=function(a){this._beforeAdding=a},a.prototype.beforeRemoving=function(a){this._beforeRemoving=a},a}(),g=function(b,d){var g=new f,h=this,i={fireOnAttributesModification:!1};return g.beforeAdding(function(c){var i,e=c.target;c.selector,c.callback;e!==a.document&&e!==a||(e=document.getElementsByTagName("html")[0]),i=new MutationObserver(function(a){d.call(this,a,c)});var j=b(c.options);i.observe(e,j),c.observer=i,c.me=h}),g.beforeRemoving(function(a){a.observer.disconnect()}),this.bindEvent=function(a,b,c){b=e.mergeArrays(i,b);for(var d=e.toElementsArray(this),f=0;f<d.length;f++)g.addEvent(d[f],a,b,c)},this.unbindEvent=function(){var a=e.toElementsArray(this);g.removeEvent(function(b){for(var d=0;d<a.length;d++)if(this===c||b.target===a[d])return!0;return!1})},this.unbindEventWithSelectorOrCallback=function(a){var f,b=e.toElementsArray(this),d=a;f="function"==typeof a?function(a){for(var e=0;e<b.length;e++)if((this===c||a.target===b[e])&&a.callback===d)return!0;return!1}:function(d){for(var e=0;e<b.length;e++)if((this===c||d.target===b[e])&&d.selector===a)return!0;return!1},g.removeEvent(f)},this.unbindEventWithSelectorAndCallback=function(a,b){var d=e.toElementsArray(this);g.removeEvent(function(e){for(var f=0;f<d.length;f++)if((this===c||e.target===d[f])&&e.selector===a&&e.callback===b)return!0;return!1})},this},h=function(){function h(a){var b={attributes:!1,childList:!0,subtree:!0};return a.fireOnAttributesModification&&(b.attributes=!0),b}function i(a,b){a.forEach(function(a){var c=a.addedNodes,d=a.target,f=[];null!==c&&c.length>0?e.checkChildNodesRecursively(c,b,k,f):"attributes"===a.type&&k(d,b,f)&&f.push({callback:b.callback,elem:node}),e.callCallbacks(f)})}function k(a,b,f){if(e.matchesSelector(a,b.selector)&&(a._id===c&&(a._id=d++),-1==b.firedElems.indexOf(a._id))){if(b.options.onceOnly){if(0!==b.firedElems.length)return;b.me.unbindEventWithSelectorAndCallback.call(b.target,b.selector,b.callback)}b.firedElems.push(a._id),f.push({callback:b.callback,elem:a})}}var f={fireOnAttributesModification:!1,onceOnly:!1,existing:!1};j=new g(h,i);var l=j.bindEvent;return j.bindEvent=function(a,b,c){void 0===c?(c=b,b=f):b=e.mergeArrays(f,b);var d=e.toElementsArray(this);if(b.existing){for(var g=[],h=0;h<d.length;h++)for(var i=d[h].querySelectorAll(a),j=0;j<i.length;j++)g.push({callback:c,elem:i[j]});if(b.onceOnly&&g.length)return c.call(g[0].elem);setTimeout(e.callCallbacks,1,g)}l.call(this,a,b,c)},j},i=function(){function d(a){return{childList:!0,subtree:!0}}function f(a,b){a.forEach(function(a){var c=a.removedNodes,f=(a.target,[]);null!==c&&c.length>0&&e.checkChildNodesRecursively(c,b,h,f),e.callCallbacks(f)})}function h(a,b){return e.matchesSelector(a,b.selector)}var c={};k=new g(d,f);var i=k.bindEvent;return k.bindEvent=function(a,b,d){void 0===d?(d=b,b=c):b=e.mergeArrays(c,b),i.call(this,a,b,d)},k},j=new h,k=new i;b&&m(b.fn),m(HTMLElement.prototype),m(NodeList.prototype),m(HTMLCollection.prototype),m(HTMLDocument.prototype),m(Window.prototype);var n={};return l(j,n,"unbindAllArrive"),l(k,n,"unbindAllLeave"),n}}(window,"undefined"==typeof jQuery?null:jQuery,void 0);"undefined"!=typeof jQuery&&check_webp_feature("alpha",ewww_load_images);
includes/media.js ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).on( 'click', '.ewww-manual-optimize', function() {
2
+ var post_id = jQuery(this).data('id');
3
+ var ewww_nonce = jQuery(this).data('nonce');
4
+ var ewww_manual_optimize_data = {
5
+ action: 'ewww_manual_optimize',
6
+ ewww_manual_nonce: ewww_nonce,
7
+ ewww_force: 1,
8
+ ewww_attachment_ID: post_id,
9
+ };
10
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.optimizing );
11
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
12
+ var ewww_manual_response = jQuery.parseJSON(response);
13
+ if (ewww_manual_response.error) {
14
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
15
+ } else if (ewww_manual_response.success) {
16
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
17
+ }
18
+ });
19
+ return false;
20
+ });
21
+ jQuery(document).on( 'click', '.ewww-manual-convert', function() {
22
+ var post_id = jQuery(this).data('id');
23
+ var ewww_nonce = jQuery(this).data('nonce');
24
+ var ewww_manual_optimize_data = {
25
+ action: 'ewww_manual_optimize',
26
+ ewww_manual_nonce: ewww_nonce,
27
+ ewww_force: 1,
28
+ ewww_convert: 1,
29
+ ewww_attachment_ID: post_id,
30
+ };
31
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.optimizing );
32
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
33
+ var ewww_manual_response = jQuery.parseJSON(response);
34
+ if (ewww_manual_response.error) {
35
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
36
+ } else if (ewww_manual_response.success) {
37
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
38
+ }
39
+ if (ewww_manual_response.basename) {
40
+ var attachment_span = jQuery('#post-' + post_id + ' .column-title .filename .screen-reader-text').html();
41
+ jQuery('#post-' + post_id + ' .column-title .filename').html('<span class="screen-reader-text">' + attachment_span + '</span>' + ewww_manual_response.basename);
42
+ }
43
+ });
44
+ return false;
45
+ });
46
+ jQuery(document).on( 'click', '.ewww-manual-restore', function() {
47
+ var post_id = jQuery(this).data('id');
48
+ var ewww_nonce = jQuery(this).data('nonce');
49
+ var ewww_manual_optimize_data = {
50
+ action: 'ewww_manual_restore',
51
+ ewww_manual_nonce: ewww_nonce,
52
+ ewww_attachment_ID: post_id,
53
+ };
54
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.restoring );
55
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
56
+ var ewww_manual_response = jQuery.parseJSON(response);
57
+ if (ewww_manual_response.error) {
58
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
59
+ } else if (ewww_manual_response.success) {
60
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
61
+ }
62
+ if (ewww_manual_response.basename) {
63
+ var attachment_span = jQuery('#post-' + post_id + ' .column-title .filename .screen-reader-text').html();
64
+ jQuery('#post-' + post_id + ' .column-title .filename').html('<span class="screen-reader-text">' + attachment_span + '</span>' + ewww_manual_response.basename);
65
+ }
66
+ });
67
+ return false;
68
+ });
69
+ jQuery(document).on( 'click', '.ewww-manual-cloud-restore', function() {
70
+ var post_id = jQuery(this).data('id');
71
+ var ewww_nonce = jQuery(this).data('nonce');
72
+ var ewww_manual_optimize_data = {
73
+ action: 'ewww_manual_cloud_restore',
74
+ ewww_manual_nonce: ewww_nonce,
75
+ ewww_attachment_ID: post_id,
76
+ };
77
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.restoring );
78
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
79
+ var ewww_manual_response = jQuery.parseJSON(response);
80
+ if (ewww_manual_response.error) {
81
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
82
+ } else if (ewww_manual_response.success) {
83
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
84
+ }
85
+ /* if (ewww_manual_response.basename) {
86
+ var attachment_span = jQuery('#post-' + post_id + ' .column-title .filename .screen-reader-text').html();
87
+ jQuery('#post-' + post_id + ' .column-title .filename').html('<span class="screen-reader-text">' + attachment_span + '</span>' + ewww_manual_response.basename);
88
+ }*/
89
+ });
90
+ return false;
91
+ });
includes/nextcellent.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).on( 'click', '.ewww-manual-optimize', function() {
2
+ var post_id = jQuery(this).data('id');
3
+ var ewww_nonce = jQuery(this).data('nonce');
4
+ var ewww_manual_optimize_data = {
5
+ action: 'ewww_ngg_manual',
6
+ ewww_manual_nonce: ewww_nonce,
7
+ ewww_force: 1,
8
+ ewww_attachment_ID: post_id,
9
+ };
10
+ jQuery('#ewww-nextcellent-status-' + post_id ).html( ewww_vars.optimizing );
11
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
12
+ var ewww_manual_response = jQuery.parseJSON(response);
13
+ if (ewww_manual_response.error) {
14
+ jQuery('#ewww-nextcellent-status-' + post_id ).html( ewww_manual_response.error );
15
+ } else if (ewww_manual_response.success) {
16
+ jQuery('#ewww-nextcellent-status-' + post_id ).html( ewww_manual_response.success );
17
+ }
18
+ });
19
+ return false;
20
+ });
21
+ jQuery(document).on( 'click', '.ewww-manual-cloud-restore', function() {
22
+ var post_id = jQuery(this).data('id');
23
+ var ewww_nonce = jQuery(this).data('nonce');
24
+ var ewww_manual_optimize_data = {
25
+ action: 'ewww_ngg_cloud_restore',
26
+ ewww_manual_nonce: ewww_nonce,
27
+ ewww_attachment_ID: post_id,
28
+ };
29
+ jQuery('#ewww-nextcellent-status-' + post_id ).html( ewww_vars.restoring );
30
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
31
+ var ewww_manual_response = jQuery.parseJSON(response);
32
+ if (ewww_manual_response.error) {
33
+ jQuery('#ewww-nextcellent-status-' + post_id ).html( ewww_manual_response.error );
34
+ } else if (ewww_manual_response.success) {
35
+ jQuery('#ewww-nextcellent-status-' + post_id ).html( ewww_manual_response.success );
36
+ }
37
+ });
38
+ return false;
39
+ });
includes/nextgen.js ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).on( 'click', '.ewww-manual-optimize', function() {
2
+ var post_id = jQuery(this).data('id');
3
+ var ewww_nonce = jQuery(this).data('nonce');
4
+ var ewww_manual_optimize_data = {
5
+ action: 'ewww_ngg_manual',
6
+ ewww_manual_nonce: ewww_nonce,
7
+ ewww_force: 1,
8
+ ewww_attachment_ID: post_id,
9
+ };
10
+ jQuery('#ewww-nextgen-status-' + post_id ).html( ewww_vars.optimizing );
11
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
12
+ var ewww_manual_response = jQuery.parseJSON(response);
13
+ if (ewww_manual_response.error) {
14
+ jQuery('#ewww-nextgen-status-' + post_id ).html( ewww_manual_response.error );
15
+ } else if (ewww_manual_response.success) {
16
+ jQuery('#ewww-nextgen-status-' + post_id ).html( ewww_manual_response.success );
17
+ }
18
+ });
19
+ return false;
20
+ });
21
+ /*jQuery(document).on( 'click', '.ewww-manual-convert', function() {
22
+ var post_id = jQuery(this).data('id');
23
+ var ewww_nonce = jQuery(this).data('nonce');
24
+ var ewww_manual_optimize_data = {
25
+ action: 'ewww_manual_optimize',
26
+ ewww_manual_nonce: ewww_nonce,
27
+ ewww_force: 1,
28
+ ewww_convert: 1,
29
+ ewww_attachment_ID: post_id,
30
+ };
31
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.optimizing );
32
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
33
+ var ewww_manual_response = jQuery.parseJSON(response);
34
+ if (ewww_manual_response.error) {
35
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
36
+ } else if (ewww_manual_response.success) {
37
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
38
+ }
39
+ if (ewww_manual_response.basename) {
40
+ var attachment_span = jQuery('#post-' + post_id + ' .column-title .filename .screen-reader-text').html();
41
+ jQuery('#post-' + post_id + ' .column-title .filename').html('<span class="screen-reader-text">' + attachment_span + '</span>' + ewww_manual_response.basename);
42
+ }
43
+ });
44
+ return false;
45
+ });
46
+ jQuery(document).on( 'click', '.ewww-manual-restore', function() {
47
+ var post_id = jQuery(this).data('id');
48
+ var ewww_nonce = jQuery(this).data('nonce');
49
+ var ewww_manual_optimize_data = {
50
+ action: 'ewww_manual_restore',
51
+ ewww_manual_nonce: ewww_nonce,
52
+ ewww_attachment_ID: post_id,
53
+ };
54
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_vars.restoring );
55
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
56
+ var ewww_manual_response = jQuery.parseJSON(response);
57
+ if (ewww_manual_response.error) {
58
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.error );
59
+ } else if (ewww_manual_response.success) {
60
+ jQuery('#ewww-media-status-' + post_id ).html( ewww_manual_response.success );
61
+ }
62
+ if (ewww_manual_response.basename) {
63
+ var attachment_span = jQuery('#post-' + post_id + ' .column-title .filename .screen-reader-text').html();
64
+ jQuery('#post-' + post_id + ' .column-title .filename').html('<span class="screen-reader-text">' + attachment_span + '</span>' + ewww_manual_response.basename);
65
+ }
66
+ });
67
+ return false;
68
+ });*/
69
+ jQuery(document).on( 'click', '.ewww-manual-cloud-restore', function() {
70
+ var post_id = jQuery(this).data('id');
71
+ var ewww_nonce = jQuery(this).data('nonce');
72
+ var ewww_manual_optimize_data = {
73
+ action: 'ewww_ngg_cloud_restore',
74
+ ewww_manual_nonce: ewww_nonce,
75
+ ewww_attachment_ID: post_id,
76
+ };
77
+ jQuery('#ewww-nextgen-status-' + post_id ).html( ewww_vars.restoring );
78
+ jQuery.post(ajaxurl, ewww_manual_optimize_data, function(response) {
79
+ var ewww_manual_response = jQuery.parseJSON(response);
80
+ if (ewww_manual_response.error) {
81
+ jQuery('#ewww-nextgen-status-' + post_id ).html( ewww_manual_response.error );
82
+ } else if (ewww_manual_response.success) {
83
+ jQuery('#ewww-nextgen-status-' + post_id ).html( ewww_manual_response.success );
84
+ }
85
+ });
86
+ return false;
87
+ });
includes/resize_detection.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // @name Show scaled images
2
+ // @namespace http://nedbatchelder.com/
3
+
4
+ window.onload = function() {
5
+ checkImageSizes();
6
+ document.getElementById('wp-admin-bar-resize-detection').onclick = function() {
7
+ checkImageSizes();
8
+ };
9
+ }
10
+ function checkImageSizes() {
11
+ // Find images which have width or height different than their natural
12
+ // width or height, and give them a stark and ugly marker, as well
13
+ // as a useful title.
14
+ var imgs = document.getElementsByTagName("img");
15
+ for (i = 0; i < imgs.length; i++) {
16
+ var img = imgs[i];
17
+ if (img.naturalWidth) {
18
+ if ((img.naturalWidth != 1) && (img.naturalHeight != 1)) {
19
+ // For each image with a natural width which isn't
20
+ // a 1x1 image, check its size.
21
+ var wrongWidth = (img.width != img.naturalWidth);
22
+ var wrongHeight = (img.height != img.naturalHeight);
23
+ if (wrongWidth || wrongHeight) {
24
+ img.style.border = "3px red dotted";
25
+ img.style.margin = "-3px";
26
+ img.style.background = "yellow";
27
+ img.title = "Forced to wrong size: " +
28
+ img.width + "x" + img.height + ", natural is " +
29
+ img.naturalWidth + "x" + img.naturalHeight + "!";
30
+ }
31
+ }
32
+ }
33
+ }
34
+ return false;
35
+ }
includes/webp.js ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ var ewww_error_counter = 30;
3
+ var sleep_action = 'ewww_sleep';
4
+ var init_action = 'webp_init';
5
+ var loop_action = 'webp_loop';
6
+ var cleanup_action = 'webp_cleanup';
7
+ var init_data = {
8
+ action: init_action,
9
+ ewww_wpnonce: ewww_vars.ewww_wpnonce,
10
+ };
11
+ $('#webp-start').submit(function() {
12
+ startMigrate();
13
+ return false;
14
+ });
15
+ function startMigrate () {
16
+ $('.webp-form').hide();
17
+ $.post(ajaxurl, init_data, function(response) {
18
+ $('#webp-loading').html(response);
19
+ processLoop();
20
+ });
21
+ }
22
+ function processLoop () {
23
+ var loop_data = {
24
+ action: loop_action,
25
+ ewww_wpnonce: ewww_vars.ewww_wpnonce,
26
+ };
27
+ var jqxhr = $.post(ajaxurl, loop_data, function(response) {
28
+ if (response) {
29
+ $('#webp-status').append( response );
30
+ $('#webp-loading').hide();
31
+ processLoop();
32
+ } else {
33
+ var cleanup_data = {
34
+ action: cleanup_action,
35
+ ewww_wpnonce: ewww_vars.ewww_wpnonce,
36
+ };
37
+ $.post(ajaxurl, cleanup_data, function(response) {
38
+ $('#webp-loading').hide();
39
+ $('#webp-status').append(response);
40
+ });
41
+ }
42
+ })
43
+ .fail(function() {
44
+ if (ewww_error_counter == 0) {
45
+ $('#webp-loading').html('<p style="color: red"><b>Operation Interrupted</b></p>');
46
+ } else {
47
+ $('#webp-loading').html('<p style="color: red"><b>Temporary failure, retrying for ' + ewww_error_counter + ' more seconds.</b></p>');
48
+ ewww_error_counter--;
49
+ setTimeout(function() {
50
+ processLoop();
51
+ }, 1000);
52
+ }
53
+ });
54
+ }
55
+ });
languages/ewww-image-optimizer-he_IL.mo ADDED
Binary file
languages/ewww-image-optimizer-he_IL.po ADDED
@@ -0,0 +1,1281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Translation of EWWW Image Optimizer in Hebrew
2
+ # This file is distributed under the same license as the EWWW Image Optimizer package.
3
+ msgid ""
4
+ msgstr ""
5
+ "PO-Revision-Date: 2015-05-10 23:45:33+0000\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
10
+ "X-Generator: GlotPress/1.0-alpha-1000\n"
11
+ "Project-Id-Version: EWWW Image Optimizer\n"
12
+
13
+ #: aux-optimize.php:97 aux-optimize.php:134 aux-optimize.php:383
14
+ #: aux-optimize.php:422 bulk.php:405 bulk.php:429 bulk.php:496 common.php:1922
15
+ #: flag-integration.php:285 flag-integration.php:306 flag-integration.php:362
16
+ #: mwebp.php:102 mwebp.php:193 nextcellent-integration.php:419
17
+ #: nextcellent-integration.php:439 nextcellent-integration.php:487
18
+ #: nextgen-integration.php:365 nextgen-integration.php:382
19
+ #: nextgen-integration.php:427 nextgen2-integration.php:422
20
+ #: nextgen2-integration.php:445 nextgen2-integration.php:498
21
+ msgid "Access token has expired, please reload the page."
22
+ msgstr ""
23
+
24
+ #: aux-optimize.php:684 aux-optimize.php:707 aux-optimize.php:723 bulk.php:388
25
+ #: common.php:3055 flag-integration.php:271 mwebp.php:84
26
+ #: nextcellent-integration.php:94 nextcellent-integration.php:405
27
+ #: nextgen-integration.php:71 nextgen-integration.php:352
28
+ #: nextgen2-integration.php:408
29
+ msgid "Access denied."
30
+ msgstr ""
31
+
32
+ #: ewww-image-optimizer.php:152
33
+ msgid "The regular version of the EWWW Image Optimizer plugin is not permitted on WP Engine sites. However, the cloud version has been approved by WP Engine. Please deactivate EWWW Image Optimizer and install EWWW Image Optimizer Cloud to optimize your images."
34
+ msgstr ""
35
+
36
+ #: common.php:3575
37
+ msgctxt "abbreviation for Virtual Private Server"
38
+ msgid "VPS:"
39
+ msgstr ""
40
+
41
+ #: common.php:3581
42
+ msgctxt "abbreviation for Content Delivery Network"
43
+ msgid "CDN:"
44
+ msgstr ""
45
+
46
+ #: common.php:3563
47
+ msgid "Help translate EWWW I.O."
48
+ msgstr ""
49
+
50
+ #: common.php:3564
51
+ msgid "Write a review."
52
+ msgstr ""
53
+
54
+ #: common.php:3569
55
+ msgctxt "A2 Hosting:"
56
+ msgid "with automatic EWWW IO setup"
57
+ msgstr ""
58
+
59
+ #: common.php:3561
60
+ msgid "Support EWWW I.O."
61
+ msgstr ""
62
+
63
+ #: common.php:3562
64
+ msgid "Would you like to help support development of this plugin?"
65
+ msgstr ""
66
+
67
+ #: common.php:3565
68
+ msgid "Contribute directly via %s."
69
+ msgstr ""
70
+
71
+ #: common.php:3567
72
+ msgid "Use any of these referral links to show your appreciation:"
73
+ msgstr ""
74
+
75
+ #: common.php:3568
76
+ msgid "Web Hosting:"
77
+ msgstr ""
78
+
79
+ #: common.php:3581
80
+ msgid "Add MaxCDN to increase website speeds dramatically! Sign Up Now and Save 25%."
81
+ msgstr ""
82
+
83
+ #: common.php:3581
84
+ msgid "Integrate MaxCDN within Wordpress using the W3 Total Cache plugin."
85
+ msgstr ""
86
+
87
+ #: common.php:3603
88
+ msgid "Debugging Information"
89
+ msgstr ""
90
+
91
+ #: common.php:3603
92
+ msgid "Select All"
93
+ msgstr ""
94
+
95
+ #: ewww-image-optimizer.php:2109 ewww-image-optimizer.php:2128
96
+ msgid "extraction of files failed"
97
+ msgstr ""
98
+
99
+ #: common.php:2717
100
+ msgid "Azure Storage image"
101
+ msgstr ""
102
+
103
+ #: common.php:2721
104
+ msgid "Amazon S3 image"
105
+ msgstr ""
106
+
107
+ #: common.php:3462
108
+ msgid "Deferred Optimization"
109
+ msgstr ""
110
+
111
+ #: common.php:3462
112
+ msgid "Optimize images later via wp_cron, after image upload or generation is complete."
113
+ msgstr ""
114
+
115
+ #: ewww-image-optimizer.php:1181
116
+ msgid "Could not find %s"
117
+ msgstr ""
118
+
119
+ #: ewww-image-optimizer.php:1189
120
+ msgid "%s is not writable"
121
+ msgstr ""
122
+
123
+ #: bulk.php:356
124
+ msgid "Operation timed out, you may need to increase the max_execution_time for PHP"
125
+ msgstr "תם הזמן שהוקצב לפעולה, ייתכן שתהיה צריך להגדיל את max_execution_time עבור PHP"
126
+
127
+ #: bulk.php:357 flag-integration.php:186 nextcellent-integration.php:392
128
+ #: nextgen-integration.php:340 nextgen2-integration.php:395
129
+ msgid "License Exceeded"
130
+ msgstr "הרישיון עודכן"
131
+
132
+ #: bulk.php:358 flag-integration.php:187 nextcellent-integration.php:393
133
+ #: nextgen-integration.php:341 nextgen2-integration.php:396
134
+ msgid "Optimization stopped, reload page to resume."
135
+ msgstr "הייעול הופסק, טען מחדש כדי לחדש את הפעולה."
136
+
137
+ #: bulk.php:359 flag-integration.php:188 nextcellent-integration.php:394
138
+ #: nextgen-integration.php:342 nextgen2-integration.php:397
139
+ msgid "Operation Interrupted"
140
+ msgstr "הייעול הופרע"
141
+
142
+ #: bulk.php:360 flag-integration.php:189 nextcellent-integration.php:395
143
+ #: nextgen-integration.php:343 nextgen2-integration.php:398
144
+ msgid "Temporary failure, seconds left to retry:"
145
+ msgstr "כישלון זמני, שניות לסיום כדי לנסות שוב:"
146
+
147
+ #: bulk.php:361 flag-integration.php:190 nextcellent-integration.php:396
148
+ #: nextgen-integration.php:344 nextgen2-integration.php:399
149
+ msgid "Could not remove image from table."
150
+ msgstr "לא ניתן למחוק את התמונה מהטבלה."
151
+
152
+ #: common.php:3170
153
+ msgid "Click to toggle"
154
+ msgstr "לחץ כדי לעבור"
155
+
156
+ #: common.php:3467
157
+ msgid "Disable Resizes"
158
+ msgstr "בטל שינוי גודל"
159
+
160
+ #: common.php:3467
161
+ msgid "Wordpress, your theme, and other plugins generate various image sizes. You may disable optimization for certain sizes, or completely prevent those sizes from being created."
162
+ msgstr "Worrdpress, ערכת העיצוב שלך, ותוספים אחרים יוצרים תמונות בגדלים שונים. ניתן לבטל את הייעול לגדלים מסוימים, או למנוע לחלוטין את יצירתם."
163
+
164
+ #: common.php:3471
165
+ msgid "Disable Optimization"
166
+ msgstr "בטל ייעול"
167
+
168
+ #: common.php:3471
169
+ msgid "Disable Creation"
170
+ msgstr "בטל יצירה"
171
+
172
+ #: common.php:1353
173
+ msgid "You don't have permission to optimize images."
174
+ msgstr "אין לך הרשאה כדי לייעל את התמונות."
175
+
176
+ #: common.php:3400
177
+ msgid "Faster lossy optimization"
178
+ msgstr "ייעול מהיר ועם איבוד נתונים גדול"
179
+
180
+ #: common.php:3400
181
+ msgid "Speed up the lossy operations by performing less compression."
182
+ msgstr "Speed up the lossy operations by performing less compression."
183
+
184
+ #: common.php:3410
185
+ msgid "Requires an EWWW Image Optimizer Cloud Subscription."
186
+ msgstr "נדרש רישיון של EWWW Image Optimizer בענן"
187
+
188
+ #: common.php:3413
189
+ msgid "Uses pngquant locally. Use EWWW I.O. Cloud for even better lossy compression."
190
+ msgstr ""
191
+
192
+ #: common.php:3512
193
+ msgid "Uses output buffering and libxml functionality from PHP. Use this if the Apache rewrite rules do not work, or if your images are served from a CDN."
194
+ msgstr ""
195
+
196
+ #: common.php:1653
197
+ msgid "optimized %1$d images, usage will reset in %2$d day."
198
+ msgid_plural "optimized %1$d images, usage will reset in %2$d days."
199
+ msgstr[0] ""
200
+ msgstr[1] ""
201
+
202
+ #: common.php:1655
203
+ msgid "%1$d image credit remaining."
204
+ msgid_plural "%1$d image credits remaining."
205
+ msgstr[0] ""
206
+ msgstr[1] ""
207
+
208
+ #: common.php:3465
209
+ msgid "Include Media Library Folders"
210
+ msgstr ""
211
+
212
+ #: common.php:3465
213
+ msgid "If you have disabled automatic optimization, enable this if you want Scheduled Optimization to include the latest two folders from the Media Library."
214
+ msgstr ""
215
+
216
+ #: common.php:3512
217
+ msgid "Alternative WebP Rewriting"
218
+ msgstr ""
219
+
220
+ #: bulk.php:355
221
+ msgid "%d images"
222
+ msgstr ""
223
+
224
+ #: common.php:3454
225
+ msgid "Provide paths containing images to be optimized using \"Scan and Optimize\" on the Bulk Optimize page or by Scheduled Optimization."
226
+ msgstr ""
227
+
228
+ #: iocli.php:55
229
+ msgctxt "string will be something like \"media\" or \"nextgen\""
230
+ msgid "Optimizing %1$s with a %2$d second pause between images."
231
+ msgstr ""
232
+
233
+ #: iocli.php:52
234
+ msgid "Forcing re-optimization of previously processed images."
235
+ msgstr ""
236
+
237
+ #: iocli.php:66 iocli.php:107 iocli.php:118 iocli.php:142 iocli.php:157
238
+ msgid "Bulk status has been reset, starting from the beginning."
239
+ msgstr ""
240
+
241
+ #: iocli.php:172
242
+ msgid "Bulk status has been reset, the next bulk operation will start from the beginning."
243
+ msgstr ""
244
+
245
+ #: iocli.php:174
246
+ msgid "Please specify a valid library option, see \"wp-cli help ewwwio optimize\" for more information."
247
+ msgstr ""
248
+
249
+ #: common.php:1003 common.php:2927
250
+ msgid "Unoptimized Images"
251
+ msgstr ""
252
+
253
+ #: common.php:2934
254
+ msgid "Optimize All Images"
255
+ msgstr ""
256
+
257
+ #: common.php:2953
258
+ msgid "There are too many images to display."
259
+ msgstr ""
260
+
261
+ #: iocli.php:87 iocli.php:162
262
+ msgid "%1$d images in other folders need optimizing."
263
+ msgstr ""
264
+
265
+ #: iocli.php:136
266
+ msgid "NextGEN/Nextcellent not installed."
267
+ msgstr ""
268
+
269
+ #: iocli.php:151
270
+ msgid "Grand Flagallery not installed."
271
+ msgstr ""
272
+
273
+ #: common.php:3463
274
+ msgid "Disable Automatic Optimization"
275
+ msgstr ""
276
+
277
+ #: common.php:3463
278
+ msgid "Images will not be optimized on upload. Images may be optimized with the Bulk Optimize tools or with Scheduled optimization."
279
+ msgstr ""
280
+
281
+ #: common.php:3489
282
+ msgid "Exclude full-size images from metadata removal"
283
+ msgstr ""
284
+
285
+ #: aux-optimize.php:9
286
+ msgid "Scan and optimize"
287
+ msgstr ""
288
+
289
+ #: aux-optimize.php:11
290
+ msgid "Resume previous optimization"
291
+ msgstr ""
292
+
293
+ #: aux-optimize.php:29
294
+ msgid "Optimize Everything Else"
295
+ msgstr ""
296
+
297
+ #: aux-optimize.php:30
298
+ msgid "Use this tool to optimize images outside of the Media Library and galleries where we have full integration. Examples: theme images, BuddyPress, WP Symposium, and any folders that you have specified on the settings page."
299
+ msgstr ""
300
+
301
+ #: aux-optimize.php:32
302
+ msgid "The database schema has changed, you need to convert to the new format."
303
+ msgstr ""
304
+
305
+ #: aux-optimize.php:36
306
+ msgid "Convert Table"
307
+ msgstr ""
308
+
309
+ #: aux-optimize.php:39
310
+ msgid "There are no images to optimize."
311
+ msgstr ""
312
+
313
+ #: aux-optimize.php:40 iocli.php:68 iocli.php:159
314
+ msgid "Scanning, this could take a while"
315
+ msgstr ""
316
+
317
+ #: aux-optimize.php:42
318
+ msgid "Last optimization was completed on %1$s at %2$s and optimized %3$d images"
319
+ msgstr ""
320
+
321
+ #: aux-optimize.php:46 bulk.php:57
322
+ msgid "Optimize Again"
323
+ msgstr ""
324
+
325
+ #: aux-optimize.php:51 bulk.php:63 flag-integration.php:105
326
+ #: nextcellent-integration.php:296 nextgen-integration.php:258
327
+ #: nextgen2-integration.php:303
328
+ msgid "If you would like to start over again, press the Reset Status button to reset the bulk operation status."
329
+ msgstr ""
330
+
331
+ #: aux-optimize.php:55 bulk.php:67 flag-integration.php:109
332
+ #: nextcellent-integration.php:300 nextgen-integration.php:262
333
+ #: nextgen2-integration.php:307
334
+ msgid "Reset Status"
335
+ msgstr ""
336
+
337
+ #: aux-optimize.php:64
338
+ msgid "The plugin keeps track of already optimized images to prevent re-optimization. There are %d images that have been optimized so far."
339
+ msgstr ""
340
+
341
+ #: aux-optimize.php:66
342
+ msgid "Show Optimized Images"
343
+ msgstr ""
344
+
345
+ #: aux-optimize.php:74
346
+ msgid "page"
347
+ msgstr ""
348
+
349
+ #: aux-optimize.php:74
350
+ msgid "of"
351
+ msgstr ""
352
+
353
+ #: aux-optimize.php:254 aux-optimize.php:296 aux-optimize.php:299
354
+ msgid "Unknown Savings"
355
+ msgstr ""
356
+
357
+ #: aux-optimize.php:319
358
+ msgid "Finished importing"
359
+ msgstr ""
360
+
361
+ #: aux-optimize.php:336 common.php:1830 common.php:2378 common.php:2387
362
+ msgid "Previously Optimized"
363
+ msgstr ""
364
+
365
+ #: aux-optimize.php:339 common.php:1869 common.php:2314
366
+ msgid "No savings"
367
+ msgstr ""
368
+
369
+ #: aux-optimize.php:346 bulk.php:122 common.php:1835
370
+ #: ewww-image-optimizer.php:2029
371
+ msgid "License exceeded"
372
+ msgstr ""
373
+
374
+ #: aux-optimize.php:391
375
+ msgid "Filename"
376
+ msgstr ""
377
+
378
+ #: aux-optimize.php:391
379
+ msgid "Image Type"
380
+ msgstr ""
381
+
382
+ #: aux-optimize.php:391 common.php:1088 common.php:1120 common.php:2691
383
+ #: common.php:2939 flag-integration.php:374 nextcellent-integration.php:173
384
+ #: nextgen-integration.php:142 nextgen2-integration.php:131
385
+ #: nextgen2-integration.php:134
386
+ msgid "Image Optimizer"
387
+ msgstr ""
388
+
389
+ #: aux-optimize.php:408 common.php:2809 common.php:2863
390
+ #: flag-integration.php:431 flag-integration.php:440
391
+ #: nextcellent-integration.php:229 nextcellent-integration.php:238
392
+ #: nextgen-integration.php:198 nextgen-integration.php:205
393
+ #: nextgen2-integration.php:215
394
+ msgid "Image Size: %s"
395
+ msgstr ""
396
+
397
+ #: aux-optimize.php:408
398
+ msgid "Remove from table"
399
+ msgstr ""
400
+
401
+ #: aux-optimize.php:666
402
+ msgid "Nothing to optimize"
403
+ msgstr ""
404
+
405
+ #: aux-optimize.php:695 aux-optimize.php:712 bulk.php:395 bulk.php:414
406
+ #: flag-integration.php:277 flag-integration.php:296 iocli.php:210
407
+ #: nextcellent-integration.php:411 nextcellent-integration.php:429
408
+ #: nextgen-integration.php:358 nextgen-integration.php:375
409
+ #: nextgen2-integration.php:435
410
+ msgid "Optimizing"
411
+ msgstr ""
412
+
413
+ #: aux-optimize.php:732 bulk.php:502 mwebp.php:204
414
+ #: nextcellent-integration.php:129 nextgen-integration.php:106
415
+ msgid "Finished"
416
+ msgstr ""
417
+
418
+ #: bulk.php:11 common.php:1002 common.php:2892 common.php:2894 common.php:3159
419
+ #: common.php:3161 flag-integration.php:36 flag-integration.php:41
420
+ #: flag-integration.php:46 flag-integration.php:73
421
+ #: nextcellent-integration.php:37 nextcellent-integration.php:269
422
+ #: nextgen-integration.php:33 nextgen-integration.php:234
423
+ #: nextgen2-integration.php:37 nextgen2-integration.php:272
424
+ #: nextgen2-integration.php:516
425
+ msgid "Bulk Optimize"
426
+ msgstr ""
427
+
428
+ #: bulk.php:15 flag-integration.php:78 nextcellent-integration.php:274
429
+ #: nextgen-integration.php:239 nextgen2-integration.php:277
430
+ msgid "Start optimizing"
431
+ msgstr ""
432
+
433
+ #: bulk.php:17 flag-integration.php:80 nextcellent-integration.php:276
434
+ #: nextgen-integration.php:241 nextgen2-integration.php:279
435
+ msgid "Resume previous bulk operation"
436
+ msgstr ""
437
+
438
+ #: bulk.php:23
439
+ msgid "Importing"
440
+ msgstr ""
441
+
442
+ #: bulk.php:28 flag-integration.php:87 nextcellent-integration.php:283
443
+ #: nextgen2-integration.php:286
444
+ msgid "Stop Optimizing"
445
+ msgstr ""
446
+
447
+ #: bulk.php:32
448
+ msgid "You should import Media Library images into the table to prevent duplicate optimization."
449
+ msgstr ""
450
+
451
+ #: bulk.php:34
452
+ msgid "Import Images"
453
+ msgstr ""
454
+
455
+ #: bulk.php:40 flag-integration.php:91 nextgen2-integration.php:290
456
+ msgid "Force re-optimize"
457
+ msgstr ""
458
+
459
+ #: bulk.php:41 common.php:3415 flag-integration.php:92
460
+ #: nextgen2-integration.php:291
461
+ msgid "Choose how long to pause between images (in seconds, 0 = disabled)"
462
+ msgstr ""
463
+
464
+ #: bulk.php:44
465
+ msgid "Optimize Media Library"
466
+ msgstr ""
467
+
468
+ #: bulk.php:46 flag-integration.php:68 nextcellent-integration.php:264
469
+ #: nextgen-integration.php:229 nextgen2-integration.php:266
470
+ msgid "You do not appear to have uploaded any images yet."
471
+ msgstr ""
472
+
473
+ #: bulk.php:50
474
+ msgid "%1$d images in the Media Library have been selected, unable to determine how many resizes and how many are unoptimized."
475
+ msgstr ""
476
+
477
+ #: bulk.php:52 iocli.php:70 iocli.php:111
478
+ msgid "%1$d images in the Media Library have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized)."
479
+ msgstr ""
480
+
481
+ #: bulk.php:54 flag-integration.php:97 nextcellent-integration.php:288
482
+ #: nextgen-integration.php:250 nextgen2-integration.php:296
483
+ msgid "Previously optimized images will be skipped by default."
484
+ msgstr ""
485
+
486
+ #: bulk.php:449 common.php:1956 flag-integration.php:328 iocli.php:386
487
+ #: iocli.php:430 iocli.php:510 iocli.php:582 nextcellent-integration.php:466
488
+ #: nextgen-integration.php:403 nextgen2-integration.php:466
489
+ msgid "Optimized image:"
490
+ msgstr ""
491
+
492
+ #: bulk.php:451 iocli.php:218
493
+ msgid "Skipped image, ID:"
494
+ msgstr ""
495
+
496
+ #: bulk.php:455 flag-integration.php:329 iocli.php:222 iocli.php:431
497
+ msgid "Full size – %s"
498
+ msgstr ""
499
+
500
+ #: bulk.php:470 common.php:1962 flag-integration.php:347 iocli.php:237
501
+ #: iocli.php:392 iocli.php:449 iocli.php:527 iocli.php:592
502
+ #: nextcellent-integration.php:123 nextcellent-integration.php:472
503
+ #: nextgen-integration.php:100 nextgen-integration.php:413
504
+ #: nextgen2-integration.php:483
505
+ msgid "Elapsed: %.3f seconds"
506
+ msgstr ""
507
+
508
+ #: bulk.php:502
509
+ msgid "Return to Media Library"
510
+ msgstr ""
511
+
512
+ #: common.php:711
513
+ msgid "Settings saved"
514
+ msgstr ""
515
+
516
+ #: common.php:1004 mwebp.php:5
517
+ msgid "Migrate WebP Images"
518
+ msgstr ""
519
+
520
+ #: common.php:1023
521
+ msgid "Image Store Optimize"
522
+ msgstr ""
523
+
524
+ #: common.php:1023
525
+ msgid "Optimize"
526
+ msgstr ""
527
+
528
+ #: common.php:1077
529
+ msgid "Image Store Optimization"
530
+ msgstr ""
531
+
532
+ #: common.php:1087
533
+ msgid "Choose a gallery or"
534
+ msgstr ""
535
+
536
+ #: common.php:1087
537
+ msgid "optimize all galleries"
538
+ msgstr ""
539
+
540
+ #: common.php:1088
541
+ msgid "Gallery ID"
542
+ msgstr ""
543
+
544
+ #: common.php:1088
545
+ msgid "Gallery Name"
546
+ msgstr ""
547
+
548
+ #: common.php:1088
549
+ msgid "Images"
550
+ msgstr ""
551
+
552
+ #: common.php:1104 common.php:1119
553
+ msgid "Optimize Gallery"
554
+ msgstr ""
555
+
556
+ #: common.php:1120 common.php:2939
557
+ msgid "Title"
558
+ msgstr ""
559
+
560
+ #: common.php:1120 nextgen-integration.php:14 nextgen-integration.php:274
561
+ msgid "Gallery"
562
+ msgstr ""
563
+
564
+ #: common.php:1203
565
+ msgid "Settings"
566
+ msgstr ""
567
+
568
+ #: flag-integration.php:231 nextcellent-integration.php:137
569
+ #: nextgen-integration.php:113 nextgen2-integration.php:96
570
+ msgid "You don't have permission to work with uploaded files."
571
+ msgstr ""
572
+
573
+ #: common.php:1358 flag-integration.php:235 nextcellent-integration.php:141
574
+ #: nextgen-integration.php:117 nextgen2-integration.php:100
575
+ msgid "No attachment ID was provided."
576
+ msgstr ""
577
+
578
+ #: common.php:1401 common.php:1436
579
+ msgid "Original Restored"
580
+ msgstr ""
581
+
582
+ #: common.php:1657
583
+ msgid "used %1$d of %2$d, usage will reset in %3$d day."
584
+ msgid_plural "used %1$d of %2$d, usage will reset in %3$d days."
585
+ msgstr[0] ""
586
+ msgstr[1] ""
587
+
588
+ #: common.php:1881
589
+ msgid "Reduced by %01.1f%% (%s)"
590
+ msgstr ""
591
+
592
+ #: common.php:2713
593
+ msgid "Cloudinary image"
594
+ msgstr ""
595
+
596
+ #: common.php:2737
597
+ msgid "Could not retrieve file path."
598
+ msgstr ""
599
+
600
+ #: common.php:2758 common.php:2769 common.php:2780
601
+ #: ewww-image-optimizer.php:1365 ewww-image-optimizer.php:1636
602
+ #: ewww-image-optimizer.php:1640 ewww-image-optimizer.php:1922
603
+ #: flag-integration.php:405 flag-integration.php:411 flag-integration.php:417
604
+ #: nextcellent-integration.php:201 nextcellent-integration.php:208
605
+ #: nextcellent-integration.php:215 nextgen-integration.php:170
606
+ #: nextgen-integration.php:177 nextgen-integration.php:184
607
+ #: nextgen2-integration.php:174 nextgen2-integration.php:181
608
+ #: nextgen2-integration.php:188
609
+ msgid "%s is missing"
610
+ msgstr ""
611
+
612
+ #: common.php:2760
613
+ msgid "JPG to PNG"
614
+ msgstr ""
615
+
616
+ #: common.php:2762
617
+ msgid "WARNING: Removes metadata. Requires GD or ImageMagick. PNG is generally much better than JPG for logos and other images with a limited range of colors."
618
+ msgstr ""
619
+
620
+ #: common.php:2771
621
+ msgid "PNG to JPG"
622
+ msgstr ""
623
+
624
+ #: common.php:2773
625
+ msgid "WARNING: This is not a lossless conversion and requires GD or ImageMagick. JPG is much better than PNG for photographic use because it compresses the image and discards data. Transparent images will only be converted if a background color has been set."
626
+ msgstr ""
627
+
628
+ #: common.php:2782
629
+ msgid "GIF to PNG"
630
+ msgstr ""
631
+
632
+ #: common.php:2784
633
+ msgid "PNG is generally better than GIF, but does not support animation. Animated images will not be converted."
634
+ msgstr ""
635
+
636
+ #: common.php:2789 flag-integration.php:425 nextcellent-integration.php:223
637
+ #: nextgen-integration.php:192 nextgen2-integration.php:196
638
+ msgid "Unsupported file type"
639
+ msgstr ""
640
+
641
+ #: common.php:2814 flag-integration.php:435 nextcellent-integration.php:233
642
+ #: nextgen-integration.php:201 nextgen2-integration.php:235
643
+ msgid "Re-optimize"
644
+ msgstr ""
645
+
646
+ #: common.php:2841
647
+ msgid "Restore original"
648
+ msgstr ""
649
+
650
+ #: common.php:2861 flag-integration.php:439 nextcellent-integration.php:237
651
+ #: nextgen-integration.php:204 nextgen2-integration.php:209
652
+ msgid "Not processed"
653
+ msgstr ""
654
+
655
+ #: common.php:2800 common.php:2866 flag-integration.php:444
656
+ #: nextcellent-integration.php:242 nextgen-integration.php:208
657
+ #: nextgen2-integration.php:239
658
+ msgid "Optimize now!"
659
+ msgstr ""
660
+
661
+ #: common.php:3059
662
+ msgid "Insertion successful"
663
+ msgstr ""
664
+
665
+ #: common.php:3061
666
+ msgid "Insertion failed"
667
+ msgstr ""
668
+
669
+ #: common.php:3127
670
+ msgid "Pngout was successfully installed, check the Plugin Status area for version information."
671
+ msgstr ""
672
+
673
+ #: common.php:3132
674
+ msgid "Pngout was not installed: %1$s. Make sure this folder is writable: %2$s"
675
+ msgstr ""
676
+
677
+ #: common.php:3154
678
+ msgid "Plugin Home Page"
679
+ msgstr ""
680
+
681
+ #: common.php:3155 ewww-image-optimizer.php:410 ewww-image-optimizer.php:536
682
+ msgid "Installation Instructions"
683
+ msgstr ""
684
+
685
+ #: common.php:3156
686
+ msgid "Plugin Support"
687
+ msgstr ""
688
+
689
+ #: common.php:3157
690
+ msgid "Cloud Status"
691
+ msgstr ""
692
+
693
+ #: common.php:3159
694
+ msgid "Media Library"
695
+ msgstr ""
696
+
697
+ #: common.php:3163
698
+ msgid "New images uploaded to the Media Library will be optimized automatically. If you have existing images you would like to optimize, you can use the %s tool."
699
+ msgstr ""
700
+
701
+ #: common.php:3171
702
+ msgid "Plugin Status"
703
+ msgstr ""
704
+
705
+ #: common.php:3172
706
+ msgid "All Clear"
707
+ msgstr ""
708
+
709
+ #: common.php:3173
710
+ msgid "Requires Attention"
711
+ msgstr ""
712
+
713
+ #: common.php:3176
714
+ msgid "Total Savings:"
715
+ msgstr ""
716
+
717
+ #: common.php:3178 common.php:3391
718
+ msgid "Cloud optimization API Key"
719
+ msgstr ""
720
+
721
+ #: common.php:3181 common.php:3183
722
+ msgid "Verified,"
723
+ msgstr ""
724
+
725
+ #: common.php:3185
726
+ msgid "Not Verified"
727
+ msgstr ""
728
+
729
+ #: common.php:3197
730
+ msgid "If updated versions are available below you may either download the newer versions and install them yourself, or uncheck \"Use System Paths\" and use the bundled tools."
731
+ msgstr ""
732
+
733
+ #: common.php:3198 common.php:3201
734
+ msgid "Updates are optional, but may contain increased optimization or security patches"
735
+ msgstr ""
736
+
737
+ #: common.php:3200
738
+ msgid "If updated versions are available below, you may need to enable write permission on the %s folder to use the automatic installs."
739
+ msgstr ""
740
+
741
+ #: common.php:3211 common.php:3221 common.php:3231 common.php:3241
742
+ #: common.php:3251 common.php:3261 common.php:3285 common.php:3293
743
+ #: common.php:3301 common.php:3309 common.php:3323 common.php:3329
744
+ #: common.php:3337
745
+ msgid "Installed"
746
+ msgstr ""
747
+
748
+ #: common.php:3211 common.php:3221 common.php:3231 common.php:3241
749
+ #: common.php:3251 common.php:3261
750
+ msgid "version"
751
+ msgstr ""
752
+
753
+ #: common.php:3213 common.php:3223 common.php:3233 common.php:3243
754
+ #: common.php:3253 common.php:3263 common.php:3288 common.php:3296
755
+ #: common.php:3304 common.php:3312 common.php:3326 common.php:3334
756
+ #: common.php:3340
757
+ msgid "Missing"
758
+ msgstr ""
759
+
760
+ #: common.php:3243
761
+ msgid "Install"
762
+ msgstr ""
763
+
764
+ #: common.php:3243
765
+ msgid "automatically"
766
+ msgstr ""
767
+
768
+ #: common.php:3243
769
+ msgid "manually"
770
+ msgstr ""
771
+
772
+ #: common.php:3243
773
+ msgid "Pngout is free closed-source software that can produce drastically reduced filesizes for PNGs, but can be very time consuming to process images"
774
+ msgstr ""
775
+
776
+ #: common.php:3269
777
+ msgid "On"
778
+ msgstr ""
779
+
780
+ #: common.php:3272
781
+ msgid "Off"
782
+ msgstr ""
783
+
784
+ #: common.php:3275
785
+ msgid "Disabled"
786
+ msgstr ""
787
+
788
+ #: common.php:3278
789
+ msgid "Enabled"
790
+ msgstr ""
791
+
792
+ #: common.php:3281
793
+ msgid "%s only need one, used for conversion, not optimization"
794
+ msgstr ""
795
+
796
+ #: common.php:3281
797
+ msgid "Graphics libraries"
798
+ msgstr ""
799
+
800
+ #: common.php:3319
801
+ msgid "Only need one of these:"
802
+ msgstr ""
803
+
804
+ #: common.php:3344 common.php:3348 common.php:3351
805
+ msgid "command not found on your system"
806
+ msgstr ""
807
+
808
+ #: common.php:3348
809
+ msgid "not required"
810
+ msgstr ""
811
+
812
+ #: common.php:3351
813
+ msgid "required for automatic pngout installer"
814
+ msgstr ""
815
+
816
+ #: common.php:3375
817
+ msgid "Cloud Settings"
818
+ msgstr ""
819
+
820
+ #: common.php:3376
821
+ msgid "Basic Settings"
822
+ msgstr ""
823
+
824
+ #: common.php:3377
825
+ msgid "Advanced Settings"
826
+ msgstr ""
827
+
828
+ #: common.php:3378
829
+ msgid "Conversion Settings"
830
+ msgstr ""
831
+
832
+ #: common.php:3389
833
+ msgid "If exec() is disabled for security reasons (and enabling it is not an option), or you would like to offload image optimization to a third-party server, you may purchase an API key for our cloud optimization service. The API key should be entered below, and cloud optimization must be enabled for each image format individually."
834
+ msgstr ""
835
+
836
+ #: common.php:3389 common.php:3391 common.php:3410 common.php:3413
837
+ msgid "Purchase an API key."
838
+ msgstr ""
839
+
840
+ #: common.php:3391
841
+ msgid "API Key will be validated when you save your settings."
842
+ msgstr ""
843
+
844
+ #: common.php:3392
845
+ msgid "JPG cloud optimization"
846
+ msgstr ""
847
+
848
+ #: common.php:3394
849
+ msgid "PNG cloud optimization"
850
+ msgstr ""
851
+
852
+ #: common.php:3396
853
+ msgid "extra PNG compression (slower)"
854
+ msgstr ""
855
+
856
+ #: common.php:3398
857
+ msgid "GIF cloud optimization"
858
+ msgstr ""
859
+
860
+ #: common.php:3405
861
+ msgid "Debugging"
862
+ msgstr ""
863
+
864
+ #: common.php:3405
865
+ msgid "Use this to provide information for support purposes, or if you feel comfortable digging around in the code to fix a problem you are experiencing."
866
+ msgstr ""
867
+
868
+ #: common.php:3406
869
+ msgid "Remove metadata"
870
+ msgstr ""
871
+
872
+ #: common.php:3407
873
+ msgid "This will remove ALL metadata: EXIF and comments."
874
+ msgstr ""
875
+
876
+ #: common.php:3409
877
+ msgid "Lossy JPG optimization"
878
+ msgstr ""
879
+
880
+ #: common.php:3409 common.php:3412 common.php:3509 common.php:3515
881
+ #: common.php:3518
882
+ msgid "WARNING:"
883
+ msgstr ""
884
+
885
+ #: common.php:3409 common.php:3412
886
+ msgid "While most users will not notice a difference in image quality, lossy means there IS a loss in image quality."
887
+ msgstr ""
888
+
889
+ #: common.php:3412
890
+ msgid "Lossy PNG optimization"
891
+ msgstr ""
892
+
893
+ #: common.php:3415
894
+ msgid "Bulk Delay"
895
+ msgstr ""
896
+
897
+ #: common.php:3418
898
+ msgid "Automatic Cloudinary upload"
899
+ msgstr ""
900
+
901
+ #: common.php:3418
902
+ msgid "When enabled, uploads to the Media Library will be transferred to Cloudinary after optimization. Cloudinary generates resizes, so only the full-size image is uploaded."
903
+ msgstr ""
904
+
905
+ #: common.php:3428
906
+ msgid "optipng optimization level"
907
+ msgstr ""
908
+
909
+ #: common.php:3430 common.php:3431 common.php:3432 common.php:3433
910
+ #: common.php:3434 common.php:3435 common.php:3436 common.php:3442
911
+ #: common.php:3443 common.php:3444 common.php:3445
912
+ msgid "Level %d"
913
+ msgstr ""
914
+
915
+ #: common.php:3430
916
+ msgid "%d trial"
917
+ msgstr ""
918
+
919
+ #: common.php:3431 common.php:3432 common.php:3433 common.php:3434
920
+ #: common.php:3435 common.php:3436
921
+ msgid "%d trials"
922
+ msgstr ""
923
+
924
+ #: common.php:3437 common.php:3446
925
+ msgid "default"
926
+ msgstr ""
927
+
928
+ #: common.php:3438
929
+ msgid "According to the author of optipng, 10 trials should satisfy most people, 30 trials should satisfy everyone."
930
+ msgstr ""
931
+
932
+ #: common.php:3440
933
+ msgid "pngout optimization level"
934
+ msgstr ""
935
+
936
+ #: common.php:3442
937
+ msgid "Xtreme! (Slowest)"
938
+ msgstr ""
939
+
940
+ #: common.php:3443
941
+ msgid "Intense (Slow)"
942
+ msgstr ""
943
+
944
+ #: common.php:3444
945
+ msgid "Longest Match (Fast)"
946
+ msgstr ""
947
+
948
+ #: common.php:3445
949
+ msgid "Huffman Only (Faster)"
950
+ msgstr ""
951
+
952
+ #: common.php:3447
953
+ msgid "If you have CPU cycles to spare, go with level %d"
954
+ msgstr ""
955
+
956
+ #: common.php:3450
957
+ msgid "Scheduled optimization"
958
+ msgstr ""
959
+
960
+ #: common.php:3450
961
+ msgid "This will enable scheduled optimization of unoptimized images for your theme, buddypress, and any additional folders you have configured below. Runs hourly: wp_cron only runs when your site is visited, so it may be even longer between optimizations."
962
+ msgstr ""
963
+
964
+ #: common.php:3452
965
+ msgid "Folders to optimize"
966
+ msgstr ""
967
+
968
+ #: common.php:3452
969
+ msgid "One path per line, must be within %s. Use full paths, not relative paths."
970
+ msgstr ""
971
+
972
+ #: common.php:3483
973
+ msgid "Skip Small Images"
974
+ msgstr ""
975
+
976
+ #: common.php:3483
977
+ msgid "Do not optimize images smaller than this (in bytes)"
978
+ msgstr ""
979
+
980
+ #: common.php:3485
981
+ msgid "Skip Large PNG Images"
982
+ msgstr ""
983
+
984
+ #: common.php:3485
985
+ msgid "Do not optimize PNG images larger than this (in bytes)"
986
+ msgstr ""
987
+
988
+ #: common.php:3487
989
+ msgid "Exclude full-size images from lossy optimization"
990
+ msgstr ""
991
+
992
+ #: common.php:3491 ewww-image-optimizer.php:410
993
+ msgid "Use System Paths"
994
+ msgstr ""
995
+
996
+ #: common.php:3491
997
+ msgid "If you have already installed the utilities in a system location, such as %s or %s, use this to force the plugin to use those versions and skip the auto-installers."
998
+ msgstr ""
999
+
1000
+ #: common.php:3493 common.php:3495 common.php:3497 common.php:3499
1001
+ msgid "disable"
1002
+ msgstr ""
1003
+
1004
+ #: common.php:3503
1005
+ msgid "Conversion is only available for images in the Media Library (except WebP). By default, all images have a link available in the Media Library for one-time conversion. Turning on individual conversion operations below will enable conversion filters any time an image is uploaded or modified."
1006
+ msgstr ""
1007
+
1008
+ #: common.php:3504
1009
+ msgid "NOTE:"
1010
+ msgstr ""
1011
+
1012
+ #: common.php:3504
1013
+ msgid "The plugin will attempt to update image locations for any posts that contain the images. You may still need to manually update locations/urls for converted images."
1014
+ msgstr ""
1015
+
1016
+ #: common.php:3507
1017
+ msgid "Hide Conversion Links"
1018
+ msgstr ""
1019
+
1020
+ #: common.php:3507
1021
+ msgid "Site or Network admins can use this to prevent other users from using the conversion links in the Media Library which bypass the settings below."
1022
+ msgstr ""
1023
+
1024
+ #: common.php:3508
1025
+ msgid "Delete originals"
1026
+ msgstr ""
1027
+
1028
+ #: common.php:3508
1029
+ msgid "This will remove the original image from the server after a successful conversion."
1030
+ msgstr ""
1031
+
1032
+ #: common.php:3509
1033
+ msgid "JPG/PNG to WebP"
1034
+ msgstr ""
1035
+
1036
+ #: common.php:3509
1037
+ msgid "JPG to WebP conversion is lossy, but quality loss is minimal. PNG to WebP conversion is lossless."
1038
+ msgstr ""
1039
+
1040
+ #: common.php:3510
1041
+ msgid "Originals are never deleted, and WebP images should only be served to supported browsers."
1042
+ msgstr ""
1043
+
1044
+ #: common.php:3510
1045
+ msgid "You can use the rewrite rules below to serve WebP images with Apache."
1046
+ msgstr ""
1047
+
1048
+ #: common.php:3515 common.php:3518 common.php:3525
1049
+ msgid "enable %s to %s conversion"
1050
+ msgstr ""
1051
+
1052
+ #: common.php:3515
1053
+ msgid "Removes metadata and increases cpu usage dramatically."
1054
+ msgstr ""
1055
+
1056
+ #: common.php:3516
1057
+ msgid "PNG is generally much better than JPG for logos and other images with a limited range of colors. Checking this option will slow down JPG processing significantly, and you may want to enable it only temporarily."
1058
+ msgstr ""
1059
+
1060
+ #: common.php:3518
1061
+ msgid "This is not a lossless conversion."
1062
+ msgstr ""
1063
+
1064
+ #: common.php:3519
1065
+ msgid "JPG is generally much better than PNG for photographic use because it compresses the image and discards data. PNGs with transparency are not converted by default."
1066
+ msgstr ""
1067
+
1068
+ #: common.php:3520
1069
+ msgid "JPG background color:"
1070
+ msgstr ""
1071
+
1072
+ #: common.php:3520
1073
+ msgid "HEX format (#123def)"
1074
+ msgstr ""
1075
+
1076
+ #: common.php:3521
1077
+ msgid "Background color is used only if the PNG has transparency. Leave this value blank to skip PNGs with transparency."
1078
+ msgstr ""
1079
+
1080
+ #: common.php:3522
1081
+ msgid "JPG quality level:"
1082
+ msgstr ""
1083
+
1084
+ #: common.php:3522
1085
+ msgid "Valid values are 1-100."
1086
+ msgstr ""
1087
+
1088
+ #: common.php:3523
1089
+ msgid "If JPG quality is blank, the plugin will attempt to set the optimal quality level or default to 92. Remember, this is a lossy conversion, so you are losing pixels, and it is not recommended to actually set the level here unless you want noticable loss of image quality."
1090
+ msgstr ""
1091
+
1092
+ #: common.php:3525
1093
+ msgid "No warnings here, just do it."
1094
+ msgstr ""
1095
+
1096
+ #: common.php:3526
1097
+ msgid "PNG is generally better than GIF, but animated images cannot be converted."
1098
+ msgstr ""
1099
+
1100
+ #: common.php:3529
1101
+ msgid "Save Changes"
1102
+ msgstr ""
1103
+
1104
+ #: common.php:3533
1105
+ msgid "There are many ways to serve WebP images to visitors with supported browsers. You may choose any you wish, but it is recommended to serve them with an .htaccess file using mod_rewrite and mod_headers. The plugin can insert the rules for you if the file is writable, or you can edit .htaccess yourself."
1106
+ msgstr ""
1107
+
1108
+ #: common.php:3536
1109
+ msgid "Rules verified successfully"
1110
+ msgstr ""
1111
+
1112
+ #: common.php:3552
1113
+ msgid "The image to the right will display a WebP image with WEBP in white text, if your site is serving WebP images and your browser supports WebP."
1114
+ msgstr ""
1115
+
1116
+ #: common.php:3553
1117
+ msgid "Insert Rewrite Rules"
1118
+ msgstr ""
1119
+
1120
+ #: ewww-image-optimizer.php:146
1121
+ msgid "EWWW Image Optimizer is supported on Linux, FreeBSD, Mac OSX, and Windows"
1122
+ msgstr ""
1123
+
1124
+ #: ewww-image-optimizer.php:146
1125
+ msgid "Unfortunately, the EWWW Image Optimizer plugin does not work with %s"
1126
+ msgstr ""
1127
+
1128
+ #: ewww-image-optimizer.php:248
1129
+ msgid "EWWW Image Optimizer could not create the tool folder"
1130
+ msgstr ""
1131
+
1132
+ #: ewww-image-optimizer.php:248
1133
+ msgid "Please adjust permissions or create the folder"
1134
+ msgstr ""
1135
+
1136
+ #: ewww-image-optimizer.php:410
1137
+ msgid "EWWW Image Optimizer could not install tools in %s"
1138
+ msgstr ""
1139
+
1140
+ #: ewww-image-optimizer.php:410
1141
+ msgid "Please adjust permissions or create the folder. If you have installed the tools elsewhere on your system, check the option to %s."
1142
+ msgstr ""
1143
+
1144
+ #: ewww-image-optimizer.php:410
1145
+ msgid "For more details, visit the %1$s or the %2$s."
1146
+ msgstr ""
1147
+
1148
+ #: ewww-image-optimizer.php:410 ewww-image-optimizer.php:536
1149
+ msgid "Settings Page"
1150
+ msgstr ""
1151
+
1152
+ #: ewww-image-optimizer.php:422
1153
+ msgid "EWWW Image Optimizer requires exec(). Your system administrator has disabled this function."
1154
+ msgstr ""
1155
+
1156
+ #: ewww-image-optimizer.php:430
1157
+ msgid "Safe Mode is turned on for PHP. This plugin cannot operate in Safe Mode."
1158
+ msgstr ""
1159
+
1160
+ #: ewww-image-optimizer.php:536
1161
+ msgid "EWWW Image Optimizer uses %1$s, %2$s, %3$s, %4$s, %5$s, and %6$s. You are missing: %7$s. Please install via the %8$s or the %9$s."
1162
+ msgstr ""
1163
+
1164
+ #: ewww-image-optimizer.php:1170 ewww-image-optimizer.php:1295
1165
+ #: ewww-image-optimizer.php:1302
1166
+ msgid "Optimization skipped"
1167
+ msgstr ""
1168
+
1169
+ #: ewww-image-optimizer.php:1210
1170
+ msgid "Missing finfo_file(), getimagesize() and mime_content_type() PHP functions"
1171
+ msgstr ""
1172
+
1173
+ #: ewww-image-optimizer.php:1361 ewww-image-optimizer.php:1918
1174
+ msgid "%s is disabled"
1175
+ msgstr ""
1176
+
1177
+ #: ewww-image-optimizer.php:1404
1178
+ msgid "Unable to write file"
1179
+ msgstr ""
1180
+
1181
+ #: ewww-image-optimizer.php:1407
1182
+ msgid "Optimization failed"
1183
+ msgstr ""
1184
+
1185
+ #: ewww-image-optimizer.php:1632
1186
+ msgid "png tools are disabled"
1187
+ msgstr ""
1188
+
1189
+ #: ewww-image-optimizer.php:2077
1190
+ msgid "You don't have permission to install image optimizer utilities."
1191
+ msgstr ""
1192
+
1193
+ #: ewww-image-optimizer.php:2083
1194
+ msgid "tar command not found"
1195
+ msgstr ""
1196
+
1197
+ #: ewww-image-optimizer.php:2102 ewww-image-optimizer.php:2121
1198
+ #: ewww-image-optimizer.php:2139
1199
+ msgid "could not move pngout"
1200
+ msgstr ""
1201
+
1202
+ #: ewww-image-optimizer.php:2105 ewww-image-optimizer.php:2124
1203
+ msgid "could not set permissions"
1204
+ msgstr ""
1205
+
1206
+ #: flag-integration.php:96 iocli.php:75 iocli.php:83 iocli.php:125
1207
+ #: iocli.php:147 nextgen2-integration.php:295
1208
+ msgid "%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized)."
1209
+ msgstr ""
1210
+
1211
+ #: flag-integration.php:335 iocli.php:437
1212
+ msgid "Optimized size – %s"
1213
+ msgstr ""
1214
+
1215
+ #: flag-integration.php:342 iocli.php:444 nextcellent-integration.php:120
1216
+ #: nextgen-integration.php:97
1217
+ msgid "Thumbnail – %s"
1218
+ msgstr ""
1219
+
1220
+ #: flag-integration.php:368 iocli.php:251 iocli.php:400 iocli.php:462
1221
+ #: iocli.php:541 iocli.php:605 nextcellent-integration.php:493
1222
+ #: nextgen-integration.php:433 nextgen2-integration.php:504
1223
+ msgid "Finished Optimization!"
1224
+ msgstr ""
1225
+
1226
+ #: mwebp.php:6
1227
+ msgid "The migration is split into two parts. First, the plugin needs to scan all folders for webp images. Once it has obtained the list of images to rename, it will proceed with the renaming"
1228
+ msgstr ""
1229
+
1230
+ #: mwebp.php:7
1231
+ msgid "Start Migration"
1232
+ msgstr ""
1233
+
1234
+ #: mwebp.php:93
1235
+ msgid "Scanning"
1236
+ msgstr ""
1237
+
1238
+ #: mwebp.php:114
1239
+ msgid "%d Webp images left to rename."
1240
+ msgstr ""
1241
+
1242
+ #: mwebp.php:200
1243
+ msgid "Skipped:"
1244
+ msgstr ""
1245
+
1246
+ #: nextcellent-integration.php:38 nextcellent-integration.php:97
1247
+ #: nextgen-integration.php:34 nextgen-integration.php:74
1248
+ #: nextgen2-integration.php:38
1249
+ msgid "Bulk Thumbnail Optimize"
1250
+ msgstr ""
1251
+
1252
+ #: nextcellent-integration.php:86 nextgen-integration.php:60
1253
+ msgid "The thumbnails for your new images have not been optimized."
1254
+ msgstr ""
1255
+
1256
+ #: nextcellent-integration.php:86 nextgen-integration.php:64
1257
+ msgid "Optimize Thumbs"
1258
+ msgstr ""
1259
+
1260
+ #: nextcellent-integration.php:110 nextgen-integration.php:87
1261
+ msgid "Processing"
1262
+ msgstr ""
1263
+
1264
+ #: common.php:2928 iocli.php:78 iocli.php:131 nextcellent-integration.php:287
1265
+ #: nextgen-integration.php:249
1266
+ msgid "We have %d images to optimize."
1267
+ msgstr ""
1268
+
1269
+ #: nextcellent-integration.php:318
1270
+ msgid "Galleries"
1271
+ msgstr ""
1272
+
1273
+ #: iocli.php:516 iocli.php:583 nextcellent-integration.php:467
1274
+ #: nextgen-integration.php:404 nextgen2-integration.php:472
1275
+ msgid "Full size - %s"
1276
+ msgstr ""
1277
+
1278
+ #: iocli.php:519 iocli.php:589 nextcellent-integration.php:469
1279
+ #: nextgen-integration.php:410 nextgen2-integration.php:475
1280
+ msgid "Thumbnail - %s"
1281
+ msgstr ""
languages/ewww-image-optimizer-id_ID.mo ADDED
Binary file
languages/ewww-image-optimizer-id_ID.po ADDED
@@ -0,0 +1,1281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Translation of EWWW Image Optimizer in Indonesian
2
+ # This file is distributed under the same license as the EWWW Image Optimizer package.
3
+ msgid ""
4
+ msgstr ""
5
+ "PO-Revision-Date: 2015-09-01 15:24:46+0000\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=2; plural=n > 1;\n"
10
+ "X-Generator: GlotPress/1.0-alpha-1000\n"
11
+ "Project-Id-Version: EWWW Image Optimizer\n"
12
+
13
+ #: aux-optimize.php:97 aux-optimize.php:134 aux-optimize.php:383
14
+ #: aux-optimize.php:422 bulk.php:405 bulk.php:429 bulk.php:496 common.php:1922
15
+ #: flag-integration.php:285 flag-integration.php:306 flag-integration.php:362
16
+ #: mwebp.php:102 mwebp.php:193 nextcellent-integration.php:419
17
+ #: nextcellent-integration.php:439 nextcellent-integration.php:487
18
+ #: nextgen-integration.php:365 nextgen-integration.php:382
19
+ #: nextgen-integration.php:427 nextgen2-integration.php:422
20
+ #: nextgen2-integration.php:445 nextgen2-integration.php:498
21
+ msgid "Access token has expired, please reload the page."
22
+ msgstr ""
23
+
24
+ #: aux-optimize.php:684 aux-optimize.php:707 aux-optimize.php:723 bulk.php:388
25
+ #: common.php:3055 flag-integration.php:271 mwebp.php:84
26
+ #: nextcellent-integration.php:94 nextcellent-integration.php:405
27
+ #: nextgen-integration.php:71 nextgen-integration.php:352
28
+ #: nextgen2-integration.php:408
29
+ msgid "Access denied."
30
+ msgstr ""
31
+
32
+ #: ewww-image-optimizer.php:152
33
+ msgid "The regular version of the EWWW Image Optimizer plugin is not permitted on WP Engine sites. However, the cloud version has been approved by WP Engine. Please deactivate EWWW Image Optimizer and install EWWW Image Optimizer Cloud to optimize your images."
34
+ msgstr ""
35
+
36
+ #: common.php:3575
37
+ msgctxt "abbreviation for Virtual Private Server"
38
+ msgid "VPS:"
39
+ msgstr ""
40
+
41
+ #: common.php:3581
42
+ msgctxt "abbreviation for Content Delivery Network"
43
+ msgid "CDN:"
44
+ msgstr ""
45
+
46
+ #: common.php:3563
47
+ msgid "Help translate EWWW I.O."
48
+ msgstr "Bantu menerjemahkan EWWW I.O."
49
+
50
+ #: common.php:3564
51
+ msgid "Write a review."
52
+ msgstr "Tulis ulasan."
53
+
54
+ #: common.php:3569
55
+ msgctxt "A2 Hosting:"
56
+ msgid "with automatic EWWW IO setup"
57
+ msgstr ""
58
+
59
+ #: common.php:3561
60
+ msgid "Support EWWW I.O."
61
+ msgstr "Dukung EWWW I.O."
62
+
63
+ #: common.php:3562
64
+ msgid "Would you like to help support development of this plugin?"
65
+ msgstr "Maukah Anda mendukung pengembangan plugin ini?"
66
+
67
+ #: common.php:3565
68
+ msgid "Contribute directly via %s."
69
+ msgstr "Berkontribusi secara langsung melalui %s."
70
+
71
+ #: common.php:3567
72
+ msgid "Use any of these referral links to show your appreciation:"
73
+ msgstr ""
74
+
75
+ #: common.php:3568
76
+ msgid "Web Hosting:"
77
+ msgstr "Web Hosting:"
78
+
79
+ #: common.php:3581
80
+ msgid "Add MaxCDN to increase website speeds dramatically! Sign Up Now and Save 25%."
81
+ msgstr "Tambahkan MaxCDN untuk meningkatkan kecepatan situs secara dramatis! Daftar Sekarang dan Hemat 25%."
82
+
83
+ #: common.php:3581
84
+ msgid "Integrate MaxCDN within Wordpress using the W3 Total Cache plugin."
85
+ msgstr ""
86
+
87
+ #: common.php:3603
88
+ msgid "Debugging Information"
89
+ msgstr ""
90
+
91
+ #: common.php:3603
92
+ msgid "Select All"
93
+ msgstr "Pilih Semua"
94
+
95
+ #: ewww-image-optimizer.php:2109 ewww-image-optimizer.php:2128
96
+ msgid "extraction of files failed"
97
+ msgstr ""
98
+
99
+ #: common.php:2717
100
+ msgid "Azure Storage image"
101
+ msgstr ""
102
+
103
+ #: common.php:2721
104
+ msgid "Amazon S3 image"
105
+ msgstr "Gambar Amazon S3"
106
+
107
+ #: common.php:3462
108
+ msgid "Deferred Optimization"
109
+ msgstr ""
110
+
111
+ #: common.php:3462
112
+ msgid "Optimize images later via wp_cron, after image upload or generation is complete."
113
+ msgstr ""
114
+
115
+ #: ewww-image-optimizer.php:1181
116
+ msgid "Could not find %s"
117
+ msgstr "Tidak bisa menemukan %s"
118
+
119
+ #: ewww-image-optimizer.php:1189
120
+ msgid "%s is not writable"
121
+ msgstr ""
122
+
123
+ #: bulk.php:356
124
+ msgid "Operation timed out, you may need to increase the max_execution_time for PHP"
125
+ msgstr ""
126
+
127
+ #: bulk.php:357 flag-integration.php:186 nextcellent-integration.php:392
128
+ #: nextgen-integration.php:340 nextgen2-integration.php:395
129
+ msgid "License Exceeded"
130
+ msgstr ""
131
+
132
+ #: bulk.php:358 flag-integration.php:187 nextcellent-integration.php:393
133
+ #: nextgen-integration.php:341 nextgen2-integration.php:396
134
+ msgid "Optimization stopped, reload page to resume."
135
+ msgstr ""
136
+
137
+ #: bulk.php:359 flag-integration.php:188 nextcellent-integration.php:394
138
+ #: nextgen-integration.php:342 nextgen2-integration.php:397
139
+ msgid "Operation Interrupted"
140
+ msgstr ""
141
+
142
+ #: bulk.php:360 flag-integration.php:189 nextcellent-integration.php:395
143
+ #: nextgen-integration.php:343 nextgen2-integration.php:398
144
+ msgid "Temporary failure, seconds left to retry:"
145
+ msgstr ""
146
+
147
+ #: bulk.php:361 flag-integration.php:190 nextcellent-integration.php:396
148
+ #: nextgen-integration.php:344 nextgen2-integration.php:399
149
+ msgid "Could not remove image from table."
150
+ msgstr "Tidak bisa menghapus gambar dari tabel."
151
+
152
+ #: common.php:3170
153
+ msgid "Click to toggle"
154
+ msgstr ""
155
+
156
+ #: common.php:3467
157
+ msgid "Disable Resizes"
158
+ msgstr ""
159
+
160
+ #: common.php:3467
161
+ msgid "Wordpress, your theme, and other plugins generate various image sizes. You may disable optimization for certain sizes, or completely prevent those sizes from being created."
162
+ msgstr ""
163
+
164
+ #: common.php:3471
165
+ msgid "Disable Optimization"
166
+ msgstr ""
167
+
168
+ #: common.php:3471
169
+ msgid "Disable Creation"
170
+ msgstr ""
171
+
172
+ #: common.php:1353
173
+ msgid "You don't have permission to optimize images."
174
+ msgstr "Anda tidak memiliki otorisasi untuk mengoptimalkan gambar."
175
+
176
+ #: common.php:3400
177
+ msgid "Faster lossy optimization"
178
+ msgstr "Pengoptimalan lossy yang lebih cepat"
179
+
180
+ #: common.php:3400
181
+ msgid "Speed up the lossy operations by performing less compression."
182
+ msgstr "Percepat operasi lossy dengan melakukan lebih sedikit kompresi."
183
+
184
+ #: common.php:3410
185
+ msgid "Requires an EWWW Image Optimizer Cloud Subscription."
186
+ msgstr "Memerlukan langganan EWWW Image Optimizer Cloud."
187
+
188
+ #: common.php:3413
189
+ msgid "Uses pngquant locally. Use EWWW I.O. Cloud for even better lossy compression."
190
+ msgstr "Menggunakan pngquant secara lokal. Gunakan EWWW I.O. Cloud untuk mendapatkan kompresi lossy yang lebih baik."
191
+
192
+ #: common.php:3512
193
+ msgid "Uses output buffering and libxml functionality from PHP. Use this if the Apache rewrite rules do not work, or if your images are served from a CDN."
194
+ msgstr "Menggunakan penyanggaan output dan fungsionalitas libxml dari PHP. Gunakan ini jika aturan penulisan ulang Apache tidak berfungsi, atau jika gambar Anda disajikan dari CDN."
195
+
196
+ #: common.php:1653
197
+ msgid "optimized %1$d images, usage will reset in %2$d day."
198
+ msgid_plural "optimized %1$d images, usage will reset in %2$d days."
199
+ msgstr[0] "%1$d gambar telah dioptimalkan, penggunaan akan di-reset dalam %2$d hari."
200
+ msgstr[1] "%1$d gambar telah dioptimalkan, penggunaan akan di-reset dalam %2$d hari."
201
+
202
+ #: common.php:1655
203
+ msgid "%1$d image credit remaining."
204
+ msgid_plural "%1$d image credits remaining."
205
+ msgstr[0] "%1$d kredit gambar tersisa."
206
+ msgstr[1] "%1$d kredit gambar tersisa."
207
+
208
+ #: common.php:3465
209
+ msgid "Include Media Library Folders"
210
+ msgstr "Sertakan folder Media Library"
211
+
212
+ #: common.php:3465
213
+ msgid "If you have disabled automatic optimization, enable this if you want Scheduled Optimization to include the latest two folders from the Media Library."
214
+ msgstr "Jika Anda telah menonaktifkan pengoptimalan otomatis, aktifkan ini jika Anda ingin Pengoptimalan Terjadwal untuk menyertakan dua folder terbaru dari Media Library."
215
+
216
+ #: common.php:3512
217
+ msgid "Alternative WebP Rewriting"
218
+ msgstr "Penulisan ulang WebP alternatif"
219
+
220
+ #: bulk.php:355
221
+ msgid "%d images"
222
+ msgstr "%d gambar"
223
+
224
+ #: common.php:3454
225
+ msgid "Provide paths containing images to be optimized using \"Scan and Optimize\" on the Bulk Optimize page or by Scheduled Optimization."
226
+ msgstr "Sediakan jalur yang berisikan gambar yang akan dioptimalkan menggunakan \"Pindai dan Optimalkan\" pada halaman Pengoptimalan Gabungan atau dengan Pengoptimalan Terjadwal."
227
+
228
+ #: iocli.php:55
229
+ msgctxt "string will be something like \"media\" or \"nextgen\""
230
+ msgid "Optimizing %1$s with a %2$d second pause between images."
231
+ msgstr "Mengoptimalkan %1$s dengan %2$d detik jeda antara gambar."
232
+
233
+ #: iocli.php:52
234
+ msgid "Forcing re-optimization of previously processed images."
235
+ msgstr "Memaksakan pengoptimalan ulang untuk gambar yang telah diproses."
236
+
237
+ #: iocli.php:66 iocli.php:107 iocli.php:118 iocli.php:142 iocli.php:157
238
+ msgid "Bulk status has been reset, starting from the beginning."
239
+ msgstr ""
240
+
241
+ #: iocli.php:172
242
+ msgid "Bulk status has been reset, the next bulk operation will start from the beginning."
243
+ msgstr ""
244
+
245
+ #: iocli.php:174
246
+ msgid "Please specify a valid library option, see \"wp-cli help ewwwio optimize\" for more information."
247
+ msgstr ""
248
+
249
+ #: common.php:1003 common.php:2927
250
+ msgid "Unoptimized Images"
251
+ msgstr "Gambar Belum Dioptimisasi"
252
+
253
+ #: common.php:2934
254
+ msgid "Optimize All Images"
255
+ msgstr "Optimisasi Semua Gambar"
256
+
257
+ #: common.php:2953
258
+ msgid "There are too many images to display."
259
+ msgstr "Terlalu banyak gambar untuk ditampilkan."
260
+
261
+ #: iocli.php:87 iocli.php:162
262
+ msgid "%1$d images in other folders need optimizing."
263
+ msgstr ""
264
+
265
+ #: iocli.php:136
266
+ msgid "NextGEN/Nextcellent not installed."
267
+ msgstr ""
268
+
269
+ #: iocli.php:151
270
+ msgid "Grand Flagallery not installed."
271
+ msgstr "Grand Flagallery tidak terpasang."
272
+
273
+ #: common.php:3463
274
+ msgid "Disable Automatic Optimization"
275
+ msgstr ""
276
+
277
+ #: common.php:3463
278
+ msgid "Images will not be optimized on upload. Images may be optimized with the Bulk Optimize tools or with Scheduled optimization."
279
+ msgstr ""
280
+
281
+ #: common.php:3489
282
+ msgid "Exclude full-size images from metadata removal"
283
+ msgstr ""
284
+
285
+ #: aux-optimize.php:9
286
+ msgid "Scan and optimize"
287
+ msgstr ""
288
+
289
+ #: aux-optimize.php:11
290
+ msgid "Resume previous optimization"
291
+ msgstr ""
292
+
293
+ #: aux-optimize.php:29
294
+ msgid "Optimize Everything Else"
295
+ msgstr ""
296
+
297
+ #: aux-optimize.php:30
298
+ msgid "Use this tool to optimize images outside of the Media Library and galleries where we have full integration. Examples: theme images, BuddyPress, WP Symposium, and any folders that you have specified on the settings page."
299
+ msgstr ""
300
+
301
+ #: aux-optimize.php:32
302
+ msgid "The database schema has changed, you need to convert to the new format."
303
+ msgstr ""
304
+
305
+ #: aux-optimize.php:36
306
+ msgid "Convert Table"
307
+ msgstr ""
308
+
309
+ #: aux-optimize.php:39
310
+ msgid "There are no images to optimize."
311
+ msgstr ""
312
+
313
+ #: aux-optimize.php:40 iocli.php:68 iocli.php:159
314
+ msgid "Scanning, this could take a while"
315
+ msgstr ""
316
+
317
+ #: aux-optimize.php:42
318
+ msgid "Last optimization was completed on %1$s at %2$s and optimized %3$d images"
319
+ msgstr ""
320
+
321
+ #: aux-optimize.php:46 bulk.php:57
322
+ msgid "Optimize Again"
323
+ msgstr "Optimalkan Lagi"
324
+
325
+ #: aux-optimize.php:51 bulk.php:63 flag-integration.php:105
326
+ #: nextcellent-integration.php:296 nextgen-integration.php:258
327
+ #: nextgen2-integration.php:303
328
+ msgid "If you would like to start over again, press the Reset Status button to reset the bulk operation status."
329
+ msgstr ""
330
+
331
+ #: aux-optimize.php:55 bulk.php:67 flag-integration.php:109
332
+ #: nextcellent-integration.php:300 nextgen-integration.php:262
333
+ #: nextgen2-integration.php:307
334
+ msgid "Reset Status"
335
+ msgstr ""
336
+
337
+ #: aux-optimize.php:64
338
+ msgid "The plugin keeps track of already optimized images to prevent re-optimization. There are %d images that have been optimized so far."
339
+ msgstr "Plugin mencatat gambar-gambar yang telah dioptimalkan untuk mencegah pengoptimalan ulang. Ada %d gambar yang telah dioptimalkan sejauh ini."
340
+
341
+ #: aux-optimize.php:66
342
+ msgid "Show Optimized Images"
343
+ msgstr ""
344
+
345
+ #: aux-optimize.php:74
346
+ msgid "page"
347
+ msgstr "laman"
348
+
349
+ #: aux-optimize.php:74
350
+ msgid "of"
351
+ msgstr ""
352
+
353
+ #: aux-optimize.php:254 aux-optimize.php:296 aux-optimize.php:299
354
+ msgid "Unknown Savings"
355
+ msgstr ""
356
+
357
+ #: aux-optimize.php:319
358
+ msgid "Finished importing"
359
+ msgstr ""
360
+
361
+ #: aux-optimize.php:336 common.php:1830 common.php:2378 common.php:2387
362
+ msgid "Previously Optimized"
363
+ msgstr "Telah Dioptimalkan Sebelumnya"
364
+
365
+ #: aux-optimize.php:339 common.php:1869 common.php:2314
366
+ msgid "No savings"
367
+ msgstr "Tidak ada simpanan"
368
+
369
+ #: aux-optimize.php:346 bulk.php:122 common.php:1835
370
+ #: ewww-image-optimizer.php:2029
371
+ msgid "License exceeded"
372
+ msgstr ""
373
+
374
+ #: aux-optimize.php:391
375
+ msgid "Filename"
376
+ msgstr "Nama berkas"
377
+
378
+ #: aux-optimize.php:391
379
+ msgid "Image Type"
380
+ msgstr "Tipe Gambar"
381
+
382
+ #: aux-optimize.php:391 common.php:1088 common.php:1120 common.php:2691
383
+ #: common.php:2939 flag-integration.php:374 nextcellent-integration.php:173
384
+ #: nextgen-integration.php:142 nextgen2-integration.php:131
385
+ #: nextgen2-integration.php:134
386
+ msgid "Image Optimizer"
387
+ msgstr ""
388
+
389
+ #: aux-optimize.php:408 common.php:2809 common.php:2863
390
+ #: flag-integration.php:431 flag-integration.php:440
391
+ #: nextcellent-integration.php:229 nextcellent-integration.php:238
392
+ #: nextgen-integration.php:198 nextgen-integration.php:205
393
+ #: nextgen2-integration.php:215
394
+ msgid "Image Size: %s"
395
+ msgstr "Ukuran Gambar: %s"
396
+
397
+ #: aux-optimize.php:408
398
+ msgid "Remove from table"
399
+ msgstr "Hapus dari tabel"
400
+
401
+ #: aux-optimize.php:666
402
+ msgid "Nothing to optimize"
403
+ msgstr ""
404
+
405
+ #: aux-optimize.php:695 aux-optimize.php:712 bulk.php:395 bulk.php:414
406
+ #: flag-integration.php:277 flag-integration.php:296 iocli.php:210
407
+ #: nextcellent-integration.php:411 nextcellent-integration.php:429
408
+ #: nextgen-integration.php:358 nextgen-integration.php:375
409
+ #: nextgen2-integration.php:435
410
+ msgid "Optimizing"
411
+ msgstr ""
412
+
413
+ #: aux-optimize.php:732 bulk.php:502 mwebp.php:204
414
+ #: nextcellent-integration.php:129 nextgen-integration.php:106
415
+ msgid "Finished"
416
+ msgstr "Selesai"
417
+
418
+ #: bulk.php:11 common.php:1002 common.php:2892 common.php:2894 common.php:3159
419
+ #: common.php:3161 flag-integration.php:36 flag-integration.php:41
420
+ #: flag-integration.php:46 flag-integration.php:73
421
+ #: nextcellent-integration.php:37 nextcellent-integration.php:269
422
+ #: nextgen-integration.php:33 nextgen-integration.php:234
423
+ #: nextgen2-integration.php:37 nextgen2-integration.php:272
424
+ #: nextgen2-integration.php:516
425
+ msgid "Bulk Optimize"
426
+ msgstr ""
427
+
428
+ #: bulk.php:15 flag-integration.php:78 nextcellent-integration.php:274
429
+ #: nextgen-integration.php:239 nextgen2-integration.php:277
430
+ msgid "Start optimizing"
431
+ msgstr ""
432
+
433
+ #: bulk.php:17 flag-integration.php:80 nextcellent-integration.php:276
434
+ #: nextgen-integration.php:241 nextgen2-integration.php:279
435
+ msgid "Resume previous bulk operation"
436
+ msgstr ""
437
+
438
+ #: bulk.php:23
439
+ msgid "Importing"
440
+ msgstr ""
441
+
442
+ #: bulk.php:28 flag-integration.php:87 nextcellent-integration.php:283
443
+ #: nextgen2-integration.php:286
444
+ msgid "Stop Optimizing"
445
+ msgstr "Hentikan Pengoptimalan"
446
+
447
+ #: bulk.php:32
448
+ msgid "You should import Media Library images into the table to prevent duplicate optimization."
449
+ msgstr ""
450
+
451
+ #: bulk.php:34
452
+ msgid "Import Images"
453
+ msgstr "Impor Gambar"
454
+
455
+ #: bulk.php:40 flag-integration.php:91 nextgen2-integration.php:290
456
+ msgid "Force re-optimize"
457
+ msgstr ""
458
+
459
+ #: bulk.php:41 common.php:3415 flag-integration.php:92
460
+ #: nextgen2-integration.php:291
461
+ msgid "Choose how long to pause between images (in seconds, 0 = disabled)"
462
+ msgstr ""
463
+
464
+ #: bulk.php:44
465
+ msgid "Optimize Media Library"
466
+ msgstr ""
467
+
468
+ #: bulk.php:46 flag-integration.php:68 nextcellent-integration.php:264
469
+ #: nextgen-integration.php:229 nextgen2-integration.php:266
470
+ msgid "You do not appear to have uploaded any images yet."
471
+ msgstr ""
472
+
473
+ #: bulk.php:50
474
+ msgid "%1$d images in the Media Library have been selected, unable to determine how many resizes and how many are unoptimized."
475
+ msgstr ""
476
+
477
+ #: bulk.php:52 iocli.php:70 iocli.php:111
478
+ msgid "%1$d images in the Media Library have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized)."
479
+ msgstr ""
480
+
481
+ #: bulk.php:54 flag-integration.php:97 nextcellent-integration.php:288
482
+ #: nextgen-integration.php:250 nextgen2-integration.php:296
483
+ msgid "Previously optimized images will be skipped by default."
484
+ msgstr ""
485
+
486
+ #: bulk.php:449 common.php:1956 flag-integration.php:328 iocli.php:386
487
+ #: iocli.php:430 iocli.php:510 iocli.php:582 nextcellent-integration.php:466
488
+ #: nextgen-integration.php:403 nextgen2-integration.php:466
489
+ msgid "Optimized image:"
490
+ msgstr ""
491
+
492
+ #: bulk.php:451 iocli.php:218
493
+ msgid "Skipped image, ID:"
494
+ msgstr "Gambar yang dilewati: ID:"
495
+
496
+ #: bulk.php:455 flag-integration.php:329 iocli.php:222 iocli.php:431
497
+ msgid "Full size – %s"
498
+ msgstr "Ukuran penuh – %s"
499
+
500
+ #: bulk.php:470 common.php:1962 flag-integration.php:347 iocli.php:237
501
+ #: iocli.php:392 iocli.php:449 iocli.php:527 iocli.php:592
502
+ #: nextcellent-integration.php:123 nextcellent-integration.php:472
503
+ #: nextgen-integration.php:100 nextgen-integration.php:413
504
+ #: nextgen2-integration.php:483
505
+ msgid "Elapsed: %.3f seconds"
506
+ msgstr ""
507
+
508
+ #: bulk.php:502
509
+ msgid "Return to Media Library"
510
+ msgstr ""
511
+
512
+ #: common.php:711
513
+ msgid "Settings saved"
514
+ msgstr "Pengaturan disimpan"
515
+
516
+ #: common.php:1004 mwebp.php:5
517
+ msgid "Migrate WebP Images"
518
+ msgstr ""
519
+
520
+ #: common.php:1023
521
+ msgid "Image Store Optimize"
522
+ msgstr ""
523
+
524
+ #: common.php:1023
525
+ msgid "Optimize"
526
+ msgstr ""
527
+
528
+ #: common.php:1077
529
+ msgid "Image Store Optimization"
530
+ msgstr ""
531
+
532
+ #: common.php:1087
533
+ msgid "Choose a gallery or"
534
+ msgstr "Pilih galeri atau"
535
+
536
+ #: common.php:1087
537
+ msgid "optimize all galleries"
538
+ msgstr "optimalkan semua galeri"
539
+
540
+ #: common.php:1088
541
+ msgid "Gallery ID"
542
+ msgstr "ID Galeri"
543
+
544
+ #: common.php:1088
545
+ msgid "Gallery Name"
546
+ msgstr "Nama Galeri"
547
+
548
+ #: common.php:1088
549
+ msgid "Images"
550
+ msgstr "Gambar"
551
+
552
+ #: common.php:1104 common.php:1119
553
+ msgid "Optimize Gallery"
554
+ msgstr "Optimalkan Galeri"
555
+
556
+ #: common.php:1120 common.php:2939
557
+ msgid "Title"
558
+ msgstr "Judul"
559
+
560
+ #: common.php:1120 nextgen-integration.php:14 nextgen-integration.php:274
561
+ msgid "Gallery"
562
+ msgstr "Galeri"
563
+
564
+ #: common.php:1203
565
+ msgid "Settings"
566
+ msgstr "Pengaturan"
567
+
568
+ #: flag-integration.php:231 nextcellent-integration.php:137
569
+ #: nextgen-integration.php:113 nextgen2-integration.php:96
570
+ msgid "You don't have permission to work with uploaded files."
571
+ msgstr "Anda tidak punya izin untuk bekerja dengan berkas-berkas yang diunggah."
572
+
573
+ #: common.php:1358 flag-integration.php:235 nextcellent-integration.php:141
574
+ #: nextgen-integration.php:117 nextgen2-integration.php:100
575
+ msgid "No attachment ID was provided."
576
+ msgstr ""
577
+
578
+ #: common.php:1401 common.php:1436
579
+ msgid "Original Restored"
580
+ msgstr "Gambar Asli Telah Dikembalikan"
581
+
582
+ #: common.php:1657
583
+ msgid "used %1$d of %2$d, usage will reset in %3$d day."
584
+ msgid_plural "used %1$d of %2$d, usage will reset in %3$d days."
585
+ msgstr[0] ""
586
+ msgstr[1] ""
587
+
588
+ #: common.php:1881
589
+ msgid "Reduced by %01.1f%% (%s)"
590
+ msgstr ""
591
+
592
+ #: common.php:2713
593
+ msgid "Cloudinary image"
594
+ msgstr ""
595
+
596
+ #: common.php:2737
597
+ msgid "Could not retrieve file path."
598
+ msgstr ""
599
+
600
+ #: common.php:2758 common.php:2769 common.php:2780
601
+ #: ewww-image-optimizer.php:1365 ewww-image-optimizer.php:1636
602
+ #: ewww-image-optimizer.php:1640 ewww-image-optimizer.php:1922
603
+ #: flag-integration.php:405 flag-integration.php:411 flag-integration.php:417
604
+ #: nextcellent-integration.php:201 nextcellent-integration.php:208
605
+ #: nextcellent-integration.php:215 nextgen-integration.php:170
606
+ #: nextgen-integration.php:177 nextgen-integration.php:184
607
+ #: nextgen2-integration.php:174 nextgen2-integration.php:181
608
+ #: nextgen2-integration.php:188
609
+ msgid "%s is missing"
610
+ msgstr ""
611
+
612
+ #: common.php:2760
613
+ msgid "JPG to PNG"
614
+ msgstr "JPG ke PNG"
615
+
616
+ #: common.php:2762
617
+ msgid "WARNING: Removes metadata. Requires GD or ImageMagick. PNG is generally much better than JPG for logos and other images with a limited range of colors."
618
+ msgstr ""
619
+
620
+ #: common.php:2771
621
+ msgid "PNG to JPG"
622
+ msgstr "PNG ke JPG"
623
+
624
+ #: common.php:2773
625
+ msgid "WARNING: This is not a lossless conversion and requires GD or ImageMagick. JPG is much better than PNG for photographic use because it compresses the image and discards data. Transparent images will only be converted if a background color has been set."
626
+ msgstr ""
627
+
628
+ #: common.php:2782
629
+ msgid "GIF to PNG"
630
+ msgstr "GIF ke PNG"
631
+
632
+ #: common.php:2784
633
+ msgid "PNG is generally better than GIF, but does not support animation. Animated images will not be converted."
634
+ msgstr ""
635
+
636
+ #: common.php:2789 flag-integration.php:425 nextcellent-integration.php:223
637
+ #: nextgen-integration.php:192 nextgen2-integration.php:196
638
+ msgid "Unsupported file type"
639
+ msgstr ""
640
+
641
+ #: common.php:2814 flag-integration.php:435 nextcellent-integration.php:233
642
+ #: nextgen-integration.php:201 nextgen2-integration.php:235
643
+ msgid "Re-optimize"
644
+ msgstr ""
645
+
646
+ #: common.php:2841
647
+ msgid "Restore original"
648
+ msgstr "Kembalikan ke gambar asli"
649
+
650
+ #: common.php:2861 flag-integration.php:439 nextcellent-integration.php:237
651
+ #: nextgen-integration.php:204 nextgen2-integration.php:209
652
+ msgid "Not processed"
653
+ msgstr "Tidak diproses"
654
+
655
+ #: common.php:2800 common.php:2866 flag-integration.php:444
656
+ #: nextcellent-integration.php:242 nextgen-integration.php:208
657
+ #: nextgen2-integration.php:239
658
+ msgid "Optimize now!"
659
+ msgstr ""
660
+
661
+ #: common.php:3059
662
+ msgid "Insertion successful"
663
+ msgstr ""
664
+
665
+ #: common.php:3061
666
+ msgid "Insertion failed"
667
+ msgstr ""
668
+
669
+ #: common.php:3127
670
+ msgid "Pngout was successfully installed, check the Plugin Status area for version information."
671
+ msgstr ""
672
+
673
+ #: common.php:3132
674
+ msgid "Pngout was not installed: %1$s. Make sure this folder is writable: %2$s"
675
+ msgstr ""
676
+
677
+ #: common.php:3154
678
+ msgid "Plugin Home Page"
679
+ msgstr "Laman Depan Plugin"
680
+
681
+ #: common.php:3155 ewww-image-optimizer.php:410 ewww-image-optimizer.php:536
682
+ msgid "Installation Instructions"
683
+ msgstr "Instruksi Pemasangan"
684
+
685
+ #: common.php:3156
686
+ msgid "Plugin Support"
687
+ msgstr "Dukungan Plugin"
688
+
689
+ #: common.php:3157
690
+ msgid "Cloud Status"
691
+ msgstr ""
692
+
693
+ #: common.php:3159
694
+ msgid "Media Library"
695
+ msgstr ""
696
+
697
+ #: common.php:3163
698
+ msgid "New images uploaded to the Media Library will be optimized automatically. If you have existing images you would like to optimize, you can use the %s tool."
699
+ msgstr ""
700
+
701
+ #: common.php:3171
702
+ msgid "Plugin Status"
703
+ msgstr "Status Plugin"
704
+
705
+ #: common.php:3172
706
+ msgid "All Clear"
707
+ msgstr ""
708
+
709
+ #: common.php:3173
710
+ msgid "Requires Attention"
711
+ msgstr ""
712
+
713
+ #: common.php:3176
714
+ msgid "Total Savings:"
715
+ msgstr ""
716
+
717
+ #: common.php:3178 common.php:3391
718
+ msgid "Cloud optimization API Key"
719
+ msgstr ""
720
+
721
+ #: common.php:3181 common.php:3183
722
+ msgid "Verified,"
723
+ msgstr "Terverifikasi,"
724
+
725
+ #: common.php:3185
726
+ msgid "Not Verified"
727
+ msgstr "Tidak Terverifikasi"
728
+
729
+ #: common.php:3197
730
+ msgid "If updated versions are available below you may either download the newer versions and install them yourself, or uncheck \"Use System Paths\" and use the bundled tools."
731
+ msgstr ""
732
+
733
+ #: common.php:3198 common.php:3201
734
+ msgid "Updates are optional, but may contain increased optimization or security patches"
735
+ msgstr ""
736
+
737
+ #: common.php:3200
738
+ msgid "If updated versions are available below, you may need to enable write permission on the %s folder to use the automatic installs."
739
+ msgstr ""
740
+
741
+ #: common.php:3211 common.php:3221 common.php:3231 common.php:3241
742
+ #: common.php:3251 common.php:3261 common.php:3285 common.php:3293
743
+ #: common.php:3301 common.php:3309 common.php:3323 common.php:3329
744
+ #: common.php:3337
745
+ msgid "Installed"
746
+ msgstr "Terpasang"
747
+
748
+ #: common.php:3211 common.php:3221 common.php:3231 common.php:3241
749
+ #: common.php:3251 common.php:3261
750
+ msgid "version"
751
+ msgstr "versi"
752
+
753
+ #: common.php:3213 common.php:3223 common.php:3233 common.php:3243
754
+ #: common.php:3253 common.php:3263 common.php:3288 common.php:3296
755
+ #: common.php:3304 common.php:3312 common.php:3326 common.php:3334
756
+ #: common.php:3340
757
+ msgid "Missing"
758
+ msgstr "Hilang"
759
+
760
+ #: common.php:3243
761
+ msgid "Install"
762
+ msgstr "Pasang"
763
+
764
+ #: common.php:3243
765
+ msgid "automatically"
766
+ msgstr "secara otomatis"
767
+
768
+ #: common.php:3243
769
+ msgid "manually"
770
+ msgstr "secara manual"
771
+
772
+ #: common.php:3243
773
+ msgid "Pngout is free closed-source software that can produce drastically reduced filesizes for PNGs, but can be very time consuming to process images"
774
+ msgstr ""
775
+
776
+ #: common.php:3269
777
+ msgid "On"
778
+ msgstr ""
779
+
780
+ #: common.php:3272
781
+ msgid "Off"
782
+ msgstr ""
783
+
784
+ #: common.php:3275
785
+ msgid "Disabled"
786
+ msgstr ""
787
+
788
+ #: common.php:3278
789
+ msgid "Enabled"
790
+ msgstr ""
791
+
792
+ #: common.php:3281
793
+ msgid "%s only need one, used for conversion, not optimization"
794
+ msgstr ""
795
+
796
+ #: common.php:3281
797
+ msgid "Graphics libraries"
798
+ msgstr ""
799
+
800
+ #: common.php:3319
801
+ msgid "Only need one of these:"
802
+ msgstr "Hanya butuh salah satu dari ini:"
803
+
804
+ #: common.php:3344 common.php:3348 common.php:3351
805
+ msgid "command not found on your system"
806
+ msgstr "perintah tidak ditemukan di sistem Anda"
807
+
808
+ #: common.php:3348
809
+ msgid "not required"
810
+ msgstr "tidak diperlukan"
811
+
812
+ #: common.php:3351
813
+ msgid "required for automatic pngout installer"
814
+ msgstr ""
815
+
816
+ #: common.php:3375
817
+ msgid "Cloud Settings"
818
+ msgstr ""
819
+
820
+ #: common.php:3376
821
+ msgid "Basic Settings"
822
+ msgstr "Pengaturan Dasar"
823
+
824
+ #: common.php:3377
825
+ msgid "Advanced Settings"
826
+ msgstr "Pengaturan Lanjutan"
827
+
828
+ #: common.php:3378
829
+ msgid "Conversion Settings"
830
+ msgstr "Pengaturan Pengkonversian"
831
+
832
+ #: common.php:3389
833
+ msgid "If exec() is disabled for security reasons (and enabling it is not an option), or you would like to offload image optimization to a third-party server, you may purchase an API key for our cloud optimization service. The API key should be entered below, and cloud optimization must be enabled for each image format individually."
834
+ msgstr ""
835
+
836
+ #: common.php:3389 common.php:3391 common.php:3410 common.php:3413
837
+ msgid "Purchase an API key."
838
+ msgstr "Beli sebuah kunci API."
839
+
840
+ #: common.php:3391
841
+ msgid "API Key will be validated when you save your settings."
842
+ msgstr ""
843
+
844
+ #: common.php:3392
845
+ msgid "JPG cloud optimization"
846
+ msgstr ""
847
+
848
+ #: common.php:3394
849
+ msgid "PNG cloud optimization"
850
+ msgstr ""
851
+
852
+ #: common.php:3396
853
+ msgid "extra PNG compression (slower)"
854
+ msgstr ""
855
+
856
+ #: common.php:3398
857
+ msgid "GIF cloud optimization"
858
+ msgstr ""
859
+
860
+ #: common.php:3405
861
+ msgid "Debugging"
862
+ msgstr ""
863
+
864
+ #: common.php:3405
865
+ msgid "Use this to provide information for support purposes, or if you feel comfortable digging around in the code to fix a problem you are experiencing."
866
+ msgstr ""
867
+
868
+ #: common.php:3406
869
+ msgid "Remove metadata"
870
+ msgstr "Hapus metadata"
871
+
872
+ #: common.php:3407
873
+ msgid "This will remove ALL metadata: EXIF and comments."
874
+ msgstr "Ini akan menghapus SEMUA metadata: EXIF dan komentar-komentar."
875
+
876
+ #: common.php:3409
877
+ msgid "Lossy JPG optimization"
878
+ msgstr ""
879
+
880
+ #: common.php:3409 common.php:3412 common.php:3509 common.php:3515
881
+ #: common.php:3518
882
+ msgid "WARNING:"
883
+ msgstr "PERINGATAN:"
884
+
885
+ #: common.php:3409 common.php:3412
886
+ msgid "While most users will not notice a difference in image quality, lossy means there IS a loss in image quality."
887
+ msgstr ""
888
+
889
+ #: common.php:3412
890
+ msgid "Lossy PNG optimization"
891
+ msgstr ""
892
+
893
+ #: common.php:3415
894
+ msgid "Bulk Delay"
895
+ msgstr ""
896
+
897
+ #: common.php:3418
898
+ msgid "Automatic Cloudinary upload"
899
+ msgstr ""
900
+
901
+ #: common.php:3418
902
+ msgid "When enabled, uploads to the Media Library will be transferred to Cloudinary after optimization. Cloudinary generates resizes, so only the full-size image is uploaded."
903
+ msgstr ""
904
+
905
+ #: common.php:3428
906
+ msgid "optipng optimization level"
907
+ msgstr ""
908
+
909
+ #: common.php:3430 common.php:3431 common.php:3432 common.php:3433
910
+ #: common.php:3434 common.php:3435 common.php:3436 common.php:3442
911
+ #: common.php:3443 common.php:3444 common.php:3445
912
+ msgid "Level %d"
913
+ msgstr "Level %d"
914
+
915
+ #: common.php:3430
916
+ msgid "%d trial"
917
+ msgstr ""
918
+
919
+ #: common.php:3431 common.php:3432 common.php:3433 common.php:3434
920
+ #: common.php:3435 common.php:3436
921
+ msgid "%d trials"
922
+ msgstr ""
923
+
924
+ #: common.php:3437 common.php:3446
925
+ msgid "default"
926
+ msgstr ""
927
+
928
+ #: common.php:3438
929
+ msgid "According to the author of optipng, 10 trials should satisfy most people, 30 trials should satisfy everyone."
930
+ msgstr ""
931
+
932
+ #: common.php:3440
933
+ msgid "pngout optimization level"
934
+ msgstr ""
935
+
936
+ #: common.php:3442
937
+ msgid "Xtreme! (Slowest)"
938
+ msgstr "Ekstrem! (Paling Lambat)"
939
+
940
+ #: common.php:3443
941
+ msgid "Intense (Slow)"
942
+ msgstr "Intensif (Lambat)"
943
+
944
+ #: common.php:3444
945
+ msgid "Longest Match (Fast)"
946
+ msgstr ""
947
+
948
+ #: common.php:3445
949
+ msgid "Huffman Only (Faster)"
950
+ msgstr "Hanya Huffman (Lebih Cepat)"
951
+
952
+ #: common.php:3447
953
+ msgid "If you have CPU cycles to spare, go with level %d"
954
+ msgstr ""
955
+
956
+ #: common.php:3450
957
+ msgid "Scheduled optimization"
958
+ msgstr ""
959
+
960
+ #: common.php:3450
961
+ msgid "This will enable scheduled optimization of unoptimized images for your theme, buddypress, and any additional folders you have configured below. Runs hourly: wp_cron only runs when your site is visited, so it may be even longer between optimizations."
962
+ msgstr ""
963
+
964
+ #: common.php:3452
965
+ msgid "Folders to optimize"
966
+ msgstr ""
967
+
968
+ #: common.php:3452
969
+ msgid "One path per line, must be within %s. Use full paths, not relative paths."
970
+ msgstr ""
971
+
972
+ #: common.php:3483
973
+ msgid "Skip Small Images"
974
+ msgstr "Lewati Gambar-Gambar Kecil"
975
+
976
+ #: common.php:3483
977
+ msgid "Do not optimize images smaller than this (in bytes)"
978
+ msgstr ""
979
+
980
+ #: common.php:3485
981
+ msgid "Skip Large PNG Images"
982
+ msgstr "Lewati Gambar PNG yang Besar"
983
+
984
+ #: common.php:3485
985
+ msgid "Do not optimize PNG images larger than this (in bytes)"
986
+ msgstr "Jangan optimalkan gambar PNG yang berukuran lebih besar dari ini (dalam bytes)"
987
+
988
+ #: common.php:3487
989
+ msgid "Exclude full-size images from lossy optimization"
990
+ msgstr ""
991
+
992
+ #: common.php:3491 ewww-image-optimizer.php:410
993
+ msgid "Use System Paths"
994
+ msgstr ""
995
+
996
+ #: common.php:3491
997
+ msgid "If you have already installed the utilities in a system location, such as %s or %s, use this to force the plugin to use those versions and skip the auto-installers."
998
+ msgstr ""
999
+
1000
+ #: common.php:3493 common.php:3495 common.php:3497 common.php:3499
1001
+ msgid "disable"
1002
+ msgstr ""
1003
+
1004
+ #: common.php:3503
1005
+ msgid "Conversion is only available for images in the Media Library (except WebP). By default, all images have a link available in the Media Library for one-time conversion. Turning on individual conversion operations below will enable conversion filters any time an image is uploaded or modified."
1006
+ msgstr ""
1007
+
1008
+ #: common.php:3504
1009
+ msgid "NOTE:"
1010
+ msgstr "CATATAN:"
1011
+
1012
+ #: common.php:3504
1013
+ msgid "The plugin will attempt to update image locations for any posts that contain the images. You may still need to manually update locations/urls for converted images."
1014
+ msgstr ""
1015
+
1016
+ #: common.php:3507
1017
+ msgid "Hide Conversion Links"
1018
+ msgstr ""
1019
+
1020
+ #: common.php:3507
1021
+ msgid "Site or Network admins can use this to prevent other users from using the conversion links in the Media Library which bypass the settings below."
1022
+ msgstr ""
1023
+
1024
+ #: common.php:3508
1025
+ msgid "Delete originals"
1026
+ msgstr ""
1027
+
1028
+ #: common.php:3508
1029
+ msgid "This will remove the original image from the server after a successful conversion."
1030
+ msgstr ""
1031
+
1032
+ #: common.php:3509
1033
+ msgid "JPG/PNG to WebP"
1034
+ msgstr ""
1035
+
1036
+ #: common.php:3509
1037
+ msgid "JPG to WebP conversion is lossy, but quality loss is minimal. PNG to WebP conversion is lossless."
1038
+ msgstr ""
1039
+
1040
+ #: common.php:3510
1041
+ msgid "Originals are never deleted, and WebP images should only be served to supported browsers."
1042
+ msgstr ""
1043
+
1044
+ #: common.php:3510
1045
+ msgid "You can use the rewrite rules below to serve WebP images with Apache."
1046
+ msgstr ""
1047
+
1048
+ #: common.php:3515 common.php:3518 common.php:3525
1049
+ msgid "enable %s to %s conversion"
1050
+ msgstr ""
1051
+
1052
+ #: common.php:3515
1053
+ msgid "Removes metadata and increases cpu usage dramatically."
1054
+ msgstr ""
1055
+
1056
+ #: common.php:3516
1057
+ msgid "PNG is generally much better than JPG for logos and other images with a limited range of colors. Checking this option will slow down JPG processing significantly, and you may want to enable it only temporarily."
1058
+ msgstr ""
1059
+
1060
+ #: common.php:3518
1061
+ msgid "This is not a lossless conversion."
1062
+ msgstr ""
1063
+
1064
+ #: common.php:3519
1065
+ msgid "JPG is generally much better than PNG for photographic use because it compresses the image and discards data. PNGs with transparency are not converted by default."
1066
+ msgstr ""
1067
+
1068
+ #: common.php:3520
1069
+ msgid "JPG background color:"
1070
+ msgstr "Warna latar belakang JPG"
1071
+
1072
+ #: common.php:3520
1073
+ msgid "HEX format (#123def)"
1074
+ msgstr ""
1075
+
1076
+ #: common.php:3521
1077
+ msgid "Background color is used only if the PNG has transparency. Leave this value blank to skip PNGs with transparency."
1078
+ msgstr ""
1079
+
1080
+ #: common.php:3522
1081
+ msgid "JPG quality level:"
1082
+ msgstr "Tingkat kualitas JPG:"
1083
+
1084
+ #: common.php:3522
1085
+ msgid "Valid values are 1-100."
1086
+ msgstr "Nilai yang valid adalah 1-100."
1087
+
1088
+ #: common.php:3523
1089
+ msgid "If JPG quality is blank, the plugin will attempt to set the optimal quality level or default to 92. Remember, this is a lossy conversion, so you are losing pixels, and it is not recommended to actually set the level here unless you want noticable loss of image quality."
1090
+ msgstr ""
1091
+
1092
+ #: common.php:3525
1093
+ msgid "No warnings here, just do it."
1094
+ msgstr "Tidak ada peringatan di sini, lakukan saja."
1095
+
1096
+ #: common.php:3526
1097
+ msgid "PNG is generally better than GIF, but animated images cannot be converted."
1098
+ msgstr ""
1099
+
1100
+ #: common.php:3529
1101
+ msgid "Save Changes"
1102
+ msgstr "Simpan Perubahan"
1103
+
1104
+ #: common.php:3533
1105
+ msgid "There are many ways to serve WebP images to visitors with supported browsers. You may choose any you wish, but it is recommended to serve them with an .htaccess file using mod_rewrite and mod_headers. The plugin can insert the rules for you if the file is writable, or you can edit .htaccess yourself."
1106
+ msgstr ""
1107
+
1108
+ #: common.php:3536
1109
+ msgid "Rules verified successfully"
1110
+ msgstr ""
1111
+
1112
+ #: common.php:3552
1113
+ msgid "The image to the right will display a WebP image with WEBP in white text, if your site is serving WebP images and your browser supports WebP."
1114
+ msgstr ""
1115
+
1116
+ #: common.php:3553
1117
+ msgid "Insert Rewrite Rules"
1118
+ msgstr ""
1119
+
1120
+ #: ewww-image-optimizer.php:146
1121
+ msgid "EWWW Image Optimizer is supported on Linux, FreeBSD, Mac OSX, and Windows"
1122
+ msgstr ""
1123
+
1124
+ #: ewww-image-optimizer.php:146
1125
+ msgid "Unfortunately, the EWWW Image Optimizer plugin does not work with %s"
1126
+ msgstr ""
1127
+
1128
+ #: ewww-image-optimizer.php:248
1129
+ msgid "EWWW Image Optimizer could not create the tool folder"
1130
+ msgstr ""
1131
+
1132
+ #: ewww-image-optimizer.php:248
1133
+ msgid "Please adjust permissions or create the folder"
1134
+ msgstr "Mohon sesuaikan izin atau buat folder baru"
1135
+
1136
+ #: ewww-image-optimizer.php:410
1137
+ msgid "EWWW Image Optimizer could not install tools in %s"
1138
+ msgstr ""
1139
+
1140
+ #: ewww-image-optimizer.php:410
1141
+ msgid "Please adjust permissions or create the folder. If you have installed the tools elsewhere on your system, check the option to %s."
1142
+ msgstr ""
1143
+
1144
+ #: ewww-image-optimizer.php:410
1145
+ msgid "For more details, visit the %1$s or the %2$s."
1146
+ msgstr "Untuk lebih detail, kunjungi %1$s atau %2$s."
1147
+
1148
+ #: ewww-image-optimizer.php:410 ewww-image-optimizer.php:536
1149
+ msgid "Settings Page"
1150
+ msgstr "Laman Pengaturan"
1151
+
1152
+ #: ewww-image-optimizer.php:422
1153
+ msgid "EWWW Image Optimizer requires exec(). Your system administrator has disabled this function."
1154
+ msgstr "EWWW Image Optimizer membutuhkan exec(). Admin sistem Anda mematikan fungsi ini."
1155
+
1156
+ #: ewww-image-optimizer.php:430
1157
+ msgid "Safe Mode is turned on for PHP. This plugin cannot operate in Safe Mode."
1158
+ msgstr ""
1159
+
1160
+ #: ewww-image-optimizer.php:536
1161
+ msgid "EWWW Image Optimizer uses %1$s, %2$s, %3$s, %4$s, %5$s, and %6$s. You are missing: %7$s. Please install via the %8$s or the %9$s."
1162
+ msgstr ""
1163
+
1164
+ #: ewww-image-optimizer.php:1170 ewww-image-optimizer.php:1295
1165
+ #: ewww-image-optimizer.php:1302
1166
+ msgid "Optimization skipped"
1167
+ msgstr ""
1168
+
1169
+ #: ewww-image-optimizer.php:1210
1170
+ msgid "Missing finfo_file(), getimagesize() and mime_content_type() PHP functions"
1171
+ msgstr ""
1172
+
1173
+ #: ewww-image-optimizer.php:1361 ewww-image-optimizer.php:1918
1174
+ msgid "%s is disabled"
1175
+ msgstr ""
1176
+
1177
+ #: ewww-image-optimizer.php:1404
1178
+ msgid "Unable to write file"
1179
+ msgstr ""
1180
+
1181
+ #: ewww-image-optimizer.php:1407
1182
+ msgid "Optimization failed"
1183
+ msgstr "Pengoptimalan gagal"
1184
+
1185
+ #: ewww-image-optimizer.php:1632
1186
+ msgid "png tools are disabled"
1187
+ msgstr ""
1188
+
1189
+ #: ewww-image-optimizer.php:2077
1190
+ msgid "You don't have permission to install image optimizer utilities."
1191
+ msgstr ""
1192
+
1193
+ #: ewww-image-optimizer.php:2083
1194
+ msgid "tar command not found"
1195
+ msgstr ""
1196
+
1197
+ #: ewww-image-optimizer.php:2102 ewww-image-optimizer.php:2121
1198
+ #: ewww-image-optimizer.php:2139
1199
+ msgid "could not move pngout"
1200
+ msgstr "tidak bisa memindahkan pngout"
1201
+
1202
+ #: ewww-image-optimizer.php:2105 ewww-image-optimizer.php:2124
1203
+ msgid "could not set permissions"
1204
+ msgstr "tidak bisa mengatur izin"
1205
+
1206
+ #: flag-integration.php:96 iocli.php:75 iocli.php:83 iocli.php:125
1207
+ #: iocli.php:147 nextgen2-integration.php:295
1208
+ msgid "%1$d images have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized)."
1209
+ msgstr ""
1210
+
1211
+ #: flag-integration.php:335 iocli.php:437
1212
+ msgid "Optimized size – %s"
1213
+ msgstr ""
1214
+
1215
+ #: flag-integration.php:342 iocli.php:444 nextcellent-integration.php:120
1216
+ #: nextgen-integration.php:97
1217
+ msgid "Thumbnail – %s"
1218
+ msgstr ""
1219
+
1220
+ #: flag-integration.php:368 iocli.php:251 iocli.php:400 iocli.php:462
1221
+ #: iocli.php:541 iocli.php:605 nextcellent-integration.php:493
1222
+ #: nextgen-integration.php:433 nextgen2-integration.php:504
1223
+ msgid "Finished Optimization!"
1224
+ msgstr ""
1225
+
1226
+ #: mwebp.php:6
1227
+ msgid "The migration is split into two parts. First, the plugin needs to scan all folders for webp images. Once it has obtained the list of images to rename, it will proceed with the renaming"
1228
+ msgstr ""
1229
+
1230
+ #: mwebp.php:7
1231
+ msgid "Start Migration"
1232
+ msgstr ""
1233
+
1234
+ #: mwebp.php:93
1235
+ msgid "Scanning"
1236
+ msgstr ""
1237
+
1238
+ #: mwebp.php:114
1239
+ msgid "%d Webp images left to rename."
1240
+ msgstr ""
1241
+
1242
+ #: mwebp.php:200
1243
+ msgid "Skipped:"
1244
+ msgstr "Lewati:"
1245
+
1246
+ #: nextcellent-integration.php:38 nextcellent-integration.php:97
1247
+ #: nextgen-integration.php:34 nextgen-integration.php:74
1248
+ #: nextgen2-integration.php:38
1249
+ msgid "Bulk Thumbnail Optimize"
1250
+ msgstr ""
1251
+
1252
+ #: nextcellent-integration.php:86 nextgen-integration.php:60
1253
+ msgid "The thumbnails for your new images have not been optimized."
1254
+ msgstr ""
1255
+
1256
+ #: nextcellent-integration.php:86 nextgen-integration.php:64
1257
+ msgid "Optimize Thumbs"
1258
+ msgstr ""
1259
+
1260
+ #: nextcellent-integration.php:110 nextgen-integration.php:87
1261
+ msgid "Processing"
1262
+ msgstr "Memproses"
1263
+
1264
+ #: common.php:2928 iocli.php:78 iocli.php:131 nextcellent-integration.php:287
1265
+ #: nextgen-integration.php:249
1266
+ msgid "We have %d images to optimize."
1267
+ msgstr "Kami punya %d gambar untuk dioptimalkan."
1268
+
1269
+ #: nextcellent-integration.php:318
1270
+ msgid "Galleries"
1271
+ msgstr ""
1272
+
1273
+ #: iocli.php:516 iocli.php:583 nextcellent-integration.php:467
1274
+ #: nextgen-integration.php:404 nextgen2-integration.php:472
1275
+ msgid "Full size - %s"
1276
+ msgstr "Ukuran penuh - %s"
1277
+
1278
+ #: iocli.php:519 iocli.php:589 nextcellent-integration.php:469
1279
+ #: nextgen-integration.php:410 nextgen2-integration.php:475
1280
+ msgid "Thumbnail - %s"
1281
+ msgstr ""
languages/ewww-image-optimizer-vi.mo ADDED
Binary file
languages/ewww-image-optimizer-vi.po ADDED
@@ -0,0 +1,1278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Translation of EWWW Image Optimizer in Vietnamese
2
+ # This file is distributed under the same license as the EWWW Image Optimizer package.
3
+ msgid ""
4
+ msgstr ""
5
+ "PO-Revision-Date: 2015-08-17 03:38:47+0000\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=1; plural=0;\n"
10
+ "X-Generator: GlotPress/1.0-alpha-1000\n"
11
+ "Project-Id-Version: EWWW Image Optimizer\n"
12
+
13
+ #: aux-optimize.php:97 aux-optimize.php:134 aux-optimize.php:383
14
+ #: aux-optimize.php:422 bulk.php:405 bulk.php:429 bulk.php:496 common.php:1922
15
+ #: flag-integration.php:285 flag-integration.php:306 flag-integration.php:362
16
+ #: mwebp.php:102 mwebp.php:193 nextcellent-integration.php:419
17
+ #: nextcellent-integration.php:439 nextcellent-integration.php:487
18
+ #: nextgen-integration.php:365 nextgen-integration.php:382
19
+ #: nextgen-integration.php:427 nextgen2-integration.php:422
20
+ #: nextgen2-integration.php:445 nextgen2-integration.php:498
21
+ msgid "Access token has expired, please reload the page."
22
+ msgstr ""
23
+
24
+ #: aux-optimize.php:684 aux-optimize.php:707 aux-optimize.php:723 bulk.php:388
25
+ #: common.php:3055 flag-integration.php:271 mwebp.php:84
26
+ #: nextcellent-integration.php:94 nextcellent-integration.php:405
27
+ #: nextgen-integration.php:71 nextgen-integration.php:352
28
+ #: nextgen2-integration.php:408
29
+ msgid "Access denied."
30
+ msgstr ""
31
+
32
+ #: ewww-image-optimizer.php:152
33
+ msgid "The regular version of the EWWW Image Optimizer plugin is not permitted on WP Engine sites. However, the cloud version has been approved by WP Engine. Please deactivate EWWW Image Optimizer and install EWWW Image Optimizer Cloud to optimize your images."
34
+ msgstr ""
35
+
36
+ #: common.php:3575
37
+ msgctxt "abbreviation for Virtual Private Server"
38
+ msgid "VPS:"
39
+ msgstr ""
40
+
41
+ #: common.php:3581
42
+ msgctxt "abbreviation for Content Delivery Network"
43
+ msgid "CDN:"
44
+ msgstr ""
45
+
46
+ #: common.php:3563
47
+ msgid "Help translate EWWW I.O."
48
+ msgstr ""
49
+
50
+ #: common.php:3564
51
+ msgid "Write a review."
52
+ msgstr ""
53
+
54
+ #: common.php:3569
55
+ msgctxt "A2 Hosting:"
56
+ msgid "with automatic EWWW IO setup"
57
+ msgstr ""
58
+
59
+ #: common.php:3561
60
+ msgid "Support EWWW I.O."
61
+ msgstr ""
62
+
63
+ #: common.php:3562
64
+ msgid "Would you like to help support development of this plugin?"
65
+ msgstr ""
66
+
67
+ #: common.php:3565
68
+ msgid "Contribute directly via %s."
69
+ msgstr ""
70
+
71
+ #: common.php:3567
72
+ msgid "Use any of these referral links to show your appreciation:"
73
+ msgstr ""
74
+
75
+ #: common.php:3568
76
+ msgid "Web Hosting:"
77
+ msgstr ""
78
+
79
+ #: common.php:3581
80
+ msgid "Add MaxCDN to increase website speeds dramatically! Sign Up Now and Save 25%."
81
+ msgstr ""
82
+
83
+ #: common.php:3581
84
+ msgid "Integrate MaxCDN within Wordpress using the W3 Total Cache plugin."
85
+ msgstr ""
86
+
87
+ #: common.php:3603
88
+ msgid "Debugging Information"
89
+ msgstr "Thông tin gỡ lỗi"
90
+
91
+ #: common.php:3603
92
+ msgid "Select All"
93
+ msgstr "Chọn tất cả"
94
+
95
+ #: ewww-image-optimizer.php:2109 ewww-image-optimizer.php:2128
96
+ msgid "extraction of files failed"
97
+ msgstr "Trích xuất các tập tin bị lỗi"
98
+
99
+ #: common.php:2717
100
+ msgid "Azure Storage image"
101
+ msgstr "Ảnh Azure Storage"
102
+
103
+ #: common.php:2721
104
+ msgid "Amazon S3 image"
105
+ msgstr "Ảnh Amazon S3"
106
+
107
+ #: common.php:3462
108
+ msgid "Deferred Optimization"
109
+ msgstr "Tối ưu bị trì hoãn"
110
+
111
+ #: common.php:3462
112
+ msgid "Optimize images later via wp_cron, after image upload or generation is complete."
113
+ msgstr "Tối ưu các ảnh sau qua wp_cron, sau khi tải lên hoặc khởi tạo ảnh hoàn thành."
114
+
115
+ #: ewww-image-optimizer.php:1181
116
+ msgid "Could not find %s"
117
+ msgstr "Không thể tìm thấy %s"
118
+
119
+ #: ewww-image-optimizer.php:1189
120
+ msgid "%s is not writable"
121
+ msgstr "%s không thể ghi được"
122
+
123
+ #: bulk.php:356
124
+ msgid "Operation timed out, you may need to increase the max_execution_time for PHP"
125
+ msgstr "Hoạt động đã hết giờ, bạn có thể cần tăng max_execution_time cho PHP"
126
+
127
+ #: bulk.php:357 flag-integration.php:186 nextcellent-integration.php:392
128
+ #: nextgen-integration.php:340 nextgen2-integration.php:395
129
+ msgid "License Exceeded"
130
+ msgstr "Giấy phép đã vượt quá"
131
+
132
+ #: bulk.php:358 flag-integration.php:187 nextcellent-integration.php:393
133
+ #: nextgen-integration.php:341 nextgen2-integration.php:396
134
+ msgid "Optimization stopped, reload page to resume."
135
+ msgstr "Tối ưu đã ngừng, nạp lại trang để phục hồi."
136
+
137
+ #: bulk.php:359 flag-integration.php:188 nextcellent-integration.php:394
138
+ #: nextgen-integration.php:342 nextgen2-integration.php:397
139
+ msgid "Operation Interrupted"
140
+ msgstr "Hoạt động đã bị ngắt"
141
+
142
+ #: bulk.php:360 flag-integration.php:189 nextcellent-integration.php:395
143
+ #: nextgen-integration.php:343 nextgen2-integration.php:398
144
+ msgid "Temporary failure, seconds left to retry:"
145
+ msgstr "Sự cố tạm thời, số giây còn lại để thử lại:"
146
+
147
+ #: bulk.php:361 flag-integration.php:190 nextcellent-integration.php:396
148
+ #: nextgen-integration.php:344 nextgen2-integration.php:399
149
+ msgid "Could not remove image from table."
150
+ msgstr "Không thể loại bỏ ảnh từ bảng."
151
+
152
+ #: common.php:3170
153
+ msgid "Click to toggle"
154
+ msgstr "Kích để bật tắt"
155
+
156
+ #: common.php:3467
157
+ msgid "Disable Resizes"
158
+ msgstr "Vô hiệu thay đổi kích cỡ"
159
+
160
+ #: common.php:3467
161
+ msgid "Wordpress, your theme, and other plugins generate various image sizes. You may disable optimization for certain sizes, or completely prevent those sizes from being created."
162
+ msgstr "Wordpress, chủ đề của bạn, và các plugin khác tạo kích cỡ hình ảnh khác nhau. Bạn có thể vô hiệu tối ưu hóa cho các kích thước nhất định, hoặc hoàn toàn ngăn chặn việc tạo ra các kích cỡ đó."
163
+
164
+ #: common.php:3471
165
+ msgid "Disable Optimization"
166
+ msgstr "Vô hiệu tối ưu"
167
+
168
+ #: common.php:3471
169
+ msgid "Disable Creation"
170
+ msgstr "Vô hiệu tạo"
171
+
172
+ #: common.php:1353
173
+ msgid "You don't have permission to optimize images."
174
+ msgstr "Bạn không có quyền để tối ưu ảnh."
175
+
176
+ #: common.php:3400
177
+ msgid "Faster lossy optimization"
178
+ msgstr "Tối ưu hao tổn nhanh hơn"
179
+
180
+ #: common.php:3400
181
+ msgid "Speed up the lossy operations by performing less compression."
182
+ msgstr "Tăng tốc độ xử lý hao tổn bằng cách thực hiện nén ít hơn."
183
+
184
+ #: common.php:3410
185
+ msgid "Requires an EWWW Image Optimizer Cloud Subscription."
186
+ msgstr "Yêu cầu một Đăng ký EWWW Image Optimizer Cloud."
187
+
188
+ #: common.php:3413
189
+ msgid "Uses pngquant locally. Use EWWW I.O. Cloud for even better lossy compression."
190
+ msgstr ""
191
+
192
+ #: common.php:3512
193
+ msgid "Uses output buffering and libxml functionality from PHP. Use this if the Apache rewrite rules do not work, or if your images are served from a CDN."
194
+ msgstr "Sử dụng bộ đệm đầu ra và chức năng libxml từ PHP. Sử dụng điều này nếu các quy tắc viết lại Apache không làm việc, hoặc nếu hình ảnh của bạn được xử lý từ một CDN."
195
+
196
+ #: common.php:1653
197
+ msgid "optimized %1$d images, usage will reset in %2$d day."
198
+ msgid_plural "optimized %1$d images, usage will reset in %2$d days."
199
+ msgstr[0] "%1$d ảnh đã được tối ưu, việc sử dụng sẽ thiết lập lại trong %2$d ngày."
200
+
201
+ #: common.php:1655
202
+ msgid "%1$d image credit remaining."
203
+ msgid_plural "%1$d image credits remaining."
204
+ msgstr[0] ""
205
+
206
+ #: common.php:3465
207
+ msgid "Include Media Library Folders"
208
+ msgstr ""
209
+
210
+ #: common.php:3465
211
+ msgid "If you have disabled automatic optimization, enable this if you want Scheduled Optimization to include the latest two folders from the Media Library."
212
+ msgstr ""
213
+
214
+ #: common.php:3512
215
+ msgid "Alternative WebP Rewriting"
216
+ msgstr ""
217
+
218
+ #: bulk.php:355
219
+ msgid "%d images"
220
+ msgstr "%d hình ảnh"
221
+
222
+ #: common.php:3454
223
+ msgid "Provide paths containing images to be optimized using \"Scan and Optimize\" on the Bulk Optimize page or by Scheduled Optimization."
224
+ msgstr ""
225
+
226
+ #: iocli.php:55
227
+ msgctxt "string will be something like \"media\" or \"nextgen\""
228
+ msgid "Optimizing %1$s with a %2$d second pause between images."
229
+ msgstr ""
230
+
231
+ #: iocli.php:52
232
+ msgid "Forcing re-optimization of previously processed images."
233
+ msgstr ""
234
+
235
+ #: iocli.php:66 iocli.php:107 iocli.php:118 iocli.php:142 iocli.php:157
236
+ msgid "Bulk status has been reset, starting from the beginning."
237
+ msgstr ""
238
+
239
+ #: iocli.php:172
240
+ msgid "Bulk status has been reset, the next bulk operation will start from the beginning."
241
+ msgstr ""
242
+
243
+ #: iocli.php:174
244
+ msgid "Please specify a valid library option, see \"wp-cli help ewwwio optimize\" for more information."
245
+ msgstr ""
246
+
247
+ #: common.php:1003 common.php:2927
248
+ msgid "Unoptimized Images"
249
+ msgstr "Các ảnh chưa được tối ưu"
250
+
251
+ #: common.php:2934
252
+ msgid "Optimize All Images"
253
+ msgstr "Tối ưu tất cả ảnh"
254
+
255
+ #: common.php:2953
256
+ msgid "There are too many images to display."
257
+ msgstr "Có quá nhiều ảnh để hiển thị."
258
+
259
+ #: iocli.php:87 iocli.php:162
260
+ msgid "%1$d images in other folders need optimizing."
261
+ msgstr "%1$d ảnh trong các thư mục khác cần tối ưu."
262
+
263
+ #: iocli.php:136
264
+ msgid "NextGEN/Nextcellent not installed."
265
+ msgstr "NextGEN/Nextcellent chưa được cài đặt."
266
+
267
+ #: iocli.php:151
268
+ msgid "Grand Flagallery not installed."
269
+ msgstr "Grand Flagallery chưa được cài đặt."
270
+
271
+ #: common.php:3463
272
+ msgid "Disable Automatic Optimization"
273
+ msgstr "Vô hiệu Tối Ưu Tự Động"
274
+
275
+ #: common.php:3463
276
+ msgid "Images will not be optimized on upload. Images may be optimized with the Bulk Optimize tools or with Scheduled optimization."
277
+ msgstr "Các hình ảnh sẽ không được tối ưu lúc tải lên. Các hình ảnh có thể được tối ưu với các công cụ Bulk Optimize hoặc với Tối ưu đã lên lịch trình."
278
+
279
+ #: common.php:3489
280
+ msgid "Exclude full-size images from metadata removal"
281
+ msgstr ""
282
+
283
+ #: aux-optimize.php:9
284
+ msgid "Scan and optimize"
285
+ msgstr "Quét và tối ưu"
286
+
287
+ #: aux-optimize.php:11
288
+ msgid "Resume previous optimization"
289
+ msgstr "Phục hồi trước tối ưu"
290
+
291
+ #: aux-optimize.php:29
292
+ msgid "Optimize Everything Else"
293
+ msgstr "Tối ưu mọi thứ khác"
294
+
295
+ #: aux-optimize.php:30
296
+ msgid "Use this tool to optimize images outside of the Media Library and galleries where we have full integration. Examples: theme images, BuddyPress, WP Symposium, and any folders that you have specified on the settings page."
297
+ msgstr ""
298
+
299
+ #: aux-optimize.php:32
300
+ msgid "The database schema has changed, you need to convert to the new format."
301
+ msgstr ""
302
+
303
+ #: aux-optimize.php:36
304
+ msgid "Convert Table"
305
+ msgstr "Chuyển đổi Bảng"
306
+
307
+ #: aux-optimize.php:39
308
+ msgid "There are no images to optimize."
309
+ msgstr "Không có ảnh nào để tối ưu."
310
+
311
+ #: aux-optimize.php:40 iocli.php:68 iocli.php:159
312
+ msgid "Scanning, this could take a while"
313
+ msgstr "Đang quét, việc này có thể mất một lúc"
314
+
315
+ #: aux-optimize.php:42
316
+ msgid "Last optimization was completed on %1$s at %2$s and optimized %3$d images"
317
+ msgstr "Lần tối ưu cuối cùng được hoàn thành lúc %1$s tại %2$s và đã tối ưu %3$d ảnh"
318
+
319
+ #: aux-optimize.php:46 bulk.php:57
320
+ msgid "Optimize Again"
321
+ msgstr "Tối ưu lần nữa"
322
+
323
+ #: aux-optimize.php:51 bulk.php:63 flag-integration.php:105
324
+ #: nextcellent-integration.php:296 nextgen-integration.php:258
325
+ #: nextgen2-integration.php:303
326
+ msgid "If you would like to start over again, press the Reset Status button to reset the bulk operation status."
327
+ msgstr "Nếu bạn muốn bắt đầu lại một lần nữa, nhấn nút Đặt lại Trạng thái để thiết lập lại trạng thái hoạt động với số lượng lớn."
328
+
329
+ #: aux-optimize.php:55 bulk.php:67 flag-integration.php:109
330
+ #: nextcellent-integration.php:300 nextgen-integration.php:262
331
+ #: nextgen2-integration.php:307
332
+ msgid "Reset Status"
333
+ msgstr "Đặt lại Trạng thái"
334
+
335
+ #: aux-optimize.php:64
336
+ msgid "The plugin keeps track of already optimized images to prevent re-optimization. There are %d images that have been optimized so far."
337
+ msgstr "Plugin theo dõi những hình ảnh đã được tối ưu để ngăn ngừa tái tối ưu. Có %d hình ảnh đã được tối ưu cho đến nay."
338
+
339
+ #: aux-optimize.php:66
340
+ msgid "Show Optimized Images"
341
+ msgstr "Hiện các ảnh đã được tối ưu"
342
+
343
+ #: aux-optimize.php:74
344
+ msgid "page"
345
+ msgstr "trang"
346
+
347
+ #: aux-optimize.php:74
348
+ msgid "of"
349
+ msgstr "của"
350
+
351
+ #: aux-optimize.php:254 aux-optimize.php:296 aux-optimize.php:299
352
+ msgid "Unknown Savings"
353
+ msgstr ""
354
+
355
+ #: aux-optimize.php:319
356
+ msgid "Finished importing"
357
+ msgstr "Nhập hoàn thành"
358
+
359
+ #: aux-optimize.php:336 common.php:1830 common.php:2378 common.php:2387
360
+ msgid "Previously Optimized"
361
+ msgstr "Đã tối ưu trước đó"
362
+
363
+ #: aux-optimize.php:339 common.php:1869 common.php:2314
364
+ msgid "No savings"
365
+ msgstr ""
366
+
367
+ #: aux-optimize.php:346 bulk.php:122 common.php:1835
368
+ #: ewww-image-optimizer.php:2029
369
+ msgid "License exceeded"
370
+ msgstr "Giấy phép đã quá hạn"
371
+
372
+ #: aux-optimize.php:391
373
+ msgid "Filename"
374
+ msgstr "Tên tập tin"
375
+
376
+ #: aux-optimize.php:391
377
+ msgid "Image Type"
378
+ msgstr "Loại Ảnh"
379
+
380
+ #: aux-optimize.php:391 common.php:1088 common.php:1120 common.php:2691
381
+ #: common.php:2939 flag-integration.php:374 nextcellent-integration.php:173
382
+ #: nextgen-integration.php:142 nextgen2-integration.php:131
383
+ #: nextgen2-integration.php:134
384
+ msgid "Image Optimizer"
385
+ msgstr "Trình tối ưu ảnh"
386
+
387
+ #: aux-optimize.php:408 common.php:2809 common.php:2863
388
+ #: flag-integration.php:431 flag-integration.php:440
389
+ #: nextcellent-integration.php:229 nextcellent-integration.php:238
390
+ #: nextgen-integration.php:198 nextgen-integration.php:205
391
+ #: nextgen2-integration.php:215
392
+ msgid "Image Size: %s"
393
+ msgstr "Cỡ ảnh: %s"
394
+
395
+ #: aux-optimize.php:408
396
+ msgid "Remove from table"
397
+ msgstr "Loại bỏ khỏi bảng"
398
+
399
+ #: aux-optimize.php:666
400
+ msgid "Nothing to optimize"
401
+ msgstr "Không có gì để tối ưu"
402
+
403
+ #: aux-optimize.php:695 aux-optimize.php:712 bulk.php:395 bulk.php:414
404
+ #: flag-integration.php:277 flag-integration.php:296 iocli.php:210
405
+ #: nextcellent-integration.php:411 nextcellent-integration.php:429
406
+ #: nextgen-integration.php:358 nextgen-integration.php:375
407
+ #: nextgen2-integration.php:435
408
+ msgid "Optimizing"
409
+ msgstr "Đang tối ưu"
410
+
411
+ #: aux-optimize.php:732 bulk.php:502 mwebp.php:204
412
+ #: nextcellent-integration.php:129 nextgen-integration.php:106
413
+ msgid "Finished"
414
+ msgstr "Đã xong"
415
+
416
+ #: bulk.php:11 common.php:1002 common.php:2892 common.php:2894 common.php:3159
417
+ #: common.php:3161 flag-integration.php:36 flag-integration.php:41
418
+ #: flag-integration.php:46 flag-integration.php:73
419
+ #: nextcellent-integration.php:37 nextcellent-integration.php:269
420
+ #: nextgen-integration.php:33 nextgen-integration.php:234
421
+ #: nextgen2-integration.php:37 nextgen2-integration.php:272
422
+ #: nextgen2-integration.php:516
423
+ msgid "Bulk Optimize"
424
+ msgstr "Bulk Optimize"
425
+
426
+ #: bulk.php:15 flag-integration.php:78 nextcellent-integration.php:274
427
+ #: nextgen-integration.php:239 nextgen2-integration.php:277
428
+ msgid "Start optimizing"
429
+ msgstr "Bắt đầu tối ưu"
430
+
431
+ #: bulk.php:17 flag-integration.php:80 nextcellent-integration.php:276
432
+ #: nextgen-integration.php:241 nextgen2-integration.php:279
433
+ msgid "Resume previous bulk operation"
434
+ msgstr ""
435
+
436
+ #: bulk.php:23
437
+ msgid "Importing"
438
+ msgstr ""
439
+
440
+ #: bulk.php:28 flag-integration.php:87 nextcellent-integration.php:283
441
+ #: nextgen2-integration.php:286
442
+ msgid "Stop Optimizing"
443
+ msgstr ""
444
+
445
+ #: bulk.php:32
446
+ msgid "You should import Media Library images into the table to prevent duplicate optimization."
447
+ msgstr ""
448
+
449
+ #: bulk.php:34
450
+ msgid "Import Images"
451
+ msgstr ""
452
+
453
+ #: bulk.php:40 flag-integration.php:91 nextgen2-integration.php:290
454
+ msgid "Force re-optimize"
455
+ msgstr ""
456
+
457
+ #: bulk.php:41 common.php:3415 flag-integration.php:92
458
+ #: nextgen2-integration.php:291
459
+ msgid "Choose how long to pause between images (in seconds, 0 = disabled)"
460
+ msgstr ""
461
+
462
+ #: bulk.php:44
463
+ msgid "Optimize Media Library"
464
+ msgstr ""
465
+
466
+ #: bulk.php:46 flag-integration.php:68 nextcellent-integration.php:264
467
+ #: nextgen-integration.php:229 nextgen2-integration.php:266
468
+ msgid "You do not appear to have uploaded any images yet."
469
+ msgstr ""
470
+
471
+ #: bulk.php:50
472
+ msgid "%1$d images in the Media Library have been selected, unable to determine how many resizes and how many are unoptimized."
473
+ msgstr ""
474
+
475
+ #: bulk.php:52 iocli.php:70 iocli.php:111
476
+ msgid "%1$d images in the Media Library have been selected (%2$d unoptimized), with %3$d resizes (%4$d unoptimized)."
477
+ msgstr "%1$d hình ảnh trong Thư viện Media đã được chọn (%2$d chưa được tối ưu), với %3$d thay đổi kích thước (%4$d chưa được tối ưu)."
478
+
479
+ #: bulk.php:54 flag-integration.php:97 nextcellent-integration.php:288
480
+ #: nextgen-integration.php:250 nextgen2-integration.php:296
481
+ msgid "Previously optimized images will be skipped by default."
482
+ msgstr "Các hình ảnh đã tối ưu trước đó sẽ được bỏ qua theo mặc định."
483
+
484
+ #: bulk.php:449 common.php:1956 flag-integration.php:328 iocli.php:386
485
+ #: iocli.php:430 iocli.php:510 iocli.php:582 nextcellent-integration.php:466
486
+ #: nextgen-integration.php:403 nextgen2-integration.php:466
487
+ msgid "Optimized image:"
488
+ msgstr "Ảnh đã tối ưu:"
489
+
490
+ #: bulk.php:451 iocli.php:218
491
+ msgid "Skipped image, ID:"
492
+ msgstr "Ảnh đã bỏ qua, ID:"
493
+
494
+ #: bulk.php:455 flag-integration.php:329 iocli.php:222 iocli.php:431
495
+ msgid "Full size – %s"
496
+ msgstr "Cỡ đầy đủ - %s"
497
+
498
+ #: bulk.php:470 common.php:1962 flag-integration.php:347 iocli.php:237
499
+ #: iocli.php:392 iocli.php:449 iocli.php:527 iocli.php:592
500
+ #: nextcellent-integration.php:123 nextcellent-integration.php:472
501
+ #: nextgen-integration.php:100 nextgen-integration.php:413
502
+ #: nextgen2-integration.php:483
503
+ msgid "Elapsed: %.3f seconds"
504
+ msgstr "Đã qua: %.3f giây"
505
+
506
+ #: bulk.php:502
507
+ msgid "Return to Media Library"
508
+ msgstr "Trở lại Thư viện Media"
509
+
510
+ #: common.php:711
511
+ msgid "Settings saved"
512
+ msgstr "Các thiết đặt đã lưu"
513
+
514
+ #: common.php:1004 mwebp.php:5
515
+ msgid "Migrate WebP Images"
516
+ msgstr "Di chuyển các ảnh WebP"
517
+
518
+ #: common.php:1023
519
+ msgid "Image Store Optimize"
520
+ msgstr "Tối ưu hình ảnh lưu trữ"
521
+
522
+ #: common.php:1023
523
+ msgid "Optimize"
524
+ msgstr "Tối ưu"
525
+
526
+ #: common.php:1077
527
+ msgid "Image Store Optimization"
528
+ msgstr "Tối ưu hình ảnh lưu trữ"
529
+
530
+ #: common.php:1087
531
+ msgid "Choose a gallery or"
532
+ msgstr ""
533
+
534
+ #: common.php:1087
535
+ msgid "optimize all galleries"
536
+ msgstr ""
537
+
538
+ #: common.php:1088
539
+ msgid "Gallery ID"
540
+ msgstr ""
541
+
542
+ #: common.php:1088
543
+ msgid "Gallery Name"
544
+ msgstr ""
545
+
546
+ #: common.php:1088
547
+ msgid "Images"
548
+ msgstr ""
549
+
550
+ #: common.php:1104 common.php:1119
551
+ msgid "Optimize Gallery"
552
+ msgstr ""
553
+
554
+ #: common.php:1120 common.php:2939
555
+ msgid "Title"
556
+ msgstr ""
557
+
558
+ #: common.php:1120 nextgen-integration.php:14 nextgen-integration.php:274
559
+ msgid "Gallery"
560
+ msgstr ""
561
+
562
+ #: common.php:1203
563
+ msgid "Settings"
564
+ msgstr ""
565
+
566
+ #: flag-integration.php:231 nextcellent-integration.php:137
567
+ #: nextgen-integration.php:113 nextgen2-integration.php:96
568
+ msgid "You don't have permission to work with uploaded files."
569
+ msgstr ""
570
+
571
+ #: common.php:1358 flag-integration.php:235 nextcellent-integration.php:141
572
+ #: nextgen-integration.php:117 nextgen2-integration.php:100
573
+ msgid "No attachment ID was provided."
574
+ msgstr ""
575
+
576
+ #: common.php:1401 common.php:1436
577
+ msgid "Original Restored"
578
+ msgstr ""
579
+
580
+ #: common.php:1657
581
+ msgid "used %1$d of %2$d, usage will reset in %3$d day."
582
+ msgid_plural "used %1$d of %2$d, usage will reset in %3$d days."
583
+ msgstr[0] "đã sử dụng %1$d của %2$d, việc sử dụng sẽ thiết đặt lại trong %3$d ngày."
584
+
585
+ #: common.php:1881
586
+ msgid "Reduced by %01.1f%% (%s)"
587
+ msgstr ""
588
+
589
+ #: common.php:2713
590
+ msgid "Cloudinary image"
591
+ msgstr ""
592
+
593
+ #: common.php:2737
594
+ msgid "Could not retrieve file path."
595
+ msgstr ""
596
+
597
+ #: common.php:2758 common.php:2769 common.php:2780
598
+ #: ewww-image-optimizer.php:1365 ewww-image-optimizer.php:1636
599
+ #: ewww-image-optimizer.php:1640 ewww-image-optimizer.php:1922
600
+ #: flag-integration.php:405 flag-integration.php:411 flag-integration.php:417
601
+ #: nextcellent-integration.php:201 nextcellent-integration.php:208
602
+ #: nextcellent-integration.php:215 nextgen-integration.php:170
603
+ #: nextgen-integration.php:177 nextgen-integration.php:184
604
+ #: nextgen2-integration.php:174 nextgen2-integration.php:181
605
+ #: nextgen2-integration.php:188
606
+ msgid "%s is missing"
607
+ msgstr ""
608
+
609
+ #: common.php:2760
610
+ msgid "JPG to PNG"
611
+ msgstr ""
612
+
613
+ #: common.php:2762
614
+ msgid "WARNING: Removes metadata. Requires GD or ImageMagick. PNG is generally much better than JPG for logos and other images with a limited range of colors."
615
+ msgstr ""
616
+
617
+ #: common.php:2771
618
+ msgid "PNG to JPG"
619
+ msgstr "PNG sang JPG"
620
+
621
+ #: common.php:2773
622
+ msgid "WARNING: This is not a lossless conversion and requires GD or ImageMagick. JPG is much better than PNG for photographic use because it compresses the image and discards data. Transparent images will only be converted if a background color has been set."
623
+ msgstr ""
624
+
625
+ #: common.php:2782
626
+ msgid "GIF to PNG"
627
+ msgstr "GIF sang PNG"
628
+
629
+ #: common.php:2784
630
+ msgid "PNG is generally better than GIF, but does not support animation. Animated images will not be converted."
631
+ msgstr ""
632
+
633
+ #: common.php:2789 flag-integration.php:425 nextcellent-integration.php:223
634
+ #: nextgen-integration.php:192 nextgen2-integration.php:196
635
+ msgid "Unsupported file type"
636
+ msgstr "Loại tập tin không được hỗ trợ"
637
+
638
+ #: common.php:2814 flag-integration.php:435 nextcellent-integration.php:233
639
+ #: nextgen-integration.php:201 nextgen2-integration.php:235
640
+ msgid "Re-optimize"
641
+ msgstr "Tối ưu lại"
642
+
643
+ #: common.php:2841
644
+ msgid "Restore original"
645
+ msgstr "Phục hồi nguyên gốc"
646
+
647
+ #: common.php:2861 flag-integration.php:439 nextcellent-integration.php:237
648
+ #: nextgen-integration.php:204 nextgen2-integration.php:209
649
+ msgid "Not processed"
650
+ msgstr "Không được xử lý"
651
+
652
+ #: common.php:2800 common.php:2866 flag-integration.php:444
653
+ #: nextcellent-integration.php:242 nextgen-integration.php:208
654
+ #: nextgen2-integration.php:239
655
+ msgid "Optimize now!"
656
+ msgstr ""
657
+
658
+ #: common.php:3059
659
+ msgid "Insertion successful"
660
+ msgstr ""
661
+
662
+ #: common.php:3061
663
+ msgid "Insertion failed"
664
+ msgstr ""
665
+
666
+ #: common.php:3127
667
+ msgid "Pngout was successfully installed, check the Plugin Status area for version information."
668
+ msgstr ""
669
+
670
+ #: common.php:3132
671
+ msgid "Pngout was not installed: %1$s. Make sure this folder is writable: %2$s"
672
+ msgstr ""
673
+
674
+ #: common.php:3154
675
+ msgid "Plugin Home Page"
676
+ msgstr ""
677
+
678
+ #: common.php:3155 ewww-image-optimizer.php:410 ewww-image-optimizer.php:536
679
+ msgid "Installation Instructions"
680
+ msgstr ""
681
+
682
+ #: common.php:3156
683
+ msgid "Plugin Support"
684
+ msgstr "Hỗ trợ Plugin"
685
+
686
+ #: common.php:3157
687
+ msgid "Cloud Status"
688
+ msgstr "Trạng thái Cloud"
689
+
690
+ #: common.php:3159
691
+ msgid "Media Library"
692
+ msgstr "Thư viện Media"
693
+
694
+ #: common.php:3163
695
+ msgid "New images uploaded to the Media Library will be optimized automatically. If you have existing images you would like to optimize, you can use the %s tool."
696
+ msgstr ""
697
+
698
+ #: common.php:3171
699
+ msgid "Plugin Status"
700
+ msgstr "Trạng thái Plugin"
701
+
702
+ #: common.php:3172
703
+ msgid "All Clear"
704
+ msgstr ""
705
+
706
+ #: common.php:3173
707
+ msgid "Requires Attention"
708
+ msgstr ""
709
+
710
+ #: common.php:3176
711
+ msgid "Total Savings:"
712
+ msgstr ""
713
+
714
+ #: common.php:3178 common.php:3391
715
+ msgid "Cloud optimization API Key"
716
+ msgstr ""
717
+
718
+ #: common.php:3181 common.php:3183
719
+ msgid "Verified,"
720
+ msgstr "Đã xác nhận"
721
+
722
+ #: common.php:3185
723
+ msgid "Not Verified"
724
+ msgstr "Không được xác nhận"
725
+
726
+ #: common.php:3197
727
+ msgid "If updated versions are available below you may either download the newer versions and install them yourself, or uncheck \"Use System Paths\" and use the bundled tools."
728
+ msgstr ""
729
+
730
+ #: common.php:3198 common.php:3201
731
+ msgid "Updates are optional, but may contain increased optimization or security patches"
732
+ msgstr ""
733
+
734
+ #: common.php:3200
735
+ msgid "If updated versions are available below, you may need to enable write permission on the %s folder to use the automatic installs."
736
+ msgstr ""
737
+
738
+ #: common.php:3211 common.php:3221 common.php:3231 common.php:3241
739
+ #: common.php:3251 common.php:3261 common.php:3285 common.php:3293
740
+ #: common.php:3301 common.php:3309 common.php:3323 common.php:3329
741
+ #: common.php:3337
742
+ msgid "Installed"
743
+ msgstr "Đã cài đặt"
744
+
745
+ #: common.php:3211 common.php:3221 common.php:3231 common.php:3241
746
+ #: common.php:3251 common.php:3261
747
+ msgid "version"
748
+ msgstr ""
749
+
750
+ #: common.php:3213 common.php:3223 common.php:3233 common.php:3243
751
+ #: common.php:3253 common.php:3263 common.php:3288 common.php:3296
752
+ #: common.php:3304 common.php:3312 common.php:3326 common.php:3334
753
+ #: common.php:3340
754
+ msgid "Missing"
755
+ msgstr ""
756
+
757
+ #: common.php:3243
758
+ msgid "Install"
759
+ msgstr ""
760
+
761
+ #: common.php:3243
762
+ msgid "automatically"
763
+ msgstr ""
764
+
765
+ #: common.php:3243
766
+ msgid "manually"
767
+ msgstr ""
768
+
769
+ #: common.php:3243
770
+ msgid "Pngout is free closed-source software that can produce drastically reduced filesizes for PNGs, but can be very time consuming to process images"
771
+ msgstr ""
772
+
773
+ #: common.php:3269
774
+ msgid "On"
775
+ msgstr ""
776
+
777
+ #: common.php:3272
778
+ msgid "Off"
779
+ msgstr "Tắt"
780
+
781
+ #: common.php:3275
782
+ msgid "Disabled"
783
+ msgstr "Đã vô hiệu"
784
+
785
+ #: common.php:3278
786
+ msgid "Enabled"
787
+ msgstr "Đã bật"
788
+
789
+ #: common.php:3281
790
+ msgid "%s only need one, used for conversion, not optimization"
791
+ msgstr "%s chỉ cần một, được dùng cho chuyển đổi, không tối ưu"
792
+
793
+ #: common.php:3281
794
+ msgid "Graphics libraries"
795
+ msgstr "Thư viện đồ họa"
796
+
797
+ #: common.php:3319
798
+ msgid "Only need one of these:"
799
+ msgstr "Chỉ cần một trong những cái này:"
800
+
801
+ #: common.php:3344 common.php:3348 common.php:3351
802
+ msgid "command not found on your system"
803
+ msgstr "lệnh không tìm thấy trên hệ thống của bạn"
804
+
805
+ #: common.php:3348
806
+ msgid "not required"
807
+ msgstr "không yêu cầu"
808
+
809
+ #: common.php:3351
810
+ msgid "required for automatic pngout installer"
811
+ msgstr "được yêu cầu cho trình cài đặt pngout tự động"
812
+
813
+ #: common.php:3375
814
+ msgid "Cloud Settings"
815
+ msgstr "Thiết đặt Cloud"
816
+
817
+ #: common.php:3376
818
+ msgid "Basic Settings"
819
+ msgstr "Thiết đặt Cơ bản"
820
+
821
+ #: common.php:3377
822
+ msgid "Advanced Settings"
823
+ msgstr "Thiết đặt Nâng cao"
824
+
825
+ #: common.php:3378
826
+ msgid "Conversion Settings"
827
+ msgstr "Thiết đặt Chuyển đổi"
828
+
829
+ #: common.php:3389
830
+ msgid "If exec() is disabled for security reasons (and enabling it is not an option), or you would like to offload image optimization to a third-party server, you may purchase an API key for our cloud optimization service. The API key should be entered below, and cloud optimization must be enabled for each image format individually."
831
+ msgstr ""
832
+
833
+ #: common.php:3389 common.php:3391 common.php:3410 common.php:3413
834
+ msgid "Purchase an API key."
835
+ msgstr "Mua một khóa API"
836
+
837
+ #: common.php:3391
838
+ msgid "API Key will be validated when you save your settings."
839
+ msgstr ""
840
+
841
+ #: common.php:3392
842
+ msgid "JPG cloud optimization"
843
+ msgstr ""
844
+
845
+ #: common.php:3394
846
+ msgid "PNG cloud optimization"
847
+ msgstr ""
848
+
849
+ #: common.php:3396
850
+ msgid "extra PNG compression (slower)"
851
+ msgstr ""
852
+
853
+ #: common.php:3398
854
+ msgid "GIF cloud optimization"
855
+ msgstr ""
856
+
857
+ #: common.php:3405
858
+ msgid "Debugging"
859
+ msgstr ""
860
+
861
+ #: common.php:3405
862
+ msgid "Use this to provide information for support purposes, or if you feel comfortable digging around in the code to fix a problem you are experiencing."
863
+ msgstr ""
864
+
865
+ #: common.php:3406
866
+ msgid "Remove metadata"
867
+ msgstr ""
868
+
869
+ #: common.php:3407
870
+ msgid "This will remove ALL metadata: EXIF and comments."
871
+ msgstr ""
872
+
873
+ #: common.php:3409
874
+ msgid "Lossy JPG optimization"
875
+ msgstr ""
876
+
877
+ #: common.php:3409 common.php:3412 common.php:3509 common.php:3515
878
+ #: common.php:3518
879
+ msgid "WARNING:"
880
+ msgstr ""
881
+
882
+ #: common.php:3409 common.php:3412
883
+ msgid "While most users will not notice a difference in image quality, lossy means there IS a loss in image quality."
884
+ msgstr ""
885
+
886
+ #: common.php:3412
887
+ msgid "Lossy PNG optimization"
888
+ msgstr ""
889
+
890
+ #: common.php:3415
891
+ msgid "Bulk Delay"
892
+ msgstr ""
893
+
894
+ #: common.php:3418
895
+ msgid "Automatic Cloudinary upload"
896
+ msgstr ""
897
+
898
+ #: common.php:3418
899
+ msgid "When enabled, uploads to the Media Library will be transferred to Cloudinary after optimization. Cloudinary generates resizes, so only the full-size image is uploaded."
900
+ msgstr ""
901
+
902
+ #: common.php:3428
903
+ msgid "optipng optimization level"
904
+ msgstr "Mức tối ưu optipng"
905
+
906
+ #: common.php:3430 common.php:3431 common.php:3432 common.php:3433
907
+ #: common.php:3434 common.php:3435 common.php:3436 common.php:3442
908
+ #: common.php:3443 common.php:3444 common.php:3445
909
+ msgid "Level %d"
910
+ msgstr "Mức %d"
911
+
912
+ #: common.php:3430
913
+ msgid "%d trial"
914
+ msgstr "Thử %d"
915
+
916
+ #: common.php:3431 common.php:3432 common.php:3433 common.php:3434
917
+ #: common.php:3435 common.php:3436
918
+ msgid "%d trials"
919
+ msgstr "Thử %d"
920
+
921
+ #: common.php:3437 common.php:3446
922
+ msgid "default"
923
+ msgstr "mặc định"
924
+
925
+ #: common.php:3438
926
+ msgid "According to the author of optipng, 10 trials should satisfy most people, 30 trials should satisfy everyone."
927
+ msgstr ""
928
+
929
+ #: common.php:3440
930
+ msgid "pngout optimization level"
931
+ msgstr ""
932
+
933
+ #: common.php:3442
934
+ msgid "Xtreme! (Slowest)"
935
+ msgstr ""
936
+
937
+ #: common.php:3443
938
+ msgid "Intense (Slow)"
939
+ msgstr ""
940
+
941
+ #: common.php:3444
942
+ msgid "Longest Match (Fast)"
943
+ msgstr ""
944
+
945
+ #: common.php:3445
946
+ msgid "Huffman Only (Faster)"
947
+ msgstr ""
948
+
949
+ #: common.php:3447
950
+ msgid "If you have CPU cycles to spare, go with level %d"
951
+ msgstr ""
952
+
953
+ #: common.php:3450
954
+ msgid "Scheduled optimization"
955
+ msgstr ""
956
+
957
+ #: common.php:3450
958
+ msgid "This will enable scheduled optimization of unoptimized images for your theme, buddypress, and any additional folders you have configured below. Runs hourly: wp_cron only runs when your site is visited, so it may be even longer between optimizations."
959
+ msgstr ""
960
+
961
+ #: common.php:3452
962
+ msgid "Folders to optimize"
963
+ msgstr ""
964
+
965
+ #: common.php:3452
966
+ msgid "One path per line, must be within %s. Use full paths, not relative paths."
967
+ msgstr ""
968
+
969
+ #: common.php:3483
970
+ msgid "Skip Small Images"
971
+ msgstr ""
972
+
973
+ #: common.php:3483
974
+ msgid "Do not optimize images smaller than this (in bytes)"
975
+ msgstr ""
976
+
977
+ #: common.php:3485
978
+ msgid "Skip Large PNG Images"
979
+ msgstr ""
980
+
981
+ #: common.php:3485
982
+ msgid "Do not optimize PNG images larger than this (in bytes)"
983
+ msgstr ""
984
+
985
+ #: common.php:3487
986
+ msgid "Exclude full-size images from lossy optimization"
987
+ msgstr ""
988
+
989
+ #: common.php:3491 ewww-image-optimizer.php:410
990
+ msgid "Use System Paths"
991
+ msgstr ""
992
+
993
+ #: common.php:3491
994
+ msgid "If you have already installed the utilities in a system location, such as %s or %s, use this to force the plugin to use those versions and skip the auto-installers."
995
+ msgstr ""
996
+
997
+ #: common.php:3493 common.php:3495 common.php:3497 common.php:3499
998
+ msgid "disable"
999
+ msgstr ""
1000
+
1001
+ #: common.php:3503
1002
+ msgid "Conversion is only available for images in the Media Library (except WebP). By default, all images have a link available in the Media Library for one-time conversion. Turning on individual conversion operations below will enable conversion filters any time an image is uploaded or modified."
1003
+ msgstr ""
1004
+
1005
+ #: common.php:3504
1006
+ msgid "NOTE:"
1007
+ msgstr ""
1008
+
1009
+ #: common.php:3504
1010
+ msgid "The plugin will attempt to update image locations for any posts that contain the images. You may still need to manually update locations/urls for converted images."
1011
+ msgstr ""
1012
+
1013
+ #: common.php:3507
1014
+ msgid "Hide Conversion Links"
1015
+ msgstr ""
1016
+
1017
+ #: common.php:3507
1018
+ msgid "Site or Network admins can use this to prevent other users from using the conversion links in the Media Library which bypass the settings below."
1019
+ msgstr ""
1020
+
1021
+ #: common.php:3508
1022
+ msgid "Delete originals"
1023
+ msgstr ""
1024
+
1025
+ #: common.php:3508
1026
+ msgid "This will remove the original image from the server after a successful conversion."
1027
+ msgstr ""
1028
+
1029
+ #: common.php:3509
1030
+ msgid "JPG/PNG to WebP"
1031
+ msgstr ""
1032
+
1033
+ #: common.php:3509
1034
+ msgid "JPG to WebP conversion is lossy, but quality loss is minimal. PNG to WebP conversion is lossless."
1035
+ msgstr ""
1036
+
1037
+ #: common.php:3510
1038
+ msgid "Originals are never deleted, and WebP images should only be served to supported browsers."
1039
+ msgstr ""
1040
+
1041
+ #: common.php:3510
1042
+ msgid "You can use the rewrite rules below to serve WebP images with Apache."
1043
+ msgstr ""
1044
+
1045
+ #: common.php:3515 common.php:3518 common.php:3525
1046
+ msgid "enable %s to %s conversion"
1047
+ msgstr ""
1048
+
1049
+ #: common.php:3515
1050
+ msgid "Removes metadata and increases cpu usage dramatically."
1051
+ msgstr ""
1052
+
1053
+ #: common.php:3516
1054
+ msgid "PNG is generally much better than JPG for logos and other images with a limited range of colors. Checking this option will slow down JPG processing significantly, and you may want to enable it only temporarily."
1055
+ msgstr ""
1056
+
1057
+ #: common.php:3518
1058
+ msgid "This is not a lossless conversion."
1059
+ msgstr ""
1060
+
1061
+ #: common.php:3519
1062
+ msgid "JPG is generally much better than PNG for photographic use because it compresses the image and discards data. PNGs with transparency are not converted by default."
1063
+ msgstr ""
1064
+
1065
+ #: common.php:3520
1066
+ msgid "JPG background color:"
1067
+ msgstr ""
1068
+
1069
+ #: common.php:3520
1070
+ msgid "HEX format (#123def)"
1071
+ msgstr ""
1072
+
1073
+ #: common.php:3521
1074
+ msgid "Background color is used only if the PNG has transparency. Leave this value blank to skip PNGs with transparency."
1075
+ msgstr ""
1076
+
1077
+ #: common.php:3522
1078
+ msgid "JPG quality level:"
1079
+ msgstr ""
1080
+
1081
+ #: common.php:3522
1082
+ msgid "Valid values are 1-100."
1083
+ msgstr ""
1084
+
1085
+ #: common.php:3523
1086
+ msgid "If JPG quality is blank, the plugin will attempt to set the optimal quality level or default to 92. Remember, this is a lossy conversion, so you are losing pixels, and it is not recommended to actually set the level here unless you want noticable loss of image quality."
1087
+ msgstr ""
1088
+
1089
+ #: common.php:3525
1090
+ msgid "No warnings here, just do it."
1091
+ msgstr ""
1092
+
1093
+ #: common.php:3526
1094
+ msgid "PNG is generally better than GIF, but animated images cannot be converted."
1095
+ msgstr ""
1096
+
1097
+ #: common.php:3529
1098
+ msgid "Save Changes"
1099
+ msgstr ""
1100
+
1101
+ #: common.php:3533
1102
+ msgid "There are many ways to serve WebP images to visitors with supported browsers. You may choose any you wish, but it is recommended to serve them with an .htaccess file using mod_rewrite and mod_headers. The plugin can insert the rules for you if the file is writable, or you can edit .htaccess yourself."
1103
+ msgstr ""
1104
+
1105
+ #: common.php:3536
1106
+ msgid "Rules verified successfully"
1107
+ msgstr ""
1108
+
1109
+ #: common.php:3552
1110
+ msgid "The image to the right will display a WebP image with WEBP in white text, if your site is serving WebP images and your browser supports WebP."
1111
+ msgstr ""
1112
+
1113
+ #: common.php:3553
1114
+ msgid "Insert Rewrite Rules"
1115
+ msgstr ""
1116
+
1117
+ #: ewww-image-optimizer.php:146
1118
+ msgid "EWWW Image Optimizer is supported on Linux, FreeBSD, Mac OSX, and Windows"
1119
+ msgstr ""
1120
+
1121
+ #: ewww-image-optimizer.php:146
1122
+ msgid "Unfortunately, the EWWW Image Optimizer plugin does not work with %s"
1123
+ msgstr ""
1124
+
1125
+ #: ewww-image-optimizer.php:248
1126
+ msgid "EWWW Image Optimizer could not create the tool folder"
1127
+ msgstr ""
1128
+
1129
+ #: ewww-image-optimizer.php:248
1130
+ msgid "Please adjust permissions or create the folder"
1131
+ msgstr ""
1132
+
1133
+ #: ewww-image-optimizer.php:410
1134
+ msgid "EWWW Image Optimizer could not install tools in %s"
1135
+ msgstr ""
1136
+
1137
+ #: ewww-image-optimizer.php:410
1138
+ msgid "Please adjust permissions or create the folder. If you have installed the tools elsewhere on your system, check the option to %s."
1139
+ msgstr ""
1140
+
1141
+ #: ewww-image-optimizer.php:410
1142
+ msgid "For more details, visit the %1$s or the %2$s."
1143
+ msgstr ""
1144
+
1145
+ #: ewww-image-optimizer.php:410 ewww-image-optimizer.php:536
1146
+ msgid "Settings Page"
1147
+ msgstr ""
1148
+
1149
+ #: ewww-image-optimizer.php:422
1150
+ msgid "EWWW Image Optimizer requires exec(). Your system administrator has disabled this function."
1151
+ msgstr ""
1152
+
1153
+ #: ewww-image-optimizer.php:430
1154
+ msgid "Safe Mode is turned on for PHP. This plugin cannot operate in Safe Mode."
1155
+ msgstr ""
1156
+
1157
+ #: ewww-image-optimizer.php:536
1158
+ msgid "EWWW Image Optimizer uses %1$s, %2$s, %3$s, %4$s, %5$s, and %6$s. You are missing: %7$s. Pleas