Image Watermark - Version 1.7.0

Version Description

  • Tweak: WordPress 5.9 compatibility
  • Tweak: PHP 8.x compatibility
Download this release

Release Info

Developer dfactory
Plugin Icon 128x128 Image Watermark
Version 1.7.0
Comparing to
See all releases

Code changes from version 1.6.6 to 1.7.0

image-watermark.php CHANGED
@@ -1,1509 +1,1518 @@
1
- <?php
2
- /*
3
- Plugin Name: Image Watermark
4
- Description: Image Watermark allows you to automatically watermark images uploaded to the WordPress Media Library and bulk watermark previously uploaded images.
5
- Version: 1.6.6
6
- Author: dFactory
7
- Author URI: https://dfactory.eu/
8
- Plugin URI: https://dfactory.eu/plugins/image-watermark/
9
- License: MIT License
10
- License URI: http://opensource.org/licenses/MIT
11
- Text Domain: image-watermark
12
- Domain Path: /languages
13
-
14
- Image Watermark
15
- Copyright (C) 2013-2019, Digital Factory - info@digitalfactory.pl
16
-
17
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
18
-
19
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
20
-
21
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- */
23
-
24
- // exit if accessed directly
25
- if ( ! defined( 'ABSPATH' ) )
26
- exit;
27
-
28
- define( 'IMAGE_WATERMARK_URL', plugins_url( '', __FILE__ ) );
29
- define( 'IMAGE_WATERMARK_PATH', plugin_dir_path( __FILE__ ) );
30
-
31
- /**
32
- * Image Watermark class.
33
- *
34
- * @class Image_Watermark
35
- * @version 1.6.6
36
- */
37
- final class Image_Watermark {
38
-
39
- private static $instance;
40
- private $is_admin = true;
41
- private $extension = false;
42
- private $allowed_mime_types = array(
43
- 'image/jpeg',
44
- 'image/pjpeg',
45
- 'image/png'
46
- );
47
- private $is_watermarked_metakey = 'iw-is-watermarked';
48
- public $is_backup_folder_writable = null;
49
- public $extensions;
50
- public $defaults = array(
51
- 'options' => array(
52
- 'watermark_on' => array(),
53
- 'watermark_cpt_on' => array( 'everywhere' ),
54
- 'watermark_image' => array(
55
- 'extension' => '',
56
- 'url' => 0,
57
- 'width' => 80,
58
- 'plugin_off' => 0,
59
- 'frontend_active' => false,
60
- 'manual_watermarking' => 0,
61
- 'position' => 'bottom_right',
62
- 'watermark_size_type' => 2,
63
- 'offset_unit' => 'pixels',
64
- 'offset_width' => 0,
65
- 'offset_height' => 0,
66
- 'absolute_width' => 0,
67
- 'absolute_height' => 0,
68
- 'transparent' => 50,
69
- 'quality' => 90,
70
- 'jpeg_format' => 'baseline',
71
- 'deactivation_delete' => false,
72
- 'media_library_notice' => true
73
- ),
74
- 'image_protection' => array(
75
- 'rightclick' => 0,
76
- 'draganddrop' => 0,
77
- 'forlogged' => 0
78
- ),
79
- 'backup' => array(
80
- 'backup_image' => true,
81
- 'backup_quality' => 90
82
- )
83
- ),
84
- 'version' => '1.6.6'
85
- );
86
- public $options = array();
87
-
88
- /**
89
- * Class constructor.
90
- */
91
- public function __construct() {
92
- // installer
93
- register_activation_hook( __FILE__, array( $this, 'activate_watermark' ) );
94
- register_deactivation_hook( __FILE__, array( $this, 'deactivate_watermark' ) );
95
-
96
- // settings
97
- $options = get_option( 'image_watermark_options', $this->defaults['options'] );
98
-
99
- $this->options = array_merge( $this->defaults['options'], $options );
100
- $this->options['watermark_image'] = array_merge( $this->defaults['options']['watermark_image'], $options['watermark_image'] );
101
- $this->options['image_protection'] = array_merge( $this->defaults['options']['image_protection'], $options['image_protection'] );
102
- $this->options['backup'] = array_merge( $this->defaults['options']['backup'], isset( $options['backup'] ) ? $options['backup'] : array() );
103
-
104
- include_once( IMAGE_WATERMARK_PATH . 'includes/class-update.php' );
105
- include_once( IMAGE_WATERMARK_PATH . 'includes/class-settings.php' );
106
-
107
- // actions
108
- add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
109
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
110
- add_action( 'admin_print_scripts', array( $this, 'admin_print_scripts' ), 20 );
111
- add_action( 'wp_enqueue_media', array( $this, 'wp_enqueue_media' ) );
112
- add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
113
- add_action( 'load-upload.php', array( $this, 'watermark_bulk_action' ) );
114
- add_action( 'admin_init', array( $this, 'update_plugin' ) );
115
- add_action( 'admin_init', array( $this, 'check_extensions' ) );
116
- add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) );
117
- add_action( 'delete_attachment', array( $this, 'delete_attachment' ) );
118
- add_action( 'wp_ajax_iw_watermark_bulk_action', array( $this, 'watermark_action_ajax' ) );
119
-
120
- // filters
121
- add_filter( 'plugin_row_meta', array( $this, 'plugin_extend_links' ), 10, 2 );
122
- add_filter( 'plugin_action_links', array( $this, 'plugin_settings_link' ), 10, 2 );
123
- add_filter( 'wp_handle_upload', array( $this, 'handle_upload_files' ) );
124
- add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields_to_edit' ), 10, 2 );
125
-
126
- // define our backup location
127
- $upload_dir = wp_upload_dir();
128
- define( 'IMAGE_WATERMARK_BACKUP_DIR', apply_filters( 'image_watermark_backup_dir', $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'iw-backup' ) );
129
-
130
- // create backup folder and security if enabled
131
- if ( $this->options['backup']['backup_image'] ) {
132
- if ( is_writable( $upload_dir['basedir'] ) ) {
133
- $this->is_backup_folder_writable = true;
134
-
135
- // create backup folder ( if it exists this returns true: https://codex.wordpress.org/Function_Reference/wp_mkdir_p )
136
- $backup_folder_created = wp_mkdir_p( IMAGE_WATERMARK_BACKUP_DIR );
137
-
138
- // check if the folder exists and is writable
139
- if ( $backup_folder_created && is_writable( IMAGE_WATERMARK_BACKUP_DIR ) ) {
140
- // check if the htaccess file exists
141
- if ( ! file_exists( IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . '.htaccess' ) ) {
142
- // htaccess security
143
- file_put_contents( IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . '.htaccess', 'deny from all' );
144
- }
145
- } else
146
- $this->is_backup_folder_writable = false;
147
- } else
148
- $this->is_backup_folder_writable = false;
149
-
150
- if ( $this->is_backup_folder_writable !== true ) {
151
- // disable backup setting
152
- $this->options['backup']['backup_image'] = false;
153
- update_option( 'image_watermark_options', $this->options );
154
- }
155
-
156
- add_action( 'admin_notices', array( $this, 'folder_writable_admin_notice' ) );
157
- }
158
- }
159
-
160
- /**
161
- * Create single instance.
162
- *
163
- * @return object Main plugin instance
164
- */
165
- public static function instance() {
166
- if ( self::$instance === null )
167
- self::$instance = new self();
168
-
169
- return self::$instance;
170
- }
171
-
172
- /**
173
- * Plugin activation.
174
- */
175
- public function activate_watermark() {
176
- add_option( 'image_watermark_options', $this->defaults['options'], '', 'no' );
177
- add_option( 'image_watermark_version', $this->defaults['version'], '', 'no' );
178
- }
179
-
180
- /**
181
- * Plugin deactivation.
182
- */
183
- public function deactivate_watermark() {
184
- // remove options from database?
185
- if ( $this->options['watermark_image']['deactivation_delete'] )
186
- delete_option( 'image_watermark_options' );
187
- }
188
-
189
- /**
190
- * Plugin update, fix for version < 1.5.0
191
- */
192
- public function update_plugin() {
193
- if ( ! current_user_can( 'install_plugins' ) )
194
- return;
195
-
196
- $db_version = get_option( 'image_watermark_version' );
197
- $db_version = ! ( $db_version ) && ( get_option( 'df_watermark_installed' ) != false ) ? get_option( 'version' ) : $db_version;
198
-
199
- if ( $db_version != false ) {
200
- if ( version_compare( $db_version, '1.5.0', '<' ) ) {
201
- $options = array();
202
-
203
- $old_new = array(
204
- 'df_watermark_on' => 'watermark_on',
205
- 'df_watermark_cpt_on' => 'watermark_cpt_on',
206
- 'df_watermark_image' => 'watermark_image',
207
- 'df_image_protection' => 'image_protection',
208
- 'df_watermark_installed' => '',
209
- 'version' => '',
210
- 'image_watermark_version' => '',
211
- );
212
-
213
- foreach ( $old_new as $old => $new ) {
214
- if ( $new ) {
215
- $options[$new] = get_option( $old );
216
- }
217
- delete_option( $old );
218
- }
219
-
220
- add_option( 'image_watermark_options', $options, '', 'no' );
221
- add_option( 'image_watermark_version', $this->defaults['version'], '', 'no' );
222
- }
223
- }
224
- }
225
-
226
- /**
227
- * Load textdomain.
228
- */
229
- public function load_textdomain() {
230
- load_plugin_textdomain( 'image-watermark', false, basename( dirname( __FILE__ ) ) . '/languages' );
231
- }
232
-
233
- /**
234
- * Admin inline scripts.
235
- *
236
- * @global $pagenow
237
- */
238
- public function admin_print_scripts() {
239
- global $pagenow;
240
-
241
- if ( $pagenow === 'upload.php' ) {
242
- if ( $this->options['watermark_image']['manual_watermarking'] == 1 ) {
243
- ?>
244
- <script type="text/javascript">
245
- jQuery( function( $ ) {
246
- $( document ).ready( function() {
247
- var backup = <?php echo (int) $this->options['backup']['backup_image']; ?>;
248
-
249
- $( "<option>" ).val( "applywatermark" ).text( "<?php _e( 'Apply watermark', 'image-watermark' ); ?>" ).appendTo( "select[name='action'], select[name='action2']" );
250
-
251
- if ( backup === 1 ) {
252
- $( "<option>" ).val( "removewatermark" ).text( "<?php _e( 'Remove watermark', 'image-watermark' ); ?>" ).appendTo( "select[name='action'], select[name='action2']" );
253
- }
254
- });
255
- });
256
- </script>
257
- <?php
258
- }
259
- }
260
- }
261
-
262
- /**
263
- * Enqueue admin scripts and styles.
264
- */
265
- public function wp_enqueue_media( $page ) {
266
- wp_enqueue_style( 'watermark-style', plugins_url( 'css/image-watermark.css', __FILE__ ), array(), $this->defaults['version'] );
267
- }
268
-
269
- /**
270
- * Enqueue admin scripts and styles.
271
- *
272
- * @global $pagenow
273
- */
274
- public function admin_enqueue_scripts( $page ) {
275
- global $pagenow;
276
-
277
- wp_register_style( 'watermark-style', plugins_url( 'css/image-watermark.css', __FILE__ ), array(), $this->defaults['version'] );
278
-
279
- if ( $page === 'settings_page_watermark-options' ) {
280
- wp_enqueue_media();
281
-
282
- wp_enqueue_script( 'upload-manager', plugins_url( '/js/admin-upload.js', __FILE__ ), array(), $this->defaults['version'] );
283
-
284
- wp_localize_script(
285
- 'upload-manager', 'iwUploadArgs', array(
286
- 'title' => __( 'Select watermark', 'image-watermark' ),
287
- 'originalSize' => __( 'Original size', 'image-watermark' ),
288
- 'noSelectedImg' => __( 'Watermak has not been selected yet.', 'image-watermark' ),
289
- 'notAllowedImg' => __( 'This image is not supported as watermark. Use JPEG, PNG or GIF.', 'image-watermark' ),
290
- 'px' => __( 'px', 'image-watermark' ),
291
- 'frame' => 'select',
292
- 'button' => array( 'text' => __( 'Add watermark', 'image-watermark' ) ),
293
- 'multiple' => false
294
- )
295
- );
296
-
297
- wp_enqueue_script( 'watermark-admin-script', plugins_url( 'js/admin-settings.js', __FILE__ ), array( 'jquery', 'jquery-ui-core', 'jquery-ui-button', 'jquery-ui-slider' ), $this->defaults['version'] );
298
-
299
- wp_localize_script(
300
- 'watermark-admin-script', 'iwArgs', array(
301
- 'resetToDefaults' => __( 'Are you sure you want to reset settings to defaults?', 'image-watermark' )
302
- )
303
- );
304
-
305
- wp_enqueue_style( 'wp-like-ui-theme', plugins_url( 'css/wp-like-ui-theme.css', __FILE__ ), array(), $this->defaults['version'] );
306
- wp_enqueue_style( 'watermark-style' );
307
-
308
- wp_enqueue_script( 'postbox' );
309
- }
310
-
311
- if ( $pagenow === 'upload.php' ) {
312
- wp_enqueue_style( 'watermark-style' );
313
- }
314
-
315
- // I've omitted $pagenow === 'upload.php' because the image modal could be loaded in various places
316
- if ( $this->options['watermark_image']['manual_watermarking'] == 1 ) {
317
-
318
- wp_enqueue_script( 'watermark-admin-image-actions', plugins_url( '/js/admin-image-actions.js', __FILE__ ), array( 'jquery' ), $this->defaults['version'], true );
319
-
320
- wp_localize_script(
321
- 'watermark-admin-image-actions', 'iwImageActionArgs', array(
322
- 'backup_image' => (int) $this->options['backup']['backup_image'],
323
- '_nonce' => wp_create_nonce( 'image-watermark' ),
324
- '__applied_none' => __( 'Watermark could not be applied to selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ),
325
- '__applied_one' => __( 'Watermark was succesfully applied to 1 image.', 'image-watermark' ),
326
- '__applied_multi' => __( 'Watermark was succesfully applied to %s images.', 'image-watermark' ),
327
- '__removed_none' => __( 'Watermark could not be removed from selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ),
328
- '__removed_one' => __( 'Watermark was succesfully removed from 1 image.', 'image-watermark' ),
329
- '__removed_multi' => __( 'Watermark was succesfully removed from %s images.', 'image-watermark' ),
330
- '__skipped' => __( 'Skipped files', 'image-watermark' ),
331
- '__running' => __( 'Bulk action is currently running, please wait.', 'image-watermark' ),
332
- '__dismiss' => __( 'Dismiss this notice.' ), // Wordpress default string
333
- )
334
- );
335
- }
336
- }
337
-
338
- /**
339
- * Enqueue frontend script with 'no right click' and 'drag and drop' functions.
340
- */
341
- public function wp_enqueue_scripts() {
342
- $right_click = true;
343
-
344
- if ( ($this->options['image_protection']['forlogged'] == 0 && is_user_logged_in()) || ($this->options['image_protection']['draganddrop'] == 0 && $this->options['image_protection']['rightclick'] == 0) )
345
- $right_click = false;
346
-
347
- if ( apply_filters( 'iw_block_right_click', (bool) $right_click ) === true ) {
348
- wp_enqueue_script( 'iw-no-right-click', plugins_url( 'js/no-right-click.js', __FILE__ ), array(), $this->defaults['version'] );
349
-
350
- wp_localize_script(
351
- 'iw-no-right-click', 'IwNRCargs', array(
352
- 'rightclick' => ($this->options['image_protection']['rightclick'] == 1 ? 'Y' : 'N'),
353
- 'draganddrop' => ($this->options['image_protection']['draganddrop'] == 1 ? 'Y' : 'N')
354
- )
355
- );
356
- }
357
- }
358
-
359
- /**
360
- * Check which extension is available and set it.
361
- *
362
- * @return void
363
- */
364
- public function check_extensions() {
365
- $ext = null;
366
-
367
- if ( $this->check_imagick() ) {
368
- $this->extensions['imagick'] = 'ImageMagick';
369
- $ext = 'imagick';
370
- }
371
-
372
- if ( $this->check_gd() ) {
373
- $this->extensions['gd'] = 'GD';
374
-
375
- if ( is_null( $ext ) )
376
- $ext = 'gd';
377
- }
378
-
379
- if ( isset( $this->options['watermark_image']['extension'] ) ) {
380
- if ( $this->options['watermark_image']['extension'] === 'imagick' && isset( $this->extensions['imagick'] ) )
381
- $this->extension = 'imagick';
382
- elseif ( $this->options['watermark_image']['extension'] === 'gd' && isset( $this->extensions['gd'] ) )
383
- $this->extension = 'gd';
384
- else
385
- $this->extension = $ext;
386
- } else
387
- $this->extension = $ext;
388
- }
389
- /*
390
- imagick 50% jpgy sa ok, watermark troche w innych kolorach, png sa przyciemnione, tak jakby mialy 50% nie na watermarku, a na calym obrazku
391
- Imagick GD
392
- JPG PNG JPG PNG
393
- wp-logo-png24-transparent + - + +
394
- wp-logo-png24-flat + - + +
395
- wp-logo-png8-transparent + -
396
- wp-logo-png8-flat + -
397
- wp-logo-jpg + +
398
- */
399
- /**
400
- * Apply watermark everywhere or for specific post types.
401
- *
402
- * @param resource $file
403
- * @return resource
404
- */
405
- public function handle_upload_files( $file ) {
406
- // is extension available?
407
- if ( $this->extension ) {
408
- // determine ajax frontend or backend request
409
- $script_filename = isset( $_SERVER['SCRIPT_FILENAME'] ) ? $_SERVER['SCRIPT_FILENAME'] : '';
410
-
411
- // try to figure out if frontend AJAX request... if we are DOING_AJAX; let's look closer
412
- if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
413
- // from wp-includes/functions.php, wp_get_referer() function.
414
- // required to fix: https://core.trac.wordpress.org/ticket/25294
415
- $ref = '';
416
- if ( ! empty( $_REQUEST['_wp_http_referer'] ) )
417
- $ref = wp_unslash( $_REQUEST['_wp_http_referer'] );
418
- elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) )
419
- $ref = wp_unslash( $_SERVER['HTTP_REFERER'] );
420
-
421
- // if referer does not contain admin URL and we are using the admin-ajax.php endpoint, this is likely a frontend AJAX request
422
- if ( ( ( strpos( $ref, admin_url() ) === false ) && ( basename( $script_filename ) === 'admin-ajax.php' ) ) )
423
- $this->is_admin = false;
424
- else
425
- $this->is_admin = true;
426
- // not an AJAX request, simple here
427
- } else {
428
- if ( is_admin() )
429
- $this->is_admin = true;
430
- else
431
- $this->is_admin = false;
432
- }
433
-
434
- // admin
435
- if ( $this->is_admin === true ) {
436
- if ( $this->options['watermark_image']['plugin_off'] == 1 && wp_attachment_is_image( $this->options['watermark_image']['url'] ) && in_array( $file['type'], $this->allowed_mime_types ) ) {
437
- add_filter( 'wp_generate_attachment_metadata', array( $this, 'apply_watermark' ), 10, 2 );
438
- }
439
- // frontend
440
- } else {
441
- if ( $this->options['watermark_image']['frontend_active'] == 1 && wp_attachment_is_image( $this->options['watermark_image']['url'] ) && in_array( $file['type'], $this->allowed_mime_types ) ) {
442
- add_filter( 'wp_generate_attachment_metadata', array( $this, 'apply_watermark' ), 10, 2 );
443
- }
444
- }
445
- }
446
-
447
- return $file;
448
- }
449
-
450
- /**
451
- * Add watermark buttons on attachment image locations
452
- */
453
- public function attachment_fields_to_edit( $form_fields, $post ) {
454
- if ( $this->options['watermark_image']['manual_watermarking'] == 1 && $this->options['backup']['backup_image'] ) {
455
-
456
- $data = wp_get_attachment_metadata( $post->ID, false );
457
-
458
- // is this really an image?
459
- if ( in_array( get_post_mime_type( $post->ID ), $this->allowed_mime_types ) && is_array( $data ) ) {
460
- $form_fields['image_watermark'] = array(
461
- 'show_in_edit' => false,
462
- 'tr' => '
463
- <div id="image_watermark_buttons"' . ( get_post_meta( $post->ID, $this->is_watermarked_metakey, true ) ? ' class="watermarked"' : '' ) . ' data-id="' . $post->ID . '" style="display: none;">
464
- <label class="setting">
465
- <span class="name">' . __( 'Image Watermark', 'image-watermark' ) . '</span>
466
- <span class="value" style="width: 63%"><a href="#" class="iw-watermark-action" data-action="applywatermark" data-id="' . $post->ID . '">' . __( 'Apply watermark', 'image-watermark' ) . '</a> | <a href="#" class="iw-watermark-action delete-watermark" data-action="removewatermark" data-id="' . $post->ID . '">' . __( 'Remove watermark', 'image-watermark' ) . '</a></span>
467
- </label>
468
- <div class="clear"></div>
469
- </div>
470
- <script>
471
- jQuery( document ).ready( function ( $ ) {
472
- if ( typeof watermarkImageActions != "undefined" ) {
473
- $( "#image_watermark_buttons" ).show();
474
- }
475
- });
476
- </script>'
477
- );
478
- }
479
- }
480
- return $form_fields;
481
- }
482
-
483
- /**
484
- * Apply watermark for selected images on media page.
485
- */
486
- public function watermark_action_ajax() {
487
- // Security & data check
488
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! isset( $_POST['_iw_nonce'] ) || ! isset( $_POST['iw-action'] ) || ! isset( $_POST['attachment_id'] ) || ! is_numeric( $_POST['attachment_id'] ) || ! wp_verify_nonce( $_POST['_iw_nonce'], 'image-watermark' ) )
489
- wp_send_json_error( __( 'Cheatin uh?', 'image-watermark' ) );
490
-
491
- $post_id = (int) $_POST['attachment_id'];
492
- $action = false;
493
-
494
- switch ( $_POST['iw-action'] ) {
495
- case 'applywatermark':
496
- $action = 'applywatermark';
497
- break;
498
-
499
- case 'removewatermark':
500
- $action = 'removewatermark';
501
- }
502
-
503
- // only if manual watermarking is turned and we have a valid action
504
- // if the action is NOT "removewatermark" we also require a watermark image to be set
505
- if ( $post_id > 0 && $action && $this->options['watermark_image']['manual_watermarking'] == 1 && ( wp_attachment_is_image( $this->options['watermark_image']['url'] ) || $action == 'removewatermark' ) ) {
506
- $data = wp_get_attachment_metadata( $post_id, false );
507
-
508
- // is this really an image?
509
- if ( in_array( get_post_mime_type( $post_id ), $this->allowed_mime_types ) && is_array( $data ) ) {
510
- if ( $action === 'applywatermark' ) {
511
- $success = $this->apply_watermark( $data, $post_id, 'manual' );
512
-
513
- if ( ! empty( $success['error'] ) )
514
- wp_send_json_success( $success['error'] );
515
- else
516
- wp_send_json_success( 'watermarked' );
517
- } elseif ( $action === 'removewatermark' ) {
518
- $success = $this->remove_watermark( $data, $post_id, 'manual' );
519
-
520
- if ( $success )
521
- wp_send_json_success( 'watermarkremoved' );
522
- else
523
- wp_send_json_success( 'skipped' );
524
- }
525
- } else
526
- wp_send_json_success( 'skipped' );
527
- }
528
-
529
- wp_send_json_error( __( 'Cheatin uh?', 'image-watermark' ) );
530
- }
531
-
532
- /**
533
- * Apply watermark for selected images on media page.
534
- *
535
- * @return void
536
- */
537
- public function watermark_bulk_action() {
538
- global $pagenow;
539
-
540
- if ( $pagenow == 'upload.php' && $this->extension ) {
541
- $wp_list_table = _get_list_table( 'WP_Media_List_Table' );
542
- $action = false;
543
-
544
- switch ( $wp_list_table->current_action() ) {
545
- case 'applywatermark':
546
- $action = 'applywatermark';
547
- break;
548
-
549
- case 'removewatermark':
550
- $action = 'removewatermark';
551
- }
552
-
553
- // only if manual watermarking is turned and we have a valid action
554
- // if the action is NOT "removewatermark" we also require a watermark image to be set
555
- if ( $action && $this->options['watermark_image']['manual_watermarking'] == 1 && ( wp_attachment_is_image( $this->options['watermark_image']['url'] ) || $action == 'removewatermark' ) ) {
556
- // security check
557
- check_admin_referer( 'bulk-media' );
558
-
559
- $location = esc_url( remove_query_arg( array( 'watermarked', 'watermarkremoved', 'skipped', 'trashed', 'untrashed', 'deleted', 'message', 'ids', 'posted' ), wp_get_referer() ) );
560
-
561
- if ( ! $location )
562
- $location = 'upload.php';
563
-
564
- $location = esc_url( add_query_arg( 'paged', $wp_list_table->get_pagenum(), $location ) );
565
-
566
- // make sure ids are submitted. depending on the resource type, this may be 'media' or 'ids'
567
- if ( isset( $_REQUEST['media'] ) )
568
- $post_ids = array_map( 'intval', $_REQUEST['media'] );
569
-
570
- // do we have selected attachments?
571
- if ( $post_ids ) {
572
- $watermarked = $watermarkremoved = $skipped = 0;
573
- $messages = array();
574
-
575
- foreach ( $post_ids as $post_id ) {
576
- $data = wp_get_attachment_metadata( $post_id, false );
577
-
578
- // is this really an image?
579
- if ( in_array( get_post_mime_type( $post_id ), $this->allowed_mime_types ) && is_array( $data ) ) {
580
- if ( $action === 'applywatermark' ) {
581
- $success = $this->apply_watermark( $data, $post_id, 'manual' );
582
- if ( ! empty( $success['error'] ) )
583
- $messages[] = $success['error'];
584
- else {
585
- $watermarked++;
586
- $watermarkremoved = -1;
587
- }
588
- } elseif ( $action === 'removewatermark' ) {
589
- $success = $this->remove_watermark( $data, $post_id, 'manual' );
590
-
591
- if ( $success )
592
- $watermarkremoved++;
593
- else
594
- $skipped++;
595
-
596
- $watermarked = -1;
597
- }
598
- } else
599
- $skipped++;
600
- }
601
-
602
- $location = esc_url( add_query_arg( array( 'watermarked' => $watermarked, 'watermarkremoved' => $watermarkremoved, 'skipped' => $skipped, 'messages' => $messages ), $location ), null, '' );
603
- }
604
-
605
- wp_redirect( $location );
606
- exit();
607
- } else
608
- return;
609
- }
610
- }
611
-
612
- /**
613
- * Display admin notices.
614
- *
615
- * @return mixed
616
- */
617
- public function bulk_admin_notices() {
618
- global $post_type, $pagenow;
619
-
620
- if ( $pagenow === 'upload.php' ) {
621
- if ( ! current_user_can( 'upload_files' ) )
622
- return;
623
-
624
- // hide media library notice
625
- if ( isset( $_GET['iw_action'] ) && $_GET['iw_action'] == 'hide_library_notice' ) {
626
- $this->options['watermark_image']['media_library_notice'] = false;
627
- update_option( 'image_watermark_options', $this->options );
628
- }
629
-
630
- // check if manual watermarking is enabled
631
- if ( ! empty( $this->options['watermark_image']['manual_watermarking'] ) && ( ! isset( $this->options['watermark_image']['media_library_notice'] ) || $this->options['watermark_image']['media_library_notice'] === true ) ) {
632
- $mode = get_user_option( 'media_library_mode', get_current_user_id() ) ? get_user_option( 'media_library_mode', get_current_user_id() ) : 'grid';
633
-
634
- if ( isset( $_GET['mode'] ) && in_array( $_GET['mode'], array( 'grid', 'list' ) ) )
635
- $mode = $_GET['mode'];
636
-
637
- // display notice in grid mode only
638
- if ( $mode === 'grid' ) {
639
- // get current admin url
640
- $query_string = array();
641
-
642
- parse_str( $_SERVER['QUERY_STRING'], $query_string );
643
-
644
- $current_url = esc_url( add_query_arg( array_merge( (array) $query_string, array( 'iw_action' => 'hide_library_notice' ) ), '', admin_url( trailingslashit( $pagenow ) ) ) );
645
-
646
- echo '<div class="error notice"><p>' . sprintf( __( '<strong>Image Watermark:</strong> Bulk watermarking is available in list mode only, under <em>Bulk Actions</em> dropdown. <a href="%1$s">Got to List Mode</a> or <a href="%2$s">Hide this notice</a>', 'image-watermark' ), esc_url( admin_url( 'upload.php?mode=list' ) ), esc_url( $current_url ) ) . '</p></div>';
647
- }
648
- }
649
-
650
- if ( isset( $_REQUEST['watermarked'], $_REQUEST['watermarkremoved'], $_REQUEST['skipped'] ) && $post_type === 'attachment' ) {
651
- $watermarked = (int) $_REQUEST['watermarked'];
652
- $watermarkremoved = (int) $_REQUEST['watermarkremoved'];
653
- $skipped = (int) $_REQUEST['skipped'];
654
-
655
- if ( $watermarked === 0 )
656
- echo '<div class="error"><p>' . __( 'Watermark could not be applied to selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ) . ($skipped > 0 ? ' ' . __( 'Images skipped', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
657
- elseif ( $watermarked > 0 )
658
- echo '<div class="updated"><p>' . sprintf( _n( 'Watermark was succesfully applied to 1 image.', 'Watermark was succesfully applied to %s images.', $watermarked, 'image-watermark' ), number_format_i18n( $watermarked ) ) . ($skipped > 0 ? ' ' . __( 'Skipped files', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
659
-
660
- if ( $watermarkremoved === 0 )
661
- echo '<div class="error"><p>' . __( 'Watermark could not be removed from selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ) . ($skipped > 0 ? ' ' . __( 'Images skipped', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
662
- elseif ( $watermarkremoved > 0 )
663
- echo '<div class="updated"><p>' . sprintf( _n( 'Watermark was succesfully removed from 1 image.', 'Watermark was succesfully removed from %s images.', $watermarkremoved, 'image-watermark' ), number_format_i18n( $watermarkremoved ) ) . ($skipped > 0 ? ' ' . __( 'Skipped files', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
664
-
665
- $_SERVER['REQUEST_URI'] = esc_url( remove_query_arg( array( 'watermarked', 'skipped' ), $_SERVER['REQUEST_URI'] ) );
666
- }
667
- }
668
- }
669
-
670
- /**
671
- * Check whether ImageMagick extension is available.
672
- *
673
- * @return boolean True if extension is available
674
- */
675
- public function check_imagick() {
676
- // check Imagick's extension and classes
677
- if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick', false ) || ! class_exists( 'ImagickPixel', false ) )
678
- return false;
679
-
680
- // check version
681
- if ( version_compare( phpversion( 'imagick' ), '2.2.0', '<' ) )
682
- return false;
683
-
684
- // check for deep requirements within Imagick
685
- if ( ! defined( 'imagick::COMPRESSION_JPEG' ) || ! defined( 'imagick::COMPOSITE_OVERLAY' ) || ! defined( 'Imagick::INTERLACE_PLANE' ) || ! defined( 'imagick::FILTER_CATROM' ) || ! defined( 'Imagick::CHANNEL_ALL' ) )
686
- return false;
687
-
688
- // check methods
689
- if ( array_diff( array( 'clear', 'destroy', 'valid', 'getimage', 'writeimage', 'getimagegeometry', 'getimageformat', 'setimageformat', 'setimagecompression', 'setimagecompressionquality', 'scaleimage' ), get_class_methods( 'Imagick' ) ) )
690
- return false;
691
-
692
- return true;
693
- }
694
-
695
- /**
696
- * Check whether GD extension is available.
697
- *
698
- * @return boolean True if extension is available
699
- */
700
- public function check_gd( $args = array() ) {
701
- // check extension
702
- if ( ! extension_loaded( 'gd' ) || ! function_exists( 'gd_info' ) )
703
- return false;
704
-
705
- return true;
706
- }
707
-
708
- /**
709
- * Apply watermark to selected image sizes.
710
- *
711
- * @param array $data
712
- * @param int|string $attachment_id Attachment ID
713
- * @param string $method
714
- * @return array
715
- */
716
- public function apply_watermark( $data, $attachment_id, $method = '' ) {
717
- $attachment_id = (int) $attachment_id;
718
- $post = get_post( $attachment_id );
719
- $post_id = ( ! empty( $post ) ? (int) $post->post_parent : 0 );
720
-
721
- if ( $attachment_id === (int) $this->options['watermark_image']['url'] ) {
722
- // this is the current watermark, do not apply
723
- return array( 'error' => __( 'Watermark prevented, this is your selected watermark image', 'image-watermark' ) );
724
- }
725
-
726
- // something went wrong or is it automatic mode?
727
- if ( $method !== 'manual' && ( $this->is_admin === true && ! ( ( isset( $this->options['watermark_cpt_on'][0] ) && $this->options['watermark_cpt_on'][0] === 'everywhere' ) || ( $post_id > 0 && in_array( get_post_type( $post_id ), array_keys( $this->options['watermark_cpt_on'] ) ) === true ) ) ) )
728
- return $data;
729
-
730
- if ( apply_filters( 'iw_watermark_display', $attachment_id ) === false )
731
- return $data;
732
-
733
- // get upload dir data
734
- $upload_dir = wp_upload_dir();
735
-
736
- // assign original (full) file
737
- $original_file = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $data['file'];
738
-
739
- // is this really an image?
740
- if ( getimagesize( $original_file, $original_image_info ) !== false ) {
741
- $metadata = $this->get_image_metadata( $original_image_info );
742
-
743
- // remove the watermark if this image was already watermarked
744
- if ( (int) get_post_meta( $attachment_id, $this->is_watermarked_metakey, true ) === 1 )
745
- $this->remove_watermark( $data, $attachment_id, 'manual' );
746
-
747
- // create a backup if this is enabled
748
- if ( $this->options['backup']['backup_image'] )
749
- $this->do_backup( $data, $upload_dir, $attachment_id );
750
-
751
- // loop through active image sizes
752
- foreach ( $this->options['watermark_on'] as $image_size => $active_size ) {
753
- if ( $active_size === 1 ) {
754
- switch ( $image_size ) {
755
- case 'full':
756
- $filepath = $original_file;
757
- break;
758
-
759
- default:
760
- if ( ! empty( $data['sizes'] ) && array_key_exists( $image_size, $data['sizes'] ) )
761
- $filepath = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . dirname( $data['file'] ) . DIRECTORY_SEPARATOR . $data['sizes'][$image_size]['file'];
762
- else
763
- // early getaway
764
- continue 2;
765
- }
766
-
767
- do_action( 'iw_before_apply_watermark', $attachment_id, $image_size );
768
-
769
- // apply watermark
770
- $this->do_watermark( $attachment_id, $filepath, $image_size, $upload_dir, $metadata );
771
-
772
- // save metadata
773
- $this->save_image_metadata( $metadata, $filepath );
774
-
775
- do_action( 'iw_after_apply_watermark', $attachment_id, $image_size );
776
- }
777
- }
778
-
779
- // update watermark status
780
- update_post_meta( $attachment_id, $this->is_watermarked_metakey, 1 );
781
- }
782
-
783
- // pass forward attachment metadata
784
- return $data;
785
- }
786
-
787
- /**
788
- * Remove watermark from selected image sizes.
789
- *
790
- * @param array $data
791
- * @param int|string $attachment_id Attachment ID
792
- * @param string $method
793
- * @return array
794
- */
795
- private function remove_watermark( $data, $attachment_id, $method = '' ) {
796
- if ( $method !== 'manual' )
797
- return $data;
798
-
799
- $upload_dir = wp_upload_dir();
800
-
801
- // is this really an image?
802
- if ( getimagesize( $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $data['file'] ) !== false ) {
803
- // live file path (probably watermarked)
804
- $filepath = get_attached_file( $attachment_id );
805
-
806
- // backup file path (not watermarked)
807
- $backup_filepath = $this->get_image_backup_filepath( get_post_meta( $attachment_id, '_wp_attached_file', true ) );
808
-
809
- // replace the image in uploads with our backup if one exists
810
- if ( file_exists( $backup_filepath ) ) {
811
- if ( ! copy( $backup_filepath, $filepath ) ) {
812
- // Failed to copy
813
- }
814
- }
815
-
816
- // if no backup exists, use the current full-size image to regenerate
817
- // if the "full" size is enabled for watermarks and no backup has been made the removal of watermarks can't be done
818
-
819
- // regenerate metadata (and thumbs)
820
- $metadata = wp_generate_attachment_metadata( $attachment_id, $filepath );
821
-
822
- // update attachment metadata with new metadata
823
- wp_update_attachment_metadata( $attachment_id, $metadata );
824
-
825
- // update watermark status
826
- update_post_meta( $attachment_id, $this->is_watermarked_metakey, 0 );
827
-
828
- // ureturn the attachment metadata
829
- return wp_get_attachment_metadata( $attachment_id );
830
- }
831
-
832
- return false;
833
- }
834
-
835
- /**
836
- *
837
- */
838
- public function get_image_metadata( $imageinfo ) {
839
- $metadata = array(
840
- 'exif' => null,
841
- 'iptc' => null
842
- );
843
-
844
- if ( is_array( $imageinfo ) ) {
845
- // prepare EXIF data bytes from source file
846
- $exifdata = key_exists( 'APP1', $imageinfo ) ? $imageinfo['APP1'] : null;
847
-
848
- if ( $exifdata ) {
849
- $exiflength = strlen( $exifdata ) + 2;
850
-
851
- // construct EXIF segment
852
- if ( $exiflength > 0xFFFF ) {
853
- return $metadata;
854
- } else
855
- $metadata['exif'] = chr( 0xFF ) . chr( 0xE1 ) . chr( ( $exiflength >> 8 ) & 0xFF ) . chr( $exiflength & 0xFF ) . $exifdata;
856
- }
857
-
858
- // prepare IPTC data bytes from source file
859
- $iptcdata = key_exists( 'APP13', $imageinfo ) ? $imageinfo['APP13'] : null;
860
-
861
- if ( $iptcdata ) {
862
- $iptclength = strlen( $iptcdata ) + 2;
863
-
864
- // construct IPTC segment
865
- if ( $iptclength > 0xFFFF ) {
866
- return $metadata;
867
- } else
868
- $metadata['iptc'] = chr( 0xFF ) . chr( 0xED ) . chr( ( $iptclength >> 8 ) & 0xFF ) . chr( $iptclength & 0xFF ) . $iptcdata;
869
- }
870
- }
871
-
872
- return $metadata;
873
- }
874
-
875
- /**
876
- * Save EXIF and IPTC metadata from one image to another.
877
- *
878
- * @param array @metadata
879
- * @param string @destination_file
880
- * @return false|int
881
- */
882
- public function save_image_metadata( $metadata, $file ) {
883
- $mime = wp_check_filetype( $file );
884
-
885
- if ( file_exists( $file ) && $mime['type'] !== 'image/png' ) {
886
- $exifdata = $metadata['exif'];
887
- $iptcdata = $metadata['iptc'];
888
-
889
- $destfilecontent = @file_get_contents( $file );
890
-
891
- if ( ! $destfilecontent )
892
- return false;
893
-
894
- if ( strlen( $destfilecontent ) > 0 ) {
895
- $destfilecontent = substr( $destfilecontent, 2 );
896
-
897
- // variable accumulates new & original IPTC application segments
898
- $portiontoadd = chr( 0xFF ) . chr( 0xD8 );
899
-
900
- $exifadded = ! $exifdata;
901
- $iptcadded = ! $iptcdata;
902
-
903
- while ( ( $this->get_safe_chunk( substr( $destfilecontent, 0, 2 ) ) & 0xFFF0 ) === 0xFFE0 ) {
904
- $segmentlen = ( $this->get_safe_chunk( substr( $destfilecontent, 2, 2 ) ) & 0xFFFF );
905
-
906
- // last 4 bits of second byte is IPTC segment
907
- $iptcsegmentnumber = ( $this->get_safe_chunk( substr( $destfilecontent, 1, 1 ) ) & 0x0F );
908
-
909
- if ( $segmentlen <= 2 )
910
- return false;
911
-
912
- $thisexistingsegment = substr( $destfilecontent, 0, $segmentlen + 2 );
913
-
914
- if ( ( $iptcsegmentnumber >= 1 ) && ( ! $exifadded ) ) {
915
- $portiontoadd .= $exifdata;
916
- $exifadded = true;
917
-
918
- if ( $iptcsegmentnumber === 1 )
919
- $thisexistingsegment = '';
920
- }
921
-
922
- if ( ( $iptcsegmentnumber >= 13 ) && ( ! $iptcadded ) ) {
923
- $portiontoadd .= $iptcdata;
924
- $iptcadded = true;
925
-
926
- if ( $iptcsegmentnumber === 13 )
927
- $thisexistingsegment = '';
928
- }
929
-
930
- $portiontoadd .= $thisexistingsegment;
931
- $destfilecontent = substr( $destfilecontent, $segmentlen + 2 );
932
- }
933
-
934
- // add EXIF data if not added already
935
- if ( ! $exifadded )
936
- $portiontoadd .= $exifdata;
937
-
938
- // add IPTC data if not added already
939
- if ( ! $iptcadded )
940
- $portiontoadd .= $iptcdata;
941
-
942
- $outputfile = fopen( $file, 'w' );
943
-
944
- if ( $outputfile )
945
- return fwrite( $outputfile, $portiontoadd . $destfilecontent );
946
- else
947
- return false;
948
- } else
949
- return false;
950
- } else
951
- return false;
952
- }
953
-
954
- /**
955
- * Get integer value of binary chunk.
956
- *
957
- * @param bin $value Binary data
958
- * @return int
959
- */
960
- private function get_safe_chunk( $value ) {
961
- // check for numeric value
962
- if ( is_numeric( $value ) ) {
963
- // cast to integer to do bitwise AND operation
964
- return (int) $value;
965
- } else
966
- return 0;
967
- }
968
-
969
- /**
970
- * Apply watermark to image.
971
- *
972
- * @param int $attachment_id Attachment ID
973
- * @param string $image_path Path to the file
974
- * @param string $image_size Image size
975
- * @param array $upload_dir Upload media data
976
- * @param array $metadata EXIF and ITPC metadata
977
- * @return void
978
- */
979
- public function do_watermark( $attachment_id, $image_path, $image_size, $upload_dir, $metadata = array() ) {
980
- $options = apply_filters( 'iw_watermark_options', $this->options );
981
-
982
- // get image mime type
983
- $mime = wp_check_filetype( $image_path );
984
-
985
- if ( ! wp_attachment_is_image( $options['watermark_image']['url'] ) )
986
- return;
987
-
988
- // get watermark path
989
- $watermark_file = wp_get_attachment_metadata( $options['watermark_image']['url'], true );
990
- $watermark_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $watermark_file['file'];
991
-
992
- // imagick extension
993
- if ( $this->extension === 'imagick' ) {
994
- // create image resource
995
- $image = new Imagick( $image_path );
996
-
997
- // create watermark resource
998
- $watermark = new Imagick( $watermark_path );
999
-
1000
- // alpha channel exists?
1001
- if ( $watermark->getImageAlphaChannel() > 0 )
1002
- $watermark->evaluateImage( Imagick::EVALUATE_MULTIPLY, round( (float) ( $options['watermark_image']['transparent'] / 100 ), 2 ), Imagick::CHANNEL_ALPHA );
1003
- // no alpha channel
1004
- else
1005
- $watermark->setImageOpacity( round( (float) ( $options['watermark_image']['transparent'] / 100 ), 2 ) );
1006
-
1007
- // set compression quality
1008
- if ( $mime['type'] === 'image/jpeg' ) {
1009
- $image->setImageCompressionQuality( $options['watermark_image']['quality'] );
1010
- $image->setImageCompression( imagick::COMPRESSION_JPEG );
1011
- } else
1012
- $image->setImageCompressionQuality( $options['watermark_image']['quality'] );
1013
-
1014
- // set image output to progressive
1015
- if ( $options['watermark_image']['jpeg_format'] === 'progressive' )
1016
- $image->setImageInterlaceScheme( Imagick::INTERLACE_PLANE );
1017
-
1018
- // get image dimensions
1019
- $image_dim = $image->getImageGeometry();
1020
-
1021
- // get watermark dimensions
1022
- $watermark_dim = $watermark->getImageGeometry();
1023
-
1024
- // calculate watermark new dimensions
1025
- list( $width, $height ) = $this->calculate_watermark_dimensions( $image_dim['width'], $image_dim['height'], $watermark_dim['width'], $watermark_dim['height'], $options );
1026
-
1027
- // resize watermark
1028
- $watermark->resizeImage( $width, $height, imagick::FILTER_CATROM, 1 );
1029
-
1030
- // calculate image coordinates
1031
- list( $dest_x, $dest_y ) = $this->calculate_image_coordinates( $image_dim['width'], $image_dim['height'], $width, $height, $options );
1032
-
1033
- // combine two images together
1034
- $image->compositeImage( $watermark, Imagick::COMPOSITE_DEFAULT, $dest_x, $dest_y, Imagick::CHANNEL_ALL );
1035
-
1036
- // save watermarked image
1037
- $image->writeImage( $image_path );
1038
-
1039
- // clear image memory
1040
- $image->clear();
1041
- $image->destroy();
1042
- $image = null;
1043
-
1044
- // clear watermark memory
1045
- $watermark->clear();
1046
- $watermark->destroy();
1047
- $watermark = null;
1048
- // gd extension
1049
- } else {
1050
- // get image resource
1051
- $image = $this->get_image_resource( $image_path, $mime['type'] );
1052
-
1053
- if ( $image !== false ) {
1054
- // add watermark image to image
1055
- $image = $this->add_watermark_image( $image, $options, $upload_dir );
1056
-
1057
- if ( $image !== false ) {
1058
- // save watermarked image
1059
- $this->save_image_file( $image, $mime['type'], $image_path, $options['watermark_image']['quality'] );
1060
-
1061
- // clear watermark memory
1062
- imagedestroy( $image );
1063
-
1064
- $image = null;
1065
- }
1066
- }
1067
- }
1068
- }
1069
-
1070
- /**
1071
- * Make a backup of the full size image.
1072
- *
1073
- * @param array $data
1074
- * @param string $upload_dir
1075
- * @param int $attachment_id
1076
- * @return bool
1077
- */
1078
- private function do_backup( $data, $upload_dir, $attachment_id ) {
1079
- // get the filepath for the backup image we're creating
1080
- $backup_filepath = $this->get_image_backup_filepath( $data['file'] );
1081
-
1082
- // make sure the backup isn't created yet
1083
- if ( ! file_exists( $backup_filepath ) ) {
1084
- // the original (full size) image
1085
- $filepath = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $data['file'];
1086
- $mime = wp_check_filetype( $filepath );
1087
-
1088
- // get image resource
1089
- $image = $this->get_image_resource( $filepath, $mime['type'] );
1090
-
1091
- if ( $image !== false ) {
1092
- // create backup directory if needed
1093
- wp_mkdir_p( $this->get_image_backup_folder_location( $data['file'] ) );
1094
-
1095
- // get path to the backup file
1096
- $path = pathinfo( $backup_filepath );
1097
-
1098
- // create subfolders in backup folder if needed
1099
- wp_mkdir_p( $path['dirname'] );
1100
-
1101
- // save backup image
1102
- $this->save_image_file( $image, $mime['type'], $backup_filepath, $this->options['backup']['backup_quality'] );
1103
-
1104
- // clear backup memory
1105
- imagedestroy( $image );
1106
- $image = null;
1107
- }
1108
- }
1109
- }
1110
-
1111
- /**
1112
- * Get image resource accordingly to mimetype.
1113
- *
1114
- * @param string $filepath
1115
- * @param string $mime_type
1116
- * @return resource
1117
- */
1118
- private function get_image_resource( $filepath, $mime_type ) {
1119
- switch ( $mime_type ) {
1120
- case 'image/jpeg':
1121
- case 'image/pjpeg':
1122
- $image = imagecreatefromjpeg( $filepath );
1123
- break;
1124
-
1125
- case 'image/png':
1126
- $image = imagecreatefrompng( $filepath );
1127
-
1128
- if ( is_resource( $image ) )
1129
- imagefilledrectangle( $image, 0, 0, imagesx( $image ), imagesy( $image ), imagecolorallocatealpha( $image, 255, 255, 255, 127 ) );
1130
- break;
1131
-
1132
- default:
1133
- $image = false;
1134
- }
1135
-
1136
- if ( is_resource( $image ) ) {
1137
- imagealphablending( $image, false );
1138
- imagesavealpha( $image, true );
1139
- }
1140
-
1141
- return $image;
1142
- }
1143
-
1144
- /**
1145
- * Get image filename without the uploaded folders.
1146
- *
1147
- * @param string $filepath
1148
- * @return string $filename
1149
- */
1150
- private function get_image_filename( $filepath ) {
1151
- return basename( $filepath );
1152
- }
1153
-
1154
- /**
1155
- * Get image backup folder.
1156
- *
1157
- * @param string $filepath
1158
- * @return string $image_backup_folder
1159
- */
1160
- private function get_image_backup_folder_location( $filepath ) {
1161
- $path = explode( DIRECTORY_SEPARATOR, $filepath );
1162
- array_pop( $path );
1163
- $path = implode( DIRECTORY_SEPARATOR, $path );
1164
-
1165
- // Multisite?
1166
- /* if ( is_multisite() && ! is_main_site() ) {
1167
- $path = 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . $path;
1168
- } */
1169
-
1170
- return IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . $path;
1171
- }
1172
-
1173
- /**
1174
- * Get image resource from the backup folder (if available).
1175
- *
1176
- * @param string $filepath
1177
- * @return string $backup_filepath
1178
- */
1179
- private function get_image_backup_filepath( $filepath ) {
1180
- // Multisite?
1181
- /* if ( is_multisite() && ! is_main_site() ) {
1182
- $filepath = 'sites' . DIRECTORY_SEPARATOR . get_current_blog_id() . DIRECTORY_SEPARATOR . $filepath;
1183
- } */
1184
- return IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . $filepath;
1185
- }
1186
-
1187
- /**
1188
- * Delete the image backup if one exists.
1189
- *
1190
- * @param int $attachment_id
1191
- * @return bool $force_delete
1192
- */
1193
- public function delete_attachment( $attachment_id ) {
1194
- // see get_attached_file() in wp-includes/post.php
1195
- $filepath = get_post_meta( $attachment_id, '_wp_attached_file', true );
1196
- $backup_filepath = $this->get_image_backup_filepath( $filepath );
1197
-
1198
- if ( file_exists( $backup_filepath ) ) {
1199
- unlink( $backup_filepath );
1200
- }
1201
- }
1202
-
1203
- /**
1204
- * Create admin notice when we can't create the backup folder.
1205
- *
1206
- * @return void
1207
- */
1208
- function folder_writable_admin_notice() {
1209
- if ( current_user_can( 'manage_options' ) && true !== $this->is_backup_folder_writable ) {
1210
- ?>
1211
- <div class="notice notice-error is-dismissible">
1212
- <p><?php _e( 'Image Watermark', 'image-watermark' ); ?> - <?php _e( 'Image backup', 'image-watermark' ); ?>: <?php _e( "Your uploads folder is not writable so we can't create a backup of your image uploads. We've disabled this feature for now.", 'image-watermark' ); ?></p>
1213
- </div>
1214
- <?php
1215
- }
1216
- }
1217
-
1218
- /**
1219
- * Calculate watermark dimensions.
1220
- *
1221
- * @param $image_width Image width
1222
- * @param $image_height Image height
1223
- * @param $watermark_width Watermark width
1224
- * @param $watermark_height Watermark height
1225
- * @param $options Options
1226
- * @return array Watermark new dimensions
1227
- */
1228
- private function calculate_watermark_dimensions( $image_width, $image_height, $watermark_width, $watermark_height, $options ) {
1229
- // custom
1230
- if ( $options['watermark_image']['watermark_size_type'] === 1 ) {
1231
- $width = $options['watermark_image']['absolute_width'];
1232
- $height = $options['watermark_image']['absolute_height'];
1233
- // scale
1234
- } elseif ( $options['watermark_image']['watermark_size_type'] === 2 ) {
1235
- $ratio = $image_width * $options['watermark_image']['width'] / 100 / $watermark_width;
1236
-
1237
- $width = (int) ( $watermark_width * $ratio );
1238
- $height = (int) ( $watermark_height * $ratio );
1239
-
1240
- // if watermark scaled height is bigger then image watermark
1241
- if ( $height > $image_height ) {
1242
- $width = (int) ( $image_height * $width / $height );
1243
- $height = $image_height;
1244
- }
1245
- // original
1246
- } else {
1247
- $width = $watermark_width;
1248
- $height = $watermark_height;
1249
- }
1250
-
1251
- return array( $width, $height );
1252
- }
1253
-
1254
- /**
1255
- * Calculate image coordinates for watermark.
1256
- *
1257
- * @param $image_width Image width
1258
- * @param $image_height Image height
1259
- * @param $watermark_width Watermark width
1260
- * @param $watermark_height Watermark height
1261
- * @param $options Options
1262
- * @return array Image coordinates
1263
- */
1264
- private function calculate_image_coordinates( $image_width, $image_height, $watermark_width, $watermark_height, $options ) {
1265
- switch ( $options['watermark_image']['position'] ) {
1266
- case 'top_left':
1267
- $dest_x = $dest_y = 0;
1268
- break;
1269
-
1270
- case 'top_center':
1271
- $dest_x = ( $image_width / 2 ) - ( $watermark_width / 2 );
1272
- $dest_y = 0;
1273
- break;
1274
-
1275
- case 'top_right':
1276
- $dest_x = $image_width - $watermark_width;
1277
- $dest_y = 0;
1278
- break;
1279
-
1280
- case 'middle_left':
1281
- $dest_x = 0;
1282
- $dest_y = ( $image_height / 2 ) - ( $watermark_height / 2 );
1283
- break;
1284
-
1285
- case 'middle_right':
1286
- $dest_x = $image_width - $watermark_width;
1287
- $dest_y = ( $image_height / 2 ) - ( $watermark_height / 2 );
1288
- break;
1289
-
1290
- case 'bottom_left':
1291
- $dest_x = 0;
1292
- $dest_y = $image_height - $watermark_height;
1293
- break;
1294
-
1295
- case 'bottom_center':
1296
- $dest_x = ( $image_width / 2 ) - ( $watermark_width / 2 );
1297
- $dest_y = $image_height - $watermark_height;
1298
- break;
1299
-
1300
- case 'bottom_right':
1301
- $dest_x = $image_width - $watermark_width;
1302
- $dest_y = $image_height - $watermark_height;
1303
- break;
1304
-
1305
- case 'middle_center':
1306
- default:
1307
- $dest_x = ( $image_width / 2 ) - ( $watermark_width / 2 );
1308
- $dest_y = ( $image_height / 2 ) - ( $watermark_height / 2 );
1309
- }
1310
-
1311
- if ( $options['watermark_image']['offset_unit'] === 'pixels' ) {
1312
- $dest_x += $options['watermark_image']['offset_width'];
1313
- $dest_y += $options['watermark_image']['offset_height'];
1314
- } else {
1315
- $dest_x += (int) ( $image_width * $options['watermark_image']['offset_width'] / 100 );
1316
- $dest_y += (int) ( $image_height * $options['watermark_image']['offset_height'] / 100 );
1317
- }
1318
-
1319
- return array( $dest_x, $dest_y );
1320
- }
1321
-
1322
- /**
1323
- * Add watermark image to an image.
1324
- *
1325
- * @param resource $image Image resource
1326
- * @param array $options Plugin options
1327
- * @param array $upload_dir WP upload dir data
1328
- * @return mixed Watermarked image
1329
- */
1330
- private function add_watermark_image( $image, $options, $upload_dir ) {
1331
- if ( ! wp_attachment_is_image( $options['watermark_image']['url'] ) )
1332
- return false;
1333
-
1334
- $watermark_file = wp_get_attachment_metadata( $options['watermark_image']['url'], true );
1335
- $url = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $watermark_file['file'];
1336
- $watermark_file_info = getimagesize( $url );
1337
-
1338
- switch ( $watermark_file_info['mime'] ) {
1339
- case 'image/jpeg':
1340
- case 'image/pjpeg':
1341
- $watermark = imagecreatefromjpeg( $url );
1342
- break;
1343
-
1344
- case 'image/gif':
1345
- $watermark = imagecreatefromgif( $url );
1346
- break;
1347
-
1348
- case 'image/png':
1349
- $watermark = imagecreatefrompng( $url );
1350
- break;
1351
-
1352
- default:
1353
- return false;
1354
- }
1355
-
1356
- // get image dimensions
1357
- $image_width = imagesx( $image );
1358
- $image_height = imagesy( $image );
1359
-
1360
- // calculate watermark new dimensions
1361
- list( $w, $h ) = $this->calculate_watermark_dimensions( $image_width, $image_height, imagesx( $watermark ), imagesy( $watermark ), $options );
1362
-
1363
- // calculate image coordinates
1364
- list( $dest_x, $dest_y ) = $this->calculate_image_coordinates( $image_width, $image_height, $w, $h, $options );
1365
-
1366
- // combine two images together
1367
- $this->imagecopymerge_alpha( $image, $this->resize( $watermark, $w, $h, $watermark_file_info ), $dest_x, $dest_y, 0, 0, $w, $h, $options['watermark_image']['transparent'] );
1368
-
1369
- if ( $options['watermark_image']['jpeg_format'] === 'progressive' )
1370
- imageinterlace( $image, true );
1371
-
1372
- return $image;
1373
- }
1374
-
1375
- /**
1376
- * Create a new image function.
1377
- *
1378
- * @param resource $dst_im
1379
- * @param resource $src_im
1380
- * @param int $dst_x
1381
- * @param int $dst_y
1382
- * @param int $src_x
1383
- * @param int $src_y
1384
- * @param int $src_w
1385
- * @param int $src_h
1386
- * @param int $pct
1387
- */
1388
- private function imagecopymerge_alpha( $dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct ) {
1389
- // create a cut resource
1390
- $cut = imagecreatetruecolor( $src_w, $src_h );
1391
-
1392
- // copy relevant section from background to the cut resource
1393
- imagecopy( $cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h );
1394
-
1395
- // copy relevant section from watermark to the cut resource
1396
- imagecopy( $cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h );
1397
-
1398
- // insert cut resource to destination image
1399
- imagecopymerge( $dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct );
1400
- }
1401
-
1402
- /**
1403
- * Resize image.
1404
- *
1405
- * @param resource $image Image resource
1406
- * @param int $width Image width
1407
- * @param int $height Image height
1408
- * @param array $info Image data
1409
- * @return resource Resized image
1410
- */
1411
- private function resize( $image, $width, $height, $info ) {
1412
- $new_image = imagecreatetruecolor( $width, $height );
1413
-
1414
- // check if this image is PNG, then set if transparent
1415
- if ( $info[2] === 3 ) {
1416
- imagealphablending( $new_image, false );
1417
- imagesavealpha( $new_image, true );
1418
- imagefilledrectangle( $new_image, 0, 0, $width, $height, imagecolorallocatealpha( $new_image, 255, 255, 255, 127 ) );
1419
- }
1420
-
1421
- imagecopyresampled( $new_image, $image, 0, 0, 0, 0, $width, $height, $info[0], $info[1] );
1422
-
1423
- return $new_image;
1424
- }
1425
-
1426
- /**
1427
- * Save image from image resource.
1428
- *
1429
- * @param resource $image Image resource
1430
- * @param string $mime_type Image mime type
1431
- * @param string $filepath Path where image should be saved
1432
- * @return void
1433
- */
1434
- private function save_image_file( $image, $mime_type, $filepath, $quality ) {
1435
- switch ( $mime_type ) {
1436
- case 'image/jpeg':
1437
- case 'image/pjpeg':
1438
- imagejpeg( $image, $filepath, $quality );
1439
- break;
1440
-
1441
- case 'image/png':
1442
- imagepng( $image, $filepath, (int) round( 9 - ( 9 * $quality / 100 ), 0 ) );
1443
- break;
1444
- }
1445
- }
1446
-
1447
- /**
1448
- * Add links to support forum.
1449
- *
1450
- * @param array $links
1451
- * @param string $file
1452
- * @return array
1453
- */
1454
- public function plugin_extend_links( $links, $file ) {
1455
- if ( ! current_user_can( 'install_plugins' ) )
1456
- return $links;
1457
-
1458
- $plugin = plugin_basename( __FILE__ );
1459
-
1460
- if ( $file == $plugin ) {
1461
- return array_merge(
1462
- $links, array( sprintf( '<a href="http://www.dfactory.eu/support/forum/image-watermark/" target="_blank">%s</a>', __( 'Support', 'image-watermark' ) ) )
1463
- );
1464
- }
1465
-
1466
- return $links;
1467
- }
1468
-
1469
- /**
1470
- * Add links to settings page.
1471
- *
1472
- * @param array $links
1473
- * @param string $file
1474
- * @return array
1475
- */
1476
- function plugin_settings_link( $links, $file ) {
1477
- if ( ! is_admin() || ! current_user_can( 'manage_options' ) )
1478
- return $links;
1479
-
1480
- static $plugin;
1481
-
1482
- $plugin = plugin_basename( __FILE__ );
1483
-
1484
- if ( $file == $plugin ) {
1485
- $settings_link = sprintf( '<a href="%s">%s</a>', admin_url( 'options-general.php' ) . '?page=watermark-options', __( 'Settings', 'image-watermark' ) );
1486
- array_unshift( $links, $settings_link );
1487
- }
1488
-
1489
- return $links;
1490
- }
1491
-
1492
- }
1493
-
1494
- /**
1495
- * Get instance of main class.
1496
- *
1497
- * @return object Instance
1498
- */
1499
- function Image_Watermark() {
1500
- static $instance;
1501
-
1502
- // first call to instance() initializes the plugin
1503
- if ( $instance === null || ! ( $instance instanceof Image_Watermark ) )
1504
- $instance = Image_Watermark::instance();
1505
-
1506
- return $instance;
1507
- }
1508
-
 
 
 
 
 
 
 
 
 
1509
  $image_watermark = Image_Watermark();
1
+ <?php
2
+ /*
3
+ Plugin Name: Image Watermark
4
+ Description: Image Watermark allows you to automatically watermark images uploaded to the WordPress Media Library and bulk watermark previously uploaded images.
5
+ Version: 1.7.0
6
+ Author: dFactory
7
+ Author URI: https://dfactory.eu/
8
+ Plugin URI: https://dfactory.eu/plugins/image-watermark/
9
+ License: MIT License
10
+ License URI: http://opensource.org/licenses/MIT
11
+ Text Domain: image-watermark
12
+ Domain Path: /languages
13
+
14
+ Image Watermark
15
+ Copyright (C) 2013-2022, Digital Factory - info@digitalfactory.pl
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ */
23
+
24
+ // exit if accessed directly
25
+ if ( ! defined( 'ABSPATH' ) )
26
+ exit;
27
+
28
+ define( 'IMAGE_WATERMARK_URL', plugins_url( '', __FILE__ ) );
29
+ define( 'IMAGE_WATERMARK_PATH', plugin_dir_path( __FILE__ ) );
30
+
31
+ /**
32
+ * Image Watermark class.
33
+ *
34
+ * @class Image_Watermark
35
+ * @version 1.7.0
36
+ */
37
+ final class Image_Watermark {
38
+
39
+ private static $instance;
40
+ private $is_admin = true;
41
+ private $extension = false;
42
+ private $allowed_mime_types = array(
43
+ 'image/jpeg',
44
+ 'image/pjpeg',
45
+ 'image/png'
46
+ );
47
+ private $is_watermarked_metakey = 'iw-is-watermarked';
48
+ public $is_backup_folder_writable = null;
49
+ public $extensions;
50
+ public $defaults = array(
51
+ 'options' => array(
52
+ 'watermark_on' => array(),
53
+ 'watermark_cpt_on' => array( 'everywhere' ),
54
+ 'watermark_image' => array(
55
+ 'extension' => '',
56
+ 'url' => 0,
57
+ 'width' => 80,
58
+ 'plugin_off' => 0,
59
+ 'frontend_active' => false,
60
+ 'manual_watermarking' => 0,
61
+ 'position' => 'bottom_right',
62
+ 'watermark_size_type' => 2,
63
+ 'offset_unit' => 'pixels',
64
+ 'offset_width' => 0,
65
+ 'offset_height' => 0,
66
+ 'absolute_width' => 0,
67
+ 'absolute_height' => 0,
68
+ 'transparent' => 50,
69
+ 'quality' => 90,
70
+ 'jpeg_format' => 'baseline',
71
+ 'deactivation_delete' => false,
72
+ 'media_library_notice' => true
73
+ ),
74
+ 'image_protection' => array(
75
+ 'rightclick' => 0,
76
+ 'draganddrop' => 0,
77
+ 'forlogged' => 0
78
+ ),
79
+ 'backup' => array(
80
+ 'backup_image' => true,
81
+ 'backup_quality' => 90
82
+ )
83
+ ),
84
+ 'version' => '1.7.0'
85
+ );
86
+ public $options = array();
87
+
88
+ /**
89
+ * Class constructor.
90
+ *
91
+ * @return void
92
+ */
93
+ public function __construct() {
94
+ // installer
95
+ register_activation_hook( __FILE__, array( $this, 'activate_watermark' ) );
96
+ register_deactivation_hook( __FILE__, array( $this, 'deactivate_watermark' ) );
97
+
98
+ // settings
99
+ $options = get_option( 'image_watermark_options', $this->defaults['options'] );
100
+
101
+ $this->options = array_merge( $this->defaults['options'], $options );
102
+ $this->options['watermark_image'] = array_merge( $this->defaults['options']['watermark_image'], $options['watermark_image'] );
103
+ $this->options['image_protection'] = array_merge( $this->defaults['options']['image_protection'], $options['image_protection'] );
104
+ $this->options['backup'] = array_merge( $this->defaults['options']['backup'], isset( $options['backup'] ) ? $options['backup'] : array() );
105
+
106
+ include_once( IMAGE_WATERMARK_PATH . 'includes/class-update.php' );
107
+ include_once( IMAGE_WATERMARK_PATH . 'includes/class-settings.php' );
108
+
109
+ // actions
110
+ add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
111
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
112
+ add_action( 'admin_print_scripts', array( $this, 'admin_print_scripts' ), 20 );
113
+ add_action( 'wp_enqueue_media', array( $this, 'wp_enqueue_media' ) );
114
+ add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
115
+ add_action( 'load-upload.php', array( $this, 'watermark_bulk_action' ) );
116
+ add_action( 'admin_init', array( $this, 'update_plugin' ) );
117
+ add_action( 'admin_init', array( $this, 'check_extensions' ) );
118
+ add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) );
119
+ add_action( 'delete_attachment', array( $this, 'delete_attachment' ) );
120
+ add_action( 'wp_ajax_iw_watermark_bulk_action', array( $this, 'watermark_action_ajax' ) );
121
+
122
+ // filters
123
+ add_filter( 'plugin_row_meta', array( $this, 'plugin_extend_links' ), 10, 2 );
124
+ add_filter( 'plugin_action_links', array( $this, 'plugin_settings_link' ), 10, 2 );
125
+ add_filter( 'wp_handle_upload', array( $this, 'handle_upload_files' ) );
126
+ add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields_to_edit' ), 10, 2 );
127
+
128
+ // define our backup location
129
+ $upload_dir = wp_upload_dir();
130
+ define( 'IMAGE_WATERMARK_BACKUP_DIR', apply_filters( 'image_watermark_backup_dir', $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'iw-backup' ) );
131
+
132
+ // create backup folder and security if enabled
133
+ if ( $this->options['backup']['backup_image'] ) {
134
+ if ( is_writable( $upload_dir['basedir'] ) ) {
135
+ $this->is_backup_folder_writable = true;
136
+
137
+ // create backup folder ( if it exists this returns true: https://codex.wordpress.org/Function_Reference/wp_mkdir_p )
138
+ $backup_folder_created = wp_mkdir_p( IMAGE_WATERMARK_BACKUP_DIR );
139
+
140
+ // check if the folder exists and is writable
141
+ if ( $backup_folder_created && is_writable( IMAGE_WATERMARK_BACKUP_DIR ) ) {
142
+ // check if the htaccess file exists
143
+ if ( ! file_exists( IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . '.htaccess' ) ) {
144
+ // htaccess security
145
+ file_put_contents( IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . '.htaccess', 'deny from all' );
146
+ }
147
+ } else
148
+ $this->is_backup_folder_writable = false;
149
+ } else
150
+ $this->is_backup_folder_writable = false;
151
+
152
+ if ( $this->is_backup_folder_writable !== true ) {
153
+ // disable backup setting
154
+ $this->options['backup']['backup_image'] = false;
155
+ update_option( 'image_watermark_options', $this->options );
156
+ }
157
+
158
+ add_action( 'admin_notices', array( $this, 'folder_writable_admin_notice' ) );
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Disable object cloning.
164
+ *
165
+ * @return void
166
+ */
167
+ public function __clone() {}
168
+
169
+ /**
170
+ * Disable unserializing of the class.
171
+ *
172
+ * @return void
173
+ */
174
+ public function __wakeup() {}
175
+
176
+ /**
177
+ * Create single instance.
178
+ *
179
+ * @return object Main plugin instance
180
+ */
181
+ public static function instance() {
182
+ if ( self::$instance === null )
183
+ self::$instance = new self();
184
+
185
+ return self::$instance;
186
+ }
187
+
188
+ /**
189
+ * Plugin activation.
190
+ *
191
+ * @return void
192
+ */
193
+ public function activate_watermark() {
194
+ add_option( 'image_watermark_options', $this->defaults['options'], '', 'no' );
195
+ add_option( 'image_watermark_version', $this->defaults['version'], '', 'no' );
196
+ }
197
+
198
+ /**
199
+ * Plugin deactivation.
200
+ *
201
+ * @return void
202
+ */
203
+ public function deactivate_watermark() {
204
+ // remove options from database?
205
+ if ( $this->options['watermark_image']['deactivation_delete'] )
206
+ delete_option( 'image_watermark_options' );
207
+ }
208
+
209
+ /**
210
+ * Plugin update, fix for version < 1.5.0.
211
+ *
212
+ * @return void
213
+ */
214
+ public function update_plugin() {
215
+ if ( ! current_user_can( 'install_plugins' ) )
216
+ return;
217
+
218
+ $db_version = get_option( 'image_watermark_version' );
219
+ $db_version = ! ( $db_version ) && ( get_option( 'df_watermark_installed' ) != false ) ? get_option( 'version' ) : $db_version;
220
+
221
+ if ( $db_version != false ) {
222
+ if ( version_compare( $db_version, '1.5.0', '<' ) ) {
223
+ $options = array();
224
+
225
+ $old_new = array(
226
+ 'df_watermark_on' => 'watermark_on',
227
+ 'df_watermark_cpt_on' => 'watermark_cpt_on',
228
+ 'df_watermark_image' => 'watermark_image',
229
+ 'df_image_protection' => 'image_protection',
230
+ 'df_watermark_installed' => '',
231
+ 'version' => '',
232
+ 'image_watermark_version' => '',
233
+ );
234
+
235
+ foreach ( $old_new as $old => $new ) {
236
+ if ( $new ) {
237
+ $options[$new] = get_option( $old );
238
+ }
239
+ delete_option( $old );
240
+ }
241
+
242
+ add_option( 'image_watermark_options', $options, '', 'no' );
243
+ add_option( 'image_watermark_version', $this->defaults['version'], '', 'no' );
244
+ }
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Load textdomain.
250
+ *
251
+ * @return void
252
+ */
253
+ public function load_textdomain() {
254
+ load_plugin_textdomain( 'image-watermark', false, basename( dirname( __FILE__ ) ) . '/languages' );
255
+ }
256
+
257
+ /**
258
+ * Admin inline scripts.
259
+ *
260
+ * @global $pagenow
261
+ * @return void
262
+ */
263
+ public function admin_print_scripts() {
264
+ global $pagenow;
265
+
266
+ if ( $pagenow === 'upload.php' ) {
267
+ if ( $this->options['watermark_image']['manual_watermarking'] == 1 ) {
268
+ ?>
269
+ <script type="text/javascript">
270
+ jQuery( function( $ ) {
271
+ $( document ).ready( function() {
272
+ var backup = <?php echo (int) $this->options['backup']['backup_image']; ?>;
273
+
274
+ $( "<option>" ).val( "applywatermark" ).text( "<?php _e( 'Apply watermark', 'image-watermark' ); ?>" ).appendTo( "select[name='action'], select[name='action2']" );
275
+
276
+ if ( backup === 1 ) {
277
+ $( "<option>" ).val( "removewatermark" ).text( "<?php _e( 'Remove watermark', 'image-watermark' ); ?>" ).appendTo( "select[name='action'], select[name='action2']" );
278
+ }
279
+ });
280
+ });
281
+ </script>
282
+ <?php
283
+ }
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Enqueue admin scripts and styles.
289
+ *
290
+ * @return void
291
+ */
292
+ public function wp_enqueue_media( $page ) {
293
+ wp_enqueue_style( 'watermark-style', plugins_url( 'css/image-watermark.css', __FILE__ ), array(), $this->defaults['version'] );
294
+ }
295
+
296
+ /**
297
+ * Enqueue admin scripts and styles.
298
+ *
299
+ * @global $pagenow
300
+ * @return void
301
+ */
302
+ public function admin_enqueue_scripts( $page ) {
303
+ global $pagenow;
304
+
305
+ wp_register_style( 'watermark-style', plugins_url( 'css/image-watermark.css', __FILE__ ), array(), $this->defaults['version'] );
306
+
307
+ if ( $page === 'settings_page_watermark-options' ) {
308
+ wp_enqueue_media();
309
+
310
+ wp_enqueue_script( 'upload-manager', plugins_url( '/js/admin-upload.js', __FILE__ ), array(), $this->defaults['version'] );
311
+
312
+ wp_localize_script(
313
+ 'upload-manager',
314
+ 'iwUploadArgs',
315
+ array(
316
+ 'title' => __( 'Select watermark', 'image-watermark' ),
317
+ 'originalSize' => __( 'Original size', 'image-watermark' ),
318
+ 'noSelectedImg' => __( 'Watermak has not been selected yet.', 'image-watermark' ),
319
+ 'notAllowedImg' => __( 'This image is not supported as watermark. Use JPEG, PNG or GIF.', 'image-watermark' ),
320
+ 'px' => __( 'px', 'image-watermark' ),
321
+ 'frame' => 'select',
322
+ 'button' => array( 'text' => __( 'Add watermark', 'image-watermark' ) ),
323
+ 'multiple' => false
324
+ )
325
+ );
326
+
327
+ wp_enqueue_script( 'watermark-admin-script', plugins_url( 'js/admin-settings.js', __FILE__ ), array( 'jquery', 'jquery-ui-core', 'jquery-ui-button', 'jquery-ui-slider' ), $this->defaults['version'] );
328
+
329
+ wp_localize_script(
330
+ 'watermark-admin-script',
331
+ 'iwArgs',
332
+ array(
333
+ 'resetToDefaults' => __( 'Are you sure you want to reset settings to defaults?', 'image-watermark' )
334
+ )
335
+ );
336
+
337
+ wp_enqueue_style( 'wp-like-ui-theme', plugins_url( 'css/wp-like-ui-theme.css', __FILE__ ), array(), $this->defaults['version'] );
338
+ wp_enqueue_style( 'watermark-style' );
339
+
340
+ wp_enqueue_script( 'postbox' );
341
+ }
342
+
343
+ if ( $pagenow === 'upload.php' )
344
+ wp_enqueue_style( 'watermark-style' );
345
+
346
+ // image modal could be loaded in various places
347
+ if ( $this->options['watermark_image']['manual_watermarking'] == 1 ) {
348
+ wp_enqueue_script( 'watermark-admin-image-actions', plugins_url( '/js/admin-image-actions.js', __FILE__ ), array( 'jquery' ), $this->defaults['version'], true );
349
+
350
+ wp_localize_script(
351
+ 'watermark-admin-image-actions',
352
+ 'iwImageActionArgs',
353
+ array(
354
+ 'backup_image' => (int) $this->options['backup']['backup_image'],
355
+ '_nonce' => wp_create_nonce( 'image-watermark' ),
356
+ '__applied_none' => __( 'Watermark could not be applied to selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ),
357
+ '__applied_one' => __( 'Watermark was succesfully applied to 1 image.', 'image-watermark' ),
358
+ '__applied_multi' => __( 'Watermark was succesfully applied to %s images.', 'image-watermark' ),
359
+ '__removed_none' => __( 'Watermark could not be removed from selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ),
360
+ '__removed_one' => __( 'Watermark was succesfully removed from 1 image.', 'image-watermark' ),
361
+ '__removed_multi' => __( 'Watermark was succesfully removed from %s images.', 'image-watermark' ),
362
+ '__skipped' => __( 'Skipped files', 'image-watermark' ),
363
+ '__running' => __( 'Bulk action is currently running, please wait.', 'image-watermark' ),
364
+ '__dismiss' => __( 'Dismiss this notice.' ) // WordPress default string
365
+ )
366
+ );
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Enqueue frontend script with 'no right click' and 'drag and drop' functions.
372
+ *
373
+ * @return void
374
+ */
375
+ public function wp_enqueue_scripts() {
376
+ $right_click = true;
377
+
378
+ if ( ($this->options['image_protection']['forlogged'] == 0 && is_user_logged_in()) || ($this->options['image_protection']['draganddrop'] == 0 && $this->options['image_protection']['rightclick'] == 0) )
379
+ $right_click = false;
380
+
381
+ if ( apply_filters( 'iw_block_right_click', (bool) $right_click ) === true ) {
382
+ wp_enqueue_script( 'iw-no-right-click', plugins_url( 'js/no-right-click.js', __FILE__ ), array(), $this->defaults['version'] );
383
+
384
+ wp_localize_script(
385
+ 'iw-no-right-click',
386
+ 'IwNRCargs',
387
+ array(
388
+ 'rightclick' => ( $this->options['image_protection']['rightclick'] == 1 ? 'Y' : 'N' ),
389
+ 'draganddrop' => ( $this->options['image_protection']['draganddrop'] == 1 ? 'Y' : 'N' )
390
+ )
391
+ );
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Check which extension is available and set it.
397
+ *
398
+ * @return void
399
+ */
400
+ public function check_extensions() {
401
+ $ext = null;
402
+
403
+ if ( $this->check_imagick() ) {
404
+ $this->extensions['imagick'] = 'ImageMagick';
405
+ $ext = 'imagick';
406
+ }
407
+
408
+ if ( $this->check_gd() ) {
409
+ $this->extensions['gd'] = 'GD';
410
+
411
+ if ( is_null( $ext ) )
412
+ $ext = 'gd';
413
+ }
414
+
415
+ if ( isset( $this->options['watermark_image']['extension'] ) ) {
416
+ if ( $this->options['watermark_image']['extension'] === 'imagick' && isset( $this->extensions['imagick'] ) )
417
+ $this->extension = 'imagick';
418
+ elseif ( $this->options['watermark_image']['extension'] === 'gd' && isset( $this->extensions['gd'] ) )
419
+ $this->extension = 'gd';
420
+ else
421
+ $this->extension = $ext;
422
+ } else
423
+ $this->extension = $ext;
424
+ }
425
+
426
+ /**
427
+ * Apply watermark everywhere or for specific post types.
428
+ *
429
+ * @param resource $file
430
+ * @return resource
431
+ */
432
+ public function handle_upload_files( $file ) {
433
+ // is extension available?
434
+ if ( $this->extension ) {
435
+ // determine ajax frontend or backend request
436
+ $script_filename = isset( $_SERVER['SCRIPT_FILENAME'] ) ? $_SERVER['SCRIPT_FILENAME'] : '';
437
+
438
+ // try to figure out if frontend AJAX request... if we are DOING_AJAX; let's look closer
439
+ if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
440
+ // from wp-includes/functions.php, wp_get_referer() function.
441
+ // required to fix: https://core.trac.wordpress.org/ticket/25294
442
+ $ref = '';
443
+
444
+ if ( ! empty( $_REQUEST['_wp_http_referer'] ) )
445
+ $ref = wp_unslash( $_REQUEST['_wp_http_referer'] );
446
+ elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) )
447
+ $ref = wp_unslash( $_SERVER['HTTP_REFERER'] );
448
+
449
+ // if referer does not contain admin URL and we are using the admin-ajax.php endpoint, this is likely a frontend AJAX request
450
+ if ( ( ( strpos( $ref, admin_url() ) === false ) && ( basename( $script_filename ) === 'admin-ajax.php' ) ) )
451
+ $this->is_admin = false;
452
+ else
453
+ $this->is_admin = true;
454
+ // not an AJAX request, simple here
455
+ } else {
456
+ if ( is_admin() )
457
+ $this->is_admin = true;
458
+ else
459
+ $this->is_admin = false;
460
+ }
461
+
462
+ // admin
463
+ if ( $this->is_admin === true ) {
464
+ if ( $this->options['watermark_image']['plugin_off'] == 1 && wp_attachment_is_image( $this->options['watermark_image']['url'] ) && in_array( $file['type'], $this->allowed_mime_types ) )
465
+ add_filter( 'wp_generate_attachment_metadata', array( $this, 'apply_watermark' ), 10, 2 );
466
+ // frontend
467
+ } else {
468
+ if ( $this->options['watermark_image']['frontend_active'] == 1 && wp_attachment_is_image( $this->options['watermark_image']['url'] ) && in_array( $file['type'], $this->allowed_mime_types ) )
469
+ add_filter( 'wp_generate_attachment_metadata', array( $this, 'apply_watermark' ), 10, 2 );
470
+ }
471
+ }
472
+
473
+ return $file;
474
+ }
475
+
476
+ /**
477
+ * Add watermark buttons on attachment image locations.
478
+ *
479
+ * @param array $form_fields
480
+ * @param object $post
481
+ * return array
482
+ */
483
+ public function attachment_fields_to_edit( $form_fields, $post ) {
484
+ if ( $this->options['watermark_image']['manual_watermarking'] == 1 && $this->options['backup']['backup_image'] ) {
485
+ $data = wp_get_attachment_metadata( $post->ID, false );
486
+
487
+ // is this really an image?
488
+ if ( in_array( get_post_mime_type( $post->ID ), $this->allowed_mime_types ) && is_array( $data ) ) {
489
+ $form_fields['image_watermark'] = array(
490
+ 'show_in_edit' => false,
491
+ 'tr' => '
492
+ <div id="image_watermark_buttons"' . ( get_post_meta( $post->ID, $this->is_watermarked_metakey, true ) ? ' class="watermarked"' : '' ) . ' data-id="' . $post->ID . '" style="display: none;">
493
+ <label class="setting">
494
+ <span class="name">' . __( 'Image Watermark', 'image-watermark' ) . '</span>
495
+ <span class="value" style="width: 63%"><a href="#" class="iw-watermark-action" data-action="applywatermark" data-id="' . $post->ID . '">' . __( 'Apply watermark', 'image-watermark' ) . '</a> | <a href="#" class="iw-watermark-action delete-watermark" data-action="removewatermark" data-id="' . $post->ID . '">' . __( 'Remove watermark', 'image-watermark' ) . '</a></span>
496
+ </label>
497
+ <div class="clear"></div>
498
+ </div>
499
+ <script>
500
+ jQuery( document ).ready( function( $ ) {
501
+ if ( typeof watermarkImageActions !== "undefined" ) {
502
+ $( "#image_watermark_buttons" ).show();
503
+ }
504
+ } );
505
+ </script>'
506
+ );
507
+ }
508
+ }
509
+
510
+ return $form_fields;
511
+ }
512
+
513
+ /**
514
+ * Apply watermark for selected images on media page.
515
+ *
516
+ * @return void
517
+ */
518
+ public function watermark_action_ajax() {
519
+ // Security & data check
520
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! isset( $_POST['_iw_nonce'] ) || ! isset( $_POST['iw-action'] ) || ! isset( $_POST['attachment_id'] ) || ! is_numeric( $_POST['attachment_id'] ) || ! wp_verify_nonce( $_POST['_iw_nonce'], 'image-watermark' ) )
521
+ wp_send_json_error( __( 'Cheatin uh?', 'image-watermark' ) );
522
+
523
+ $post_id = (int) $_POST['attachment_id'];
524
+ $action = in_array( $_POST['iw-action'], array( 'applywatermark', 'removewatermark' ), true ) ? $_POST['iw-action'] : false;
525
+
526
+ // only if manual watermarking is turned and we have a valid action
527
+ // if the action is NOT "removewatermark" we also require a watermark image to be set
528
+ if ( $post_id > 0 && $action && $this->options['watermark_image']['manual_watermarking'] == 1 && ( wp_attachment_is_image( $this->options['watermark_image']['url'] ) || $action == 'removewatermark' ) ) {
529
+ $data = wp_get_attachment_metadata( $post_id, false );
530
+
531
+ // is this really an image?
532
+ if ( in_array( get_post_mime_type( $post_id ), $this->allowed_mime_types ) && is_array( $data ) ) {
533
+ if ( $action === 'applywatermark' ) {
534
+ $success = $this->apply_watermark( $data, $post_id, 'manual' );
535
+
536
+ if ( ! empty( $success['error'] ) )
537
+ wp_send_json_success( $success['error'] );
538
+ else
539
+ wp_send_json_success( 'watermarked' );
540
+ } elseif ( $action === 'removewatermark' ) {
541
+ $success = $this->remove_watermark( $data, $post_id, 'manual' );
542
+
543
+ if ( $success )
544
+ wp_send_json_success( 'watermarkremoved' );
545
+ else
546
+ wp_send_json_success( 'skipped' );
547
+ }
548
+ } else
549
+ wp_send_json_success( 'skipped' );
550
+ }
551
+
552
+ wp_send_json_error( __( 'Cheatin uh?', 'image-watermark' ) );
553
+ }
554
+
555
+ /**
556
+ * Apply watermark for selected images on media page.
557
+ *
558
+ * @return void
559
+ */
560
+ public function watermark_bulk_action() {
561
+ global $pagenow;
562
+
563
+ if ( $pagenow == 'upload.php' && $this->extension ) {
564
+ $wp_list_table = _get_list_table( 'WP_Media_List_Table' );
565
+ $action = $wp_list_table->current_action();
566
+ $action = in_array( $action, array( 'applywatermark', 'removewatermark' ), true ) ? $action : false;
567
+
568
+ // only if manual watermarking is turned and we have a valid action
569
+ // if the action is NOT "removewatermark" we also require a watermark image to be set
570
+ if ( $action && $this->options['watermark_image']['manual_watermarking'] == 1 && ( wp_attachment_is_image( $this->options['watermark_image']['url'] ) || $action == 'removewatermark' ) ) {
571
+ // security check
572
+ check_admin_referer( 'bulk-media' );
573
+
574
+ $location = esc_url( remove_query_arg( array( 'watermarked', 'watermarkremoved', 'skipped', 'trashed', 'untrashed', 'deleted', 'message', 'ids', 'posted' ), wp_get_referer() ) );
575
+
576
+ if ( ! $location )
577
+ $location = 'upload.php';
578
+
579
+ $location = esc_url( add_query_arg( 'paged', $wp_list_table->get_pagenum(), $location ) );
580
+
581
+ // make sure ids are submitted. depending on the resource type, this may be 'media' or 'ids'
582
+ if ( isset( $_REQUEST['media'] ) )
583
+ $post_ids = array_map( 'intval', $_REQUEST['media'] );
584
+
585
+ // do we have selected attachments?
586
+ if ( $post_ids ) {
587
+ $watermarked = $watermarkremoved = $skipped = 0;
588
+ $messages = array();
589
+
590
+ foreach ( $post_ids as $post_id ) {
591
+ $data = wp_get_attachment_metadata( $post_id, false );
592
+
593
+ // is this really an image?
594
+ if ( in_array( get_post_mime_type( $post_id ), $this->allowed_mime_types ) && is_array( $data ) ) {
595
+ if ( $action === 'applywatermark' ) {
596
+ $success = $this->apply_watermark( $data, $post_id, 'manual' );
597
+ if ( ! empty( $success['error'] ) )
598
+ $messages[] = $success['error'];
599
+ else {
600
+ $watermarked++;
601
+ $watermarkremoved = -1;
602
+ }
603
+ } elseif ( $action === 'removewatermark' ) {
604
+ $success = $this->remove_watermark( $data, $post_id, 'manual' );
605
+
606
+ if ( $success )
607
+ $watermarkremoved++;
608
+ else
609
+ $skipped++;
610
+
611
+ $watermarked = -1;
612
+ }
613
+ } else
614
+ $skipped++;
615
+ }
616
+
617
+ $location = esc_url( add_query_arg( array( 'watermarked' => $watermarked, 'watermarkremoved' => $watermarkremoved, 'skipped' => $skipped, 'messages' => $messages ), $location ), null, '' );
618
+ }
619
+
620
+ wp_redirect( $location );
621
+ exit();
622
+ } else
623
+ return;
624
+ }
625
+ }
626
+
627
+ /**
628
+ * Display admin notices.
629
+ *
630
+ * @return void
631
+ */
632
+ public function bulk_admin_notices() {
633
+ global $post_type, $pagenow;
634
+
635
+ if ( $pagenow === 'upload.php' ) {
636
+ if ( ! current_user_can( 'upload_files' ) )
637
+ return;
638
+
639
+ // hide media library notice
640
+ if ( isset( $_GET['iw_action'] ) && $_GET['iw_action'] == 'hide_library_notice' ) {
641
+ $this->options['watermark_image']['media_library_notice'] = false;
642
+
643
+ update_option( 'image_watermark_options', $this->options );
644
+ }
645
+
646
+ // check if manual watermarking is enabled
647
+ if ( ! empty( $this->options['watermark_image']['manual_watermarking'] ) && ( ! isset( $this->options['watermark_image']['media_library_notice'] ) || $this->options['watermark_image']['media_library_notice'] === true ) ) {
648
+ $mode = get_user_option( 'media_library_mode', get_current_user_id() ) ? get_user_option( 'media_library_mode', get_current_user_id() ) : 'grid';
649
+
650
+ if ( isset( $_GET['mode'] ) && in_array( $_GET['mode'], array( 'grid', 'list' ) ) )
651
+ $mode = $_GET['mode'];
652
+
653
+ // display notice in grid mode only
654
+ if ( $mode === 'grid' ) {
655
+ // get current admin url
656
+ $query_string = array();
657
+
658
+ parse_str( $_SERVER['QUERY_STRING'], $query_string );
659
+
660
+ $current_url = esc_url( add_query_arg( array_merge( (array) $query_string, array( 'iw_action' => 'hide_library_notice' ) ), '', admin_url( trailingslashit( $pagenow ) ) ) );
661
+
662
+ echo '<div class="error notice"><p>' . sprintf( __( '<strong>Image Watermark:</strong> Bulk watermarking is available in list mode only, under <em>Bulk Actions</em> dropdown. <a href="%1$s">Got to List Mode</a> or <a href="%2$s">Hide this notice</a>', 'image-watermark' ), esc_url( admin_url( 'upload.php?mode=list' ) ), esc_url( $current_url ) ) . '</p></div>';
663
+ }
664
+ }
665
+
666
+ if ( isset( $_REQUEST['watermarked'], $_REQUEST['watermarkremoved'], $_REQUEST['skipped'] ) && $post_type === 'attachment' ) {
667
+ $watermarked = (int) $_REQUEST['watermarked'];
668
+ $watermarkremoved = (int) $_REQUEST['watermarkremoved'];
669
+ $skipped = (int) $_REQUEST['skipped'];
670
+
671
+ if ( $watermarked === 0 )
672
+ echo '<div class="error"><p>' . __( 'Watermark could not be applied to selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ) . ($skipped > 0 ? ' ' . __( 'Images skipped', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
673
+ elseif ( $watermarked > 0 )
674
+ echo '<div class="updated"><p>' . sprintf( _n( 'Watermark was succesfully applied to 1 image.', 'Watermark was succesfully applied to %s images.', $watermarked, 'image-watermark' ), number_format_i18n( $watermarked ) ) . ($skipped > 0 ? ' ' . __( 'Skipped files', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
675
+
676
+ if ( $watermarkremoved === 0 )
677
+ echo '<div class="error"><p>' . __( 'Watermark could not be removed from selected files or no valid images (JPEG, PNG) were selected.', 'image-watermark' ) . ($skipped > 0 ? ' ' . __( 'Images skipped', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
678
+ elseif ( $watermarkremoved > 0 )
679
+ echo '<div class="updated"><p>' . sprintf( _n( 'Watermark was succesfully removed from 1 image.', 'Watermark was succesfully removed from %s images.', $watermarkremoved, 'image-watermark' ), number_format_i18n( $watermarkremoved ) ) . ($skipped > 0 ? ' ' . __( 'Skipped files', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
680
+
681
+ $_SERVER['REQUEST_URI'] = esc_url( remove_query_arg( array( 'watermarked', 'skipped' ), $_SERVER['REQUEST_URI'] ) );
682
+ }
683
+ }
684
+ }
685
+
686
+ /**
687
+ * Check whether ImageMagick extension is available.
688
+ *
689
+ * @return bool
690
+ */
691
+ public function check_imagick() {
692
+ // check Imagick's extension and classes
693
+ if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick', false ) || ! class_exists( 'ImagickPixel', false ) )
694
+ return false;
695
+
696
+ // check version
697
+ if ( version_compare( phpversion( 'imagick' ), '2.2.0', '<' ) )
698
+ return false;
699
+
700
+ // check for deep requirements within Imagick
701
+ if ( ! defined( 'imagick::COMPRESSION_JPEG' ) || ! defined( 'imagick::COMPOSITE_OVERLAY' ) || ! defined( 'Imagick::INTERLACE_PLANE' ) || ! defined( 'imagick::FILTER_CATROM' ) || ! defined( 'Imagick::CHANNEL_ALL' ) )
702
+ return false;
703
+
704
+ // check methods
705
+ if ( array_diff( array( 'clear', 'destroy', 'valid', 'getimage', 'writeimage', 'getimagegeometry', 'getimageformat', 'setimageformat', 'setimagecompression', 'setimagecompressionquality', 'scaleimage' ), get_class_methods( 'Imagick' ) ) )
706
+ return false;
707
+
708
+ return true;
709
+ }
710
+
711
+ /**
712
+ * Check whether GD extension is available.
713
+ *
714
+ * @return bool
715
+ */
716
+ public function check_gd( $args = array() ) {
717
+ // check extension
718
+ if ( ! extension_loaded( 'gd' ) || ! function_exists( 'gd_info' ) )
719
+ return false;
720
+
721
+ return true;
722
+ }
723
+
724
+ /**
725
+ * Apply watermark to selected image sizes.
726
+ *
727
+ * @param array $data
728
+ * @param int|string $attachment_id Attachment ID
729
+ * @param string $method
730
+ * @return array
731
+ */
732
+ public function apply_watermark( $data, $attachment_id, $method = '' ) {
733
+ $attachment_id = (int) $attachment_id;
734
+ $post = get_post( $attachment_id );
735
+ $post_id = ( ! empty( $post ) ? (int) $post->post_parent : 0 );
736
+
737
+ if ( $attachment_id === (int) $this->options['watermark_image']['url'] ) {
738
+ // this is the current watermark, do not apply
739
+ return array( 'error' => __( 'Watermark prevented, this is your selected watermark image', 'image-watermark' ) );
740
+ }
741
+
742
+ // something went wrong or is it automatic mode?
743
+ if ( $method !== 'manual' && ( $this->is_admin === true && ! ( ( isset( $this->options['watermark_cpt_on'][0] ) && $this->options['watermark_cpt_on'][0] === 'everywhere' ) || ( $post_id > 0 && in_array( get_post_type( $post_id ), array_keys( $this->options['watermark_cpt_on'] ) ) === true ) ) ) )
744
+ return $data;
745
+
746
+ if ( apply_filters( 'iw_watermark_display', $attachment_id ) === false )
747
+ return $data;
748
+
749
+ // get upload dir data
750
+ $upload_dir = wp_upload_dir();
751
+
752
+ // assign original (full) file
753
+ $original_file = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $data['file'];
754
+
755
+ // is this really an image?
756
+ if ( getimagesize( $original_file, $original_image_info ) !== false ) {
757
+ $metadata = $this->get_image_metadata( $original_image_info );
758
+
759
+ // remove the watermark if this image was already watermarked
760
+ if ( (int) get_post_meta( $attachment_id, $this->is_watermarked_metakey, true ) === 1 )
761
+ $this->remove_watermark( $data, $attachment_id, 'manual' );
762
+
763
+ // create a backup if this is enabled
764
+ if ( $this->options['backup']['backup_image'] )
765
+ $this->do_backup( $data, $upload_dir, $attachment_id );
766
+
767
+ // loop through active image sizes
768
+ foreach ( $this->options['watermark_on'] as $image_size => $active_size ) {
769
+ if ( $active_size === 1 ) {
770
+ switch ( $image_size ) {
771
+ case 'full':
772
+ $filepath = $original_file;
773
+ break;
774
+
775
+ default:
776
+ if ( ! empty( $data['sizes'] ) && array_key_exists( $image_size, $data['sizes'] ) )
777
+ $filepath = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . dirname( $data['file'] ) . DIRECTORY_SEPARATOR . $data['sizes'][$image_size]['file'];
778
+ else
779
+ // early getaway
780
+ continue 2;
781
+ }
782
+
783
+ do_action( 'iw_before_apply_watermark', $attachment_id, $image_size );
784
+
785
+ // apply watermark
786
+ $this->do_watermark( $attachment_id, $filepath, $image_size, $upload_dir, $metadata );
787
+
788
+ // save metadata
789
+ $this->save_image_metadata( $metadata, $filepath );
790
+
791
+ do_action( 'iw_after_apply_watermark', $attachment_id, $image_size );
792
+ }
793
+ }
794
+
795
+ // update watermark status
796
+ update_post_meta( $attachment_id, $this->is_watermarked_metakey, 1 );
797
+ }
798
+
799
+ // pass forward attachment metadata
800
+ return $data;
801
+ }
802
+
803
+ /**
804
+ * Remove watermark from selected image sizes.
805
+ *
806
+ * @param array $data
807
+ * @param int|string $attachment_id Attachment ID
808
+ * @param string $method
809
+ * @return array
810
+ */
811
+ private function remove_watermark( $data, $attachment_id, $method = '' ) {
812
+ if ( $method !== 'manual' )
813
+ return $data;
814
+
815
+ $upload_dir = wp_upload_dir();
816
+
817
+ // is this really an image?
818
+ if ( getimagesize( $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $data['file'] ) !== false ) {
819
+ // live file path (probably watermarked)
820
+ $filepath = get_attached_file( $attachment_id );
821
+
822
+ // backup file path (not watermarked)
823
+ $backup_filepath = $this->get_image_backup_filepath( get_post_meta( $attachment_id, '_wp_attached_file', true ) );
824
+
825
+ // replace the image in uploads with our backup if one exists
826
+ if ( file_exists( $backup_filepath ) ) {
827
+ if ( ! copy( $backup_filepath, $filepath ) ) {
828
+ // Failed to copy
829
+ }
830
+ }
831
+
832
+ // if no backup exists, use the current full-size image to regenerate
833
+ // if the "full" size is enabled for watermarks and no backup has been made the removal of watermarks can't be done
834
+
835
+ // regenerate metadata (and thumbs)
836
+ $metadata = wp_generate_attachment_metadata( $attachment_id, $filepath );
837
+
838
+ // update attachment metadata with new metadata
839
+ wp_update_attachment_metadata( $attachment_id, $metadata );
840
+
841
+ // update watermark status
842
+ update_post_meta( $attachment_id, $this->is_watermarked_metakey, 0 );
843
+
844
+ // ureturn the attachment metadata
845
+ return wp_get_attachment_metadata( $attachment_id );
846
+ }
847
+
848
+ return false;
849
+ }
850
+
851
+ /**
852
+ * Get image metadata.
853
+ *
854
+ * @param array $imageinfo
855
+ * @return array
856
+ */
857
+ public function get_image_metadata( $imageinfo ) {
858
+ $metadata = array(
859
+ 'exif' => null,
860
+ 'iptc' => null
861
+ );
862
+
863
+ if ( is_array( $imageinfo ) ) {
864
+ // prepare EXIF data bytes from source file
865
+ $exifdata = key_exists( 'APP1', $imageinfo ) ? $imageinfo['APP1'] : null;
866
+
867
+ if ( $exifdata ) {
868
+ $exiflength = strlen( $exifdata ) + 2;
869
+
870
+ // construct EXIF segment
871
+ if ( $exiflength > 0xFFFF ) {
872
+ return $metadata;
873
+ } else
874
+ $metadata['exif'] = chr( 0xFF ) . chr( 0xE1 ) . chr( ( $exiflength >> 8 ) & 0xFF ) . chr( $exiflength & 0xFF ) . $exifdata;
875
+ }
876
+
877
+ // prepare IPTC data bytes from source file
878
+ $iptcdata = key_exists( 'APP13', $imageinfo ) ? $imageinfo['APP13'] : null;
879
+
880
+ if ( $iptcdata ) {
881
+ $iptclength = strlen( $iptcdata ) + 2;
882
+
883
+ // construct IPTC segment
884
+ if ( $iptclength > 0xFFFF ) {
885
+ return $metadata;
886
+ } else
887
+ $metadata['iptc'] = chr( 0xFF ) . chr( 0xED ) . chr( ( $iptclength >> 8 ) & 0xFF ) . chr( $iptclength & 0xFF ) . $iptcdata;
888
+ }
889
+ }
890
+
891
+ return $metadata;
892
+ }
893
+
894
+ /**
895
+ * Save EXIF and IPTC metadata from one image to another.
896
+ *
897
+ * @param array $metadata
898
+ * @param string $file
899
+ * @return bool|int
900
+ */
901
+ public function save_image_metadata( $metadata, $file ) {
902
+ $mime = wp_check_filetype( $file );
903
+
904
+ if ( file_exists( $file ) && $mime['type'] !== 'image/png' ) {
905
+ $exifdata = $metadata['exif'];
906
+ $iptcdata = $metadata['iptc'];
907
+
908
+ $destfilecontent = @file_get_contents( $file );
909
+
910
+ if ( ! $destfilecontent )
911
+ return false;
912
+
913
+ if ( strlen( $destfilecontent ) > 0 ) {
914
+ $destfilecontent = substr( $destfilecontent, 2 );
915
+
916
+ // variable accumulates new & original IPTC application segments
917
+ $portiontoadd = chr( 0xFF ) . chr( 0xD8 );
918
+
919
+ $exifadded = ! $exifdata;
920
+ $iptcadded = ! $iptcdata;
921
+
922
+ while ( ( $this->get_safe_chunk( substr( $destfilecontent, 0, 2 ) ) & 0xFFF0 ) === 0xFFE0 ) {
923
+ $segmentlen = ( $this->get_safe_chunk( substr( $destfilecontent, 2, 2 ) ) & 0xFFFF );
924
+
925
+ // last 4 bits of second byte is IPTC segment
926
+ $iptcsegmentnumber = ( $this->get_safe_chunk( substr( $destfilecontent, 1, 1 ) ) & 0x0F );
927
+
928
+ if ( $segmentlen <= 2 )
929
+ return false;
930
+
931
+ $thisexistingsegment = substr( $destfilecontent, 0, $segmentlen + 2 );
932
+
933
+ if ( ( $iptcsegmentnumber >= 1 ) && ( ! $exifadded ) ) {
934
+ $portiontoadd .= $exifdata;
935
+ $exifadded = true;
936
+
937
+ if ( $iptcsegmentnumber === 1 )
938
+ $thisexistingsegment = '';
939
+ }
940
+
941
+ if ( ( $iptcsegmentnumber >= 13 ) && ( ! $iptcadded ) ) {
942
+ $portiontoadd .= $iptcdata;
943
+ $iptcadded = true;
944
+
945
+ if ( $iptcsegmentnumber === 13 )
946
+ $thisexistingsegment = '';
947
+ }
948
+
949
+ $portiontoadd .= $thisexistingsegment;
950
+ $destfilecontent = substr( $destfilecontent, $segmentlen + 2 );
951
+ }
952
+
953
+ // add EXIF data if not added already
954
+ if ( ! $exifadded )
955
+ $portiontoadd .= $exifdata;
956
+
957
+ // add IPTC data if not added already
958
+ if ( ! $iptcadded )
959
+ $portiontoadd .= $iptcdata;
960
+
961
+ $outputfile = fopen( $file, 'w' );
962
+
963
+ if ( $outputfile )
964
+ return fwrite( $outputfile, $portiontoadd . $destfilecontent );
965
+ else
966
+ return false;
967
+ } else
968
+ return false;
969
+ } else
970
+ return false;
971
+ }
972
+
973
+ /**
974
+ * Get integer value of binary chunk.
975
+ *
976
+ * @param bin $value Binary data
977
+ * @return int
978
+ */
979
+ private function get_safe_chunk( $value ) {
980
+ // check for numeric value
981
+ if ( is_numeric( $value ) ) {
982
+ // cast to integer to do bitwise AND operation
983
+ return (int) $value;
984
+ } else
985
+ return 0;
986
+ }
987
+
988
+ /**
989
+ * Apply watermark to image.
990
+ *
991
+ * @param int $attachment_id Attachment ID
992
+ * @param string $image_path Path to the file
993
+ * @param string $image_size Image size
994
+ * @param array $upload_dir Upload media data
995
+ * @param array $metadata EXIF and ITPC metadata
996
+ * @return void
997
+ */
998
+ public function do_watermark( $attachment_id, $image_path, $image_size, $upload_dir, $metadata = array() ) {
999
+ $options = apply_filters( 'iw_watermark_options', $this->options );
1000
+
1001
+ // get image mime type
1002
+ $mime = wp_check_filetype( $image_path );
1003
+
1004
+ if ( ! wp_attachment_is_image( $options['watermark_image']['url'] ) )
1005
+ return;
1006
+
1007
+ // get watermark path
1008
+ $watermark_file = wp_get_attachment_metadata( $options['watermark_image']['url'], true );
1009
+ $watermark_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $watermark_file['file'];
1010
+
1011
+ // imagick extension
1012
+ if ( $this->extension === 'imagick' ) {
1013
+ // create image resource
1014
+ $image = new Imagick( $image_path );
1015
+
1016
+ // create watermark resource
1017
+ $watermark = new Imagick( $watermark_path );
1018
+
1019
+ // alpha channel exists?
1020
+ if ( $watermark->getImageAlphaChannel() > 0 )
1021
+ $watermark->evaluateImage( Imagick::EVALUATE_MULTIPLY, round( (float) ( $options['watermark_image']['transparent'] / 100 ), 2 ), Imagick::CHANNEL_ALPHA );
1022
+ // no alpha channel
1023
+ else
1024
+ $watermark->setImageOpacity( round( (float) ( $options['watermark_image']['transparent'] / 100 ), 2 ) );
1025
+
1026
+ // set compression quality
1027
+ if ( $mime['type'] === 'image/jpeg' ) {
1028
+ $image->setImageCompressionQuality( $options['watermark_image']['quality'] );
1029
+ $image->setImageCompression( imagick::COMPRESSION_JPEG );
1030
+ } else
1031
+ $image->setImageCompressionQuality( $options['watermark_image']['quality'] );
1032
+
1033
+ // set image output to progressive
1034
+ if ( $options['watermark_image']['jpeg_format'] === 'progressive' )
1035
+ $image->setImageInterlaceScheme( Imagick::INTERLACE_PLANE );
1036
+
1037
+ // get image dimensions
1038
+ $image_dim = $image->getImageGeometry();
1039
+
1040
+ // get watermark dimensions
1041
+ $watermark_dim = $watermark->getImageGeometry();
1042
+
1043
+ // calculate watermark new dimensions
1044
+ list( $width, $height ) = $this->calculate_watermark_dimensions( $image_dim['width'], $image_dim['height'], $watermark_dim['width'], $watermark_dim['height'], $options );
1045
+
1046
+ // resize watermark
1047
+ $watermark->resizeImage( $width, $height, imagick::FILTER_CATROM, 1 );
1048
+
1049
+ // calculate image coordinates
1050
+ list( $dest_x, $dest_y ) = $this->calculate_image_coordinates( $image_dim['width'], $image_dim['height'], $width, $height, $options );
1051
+
1052
+ // combine two images together
1053
+ $image->compositeImage( $watermark, Imagick::COMPOSITE_DEFAULT, $dest_x, $dest_y, Imagick::CHANNEL_ALL );
1054
+
1055
+ // save watermarked image
1056
+ $image->writeImage( $image_path );
1057
+
1058
+ // clear image memory
1059
+ $image->clear();
1060
+ $image->destroy();
1061
+ $image = null;
1062
+
1063
+ // clear watermark memory
1064
+ $watermark->clear();
1065
+ $watermark->destroy();
1066
+ $watermark = null;
1067
+ // gd extension
1068
+ } else {
1069
+ // get image resource
1070
+ $image = $this->get_image_resource( $image_path, $mime['type'] );
1071
+
1072
+ if ( $image !== false ) {
1073
+ // add watermark image to image
1074
+ $image = $this->add_watermark_image( $image, $options, $upload_dir );
1075
+
1076
+ if ( $image !== false ) {
1077
+ // save watermarked image
1078
+ $this->save_image_file( $image, $mime['type'], $image_path, $options['watermark_image']['quality'] );
1079
+
1080
+ // clear watermark memory
1081
+ imagedestroy( $image );
1082
+
1083
+ $image = null;
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+
1089
+ /**
1090
+ * Make a backup of the full size image.
1091
+ *
1092
+ * @param array $data
1093
+ * @param string $upload_dir
1094
+ * @param int $attachment_id
1095
+ * @return bool
1096
+ */
1097
+ private function do_backup( $data, $upload_dir, $attachment_id ) {
1098
+ // get the filepath for the backup image we're creating
1099
+ $backup_filepath = $this->get_image_backup_filepath( $data['file'] );
1100
+
1101
+ // make sure the backup isn't created yet
1102
+ if ( ! file_exists( $backup_filepath ) ) {
1103
+ // the original (full size) image
1104
+ $filepath = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $data['file'];
1105
+ $mime = wp_check_filetype( $filepath );
1106
+
1107
+ // get image resource
1108
+ $image = $this->get_image_resource( $filepath, $mime['type'] );
1109
+
1110
+ if ( $image !== false ) {
1111
+ // create backup directory if needed
1112
+ wp_mkdir_p( $this->get_image_backup_folder_location( $data['file'] ) );
1113
+
1114
+ // get path to the backup file
1115
+ $path = pathinfo( $backup_filepath );
1116
+
1117
+ // create subfolders in backup folder if needed
1118
+ wp_mkdir_p( $path['dirname'] );
1119
+
1120
+ // save backup image
1121
+ $this->save_image_file( $image, $mime['type'], $backup_filepath, $this->options['backup']['backup_quality'] );
1122
+
1123
+ // clear backup memory
1124
+ imagedestroy( $image );
1125
+ $image = null;
1126
+ }
1127
+ }
1128
+ }
1129
+
1130
+ /**
1131
+ * Get image resource accordingly to mimetype.
1132
+ *
1133
+ * @param string $filepath
1134
+ * @param string $mime_type
1135
+ * @return resource
1136
+ */
1137
+ private function get_image_resource( $filepath, $mime_type ) {
1138
+ switch ( $mime_type ) {
1139
+ case 'image/jpeg':
1140
+ case 'image/pjpeg':
1141
+ $image = imagecreatefromjpeg( $filepath );
1142
+ break;
1143
+
1144
+ case 'image/png':
1145
+ $image = imagecreatefrompng( $filepath );
1146
+
1147
+ if ( is_resource( $image ) )
1148
+ imagefilledrectangle( $image, 0, 0, imagesx( $image ), imagesy( $image ), imagecolorallocatealpha( $image, 255, 255, 255, 127 ) );
1149
+ break;
1150
+
1151
+ default:
1152
+ $image = false;
1153
+ }
1154
+
1155
+ if ( is_resource( $image ) ) {
1156
+ imagealphablending( $image, false );
1157
+ imagesavealpha( $image, true );
1158
+ }
1159
+
1160
+ return $image;
1161
+ }
1162
+
1163
+ /**
1164
+ * Get image filename without the uploaded folders.
1165
+ *
1166
+ * @param string $filepath
1167
+ * @return string $filename
1168
+ */
1169
+ private function get_image_filename( $filepath ) {
1170
+ return basename( $filepath );
1171
+ }
1172
+
1173
+ /**
1174
+ * Get image backup folder.
1175
+ *
1176
+ * @param string $filepath
1177
+ * @return string
1178
+ */
1179
+ private function get_image_backup_folder_location( $filepath ) {
1180
+ $path = explode( DIRECTORY_SEPARATOR, $filepath );
1181
+
1182
+ array_pop( $path );
1183
+
1184
+ $path = implode( DIRECTORY_SEPARATOR, $path );
1185
+
1186
+ return IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . $path;
1187
+ }
1188
+
1189
+ /**
1190
+ * Get image resource from the backup folder (if available).
1191
+ *
1192
+ * @param string $filepath
1193
+ * @return string
1194
+ */
1195
+ private function get_image_backup_filepath( $filepath ) {
1196
+ return IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . $filepath;
1197
+ }
1198
+
1199
+ /**
1200
+ * Delete the image backup if one exists.
1201
+ *
1202
+ * @param int $attachment_id
1203
+ * @return void
1204
+ */
1205
+ public function delete_attachment( $attachment_id ) {
1206
+ // see get_attached_file() in wp-includes/post.php
1207
+ $filepath = get_post_meta( $attachment_id, '_wp_attached_file', true );
1208
+ $backup_filepath = $this->get_image_backup_filepath( $filepath );
1209
+
1210
+ if ( file_exists( $backup_filepath ) )
1211
+ unlink( $backup_filepath );
1212
+ }
1213
+
1214
+ /**
1215
+ * Create admin notice when we can't create the backup folder.
1216
+ *
1217
+ * @return void
1218
+ */
1219
+ function folder_writable_admin_notice() {
1220
+ if ( current_user_can( 'manage_options' ) && $this->is_backup_folder_writable !== true ) {
1221
+ ?>
1222
+ <div class="notice notice-error is-dismissible">
1223
+ <p><?php _e( 'Image Watermark', 'image-watermark' ); ?> - <?php _e( 'Image backup', 'image-watermark' ); ?>: <?php _e( "Your uploads folder is not writable so we can't create a backup of your image uploads. We've disabled this feature for now.", 'image-watermark' ); ?></p>
1224
+ </div>
1225
+ <?php
1226
+ }
1227
+ }
1228
+
1229
+ /**
1230
+ * Calculate watermark dimensions.
1231
+ *
1232
+ * @param int $image_width Image width
1233
+ * @param int $image_height Image height
1234
+ * @param int $watermark_width Watermark width
1235
+ * @param int $watermark_height Watermark height
1236
+ * @param array $options
1237
+ * @return array
1238
+ */
1239
+ private function calculate_watermark_dimensions( $image_width, $image_height, $watermark_width, $watermark_height, $options ) {
1240
+ // custom
1241
+ if ( $options['watermark_image']['watermark_size_type'] === 1 ) {
1242
+ $width = $options['watermark_image']['absolute_width'];
1243
+ $height = $options['watermark_image']['absolute_height'];
1244
+ // scale
1245
+ } elseif ( $options['watermark_image']['watermark_size_type'] === 2 ) {
1246
+ $ratio = $image_width * $options['watermark_image']['width'] / 100 / $watermark_width;
1247
+
1248
+ $width = (int) ( $watermark_width * $ratio );
1249
+ $height = (int) ( $watermark_height * $ratio );
1250
+
1251
+ // if watermark scaled height is bigger then image watermark
1252
+ if ( $height > $image_height ) {
1253
+ $width = (int) ( $image_height * $width / $height );
1254
+ $height = $image_height;
1255
+ }
1256
+ // original
1257
+ } else {
1258
+ $width = $watermark_width;
1259
+ $height = $watermark_height;
1260
+ }
1261
+
1262
+ return array( $width, $height );
1263
+ }
1264
+
1265
+ /**
1266
+ * Calculate image coordinates for watermark.
1267
+ *
1268
+ * @param int $image_width Image width
1269
+ * @param int $image_height Image height
1270
+ * @param int $watermark_width Watermark width
1271
+ * @param int $watermark_height Watermark height
1272
+ * @param array $options Options
1273
+ * @return array
1274
+ */
1275
+ private function calculate_image_coordinates( $image_width, $image_height, $watermark_width, $watermark_height, $options ) {
1276
+ switch ( $options['watermark_image']['position'] ) {
1277
+ case 'top_left':
1278
+ $dest_x = $dest_y = 0;
1279
+ break;
1280
+
1281
+ case 'top_center':
1282
+ $dest_x = ( $image_width / 2 ) - ( $watermark_width / 2 );
1283
+ $dest_y = 0;
1284
+ break;
1285
+
1286
+ case 'top_right':
1287
+ $dest_x = $image_width - $watermark_width;
1288
+ $dest_y = 0;
1289
+ break;
1290
+
1291
+ case 'middle_left':
1292
+ $dest_x = 0;
1293
+ $dest_y = ( $image_height / 2 ) - ( $watermark_height / 2 );
1294
+ break;
1295
+
1296
+ case 'middle_right':
1297
+ $dest_x = $image_width - $watermark_width;
1298
+ $dest_y = ( $image_height / 2 ) - ( $watermark_height / 2 );
1299
+ break;
1300
+
1301
+ case 'bottom_left':
1302
+ $dest_x = 0;
1303
+ $dest_y = $image_height - $watermark_height;
1304
+ break;
1305
+
1306
+ case 'bottom_center':
1307
+ $dest_x = ( $image_width / 2 ) - ( $watermark_width / 2 );
1308
+ $dest_y = $image_height - $watermark_height;
1309
+ break;
1310
+
1311
+ case 'bottom_right':
1312
+ $dest_x = $image_width - $watermark_width;
1313
+ $dest_y = $image_height - $watermark_height;
1314
+ break;
1315
+
1316
+ case 'middle_center':
1317
+ default:
1318
+ $dest_x = ( $image_width / 2 ) - ( $watermark_width / 2 );
1319
+ $dest_y = ( $image_height / 2 ) - ( $watermark_height / 2 );
1320
+ }
1321
+
1322
+ if ( $options['watermark_image']['offset_unit'] === 'pixels' ) {
1323
+ $dest_x += $options['watermark_image']['offset_width'];
1324
+ $dest_y += $options['watermark_image']['offset_height'];
1325
+ } else {
1326
+ $dest_x += (int) ( $image_width * $options['watermark_image']['offset_width'] / 100 );
1327
+ $dest_y += (int) ( $image_height * $options['watermark_image']['offset_height'] / 100 );
1328
+ }
1329
+
1330
+ return array( $dest_x, $dest_y );
1331
+ }
1332
+
1333
+ /**
1334
+ * Add watermark image to an image.
1335
+ *
1336
+ * @param resource $image Image resource
1337
+ * @param array $options Plugin options
1338
+ * @param array $upload_dir WP upload dir data
1339
+ * @return bool|resource
1340
+ */
1341
+ private function add_watermark_image( $image, $options, $upload_dir ) {
1342
+ if ( ! wp_attachment_is_image( $options['watermark_image']['url'] ) )
1343
+ return false;
1344
+
1345
+ $watermark_file = wp_get_attachment_metadata( $options['watermark_image']['url'], true );
1346
+ $url = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $watermark_file['file'];
1347
+ $watermark_file_info = getimagesize( $url );
1348
+
1349
+ switch ( $watermark_file_info['mime'] ) {
1350
+ case 'image/jpeg':
1351
+ case 'image/pjpeg':
1352
+ $watermark = imagecreatefromjpeg( $url );
1353
+ break;
1354
+
1355
+ case 'image/gif':
1356
+ $watermark = imagecreatefromgif( $url );
1357
+ break;
1358
+
1359
+ case 'image/png':
1360
+ $watermark = imagecreatefrompng( $url );
1361
+ break;
1362
+
1363
+ default:
1364
+ return false;
1365
+ }
1366
+
1367
+ // get image dimensions
1368
+ $image_width = imagesx( $image );
1369
+ $image_height = imagesy( $image );
1370
+
1371
+ // calculate watermark new dimensions
1372
+ list( $w, $h ) = $this->calculate_watermark_dimensions( $image_width, $image_height, imagesx( $watermark ), imagesy( $watermark ), $options );
1373
+
1374
+ // calculate image coordinates
1375
+ list( $dest_x, $dest_y ) = $this->calculate_image_coordinates( $image_width, $image_height, $w, $h, $options );
1376
+
1377
+ // combine two images together
1378
+ $this->imagecopymerge_alpha( $image, $this->resize( $watermark, $w, $h, $watermark_file_info ), $dest_x, $dest_y, 0, 0, $w, $h, $options['watermark_image']['transparent'] );
1379
+
1380
+ if ( $options['watermark_image']['jpeg_format'] === 'progressive' )
1381
+ imageinterlace( $image, true );
1382
+
1383
+ return $image;
1384
+ }
1385
+
1386
+ /**
1387
+ * Create a new image function.
1388
+ *
1389
+ * @param resource $dst_im
1390
+ * @param resource $src_im
1391
+ * @param int $dst_x
1392
+ * @param int $dst_y
1393
+ * @param int $src_x
1394
+ * @param int $src_y
1395
+ * @param int $src_w
1396
+ * @param int $src_h
1397
+ * @param int $pct
1398
+ * @return void
1399
+ */
1400
+ private function imagecopymerge_alpha( $dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct ) {
1401
+ // create a cut resource
1402
+ $cut = imagecreatetruecolor( $src_w, $src_h );
1403
+
1404
+ // copy relevant section from background to the cut resource
1405
+ imagecopy( $cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h );
1406
+
1407
+ // copy relevant section from watermark to the cut resource
1408
+ imagecopy( $cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h );
1409
+
1410
+ // insert cut resource to destination image
1411
+ imagecopymerge( $dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct );
1412
+ }
1413
+
1414
+ /**
1415
+ * Resize image.
1416
+ *
1417
+ * @param resource $image Image resource
1418
+ * @param int $width Image width
1419
+ * @param int $height Image height
1420
+ * @param array $info Image data
1421
+ * @return resource
1422
+ */
1423
+ private function resize( $image, $width, $height, $info ) {
1424
+ $new_image = imagecreatetruecolor( $width, $height );
1425
+
1426
+ // check if this image is PNG, then set if transparent
1427
+ if ( $info[2] === 3 ) {
1428
+ imagealphablending( $new_image, false );
1429
+ imagesavealpha( $new_image, true );
1430
+ imagefilledrectangle( $new_image, 0, 0, $width, $height, imagecolorallocatealpha( $new_image, 255, 255, 255, 127 ) );
1431
+ }
1432
+
1433
+ imagecopyresampled( $new_image, $image, 0, 0, 0, 0, $width, $height, $info[0], $info[1] );
1434
+
1435
+ return $new_image;
1436
+ }
1437
+
1438
+ /**
1439
+ * Save image from image resource.
1440
+ *
1441
+ * @param resource $image Image resource
1442
+ * @param string $mime_type Image mime type
1443
+ * @param string $filepath Path where image should be saved
1444
+ * @param int $quality Image quality
1445
+ * @return void
1446
+ */
1447
+ private function save_image_file( $image, $mime_type, $filepath, $quality ) {
1448
+ switch ( $mime_type ) {
1449
+ case 'image/jpeg':
1450
+ case 'image/pjpeg':
1451
+ imagejpeg( $image, $filepath, $quality );
1452
+ break;
1453
+
1454
+ case 'image/png':
1455
+ imagepng( $image, $filepath, (int) round( 9 - ( 9 * $quality / 100 ), 0 ) );
1456
+ break;
1457
+ }
1458
+ }
1459
+
1460
+ /**
1461
+ * Add links to support forum.
1462
+ *
1463
+ * @param array $links
1464
+ * @param string $file
1465
+ * @return array
1466
+ */
1467
+ public function plugin_extend_links( $links, $file ) {
1468
+ if ( ! current_user_can( 'install_plugins' ) )
1469
+ return $links;
1470
+
1471
+ if ( $file === plugin_basename( __FILE__ ) )
1472
+ return array_merge( $links, array( sprintf( '<a href="http://www.dfactory.eu/support/forum/image-watermark/" target="_blank">%s</a>', __( 'Support', 'image-watermark' ) ) ) );
1473
+
1474
+ return $links;
1475
+ }
1476
+
1477
+ /**
1478
+ * Add links to settings page.
1479
+ *
1480
+ * @param array $links
1481
+ * @param string $file
1482
+ * @return array
1483
+ */
1484
+ function plugin_settings_link( $links, $file ) {
1485
+ if ( ! is_admin() || ! current_user_can( 'manage_options' ) )
1486
+ return $links;
1487
+
1488
+ static $plugin;
1489
+
1490
+ $plugin = plugin_basename( __FILE__ );
1491
+
1492
+ if ( $file === $plugin ) {
1493
+ $settings_link = sprintf( '<a href="%s">%s</a>', admin_url( 'options-general.php' ) . '?page=watermark-options', __( 'Settings', 'image-watermark' ) );
1494
+
1495
+ array_unshift( $links, $settings_link );
1496
+ }
1497
+
1498
+ return $links;
1499
+ }
1500
+
1501
+ }
1502
+
1503
+ /**
1504
+ * Get instance of main class.
1505
+ *
1506
+ * @return object Instance
1507
+ */
1508
+ function Image_Watermark() {
1509
+ static $instance;
1510
+
1511
+ // first call to instance() initializes the plugin
1512
+ if ( $instance === null || ! ( $instance instanceof Image_Watermark ) )
1513
+ $instance = Image_Watermark::instance();
1514
+
1515
+ return $instance;
1516
+ }
1517
+
1518
  $image_watermark = Image_Watermark();
includes/class-settings.php CHANGED
@@ -19,6 +19,8 @@ class Image_Watermark_Settings {
19
 
20
  /**
21
  * Class constructor.
 
 
22
  */
23
  public function __construct( ) {
24
  // actions
@@ -29,6 +31,8 @@ class Image_Watermark_Settings {
29
 
30
  /**
31
  * Load available image sizes.
 
 
32
  */
33
  public function load_image_sizes() {
34
  $this->image_sizes = get_intermediate_image_sizes();
@@ -48,6 +52,8 @@ class Image_Watermark_Settings {
48
 
49
  /**
50
  * Register settings.
 
 
51
  */
52
  public function register_settings() {
53
  register_setting( 'image_watermark_options', 'image_watermark_options', array( $this, 'validate_options' ) );
@@ -96,6 +102,8 @@ class Image_Watermark_Settings {
96
 
97
  /**
98
  * Create options page in menu.
 
 
99
  */
100
  public function options_page() {
101
  add_options_page(
@@ -105,6 +113,8 @@ class Image_Watermark_Settings {
105
 
106
  /**
107
  * Options page output.
 
 
108
  */
109
  public function options_page_output() {
110
 
@@ -135,7 +145,7 @@ class Image_Watermark_Settings {
135
  </div>
136
  </div>
137
  <form action="options.php" method="post">
138
- <div id="main-sortables" class="meta-box-sortables ui-sortable">';
139
  settings_fields( 'image_watermark_options' );
140
  $this->do_settings_sections( 'image_watermark_options' );
141
 
@@ -154,23 +164,11 @@ class Image_Watermark_Settings {
154
  </div>
155
  <div class="clear"></div>
156
  </div>';
157
- ?>
158
- <script type="text/javascript">
159
- //<![CDATA[
160
- jQuery(document).ready( function ($) {
161
- // close postboxes that should be closed
162
- $('.if-js-closed').removeClass('if-js-closed').addClass('closed');
163
- // postboxes setup
164
- postboxes.add_postbox_toggles('watermark-options');
165
- });
166
- //]]>
167
- </script>
168
- <?php
169
  }
170
 
171
  /**
172
  * Validate options.
173
- *
174
  * @param array $input
175
  * @return array
176
  */
@@ -270,8 +268,8 @@ class Image_Watermark_Settings {
270
 
271
  /**
272
  * PHP extension.
273
- *
274
- * @return mixed
275
  */
276
  public function iw_extension() {
277
  echo '
@@ -293,36 +291,34 @@ class Image_Watermark_Settings {
293
 
294
  /**
295
  * Automatic watermarking option.
296
- *
297
- * @return mixed
298
  */
299
  public function iw_automatic_watermarking() {
300
  ?>
301
  <label for="iw_automatic_watermarking">
302
- <input id="iw_automatic_watermarking" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['plugin_off'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][plugin_off]">
303
- <?php echo __( 'Enable watermark for uploaded images.', 'image-watermark' ); ?>
304
  </label>
305
  <?php
306
  }
307
 
308
  /**
309
  * Manual watermarking option.
310
- *
311
- * @return mixed
312
  */
313
  public function iw_manual_watermarking() {
314
  ?>
315
  <label for="iw_manual_watermarking">
316
- <input id="iw_manual_watermarking" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['manual_watermarking'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][manual_watermarking]">
317
- <?php echo __( 'Enable Apply Watermark option for Media Library images.', 'image-watermark' ); ?>
318
  </label>
319
  <?php
320
  }
321
 
322
  /**
323
  * Enable watermark for option.
324
- *
325
- * @return mixed
326
  */
327
  public function iw_enable_for() {
328
  ?>
@@ -378,14 +374,13 @@ class Image_Watermark_Settings {
378
 
379
  /**
380
  * Frontend watermarking option.
381
- *
382
- * @return mixed
383
  */
384
  public function iw_frontend_watermarking() {
385
  ?>
386
  <label for="iw_frontend_watermarking">
387
- <input id="iw_frontend_watermarking" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['frontend_active'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][frontend_active]">
388
- <?php echo __( 'Enable frontend image uploading. (uploading script is not included, but you may use a plugin or custom code).', 'image-watermark' ); ?>
389
  </label>
390
  <span class="description"><?php echo __( '<br /><strong>Notice:</strong> This functionality works only if uploaded images are processed using WordPress native upload methods.', 'image-watermark' ); ?></span>
391
  <?php
@@ -393,22 +388,21 @@ class Image_Watermark_Settings {
393
 
394
  /**
395
  * Remove data on deactivation option.
396
- *
397
- * @return mixed
398
  */
399
  public function iw_deactivation() {
400
  ?>
401
  <label for="iw_deactivation">
402
- <input id="iw_deactivation" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['deactivation_delete'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][deactivation_delete]">
403
- <?php echo __( 'Delete all database settings on plugin deactivation.', 'image-watermark' ); ?>
404
  </label>
405
  <?php
406
  }
407
 
408
  /**
409
  * Watermark alignment option.
410
- *
411
- * @return mixed
412
  */
413
  public function iw_alignment() {
414
  ?>
@@ -440,7 +434,7 @@ class Image_Watermark_Settings {
440
 
441
  /**
442
  * Watermark offset unit option.
443
- *
444
  * @return void
445
  */
446
  public function iw_offset_unit() {
@@ -455,7 +449,7 @@ class Image_Watermark_Settings {
455
 
456
  /**
457
  * Watermark offset option.
458
- *
459
  * @return void
460
  */
461
  public function iw_offset() {
@@ -471,7 +465,7 @@ class Image_Watermark_Settings {
471
 
472
  /**
473
  * Watermark image option.
474
- *
475
  * @return void
476
  */
477
  public function iw_watermark_image() {
@@ -493,8 +487,8 @@ class Image_Watermark_Settings {
493
 
494
  /**
495
  * Watermark image preview.
496
- *
497
- * @return mixed
498
  */
499
  public function iw_watermark_preview() {
500
  if ( Image_Watermark()->options['watermark_image']['url'] !== NULL && Image_Watermark()->options['watermark_image']['url'] != 0 ) {
@@ -532,8 +526,8 @@ class Image_Watermark_Settings {
532
 
533
  /**
534
  * Watermark size option.
535
- *
536
- * @return mixed
537
  */
538
  public function iw_watermark_size() {
539
  ?>
@@ -550,8 +544,8 @@ class Image_Watermark_Settings {
550
 
551
  /**
552
  * Watermark custom size option.
553
- *
554
- * @return mixed
555
  */
556
  public function iw_watermark_size_custom() {
557
  ?>
@@ -566,8 +560,8 @@ class Image_Watermark_Settings {
566
 
567
  /**
568
  * Watermark scaled size option.
569
- *
570
- * @return mixed
571
  */
572
  public function iw_watermark_size_scaled() {
573
  ?>
@@ -585,8 +579,8 @@ class Image_Watermark_Settings {
585
 
586
  /**
587
  * Watermark custom size option.
588
- *
589
- * @return mixed
590
  */
591
  public function iw_watermark_opacity() {
592
  ?>
@@ -604,8 +598,8 @@ class Image_Watermark_Settings {
604
 
605
  /**
606
  * Image quality option.
607
- *
608
- * @return mixed
609
  */
610
  public function iw_image_quality() {
611
  ?>
@@ -623,8 +617,8 @@ class Image_Watermark_Settings {
623
 
624
  /**
625
  * Image format option.
626
- *
627
- * @return mixed
628
  */
629
  public function iw_image_format() {
630
  ?>
@@ -640,64 +634,60 @@ class Image_Watermark_Settings {
640
 
641
  /**
642
  * Right click image protection option.
643
- *
644
- * @return mixed
645
  */
646
  public function iw_protection_right_click() {
647
  ?>
648
  <label for="iw_protection_right_click">
649
- <input id="iw_protection_right_click" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['image_protection']['rightclick'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[image_protection][rightclick]">
650
- <?php _e( 'Disable right mouse click on images', 'image-watermark' ); ?>
651
  </label>
652
  <?php
653
  }
654
 
655
  /**
656
  * Drag and drop image protection option.
657
- *
658
- * @return mixed
659
  */
660
  public function iw_protection_drag_drop() {
661
  ?>
662
  <label for="iw_protection_drag_drop">
663
- <input id="iw_protection_drag_drop" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['image_protection']['draganddrop'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[image_protection][draganddrop]">
664
- <?php _e( 'Prevent drag and drop', 'image-watermark' ); ?>
665
  </label>
666
  <?php
667
  }
668
 
669
  /**
670
  * Logged-in users image protection option.
671
- *
672
- * @return mixed
673
  */
674
  public function iw_protection_logged() {
675
  ?>
676
  <label for="iw_protection_logged">
677
- <input id="iw_protection_logged" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['image_protection']['forlogged'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[image_protection][forlogged]">
678
- <?php _e( 'Enable image protection for logged-in users also', 'image-watermark' ); ?>
679
  </label>
680
  <?php
681
  }
682
 
683
  /**
684
- * Backup the original image
685
- *
686
- * @return mixed
687
  */
688
  public function iw_backup_image() {
689
  ?>
690
  <label for="iw_backup_size_full">
691
- <input id="iw_backup_size_full" type="checkbox" <?php checked( ! empty( Image_Watermark()->options['backup']['backup_image'] ), true, true ); ?> value="1" name="iw_options[backup][backup_image]">
692
- <?php echo __( 'Backup the full size image.', 'image-watermark' ); ?>
693
  </label>
694
  <?php
695
  }
696
 
697
  /**
698
  * Image backup quality option.
699
- *
700
- * @return mixed
701
  */
702
  public function iw_backup_image_quality() {
703
  ?>
@@ -712,7 +702,7 @@ class Image_Watermark_Settings {
712
  <p class="description"><?php _e( 'Set output image quality.', 'image-watermark' ); ?></p>
713
  <?php
714
  }
715
-
716
  /**
717
  * This function is similar to the function in the Settings API, only the output HTML is changed.
718
  * Print out the settings fields for a particular settings section
@@ -722,30 +712,31 @@ class Image_Watermark_Settings {
722
  * @since 0.1
723
  *
724
  * @param string $page Slug title of the admin page who's settings fields you want to show.
725
- * @param string $section Slug title of the settings section who's fields you want to show.
726
  */
727
  function do_settings_sections( $page ) {
728
  global $wp_settings_sections, $wp_settings_fields;
729
-
730
  if ( ! isset( $wp_settings_sections[$page] ) )
731
  return;
732
-
733
  foreach ( (array) $wp_settings_sections[$page] as $section ) {
734
- echo '<div id="" class="stuffbox postbox '.$section['id'].'">';
735
- echo '<button type="button" class="handlediv button-link" aria-expanded="true"><span class="screen-reader-text">' . __('Toggle panel', 'image-watermark') . '</span><span class="toggle-indicator" aria-hidden="true"></span></button>';
736
  if ( $section['title'] )
737
- echo "<h3 class=\"hndle\"><span>{$section['title']}</span></h3>\n";
738
-
739
  if ( $section['callback'] )
740
  call_user_func( $section['callback'], $section );
741
-
742
- if ( ! isset( $wp_settings_fields ) || !isset( $wp_settings_fields[$page] ) || !isset( $wp_settings_fields[$page][$section['id']] ) )
743
  continue;
 
744
  echo '<div class="inside"><table class="form-table">';
 
745
  do_settings_fields( $page, $section['id'] );
746
- echo '</table></div>';
747
- echo '</div>';
748
  }
749
  }
750
-
751
- }
19
 
20
  /**
21
  * Class constructor.
22
+ *
23
+ * @return void
24
  */
25
  public function __construct( ) {
26
  // actions
31
 
32
  /**
33
  * Load available image sizes.
34
+ *
35
+ * @return void
36
  */
37
  public function load_image_sizes() {
38
  $this->image_sizes = get_intermediate_image_sizes();
52
 
53
  /**
54
  * Register settings.
55
+ *
56
+ * @return void
57
  */
58
  public function register_settings() {
59
  register_setting( 'image_watermark_options', 'image_watermark_options', array( $this, 'validate_options' ) );
102
 
103
  /**
104
  * Create options page in menu.
105
+ *
106
+ * @return void
107
  */
108
  public function options_page() {
109
  add_options_page(
113
 
114
  /**
115
  * Options page output.
116
+ *
117
+ * @return void
118
  */
119
  public function options_page_output() {
120
 
145
  </div>
146
  </div>
147
  <form action="options.php" method="post">
148
+ <div id="main-sortables">';
149
  settings_fields( 'image_watermark_options' );
150
  $this->do_settings_sections( 'image_watermark_options' );
151
 
164
  </div>
165
  <div class="clear"></div>
166
  </div>';
 
 
 
 
 
 
 
 
 
 
 
 
167
  }
168
 
169
  /**
170
  * Validate options.
171
+ *
172
  * @param array $input
173
  * @return array
174
  */
268
 
269
  /**
270
  * PHP extension.
271
+ *
272
+ * @return void
273
  */
274
  public function iw_extension() {
275
  echo '
291
 
292
  /**
293
  * Automatic watermarking option.
294
+ *
295
+ * @return void
296
  */
297
  public function iw_automatic_watermarking() {
298
  ?>
299
  <label for="iw_automatic_watermarking">
300
+ <input id="iw_automatic_watermarking" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['plugin_off'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][plugin_off]"><?php echo __( 'Enable watermark for uploaded images.', 'image-watermark' ); ?>
 
301
  </label>
302
  <?php
303
  }
304
 
305
  /**
306
  * Manual watermarking option.
307
+ *
308
+ * @return void
309
  */
310
  public function iw_manual_watermarking() {
311
  ?>
312
  <label for="iw_manual_watermarking">
313
+ <input id="iw_manual_watermarking" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['manual_watermarking'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][manual_watermarking]"><?php echo __( 'Enable Apply Watermark option for Media Library images.', 'image-watermark' ); ?>
 
314
  </label>
315
  <?php
316
  }
317
 
318
  /**
319
  * Enable watermark for option.
320
+ *
321
+ * @return void
322
  */
323
  public function iw_enable_for() {
324
  ?>
374
 
375
  /**
376
  * Frontend watermarking option.
377
+ *
378
+ * @return void
379
  */
380
  public function iw_frontend_watermarking() {
381
  ?>
382
  <label for="iw_frontend_watermarking">
383
+ <input id="iw_frontend_watermarking" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['frontend_active'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][frontend_active]"><?php echo __( 'Enable frontend image uploading. (uploading script is not included, but you may use a plugin or custom code).', 'image-watermark' ); ?>
 
384
  </label>
385
  <span class="description"><?php echo __( '<br /><strong>Notice:</strong> This functionality works only if uploaded images are processed using WordPress native upload methods.', 'image-watermark' ); ?></span>
386
  <?php
388
 
389
  /**
390
  * Remove data on deactivation option.
391
+ *
392
+ * @return void
393
  */
394
  public function iw_deactivation() {
395
  ?>
396
  <label for="iw_deactivation">
397
+ <input id="iw_deactivation" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['watermark_image']['deactivation_delete'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[watermark_image][deactivation_delete]"><?php echo __( 'Delete all database settings on plugin deactivation.', 'image-watermark' ); ?>
 
398
  </label>
399
  <?php
400
  }
401
 
402
  /**
403
  * Watermark alignment option.
404
+ *
405
+ * @return void
406
  */
407
  public function iw_alignment() {
408
  ?>
434
 
435
  /**
436
  * Watermark offset unit option.
437
+ *
438
  * @return void
439
  */
440
  public function iw_offset_unit() {
449
 
450
  /**
451
  * Watermark offset option.
452
+ *
453
  * @return void
454
  */
455
  public function iw_offset() {
465
 
466
  /**
467
  * Watermark image option.
468
+ *
469
  * @return void
470
  */
471
  public function iw_watermark_image() {
487
 
488
  /**
489
  * Watermark image preview.
490
+ *
491
+ * @return void
492
  */
493
  public function iw_watermark_preview() {
494
  if ( Image_Watermark()->options['watermark_image']['url'] !== NULL && Image_Watermark()->options['watermark_image']['url'] != 0 ) {
526
 
527
  /**
528
  * Watermark size option.
529
+ *
530
+ * @return void
531
  */
532
  public function iw_watermark_size() {
533
  ?>
544
 
545
  /**
546
  * Watermark custom size option.
547
+ *
548
+ * @return void
549
  */
550
  public function iw_watermark_size_custom() {
551
  ?>
560
 
561
  /**
562
  * Watermark scaled size option.
563
+ *
564
+ * @return void
565
  */
566
  public function iw_watermark_size_scaled() {
567
  ?>
579
 
580
  /**
581
  * Watermark custom size option.
582
+ *
583
+ * @return void
584
  */
585
  public function iw_watermark_opacity() {
586
  ?>
598
 
599
  /**
600
  * Image quality option.
601
+ *
602
+ * @return void
603
  */
604
  public function iw_image_quality() {
605
  ?>
617
 
618
  /**
619
  * Image format option.
620
+ *
621
+ * @return void
622
  */
623
  public function iw_image_format() {
624
  ?>
634
 
635
  /**
636
  * Right click image protection option.
637
+ *
638
+ * @return void
639
  */
640
  public function iw_protection_right_click() {
641
  ?>
642
  <label for="iw_protection_right_click">
643
+ <input id="iw_protection_right_click" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['image_protection']['rightclick'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[image_protection][rightclick]"><?php _e( 'Disable right mouse click on images', 'image-watermark' ); ?>
 
644
  </label>
645
  <?php
646
  }
647
 
648
  /**
649
  * Drag and drop image protection option.
650
+ *
651
+ * @return void
652
  */
653
  public function iw_protection_drag_drop() {
654
  ?>
655
  <label for="iw_protection_drag_drop">
656
+ <input id="iw_protection_drag_drop" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['image_protection']['draganddrop'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[image_protection][draganddrop]"><?php _e( 'Prevent drag and drop', 'image-watermark' ); ?>
 
657
  </label>
658
  <?php
659
  }
660
 
661
  /**
662
  * Logged-in users image protection option.
663
+ *
664
+ * @return void
665
  */
666
  public function iw_protection_logged() {
667
  ?>
668
  <label for="iw_protection_logged">
669
+ <input id="iw_protection_logged" type="checkbox" <?php checked( ( ! empty( Image_Watermark()->options['image_protection']['forlogged'] ) ? 1 : 0 ), 1, true ); ?> value="1" name="iw_options[image_protection][forlogged]"><?php _e( 'Enable image protection for logged-in users also', 'image-watermark' ); ?>
 
670
  </label>
671
  <?php
672
  }
673
 
674
  /**
675
+ * Backup the original image.
676
+ *
677
+ * @return void
678
  */
679
  public function iw_backup_image() {
680
  ?>
681
  <label for="iw_backup_size_full">
682
+ <input id="iw_backup_size_full" type="checkbox" <?php checked( ! empty( Image_Watermark()->options['backup']['backup_image'] ), true, true ); ?> value="1" name="iw_options[backup][backup_image]"><?php echo __( 'Backup the full size image.', 'image-watermark' ); ?>
 
683
  </label>
684
  <?php
685
  }
686
 
687
  /**
688
  * Image backup quality option.
689
+ *
690
+ * @return void
691
  */
692
  public function iw_backup_image_quality() {
693
  ?>
702
  <p class="description"><?php _e( 'Set output image quality.', 'image-watermark' ); ?></p>
703
  <?php
704
  }
705
+
706
  /**
707
  * This function is similar to the function in the Settings API, only the output HTML is changed.
708
  * Print out the settings fields for a particular settings section
712
  * @since 0.1
713
  *
714
  * @param string $page Slug title of the admin page who's settings fields you want to show.
715
+ * @return void
716
  */
717
  function do_settings_sections( $page ) {
718
  global $wp_settings_sections, $wp_settings_fields;
719
+
720
  if ( ! isset( $wp_settings_sections[$page] ) )
721
  return;
722
+
723
  foreach ( (array) $wp_settings_sections[$page] as $section ) {
724
+ echo '<div id="" class="'.$section['id'].'">';
725
+
726
  if ( $section['title'] )
727
+ echo "<h3><span>{$section['title']}</span></h3>\n";
728
+
729
  if ( $section['callback'] )
730
  call_user_func( $section['callback'], $section );
731
+
732
+ if ( ! isset( $wp_settings_fields ) || ! isset( $wp_settings_fields[$page] ) || ! isset( $wp_settings_fields[$page][$section['id']] ) )
733
  continue;
734
+
735
  echo '<div class="inside"><table class="form-table">';
736
+
737
  do_settings_fields( $page, $section['id'] );
738
+
739
+ echo '</table></div></div>';
740
  }
741
  }
742
+ }
 
includes/class-update.php CHANGED
@@ -22,7 +22,7 @@ class Image_Watermark_Update {
22
 
23
  /**
24
  * Check if update is required.
25
- *
26
  * @return void
27
  */
28
  public function check_update() {
22
 
23
  /**
24
  * Check if update is required.
25
+ *
26
  * @return void
27
  */
28
  public function check_update() {
js/admin-image-actions.js CHANGED
@@ -1,545 +1,555 @@
1
- jQuery( document ).ready( function ( $ ) {
2
-
3
- /**
4
- * wp_localize_script object: iwImageActionArgs
5
- *
6
- * Params:
7
- *
8
- * _nonce
9
- * __applied_none => 'Watermark could not be applied to selected files or no valid images (JPEG, PNG) were selected.'
10
- * __applied_one => 'Watermark was succesfully applied to 1 image.
11
- * __applied_multi => 'Watermark was succesfully applied to %s images.'
12
- * __removed_none => 'Watermark could not be removed from selected files or no valid images (JPEG, PNG) were selected.'
13
- * __removed_one => 'Watermark was succesfully removed from 1 image.'
14
- * __removed_multi => 'Watermark was succesfully removed from %s images.'
15
- * __skipped => 'Skipped files'
16
- * __running => 'Bulk action is currently running, please wait.'
17
- * __dismiss => 'Dismiss this notice.' // Wordpress default string
18
- *
19
- */
20
-
21
- watermarkImageActions = {
22
- running: false,
23
- action_location: '',
24
- action: '',
25
- response: '',
26
- selected: [ ],
27
- successCount: 0,
28
- skippedCount: 0,
29
- init: function () {
30
-
31
- // Normal (list) mode
32
- $( document ).on( 'click', '.bulkactions input#doaction, .bulkactions input#doaction2', function ( e ) {
33
- // Get the selected bulk action
34
- action = $( this ).parent().children( 'select' ).val();
35
-
36
- if ( !iwImageActionArgs.backup_image && action === 'removewatermark' ) {
37
- return;
38
- }
39
-
40
- // Validate action
41
- if ( 'applywatermark' == action || 'removewatermark' == action ) {
42
- // Stop default
43
- e.preventDefault();
44
-
45
- // Is this script running?
46
- if ( false === watermarkImageActions.running ) {
47
- // No! set it on running
48
- watermarkImageActions.running = true;
49
-
50
- // store current action
51
- watermarkImageActions.action = action;
52
-
53
- // store current location where the action was fired
54
- watermarkImageActions.action_location = 'upload-list';
55
-
56
- // store selected attachment id's
57
- $( '.wp-list-table .check-column input:checkbox:checked' ).each( function () {
58
- watermarkImageActions.selected.push( $( this ).val() );
59
- } );
60
-
61
- // remove current notices
62
- $( '.iw-notice' ).slideUp( 'fast', function () {
63
- $( this ).remove();
64
- } );
65
-
66
- // begin the update!
67
- watermarkImageActions.post_loop();
68
-
69
- } else {
70
- // script is running, can't run two at the same time
71
- watermarkImageActions.notice( 'iw-notice error', iwImageActionArgs.__running, false );
72
- }
73
-
74
- }
75
- } );
76
-
77
- // Media modal or edit attachment screen mode
78
- $( document ).on( 'click', '#image_watermark_buttons a.iw-watermark-action', function ( e ) {
79
- // Get the selected bulk action
80
- action = $( this ).attr( 'data-action' );
81
- id = $( this ).attr( 'data-id' );
82
-
83
- // Validate action
84
- if ( 'applywatermark' == action || 'removewatermark' == action && !isNaN( id ) ) {
85
- // Stop default
86
- e.preventDefault();
87
-
88
- // store current action
89
- watermarkImageActions.action = action;
90
-
91
- // Is this script running?
92
- if ( false === watermarkImageActions.running ) {
93
- // No! set it on running
94
- watermarkImageActions.running = true;
95
-
96
- // store current action
97
- watermarkImageActions.action = action;
98
-
99
- // store current location where the action was fired
100
- if ( $( this ).parents( '.media-modal ' ).length ) {
101
- watermarkImageActions.action_location = 'media-modal';
102
- } else {
103
- watermarkImageActions.action_location = 'edit';
104
- }
105
-
106
- // store attachment id
107
- watermarkImageActions.selected.push( id );
108
-
109
- // remove current notices
110
- $( '.iw-notice' ).slideUp( 'fast', function () {
111
- $( this ).remove();
112
- } );
113
-
114
- // begin the update!
115
- watermarkImageActions.post_loop();
116
- } else {
117
- // script is running, can't run two at the same time
118
- watermarkImageActions.notice( 'iw-notice error', iwMediaModal.__running, false );
119
- }
120
- }
121
- } );
122
-
123
- // Since these are added later we'll need to enable dismissing again
124
- $( document ).on( 'click', '.iw-notice.is-dismissible .notice-dismiss', function () {
125
- $( this ).parents( '.iw-notice' ).slideUp( 'fast', function () {
126
- $( this ).remove();
127
- } );
128
- } );
129
-
130
- },
131
- post_loop: function () {
132
- // do we have selected attachments?
133
- if ( watermarkImageActions.selected.length ) {
134
-
135
- // take the first id
136
- id = watermarkImageActions.selected[ 0 ];
137
-
138
- // check for a valid ID (needs to be numeric)
139
- if ( !isNaN( id ) ) {
140
-
141
- // Show loading icon
142
- watermarkImageActions.row_image_feedback( 'loading', id );
143
-
144
- // post data
145
- data = {
146
- '_iw_nonce': iwImageActionArgs._nonce,
147
- 'action': 'iw_watermark_bulk_action',
148
- 'iw-action': watermarkImageActions.action,
149
- 'attachment_id': id
150
- };
151
-
152
- if ( watermarkImageActions.action_location == 'upload-list' ) {
153
- watermarkImageActions.scroll_to( '#post-' + id, 'bottom' );
154
- }
155
-
156
- // the ajax post!
157
- $.post( ajaxurl, data, function ( response ) {
158
- // show result
159
- watermarkImageActions.result( response, id );
160
- // remove this ID/key from the selected attachments
161
- watermarkImageActions.selected.splice( 0, 1 );
162
- // Redo this function
163
- watermarkImageActions.post_loop();
164
-
165
- $( '.iw-overlay' ).first().each( function () {
166
- $( this ).fadeOut( 'fast', function () {
167
- $( this ).remove();
168
-
169
- if ( response.data === 'watermarked' ) {
170
- $( '#image_watermark_buttons .value' ).append( '<span class="dashicons dashicons-yes" style="font-size: 24px;float: none;min-width: 28px;padding: 0;margin: 0; display: none;"></span>' );
171
- $( '#image_watermark_buttons .value .dashicons' ).fadeIn( 'fast' );
172
- } else if ( response.data === 'watermarkremoved' ) {
173
- $( '#image_watermark_buttons .value' ).append( '<span class="dashicons dashicons-yes" style="font-size: 24px;float: none;min-width: 28px;padding: 0;margin: 0; display: none;"></span>' );
174
- $( '#image_watermark_buttons .value .dashicons' ).fadeIn( 'fast' );
175
- }
176
-
177
- $( '#image_watermark_buttons .value .dashicons' ).delay( 1500 ).fadeOut( 'fast', function () {
178
- $( this ).remove();
179
- } );
180
- } );
181
- } );
182
- } );
183
-
184
- } else {
185
- // ID is not valid so remove this key from the selected attachments
186
- watermarkImageActions.selected.splice( 0, 1 );
187
- // Redo this function
188
- watermarkImageActions.post_loop();
189
- }
190
- } else {
191
- // All is done, reset this "class"
192
- watermarkImageActions.reset();
193
- }
194
- },
195
- result: function ( response, id ) {
196
-
197
- // Was the ajax post successful?
198
- if ( true === response.success ) {
199
-
200
- // defaults
201
- var type = false;
202
- var message = '';
203
- var overwrite = true;
204
- // store response data
205
- watermarkImageActions.response = response.data;
206
-
207
- // Check what kind of action is done (watermarked, watermarkremoved or skipped)
208
- switch ( response.data ) {
209
- case 'watermarked':
210
- // The css classes for the notice
211
- type = 'iw-notice updated iw-watermarked';
212
- // another successful update
213
- watermarkImageActions.successCount += 1;
214
- // did we have more success updates?
215
- if ( 1 < watermarkImageActions.successCount ) {
216
- //yes
217
- message = iwImageActionArgs.__applied_multi.replace( '%s', watermarkImageActions.successCount );
218
- } else {
219
- //no
220
- message = iwImageActionArgs.__applied_one;
221
- }
222
- // update the row feedback
223
- watermarkImageActions.row_image_feedback( 'success', id );
224
- // reload the image
225
- watermarkImageActions.reload_image( id );
226
- break;
227
- case 'watermarkremoved':
228
- // The css classes for the notice
229
- type = 'iw-notice updated iw-watermarkremoved';
230
- // another successful update
231
- watermarkImageActions.successCount += 1;
232
- // did we have more success updates?
233
- if ( 1 < watermarkImageActions.successCount ) {
234
- //yes
235
- message = iwImageActionArgs.__removed_multi.replace( '%s', watermarkImageActions.successCount );
236
- } else {
237
- //no
238
- message = iwImageActionArgs.__removed_one;
239
- }
240
- // update the row feedback
241
- watermarkImageActions.row_image_feedback( 'success', id );
242
- // reload the image
243
- watermarkImageActions.reload_image( id );
244
- break;
245
- case 'skipped':
246
- // The css classes for the notice
247
- type = 'iw-notice error iw-skipped';
248
- // another skipped update
249
- watermarkImageActions.skippedCount += 1;
250
- // adjust the message with the number of skipped updates
251
- message = iwImageActionArgs.__skipped + ': ' + watermarkImageActions.skippedCount;
252
- // update the row feedback
253
- watermarkImageActions.row_image_feedback( 'error', id );
254
- break;
255
- default:
256
- // The css classes for the notice
257
- type = 'iw-notice error iw-message';
258
- // The error message
259
- message = response.data;
260
- // update the row feedback
261
- watermarkImageActions.row_image_feedback( 'error', id );
262
- // This can be anything so don't overwrite
263
- overwrite = false;
264
- break;
265
- }
266
- if ( false !== type ) {
267
- // we have a valid terun type, show the notice! (Overwrite current notice if available)
268
- watermarkImageActions.notice( type, message, overwrite );
269
- }
270
- } else {
271
- // No success...
272
- watermarkImageActions.notice( 'iw-notice error', response.data, false );
273
- // update the row feedback
274
- watermarkImageActions.row_image_feedback( 'error', id );
275
- }
276
-
277
- },
278
- row_image_feedback: function ( type, id ) {
279
- var css = { },
280
- cssinner = { },
281
- container_selector;
282
-
283
- switch ( watermarkImageActions.action_location ) {
284
- case 'upload-list':
285
- container_selector = '.wp-list-table #post-' + id + ' .media-icon';
286
- css = {
287
- display: 'table',
288
- width: $( container_selector ).width() + 'px',
289
- height: $( container_selector ).height() + 'px',
290
- top: '0',
291
- left: '0',
292
- position: 'absolute',
293
- font: 'normal normal normal dashicons',
294
- background: 'rgba(255,255,255,0.75)',
295
- content: ''
296
- };
297
- cssinner = {
298
- 'vertical-align': 'middle',
299
- 'text-align': 'center',
300
- display: 'table-cell',
301
- width: '100%',
302
- height: '100%',
303
- };
304
- break;
305
-
306
- case 'edit':
307
- container_selector = '.wp_attachment_holder #thumbnail-head-' + id + '';
308
- css = {
309
- display: 'table',
310
- width: $( container_selector + ' img' ).width() + 'px',
311
- height: $( container_selector + ' img' ).height() + 'px',
312
- top: '0',
313
- left: '0',
314
- position: 'absolute',
315
- font: 'normal normal normal dashicons',
316
- background: 'rgba(255,255,255,0.75)',
317
- content: ''
318
- };
319
- cssinner = {
320
- 'vertical-align': 'middle',
321
- 'text-align': 'center',
322
- display: 'table-cell',
323
- width: '100%',
324
- height: '100%',
325
- };
326
- break;
327
-
328
- case 'media-modal':
329
- container_selector = '.media-modal #image_watermark_buttons[data-id="' + id + '"] .value';
330
- css = {
331
- 'float': 'none'
332
- };
333
- cssinner = {
334
- 'float': 'none'
335
- };
336
- break;
337
-
338
- default:
339
- return false;
340
- }
341
-
342
- // css rules
343
- $( container_selector ).css( 'position', 'relative' );
344
-
345
- // Only create the element if it doesn't exist
346
- if ( !$( container_selector + ' .iw-overlay' ).length ) {
347
- $( container_selector ).append( '<span class="iw-overlay"><span class="iw-overlay-inner"></span></span>' );
348
- }
349
-
350
- // Overwrite with new data
351
- $( container_selector + ' .iw-overlay' ).css( css );
352
- $( container_selector + ' .iw-overlay .iw-overlay-inner' ).css( cssinner );
353
- $( container_selector + ' .iw-overlay .iw-overlay-inner' ).html( '<span class="spinner is-active"></span>' );
354
-
355
- if ( watermarkImageActions.action_location === 'media-modal' ) {
356
- $( container_selector + ' .iw-overlay .iw-overlay-inner .spinner' ).css( { 'float': 'none', 'padding': 0, 'margin': '-4px 0 0 10px' } );
357
- }
358
- },
359
- notice: function ( type, message, overwrite ) {
360
-
361
- if ( watermarkImageActions.action_location === 'media-modal' ) {
362
- return;
363
- }
364
-
365
- type += ' notice is-dismissible';
366
-
367
- // Get the prefix based on the action location
368
- switch ( watermarkImageActions.action_location ) {
369
- case 'upload-list':
370
- prefix = '.wrap > h1';
371
- break;
372
- default:
373
- prefix = '#image_watermark_buttons';
374
- break;
375
- }
376
-
377
- // Overwrite the current notice?
378
- if ( true === overwrite ) {
379
- selector = false;
380
-
381
- // Get the selector based on the response
382
- switch ( watermarkImageActions.response ) {
383
- case 'watermarked':
384
- selector = '.iw-notice.iw-watermarked';
385
- break;
386
- case 'watermarkremoved':
387
- selector = '.iw-notice.iw-watermarkremoved';
388
- break;
389
- case 'skipped':
390
- selector = '.iw-notice.iw-skipped';
391
- break;
392
- }
393
- // Do we have a selector and can we find it? If not, just create a new notice
394
- if ( selector && $( '.wrap ' + selector + ' > p' ).length ) {
395
-
396
- // Get the selector based on the action location (not not forget the ending space)
397
- switch ( watermarkImageActions.action_location ) {
398
- case 'upload-list':
399
- prefix = '.wrap ';
400
- break;
401
- default:
402
- prefix = '#image_watermark_buttons ';
403
- break;
404
- }
405
- $( prefix + selector + ' > p' ).html( message );
406
- } else {
407
- $( prefix ).after( '<div class="' + type + '" style="display: none;"><p>' + message + '</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' + iwImageActionArgs.__dismiss + '</span></button></div>' );
408
- $( '.iw-notice' ).slideDown( 'fast' );
409
- }
410
- } else {
411
- // create a new notice
412
- $( prefix ).after( '<div class="' + type + '" style="display: none;"><p>' + message + '</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' + iwImageActionArgs.__dismiss + '</span></button></div>' );
413
- $( '.iw-notice' ).slideDown( 'fast' );
414
- }
415
-
416
- },
417
- reset: function () {
418
- watermarkImageActions.running = false;
419
- watermarkImageActions.action = '';
420
- watermarkImageActions.response = '';
421
- watermarkImageActions.selected = [ ];
422
- watermarkImageActions.successCount = 0;
423
- watermarkImageActions.skippedCount = 0;
424
-
425
- // remove the overlay
426
- setTimeout( function () {
427
- $( '.iw-overlay' ).each( function () {
428
- $( this ).fadeOut( 'fast', function () {
429
- $( this ).remove();
430
- } );
431
- } );
432
- }, 100 );
433
- },
434
- reload_image: function ( id ) {
435
- // reload the images
436
- time = new Date().getTime();
437
- selector = false;
438
- // Get the selector based on the action location
439
- switch ( watermarkImageActions.action_location ) {
440
- case 'upload-list':
441
- selector = '.wp-list-table #post-' + id + ' .image-icon img';
442
- break;
443
- case 'media-modal':
444
- selector = '.attachment-details[data-id="' + id + '"] img, .attachment[data-id="' + id + '"] img';
445
- break;
446
- case 'edit':
447
- selector = '.wp_attachment_holder img';
448
- break;
449
- }
450
-
451
- if ( selector ) {
452
- image = $( selector );
453
- image.each( function () {
454
- // Remove the responsive metadata, this prevents reloading the image
455
- $( this ).removeAttr( 'srcset' );
456
- $( this ).removeAttr( 'sizes' );
457
- // Reload the image (actually a browser hack by adding a time parameter to the image)
458
- $( this ).attr( 'src', watermarkImageActions.replace_url_param( $( this ).attr( 'src' ), 't', time ) );
459
- } );
460
- }
461
- },
462
- rotate_icon: function ( icon ) {
463
- // This function accepts selectors and objects
464
- if ( typeof icon == 'string' ) {
465
- icon = $( icon );
466
- }
467
- // Check for the length of the selected object.
468
- if ( $( icon.selector ).length ) {
469
- // Set rotation to 0
470
- icon.css( {
471
- '-webkit-transform': 'rotate(0deg)',
472
- '-ms-transform': 'rotate(0deg)',
473
- 'transform': 'rotate(0deg)',
474
- 'borderSpacing': '0',
475
- } );
476
- // Do animation (one rotation)
477
- icon.animate(
478
- { borderSpacing: 360 },
479
- {
480
- duration: 1000,
481
- step: function ( now, fx ) {
482
- $( this ).css( '-webkit-transform', 'rotate(' + now + 'deg)' );
483
- $( this ).css( '-ms-transform', 'rotate(' + now + 'deg)' );
484
- $( this ).css( 'transform', 'rotate(' + now + 'deg)' );
485
- if ( now == 360 ) {
486
- // Animation finished, stop loop and restart
487
- icon.stop();
488
- watermarkImageActions.rotate_icon( icon );
489
- }
490
- },
491
- }
492
- );
493
- }
494
- },
495
- replace_url_param: function ( url, paramName, paramValue ) {
496
- var pattern = new RegExp( '\\b(' + paramName + '=).*?(&|$)' );
497
- if ( url.search( pattern ) >= 0 ) {
498
- return url.replace( pattern, '$1' + paramValue + '$2' );
499
- }
500
- return url + ( url.indexOf( '?' ) > 0 ? '&' : '?' ) + paramName + '=' + paramValue;
501
- },
502
- scroll_to: function ( elementSelector, verticalTarget ) {
503
-
504
- var offset = $( elementSelector ).offset();
505
- var offsetTop = offset.top;
506
-
507
- // If the element it above the current viewport, scroll to it
508
- if ( offset.top < $( window ).scrollTop() ) {
509
- $( window ).scrollTop( offsetTop );
510
- return; // No further actions needed
511
- }
512
-
513
- var windowTopOffset = $( window ).scrollTop();
514
- var windowBottomOffset = windowTopOffset + $( window ).outerHeight();
515
-
516
- switch ( verticalTarget ) {
517
- case 'top':
518
- offsetTop = offsetTop - $( elementSelector ).outerHeight();
519
- break;
520
- case 'bottom':
521
- if ( offset.top < windowBottomOffset ) {
522
- return; // The element is in the viewport
523
- }
524
- offsetTop = offsetTop - $( window ).outerHeight();
525
- offsetTop = offsetTop + $( elementSelector ).outerHeight();
526
- break;
527
- case 'center':
528
- if ( offsetTop < windowBottomOffset && offsetTop >= windowTopOffset ) {
529
- return; // The element is in the viewport
530
- }
531
- offsetTop = offsetTop - ( $( window ).outerHeight() / 2 );
532
- offsetTop = offsetTop + ( $( elementSelector ).outerHeight() / 2 );
533
- break;
534
- }
535
-
536
- $( window ).scrollTop( offsetTop );
537
- }
538
- };
539
-
540
- // We need that nonce!
541
- if ( typeof iwImageActionArgs._nonce != 'undefined' ) {
542
- watermarkImageActions.init();
543
- }
544
-
545
- } );
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $ ) {
2
+
3
+ // ready event
4
+ $( function() {
5
+ /**
6
+ * wp_localize_script object: iwImageActionArgs
7
+ *
8
+ * Params:
9
+ *
10
+ * _nonce
11
+ * __applied_none => 'Watermark could not be applied to selected files or no valid images (JPEG, PNG) were selected.'
12
+ * __applied_one => 'Watermark was succesfully applied to 1 image.
13
+ * __applied_multi => 'Watermark was succesfully applied to %s images.'
14
+ * __removed_none => 'Watermark could not be removed from selected files or no valid images (JPEG, PNG) were selected.'
15
+ * __removed_one => 'Watermark was succesfully removed from 1 image.'
16
+ * __removed_multi => 'Watermark was succesfully removed from %s images.'
17
+ * __skipped => 'Skipped files'
18
+ * __running => 'Bulk action is currently running, please wait.'
19
+ * __dismiss => 'Dismiss this notice.' // Wordpress default string
20
+ *
21
+ */
22
+ watermarkImageActions = {
23
+ running: false,
24
+ action_location: '',
25
+ action: '',
26
+ response: '',
27
+ selected: [],
28
+ successCount: 0,
29
+ skippedCount: 0,
30
+ init: function() {
31
+ // Normal (list) mode
32
+ $( document ).on( 'click', '.bulkactions input#doaction, .bulkactions input#doaction2', function( e ) {
33
+ // Get the selected bulk action
34
+ action = $( this ).parent().children( 'select' ).val();
35
+
36
+ if ( ! iwImageActionArgs.backup_image && action === 'removewatermark' )
37
+ return;
38
+
39
+ // Validate action
40
+ if ( 'applywatermark' === action || 'removewatermark' === action ) {
41
+ // Stop default
42
+ e.preventDefault();
43
+
44
+ // Is this script running?
45
+ if ( false === watermarkImageActions.running ) {
46
+ // No! set it on running
47
+ watermarkImageActions.running = true;
48
+
49
+ // store current action
50
+ watermarkImageActions.action = action;
51
+
52
+ // store current location where the action was fired
53
+ watermarkImageActions.action_location = 'upload-list';
54
+
55
+ // store selected attachment id's
56
+ $( '.wp-list-table .check-column input:checkbox:checked' ).each( function() {
57
+ watermarkImageActions.selected.push( $( this ).val() );
58
+ } );
59
+
60
+ // remove current notices
61
+ $( '.iw-notice' ).slideUp( 'fast', function() {
62
+ $( this ).remove();
63
+ } );
64
+
65
+ // begin the update!
66
+ watermarkImageActions.post_loop();
67
+ } else {
68
+ // script is running, can't run two at the same time
69
+ watermarkImageActions.notice( 'iw-notice error', iwImageActionArgs.__running, false );
70
+ }
71
+ }
72
+ } );
73
+
74
+ // Media modal or edit attachment screen mode
75
+ $( document ).on( 'click', '#image_watermark_buttons a.iw-watermark-action', function( e ) {
76
+ // Get the selected bulk action
77
+ action = $( this ).attr( 'data-action' );
78
+ id = $( this ).attr( 'data-id' );
79
+
80
+ // Validate action
81
+ if ( 'applywatermark' === action || 'removewatermark' === action && ! isNaN( id ) ) {
82
+ // Stop default
83
+ e.preventDefault();
84
+
85
+ // store current action
86
+ watermarkImageActions.action = action;
87
+
88
+ // Is this script running?
89
+ if ( false === watermarkImageActions.running ) {
90
+ // No! set it on running
91
+ watermarkImageActions.running = true;
92
+
93
+ // store current action
94
+ watermarkImageActions.action = action;
95
+
96
+ // store current location where the action was fired
97
+ if ( $( this ).parents( '.media-modal ' ).length )
98
+ watermarkImageActions.action_location = 'media-modal';
99
+ else
100
+ watermarkImageActions.action_location = 'edit';
101
+
102
+ // store attachment id
103
+ watermarkImageActions.selected.push( id );
104
+
105
+ // remove current notices
106
+ $( '.iw-notice' ).slideUp( 'fast', function() {
107
+ $( this ).remove();
108
+ } );
109
+
110
+ // begin the update!
111
+ watermarkImageActions.post_loop();
112
+ } else {
113
+ // script is running, can't run two at the same time
114
+ watermarkImageActions.notice( 'iw-notice error', iwMediaModal.__running, false );
115
+ }
116
+ }
117
+ } );
118
+
119
+ // Since these are added later we'll need to enable dismissing again
120
+ $( document ).on( 'click', '.iw-notice.is-dismissible .notice-dismiss', function() {
121
+ $( this ).parents( '.iw-notice' ).slideUp( 'fast', function() {
122
+ $( this ).remove();
123
+ } );
124
+ } );
125
+
126
+ },
127
+ post_loop: function() {
128
+ // do we have selected attachments?
129
+ if ( watermarkImageActions.selected.length ) {
130
+ // take the first id
131
+ id = watermarkImageActions.selected[ 0 ];
132
+
133
+ // check for a valid ID (needs to be numeric)
134
+ if ( ! isNaN( id ) ) {
135
+ // Show loading icon
136
+ watermarkImageActions.row_image_feedback( 'loading', id );
137
+
138
+ // post data
139
+ data = {
140
+ '_iw_nonce': iwImageActionArgs._nonce,
141
+ 'action': 'iw_watermark_bulk_action',
142
+ 'iw-action': watermarkImageActions.action,
143
+ 'attachment_id': id
144
+ };
145
+
146
+ if ( watermarkImageActions.action_location == 'upload-list' )
147
+ watermarkImageActions.scroll_to( '#post-' + id, 'bottom' );
148
+
149
+ // the ajax post!
150
+ $.post( ajaxurl, data, function( response ) {
151
+ // show result
152
+ watermarkImageActions.result( response, id );
153
+ // remove this ID/key from the selected attachments
154
+ watermarkImageActions.selected.splice( 0, 1 );
155
+ // Redo this function
156
+ watermarkImageActions.post_loop();
157
+
158
+ $( '.iw-overlay' ).first().each( function() {
159
+ $( this ).fadeOut( 'fast', function() {
160
+ $( this ).remove();
161
+
162
+ if ( response.data === 'watermarked' ) {
163
+ $( '#image_watermark_buttons .value' ).append( '<span class="dashicons dashicons-yes" style="font-size: 24px;float: none;min-width: 28px;padding: 0;margin: 0; display: none;"></span>' );
164
+ $( '#image_watermark_buttons .value .dashicons' ).fadeIn( 'fast' );
165
+ } else if ( response.data === 'watermarkremoved' ) {
166
+ $( '#image_watermark_buttons .value' ).append( '<span class="dashicons dashicons-yes" style="font-size: 24px;float: none;min-width: 28px;padding: 0;margin: 0; display: none;"></span>' );
167
+ $( '#image_watermark_buttons .value .dashicons' ).fadeIn( 'fast' );
168
+ }
169
+
170
+ $( '#image_watermark_buttons .value .dashicons' ).delay( 1500 ).fadeOut( 'fast', function() {
171
+ $( this ).remove();
172
+ } );
173
+ } );
174
+ } );
175
+ } );
176
+ } else {
177
+ // ID is not valid so remove this key from the selected attachments
178
+ watermarkImageActions.selected.splice( 0, 1 );
179
+ // Redo this function
180
+ watermarkImageActions.post_loop();
181
+ }
182
+ } else {
183
+ // All is done, reset this "class"
184
+ watermarkImageActions.reset();
185
+ }
186
+ },
187
+ result: function( response, id ) {
188
+ // Was the ajax post successful?
189
+ if ( true === response.success ) {
190
+ // defaults
191
+ var type = false;
192
+ var message = '';
193
+ var overwrite = true;
194
+
195
+ // store response data
196
+ watermarkImageActions.response = response.data;
197
+
198
+ // Check what kind of action is done (watermarked, watermarkremoved or skipped)
199
+ switch ( response.data ) {
200
+ case 'watermarked':
201
+ // The css classes for the notice
202
+ type = 'iw-notice updated iw-watermarked';
203
+
204
+ // another successful update
205
+ watermarkImageActions.successCount += 1;
206
+
207
+ // did we have more success updates?
208
+ if ( 1 < watermarkImageActions.successCount )
209
+ message = iwImageActionArgs.__applied_multi.replace( '%s', watermarkImageActions.successCount );
210
+ else
211
+ message = iwImageActionArgs.__applied_one;
212
+
213
+ // update the row feedback
214
+ watermarkImageActions.row_image_feedback( 'success', id );
215
+
216
+ // reload the image
217
+ watermarkImageActions.reload_image( id );
218
+ break;
219
+
220
+ case 'watermarkremoved':
221
+ // The css classes for the notice
222
+ type = 'iw-notice updated iw-watermarkremoved';
223
+
224
+ // another successful update
225
+ watermarkImageActions.successCount += 1;
226
+
227
+ // did we have more success updates?
228
+ if ( 1 < watermarkImageActions.successCount )
229
+ message = iwImageActionArgs.__removed_multi.replace( '%s', watermarkImageActions.successCount );
230
+ else
231
+ message = iwImageActionArgs.__removed_one;
232
+
233
+ // update the row feedback
234
+ watermarkImageActions.row_image_feedback( 'success', id );
235
+
236
+ // reload the image
237
+ watermarkImageActions.reload_image( id );
238
+ break;
239
+
240
+ case 'skipped':
241
+ // The css classes for the notice
242
+ type = 'iw-notice error iw-skipped';
243
+
244
+ // another skipped update
245
+ watermarkImageActions.skippedCount += 1;
246
+
247
+ // adjust the message with the number of skipped updates
248
+ message = iwImageActionArgs.__skipped + ': ' + watermarkImageActions.skippedCount;
249
+
250
+ // update the row feedback
251
+ watermarkImageActions.row_image_feedback( 'error', id );
252
+ break;
253
+
254
+ default:
255
+ // The css classes for the notice
256
+ type = 'iw-notice error iw-message';
257
+
258
+ // The error message
259
+ message = response.data;
260
+
261
+ // update the row feedback
262
+ watermarkImageActions.row_image_feedback( 'error', id );
263
+
264
+ // This can be anything so don't overwrite
265
+ overwrite = false;
266
+ break;
267
+ }
268
+
269
+ if ( false !== type ) {
270
+ // we have a valid terun type, show the notice! (Overwrite current notice if available)
271
+ watermarkImageActions.notice( type, message, overwrite );
272
+ }
273
+ } else {
274
+ // No success...
275
+ watermarkImageActions.notice( 'iw-notice error', response.data, false );
276
+
277
+ // update the row feedback
278
+ watermarkImageActions.row_image_feedback( 'error', id );
279
+ }
280
+ },
281
+ row_image_feedback: function( type, id ) {
282
+ var css = { };
283
+ var cssinner = { };
284
+ var container_selector;
285
+
286
+ switch ( watermarkImageActions.action_location ) {
287
+ case 'upload-list':
288
+ container_selector = '.wp-list-table #post-' + id + ' .media-icon';
289
+ css = {
290
+ display: 'table',
291
+ width: $( container_selector ).width() + 'px',
292
+ height: $( container_selector ).height() + 'px',
293
+ top: '0',
294
+ left: '0',
295
+ position: 'absolute',
296
+ font: 'normal normal normal dashicons',
297
+ background: 'rgba(255,255,255,0.75)',
298
+ content: ''
299
+ };
300
+ cssinner = {
301
+ 'vertical-align': 'middle',
302
+ 'text-align': 'center',
303
+ display: 'table-cell',
304
+ width: '100%',
305
+ height: '100%',
306
+ };
307
+ break;
308
+
309
+ case 'edit':
310
+ container_selector = '.wp_attachment_holder #thumbnail-head-' + id + '';
311
+ css = {
312
+ display: 'table',
313
+ width: $( container_selector + ' img' ).width() + 'px',
314
+ height: $( container_selector + ' img' ).height() + 'px',
315
+ top: '0',
316
+ left: '0',
317
+ position: 'absolute',
318
+ font: 'normal normal normal dashicons',
319
+ background: 'rgba(255,255,255,0.75)',
320
+ content: ''
321
+ };
322
+ cssinner = {
323
+ 'vertical-align': 'middle',
324
+ 'text-align': 'center',
325
+ display: 'table-cell',
326
+ width: '100%',
327
+ height: '100%',
328
+ };
329
+ break;
330
+
331
+ case 'media-modal':
332
+ container_selector = '.media-modal #image_watermark_buttons[data-id="' + id + '"] .value';
333
+ css = {
334
+ 'float': 'none'
335
+ };
336
+ cssinner = {
337
+ 'float': 'none'
338
+ };
339
+ break;
340
+
341
+ default:
342
+ return false;
343
+ }
344
+
345
+ // css rules
346
+ $( container_selector ).css( 'position', 'relative' );
347
+
348
+ // Only create the element if it doesn't exist
349
+ if ( ! $( container_selector + ' .iw-overlay' ).length )
350
+ $( container_selector ).append( '<span class="iw-overlay"><span class="iw-overlay-inner"></span></span>' );
351
+
352
+ // Overwrite with new data
353
+ $( container_selector + ' .iw-overlay' ).css( css );
354
+ $( container_selector + ' .iw-overlay .iw-overlay-inner' ).css( cssinner );
355
+ $( container_selector + ' .iw-overlay .iw-overlay-inner' ).html( '<span class="spinner is-active"></span>' );
356
+
357
+ if ( watermarkImageActions.action_location === 'media-modal' )
358
+ $( container_selector + ' .iw-overlay .iw-overlay-inner .spinner' ).css( { 'float': 'none', 'padding': 0, 'margin': '-4px 0 0 10px' } );
359
+ },
360
+ notice: function( type, message, overwrite ) {
361
+ if ( watermarkImageActions.action_location === 'media-modal' )
362
+ return;
363
+
364
+ type += ' notice is-dismissible';
365
+
366
+ // Get the prefix based on the action location
367
+ switch ( watermarkImageActions.action_location ) {
368
+ case 'upload-list':
369
+ prefix = '.wrap > h1';
370
+ break;
371
+
372
+ default:
373
+ prefix = '#image_watermark_buttons';
374
+ }
375
+
376
+ // Overwrite the current notice?
377
+ if ( true === overwrite ) {
378
+ selector = false;
379
+
380
+ // Get the selector based on the response
381
+ switch ( watermarkImageActions.response ) {
382
+ case 'watermarked':
383
+ selector = '.iw-notice.iw-watermarked';
384
+ break;
385
+
386
+ case 'watermarkremoved':
387
+ selector = '.iw-notice.iw-watermarkremoved';
388
+ break;
389
+
390
+ case 'skipped':
391
+ selector = '.iw-notice.iw-skipped';
392
+ break;
393
+ }
394
+
395
+ // Do we have a selector and can we find it? If not, just create a new notice
396
+ if ( selector && $( '.wrap ' + selector + ' > p' ).length ) {
397
+ // Get the selector based on the action location (not not forget the ending space)
398
+ switch ( watermarkImageActions.action_location ) {
399
+ case 'upload-list':
400
+ prefix = '.wrap ';
401
+ break;
402
+
403
+ default:
404
+ prefix = '#image_watermark_buttons ';
405
+ }
406
+
407
+ $( prefix + selector + ' > p' ).html( message );
408
+ } else {
409
+ $( prefix ).after( '<div class="' + type + '" style="display: none;"><p>' + message + '</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' + iwImageActionArgs.__dismiss + '</span></button></div>' );
410
+ $( '.iw-notice' ).slideDown( 'fast' );
411
+ }
412
+ } else {
413
+ // create a new notice
414
+ $( prefix ).after( '<div class="' + type + '" style="display: none;"><p>' + message + '</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' + iwImageActionArgs.__dismiss + '</span></button></div>' );
415
+ $( '.iw-notice' ).slideDown( 'fast' );
416
+ }
417
+ },
418
+ reset: function() {
419
+ watermarkImageActions.running = false;
420
+ watermarkImageActions.action = '';
421
+ watermarkImageActions.response = '';
422
+ watermarkImageActions.selected = [];
423
+ watermarkImageActions.successCount = 0;
424
+ watermarkImageActions.skippedCount = 0;
425
+
426
+ // remove the overlay
427
+ setTimeout( function() {
428
+ $( '.iw-overlay' ).each( function() {
429
+ $( this ).fadeOut( 'fast', function() {
430
+ $( this ).remove();
431
+ } );
432
+ } );
433
+ }, 100 );
434
+ },
435
+ reload_image: function( id ) {
436
+ // reload the images
437
+ time = new Date().getTime();
438
+ selector = false;
439
+
440
+ // Get the selector based on the action location
441
+ switch ( watermarkImageActions.action_location ) {
442
+ case 'upload-list':
443
+ selector = '.wp-list-table #post-' + id + ' .image-icon img';
444
+ break;
445
+
446
+ case 'media-modal':
447
+ selector = '.attachment-details[data-id="' + id + '"] img, .attachment[data-id="' + id + '"] img';
448
+ break;
449
+
450
+ case 'edit':
451
+ selector = '.wp_attachment_holder img';
452
+ break;
453
+ }
454
+
455
+ if ( selector ) {
456
+ image = $( selector );
457
+ image.each( function() {
458
+ // Remove the responsive metadata, this prevents reloading the image
459
+ $( this ).removeAttr( 'srcset' );
460
+ $( this ).removeAttr( 'sizes' );
461
+
462
+ // Reload the image (actually a browser hack by adding a time parameter to the image)
463
+ $( this ).attr( 'src', watermarkImageActions.replace_url_param( $( this ).attr( 'src' ), 't', time ) );
464
+ } );
465
+ }
466
+ },
467
+ rotate_icon: function( icon ) {
468
+ // This function accepts selectors and objects
469
+ if ( typeof icon == 'string' )
470
+ icon = $( icon );
471
+
472
+ // Check for the length of the selected object.
473
+ if ( $( icon.selector ).length ) {
474
+ // Set rotation to 0
475
+ icon.css( {
476
+ '-webkit-transform': 'rotate(0deg)',
477
+ '-ms-transform': 'rotate(0deg)',
478
+ 'transform': 'rotate(0deg)',
479
+ 'borderSpacing': '0',
480
+ } );
481
+
482
+ // Do animation (one rotation)
483
+ icon.animate(
484
+ { borderSpacing: 360 },
485
+ {
486
+ duration: 1000,
487
+ step: function( now, fx ) {
488
+ $( this ).css( '-webkit-transform', 'rotate(' + now + 'deg)' );
489
+ $( this ).css( '-ms-transform', 'rotate(' + now + 'deg)' );
490
+ $( this ).css( 'transform', 'rotate(' + now + 'deg)' );
491
+
492
+ if ( now == 360 ) {
493
+ // Animation finished, stop loop and restart
494
+ icon.stop();
495
+ watermarkImageActions.rotate_icon( icon );
496
+ }
497
+ }
498
+ }
499
+ );
500
+ }
501
+ },
502
+ replace_url_param: function( url, paramName, paramValue ) {
503
+ var pattern = new RegExp( '\\b(' + paramName + '=).*?(&|$)' );
504
+
505
+ if ( url.search( pattern ) >= 0 )
506
+ return url.replace( pattern, '$1' + paramValue + '$2' );
507
+
508
+ return url + ( url.indexOf( '?' ) > 0 ? '&' : '?' ) + paramName + '=' + paramValue;
509
+ },
510
+ scroll_to: function( elementSelector, verticalTarget ) {
511
+ var offset = $( elementSelector ).offset();
512
+ var offsetTop = offset.top;
513
+
514
+ // If the element it above the current viewport, scroll to it
515
+ if ( offset.top < $( window ).scrollTop() ) {
516
+ $( window ).scrollTop( offsetTop );
517
+
518
+ return; // No further actions needed
519
+ }
520
+
521
+ var windowTopOffset = $( window ).scrollTop();
522
+ var windowBottomOffset = windowTopOffset + $( window ).outerHeight();
523
+
524
+ switch ( verticalTarget ) {
525
+ case 'top':
526
+ offsetTop = offsetTop - $( elementSelector ).outerHeight();
527
+ break;
528
+
529
+ case 'bottom':
530
+ if ( offset.top < windowBottomOffset )
531
+ return; // The element is in the viewport
532
+
533
+ offsetTop = offsetTop - $( window ).outerHeight();
534
+ offsetTop = offsetTop + $( elementSelector ).outerHeight();
535
+ break;
536
+
537
+ case 'center':
538
+ if ( offsetTop < windowBottomOffset && offsetTop >= windowTopOffset )
539
+ return; // The element is in the viewport
540
+
541
+ offsetTop = offsetTop - ( $( window ).outerHeight() / 2 );
542
+ offsetTop = offsetTop + ( $( elementSelector ).outerHeight() / 2 );
543
+ break;
544
+ }
545
+
546
+ $( window ).scrollTop( offsetTop );
547
+ }
548
+ };
549
+
550
+ // We need that nonce!
551
+ if ( typeof iwImageActionArgs._nonce !== 'undefined' )
552
+ watermarkImageActions.init();
553
+ } );
554
+
555
+ } )( jQuery );
js/admin-settings.js CHANGED
@@ -1,88 +1,90 @@
1
- jQuery( document ).ready( function ( $ ) {
2
 
3
- // enable watermark for
4
- $( document ).on( 'change', '#df_option_everywhere, #df_option_cpt', function () {
5
- if ( $( '#cpt-specific input[type=radio]:checked' ).val() === 'everywhere' ) {
6
- $( '#cpt-select' ).fadeOut( 300 );
7
- } else if ( $( '#cpt-specific input[type=radio]:checked' ).val() === 'specific' ) {
8
- $( '#cpt-select' ).fadeIn( 300 );
9
- }
10
- } );
 
11
 
12
- $( document ).on( 'click', '#reset_image_watermark_options', function () {
13
- return confirm( iwArgs.resetToDefaults );
14
- } );
15
 
16
- // size slider
17
- $( '#iw_size_span' ).slider( {
18
- value: $( '#iw_size_input' ).val(),
19
- min: 0,
20
- max: 100,
21
- step: 1,
22
- orientation: 'horizontal',
23
- slide: function ( e, ui ) {
24
- $( '#iw_size_input' ).attr( 'value', ui.value );
25
- $( '#iw_size_span' ).attr( 'title', ui.value );
26
 
27
- var element = $( ui.handle ).prev( '.iw-current-value' );
28
 
29
- element.text( ui.value );
30
- element.css( 'left', ui.value + '%' );
31
- }
32
- } );
33
 
34
- // opacity slider
35
- $( '#iw_opacity_span' ).slider( {
36
- value: $( '#iw_opacity_input' ).val(),
37
- min: 0,
38
- max: 100,
39
- step: 1,
40
- orientation: 'horizontal',
41
- slide: function ( e, ui ) {
42
- $( '#iw_opacity_input' ).attr( 'value', ui.value );
43
- $( '#iw_opacity_span' ).attr( 'title', ui.value );
44
 
45
- var element = $( ui.handle ).prev( '.iw-current-value' );
46
 
47
- element.text( ui.value );
48
- element.css( 'left', ui.value + '%' );
49
- }
50
- } );
51
 
52
- // quality slider
53
- $( '#iw_quality_span' ).slider( {
54
- value: $( '#iw_quality_input' ).val(),
55
- min: 0,
56
- max: 100,
57
- step: 1,
58
- orientation: 'horizontal',
59
- slide: function ( e, ui ) {
60
- $( '#iw_quality_input' ).attr( 'value', ui.value );
61
- $( '#iw_quality_span' ).attr( 'title', ui.value );
62
 
63
- var element = $( ui.handle ).prev( '.iw-current-value' );
64
 
65
- element.text( ui.value );
66
- element.css( 'left', ui.value + '%' );
67
- }
68
- } );
69
 
70
- // quality slider
71
- $( '#iw_backup_quality_span' ).slider( {
72
- value: $( '#iw_backup_quality_input' ).val(),
73
- min: 0,
74
- max: 100,
75
- step: 1,
76
- orientation: 'horizontal',
77
- slide: function ( e, ui ) {
78
- $( '#iw_backup_quality_input' ).attr( 'value', ui.value );
79
- $( '#iw_backup_quality_span' ).attr( 'title', ui.value );
80
 
81
- var element = $( ui.handle ).prev( '.iw-current-value' );
82
 
83
- element.text( ui.value );
84
- element.css( 'left', ui.value + '%' );
85
- }
86
- } );
 
87
 
88
- } );
1
+ ( function( $ ) {
2
 
3
+ // ready event
4
+ $( function() {
5
+ // enable watermark for
6
+ $( document ).on( 'change', '#df_option_everywhere, #df_option_cpt', function() {
7
+ if ( $( '#cpt-specific input[type=radio]:checked' ).val() === 'everywhere' )
8
+ $( '#cpt-select' ).fadeOut( 300 );
9
+ else if ( $( '#cpt-specific input[type=radio]:checked' ).val() === 'specific' )
10
+ $( '#cpt-select' ).fadeIn( 300 );
11
+ } );
12
 
13
+ $( document ).on( 'click', '#reset_image_watermark_options', function() {
14
+ return confirm( iwArgs.resetToDefaults );
15
+ } );
16
 
17
+ // size slider
18
+ $( '#iw_size_span' ).slider( {
19
+ value: $( '#iw_size_input' ).val(),
20
+ min: 0,
21
+ max: 100,
22
+ step: 1,
23
+ orientation: 'horizontal',
24
+ slide: function( e, ui ) {
25
+ $( '#iw_size_input' ).attr( 'value', ui.value );
26
+ $( '#iw_size_span' ).attr( 'title', ui.value );
27
 
28
+ var element = $( ui.handle ).prev( '.iw-current-value' );
29
 
30
+ element.text( ui.value );
31
+ element.css( 'left', ui.value + '%' );
32
+ }
33
+ } );
34
 
35
+ // opacity slider
36
+ $( '#iw_opacity_span' ).slider( {
37
+ value: $( '#iw_opacity_input' ).val(),
38
+ min: 0,
39
+ max: 100,
40
+ step: 1,
41
+ orientation: 'horizontal',
42
+ slide: function( e, ui ) {
43
+ $( '#iw_opacity_input' ).attr( 'value', ui.value );
44
+ $( '#iw_opacity_span' ).attr( 'title', ui.value );
45
 
46
+ var element = $( ui.handle ).prev( '.iw-current-value' );
47
 
48
+ element.text( ui.value );
49
+ element.css( 'left', ui.value + '%' );
50
+ }
51
+ } );
52
 
53
+ // quality slider
54
+ $( '#iw_quality_span' ).slider( {
55
+ value: $( '#iw_quality_input' ).val(),
56
+ min: 0,
57
+ max: 100,
58
+ step: 1,
59
+ orientation: 'horizontal',
60
+ slide: function( e, ui ) {
61
+ $( '#iw_quality_input' ).attr( 'value', ui.value );
62
+ $( '#iw_quality_span' ).attr( 'title', ui.value );
63
 
64
+ var element = $( ui.handle ).prev( '.iw-current-value' );
65
 
66
+ element.text( ui.value );
67
+ element.css( 'left', ui.value + '%' );
68
+ }
69
+ } );
70
 
71
+ // backup quality slider
72
+ $( '#iw_backup_quality_span' ).slider( {
73
+ value: $( '#iw_backup_quality_input' ).val(),
74
+ min: 0,
75
+ max: 100,
76
+ step: 1,
77
+ orientation: 'horizontal',
78
+ slide: function( e, ui ) {
79
+ $( '#iw_backup_quality_input' ).attr( 'value', ui.value );
80
+ $( '#iw_backup_quality_span' ).attr( 'title', ui.value );
81
 
82
+ var element = $( ui.handle ).prev( '.iw-current-value' );
83
 
84
+ element.text( ui.value );
85
+ element.css( 'left', ui.value + '%' );
86
+ }
87
+ } );
88
+ } );
89
 
90
+ } )( jQuery );
js/admin-upload.js CHANGED
@@ -1,70 +1,70 @@
1
- jQuery( document ).ready( function ( $ ) {
2
 
3
- watermarkFileUpload = {
4
- frame: function () {
5
- if ( this._frameWatermark )
6
- return this._frameWatermark;
 
 
7
 
8
- this._frameWatermark = wp.media( {
9
- title: iwUploadArgs.title,
10
- frame: iwUploadArgs.frame,
11
- button: iwUploadArgs.button,
12
- multiple: iwUploadArgs.multiple,
13
- library: {
14
- type: 'image'
15
- }
16
- } );
17
 
18
- this._frameWatermark.on( 'open', this.updateFrame ).state( 'library' ).on( 'select', this.select );
19
- return this._frameWatermark;
20
- },
21
- select: function () {
22
- var attachment = this.frame.state().get( 'selection' ).first();
23
 
24
- if ( $.inArray( attachment.attributes.mime, [ 'image/gif', 'image/jpg', 'image/jpeg', 'image/png' ] ) !== -1 ) {
 
 
 
25
 
26
- $( '#iw_upload_image' ).val( attachment.attributes.id );
 
27
 
28
- if ( $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src' ) !== '' ) {
29
- $( 'div#previewImg_imageDiv img#previewImg_image' ).replaceWith( '<img id="previewImg_image" src="' + attachment.attributes.url + '" alt="" width="300" />' );
30
- } else {
31
- $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src', attachment.attributes.url );
32
- }
33
 
34
- $( '#iw_turn_off_image_button' ).removeAttr( 'disabled' );
35
- $( 'div#previewImg_imageDiv img#previewImg_image' ).show();
36
 
37
- var img = new Image();
38
- img.src = attachment.attributes.url;
39
 
40
- img.onload = function () {
41
- $( 'p#previewImageInfo' ).html( iwUploadArgs.originalSize + ': ' + this.width + ' ' + iwUploadArgs.px + ' / ' + this.height + ' ' + iwUploadArgs.px );
42
- }
43
 
44
- } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
- $( '#iw_turn_off_image_button' ).attr( 'disabled', 'true' );
47
- $( '#iw_upload_image' ).val( 0 );
48
- $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src', '' ).hide();
49
- $( 'p#previewImageInfo' ).html( '<strong>' + iwUploadArgs.notAllowedImg + '</strong>' );
50
 
51
- }
52
- },
53
- init: function () {
54
- $( '#wpbody' ).on( 'click', 'input#iw_upload_image_button', function ( e ) {
55
- e.preventDefault();
56
- watermarkFileUpload.frame().open();
57
- } );
58
- }
59
- };
60
 
61
- watermarkFileUpload.init();
62
-
63
- $( document ).on( 'click', '#iw_turn_off_image_button', function ( event ) {
64
- $( this ).attr( 'disabled', 'true' );
65
- $( '#iw_upload_image' ).val( 0 );
66
- $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src', '' ).hide();
67
- $( 'p#previewImageInfo' ).html( iwUploadArgs.noSelectedImg );
68
- } );
69
-
70
- } );
1
+ ( function( $ ) {
2
 
3
+ // ready event
4
+ $( function() {
5
+ watermarkFileUpload = {
6
+ frame: function() {
7
+ if ( this._frameWatermark )
8
+ return this._frameWatermark;
9
 
10
+ this._frameWatermark = wp.media( {
11
+ title: iwUploadArgs.title,
12
+ frame: iwUploadArgs.frame,
13
+ button: iwUploadArgs.button,
14
+ multiple: iwUploadArgs.multiple,
15
+ library: {
16
+ type: 'image'
17
+ }
18
+ } );
19
 
20
+ this._frameWatermark.on( 'open', this.updateFrame ).state( 'library' ).on( 'select', this.select );
 
 
 
 
21
 
22
+ return this._frameWatermark;
23
+ },
24
+ select: function() {
25
+ var attachment = this.frame.state().get( 'selection' ).first();
26
 
27
+ if ( $.inArray( attachment.attributes.mime, [ 'image/gif', 'image/jpg', 'image/jpeg', 'image/png' ] ) !== -1 ) {
28
+ $( '#iw_upload_image' ).val( attachment.attributes.id );
29
 
30
+ if ( $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src' ) !== '' )
31
+ $( 'div#previewImg_imageDiv img#previewImg_image' ).replaceWith( '<img id="previewImg_image" src="' + attachment.attributes.url + '" alt="" width="300" />' );
32
+ else
33
+ $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src', attachment.attributes.url );
 
34
 
35
+ $( '#iw_turn_off_image_button' ).removeAttr( 'disabled' );
36
+ $( 'div#previewImg_imageDiv img#previewImg_image' ).show();
37
 
38
+ var img = new Image();
 
39
 
40
+ img.src = attachment.attributes.url;
 
 
41
 
42
+ img.onload = function() {
43
+ $( 'p#previewImageInfo' ).html( iwUploadArgs.originalSize + ': ' + this.width + ' ' + iwUploadArgs.px + ' / ' + this.height + ' ' + iwUploadArgs.px );
44
+ }
45
+ } else {
46
+ $( '#iw_turn_off_image_button' ).attr( 'disabled', 'true' );
47
+ $( '#iw_upload_image' ).val( 0 );
48
+ $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src', '' ).hide();
49
+ $( 'p#previewImageInfo' ).html( '<strong>' + iwUploadArgs.notAllowedImg + '</strong>' );
50
+ }
51
+ },
52
+ init: function() {
53
+ $( '#wpbody' ).on( 'click', 'input#iw_upload_image_button', function( e ) {
54
+ e.preventDefault();
55
+ watermarkFileUpload.frame().open();
56
+ } );
57
+ }
58
+ };
59
 
60
+ watermarkFileUpload.init();
 
 
 
61
 
62
+ $( document ).on( 'click', '#iw_turn_off_image_button', function() {
63
+ $( this ).attr( 'disabled', 'true' );
64
+ $( '#iw_upload_image' ).val( 0 );
65
+ $( 'div#previewImg_imageDiv img#previewImg_image' ).attr( 'src', '' ).hide();
66
+ $( 'p#previewImageInfo' ).html( iwUploadArgs.noSelectedImg );
67
+ } );
68
+ } );
 
 
69
 
70
+ } )( jQuery );
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,191 +1,197 @@
1
- === Image Watermark ===
2
- Contributors: dfactory
3
- Donate link: http://www.dfactory.eu/
4
- Tags: image, images, picture, photo, watermark, watermarking, protection, image protection, image security
5
- Requires at least: 4.0
6
- Tested up to: 5.3
7
- Stable tag: 1.6.6
8
- License: MIT License
9
- License URI: http://opensource.org/licenses/MIT
10
-
11
- Image Watermark allows you to automatically watermark images uploaded to the WordPress Media Library and bulk watermark previously uploaded images.
12
-
13
- == Description ==
14
-
15
- [Image Watermark](http://www.dfactory.eu/plugins/image-watermark/) allows you to automatically watermark images uploaded to the WordPress Media Library and bulk watermark previously uploaded images.
16
-
17
- For more information, check out plugin page at [dFactory](http://www.dfactory.eu/), [documentation page](https://www.dfactory.eu/docs/image-watermark-plugin/) or plugin [support forum](http://www.dfactory.eu/support/forum/image-watermark/).
18
-
19
- = Features include: =
20
-
21
- * Bulk watermark - Apply watermark option in Media Library actions
22
- * Watermark images already uploaded to Media Library
23
- * GD LIbrary and ImageMagic support
24
- * Image backup functionality
25
- * Option to remove watermark
26
- * Flexible watermark position
27
- * Watermark image preview
28
- * Set watermark offset
29
- * Select post types where watermark will be aplied to uploaded images or select adding watermark during any image upload
30
- * Select from 3 methods of aplying watermark size: original, custom or scaled
31
- * Set watermark transparency / opacity
32
- * Select image format (baseline or progressive)
33
- * Set image quality
34
- * Protect your images from copying via drag&drop
35
- * Disable right mouse click on images
36
- * Disable image protection for logged-in users
37
- * .pot file for translations included
38
-
39
- == Installation ==
40
-
41
- 1. Install Image Watermark either via the WordPress.org plugin directory, or by uploading the files to your server
42
- 2. Activate the plugin through the 'Plugins' menu in WordPress
43
- 3. Go to the Watermark menu in Settings and set your watermarking options.
44
- 4. Enable watermark to apply watermark to uploaded images or go to Media Library to apply watermark to previously uploaded images
45
-
46
- == Frequently Asked Questions ==
47
-
48
- No questions yet.
49
-
50
- == Screenshots ==
51
-
52
- 1. screenshot-1.png
53
- 2. screenshot-2.png
54
-
55
- == Changelog ==
56
-
57
- = 1.6.6 =
58
- * Tweak: PHP 7.3 compatibility
59
-
60
- = 1.6.5 =
61
- * Fix: Improved support for PHP 7 and above
62
- * Fix: Backup folders handling of date based organized uploads
63
-
64
- = 1.6.4 =
65
- * Fix: Transparent PNG issues with ImageMagic library
66
-
67
- = 1.6.3.1 =
68
- * Fix: The plugin directory upload fix.
69
-
70
- = 1.6.3 =
71
- * Fix: PNG files watermarking issue
72
-
73
- = 1.6.2 =
74
- * New: Option to select watermark offset unit - pixels or percentages
75
- * Tweak: Added values to slider settings fields
76
-
77
- = 1.6.1 =
78
- * Fix: Minor bug with AJAX requests, thanks to [JoryHogeveen](https://github.com/JoryHogeveen)
79
- * Fix: Prevent watermarking the watermark image, thanks to [JoryHogeveen](https://github.com/JoryHogeveen)
80
- * Tweak: Code cleanup
81
-
82
- = 1.6.0 =
83
- * New: Image backup functionality, thanks to [JoryHogeveen](https://github.com/JoryHogeveen)
84
- * New: Option to remove watermark (if backup is available)
85
-
86
- = 1.5.6 =
87
- * New: PHP image processing library option, if more than one available.
88
- * Fix: Manual / Media library watermarking not working.
89
- * Fix: Image sizes not being generated proparly in GD library.
90
-
91
- = 1.5.5 =
92
- * Fix: Determine AJAX frontend or backend request
93
- * Tweak: Remove Polish and Russian translations, in favor of GlotPress
94
-
95
- = 1.5.4 =
96
- * Fix: Use of undefined constant DOING_AJAX
97
-
98
- = 1.5.3 =
99
- * New: ImageMagic support
100
-
101
- = 1.5.2 =
102
- * Tweak: Switch from wp_get_referer() to DOING_AJAX and is_admin().
103
-
104
- = 1.5.1 =
105
- * New: Introducing [plugin documentation](https://www.dfactory.eu/docs/image-watermark-plugin/)
106
- * Tweak: Improved transparent watermark support
107
-
108
- = 1.5.0 =
109
- * Tweak: Plugins setting adjusted to WP settings API
110
- * Tweak: General code cleanup
111
- * Tweak: Added Media Library bulk watermarking notice
112
-
113
- = 1.4.1 =
114
- * New: Hungarian translation, thanks to Meszaros Tamas
115
-
116
- = 1.4.0 =
117
- * New: Option to donate this plugin :)
118
-
119
- = 1.3.3 =
120
- * New: RUssian translation, thanks to [Sly](http://wpguru.ru)
121
-
122
- = 1.3.2 =
123
- * New: Chinese translation, thanks to [xiaoyaole](http://www.luoxiao123.cn/)
124
-
125
- = 1.3.1 =
126
- * Fix: Option to disable right click on images not working
127
-
128
- = 1.3.0 =
129
- * Tweak: Manual watermarking now works even if selected post types are selected
130
- * Tweak: UI improvements for WP 3.8
131
- * Fix: Image protection options not saving properly
132
-
133
- = 1.2.1 =
134
- * New: German translation, thanks to Matthias Siebler
135
-
136
- = 1.2.0 =
137
- * New: Frontend watermarking option (for front-end upload plugins and custom front-end upload code)
138
- * New: Introducing iw_watermark_display filter
139
- * New: Option to delete all plugin data on deactivation
140
- * Tweak: Rewritten watermark application method
141
- * Tweak: UI enhancements for settings page
142
-
143
- = 1.1.4 =
144
- * New: Arabic translation, thanks to Hassan Hisham
145
-
146
- = 1.1.3 =
147
- * New: Introducing API hooks: iw_before_apply_watermark, iw_after_apply_watermark, iw_watermark_options
148
- * Fix: Wrong watermark watermark path
149
- * Fix: Final fix (hopefully) for getimagesize() error
150
-
151
- = 1.1.2 =
152
- * New: Image quality option
153
- * New: Image format selection (progressive or baseline)
154
- * Fix: Error when getimagesize() is not available on some servers
155
- * Tweak: Files & class naming conventions
156
-
157
- = 1.1.1 =
158
- * New: Added option to enable or disable manual watermarking in Media Library
159
- * Fix: Apply watermark option not visible in Media Library actions
160
- * Fix: Warning on full size images
161
-
162
- = 1.1.0 =
163
- * New: Bulk watermark - Apply watermark in Media Library actions
164
- * New: Watermark images already uploaded to Media Library
165
-
166
- = 1.0.3 =
167
- * Fix: Error during upload of file types other than images (png, jpg)
168
- * Fix: Limit watermark file types to png, gif, jpg
169
- * Tweak: Validation for watermark size and transparency values
170
- * Tweak: Remove unnecessary functions
171
- * Tweak: Code cleanup
172
- * Tweak: Added more code comments
173
- * Tweak: Small css changes
174
-
175
- = 1.0.2 =
176
- * New: Add watermark to custom images sizes registered in theme
177
- * Tweak: Admin notices on settings page if no watermark image selected
178
- * Tweak: JavaScript enquequing on front-end
179
- * Tweak: General code cleanup
180
- * Tweak: Changed label for enabling image protection for logged-in users
181
-
182
- = 1.0.1 =
183
- * Fix: Using image ID instead of image URL during image upload
184
-
185
- = 1.0.0 =
186
- Initial release
187
-
188
- == Upgrade Notice ==
189
-
190
- = 1.6.6 =
191
- * Tweak: PHP 7.3 compatibility
 
 
 
 
 
 
1
+ === Image Watermark ===
2
+ Contributors: dfactory
3
+ Donate link: http://www.dfactory.eu/
4
+ Tags: image, images, picture, photo, watermark, watermarking, protection, image protection, image security
5
+ Requires at least: 4.3
6
+ Requires PHP: 5.3
7
+ Tested up to: 5.9.1
8
+ Stable tag: 1.7.0
9
+ License: MIT License
10
+ License URI: http://opensource.org/licenses/MIT
11
+
12
+ Image Watermark allows you to automatically watermark images uploaded to the WordPress Media Library and bulk watermark previously uploaded images.
13
+
14
+ == Description ==
15
+
16
+ [Image Watermark](http://www.dfactory.eu/plugins/image-watermark/) allows you to automatically watermark images uploaded to the WordPress Media Library and bulk watermark previously uploaded images.
17
+
18
+ For more information, check out plugin page at [dFactory](http://www.dfactory.eu/), [documentation page](https://www.dfactory.eu/docs/image-watermark-plugin/) or plugin [support forum](http://www.dfactory.eu/support/forum/image-watermark/).
19
+
20
+ = Features include: =
21
+
22
+ * Bulk watermark - Apply watermark option in Media Library actions
23
+ * Watermark images already uploaded to Media Library
24
+ * GD LIbrary and ImageMagic support
25
+ * Image backup functionality
26
+ * Option to remove watermark
27
+ * Flexible watermark position
28
+ * Watermark image preview
29
+ * Set watermark offset
30
+ * Select post types where watermark will be aplied to uploaded images or select adding watermark during any image upload
31
+ * Select from 3 methods of aplying watermark size: original, custom or scaled
32
+ * Set watermark transparency / opacity
33
+ * Select image format (baseline or progressive)
34
+ * Set image quality
35
+ * Protect your images from copying via drag&drop
36
+ * Disable right mouse click on images
37
+ * Disable image protection for logged-in users
38
+ * .pot file for translations included
39
+
40
+ == Installation ==
41
+
42
+ 1. Install Image Watermark either via the WordPress.org plugin directory, or by uploading the files to your server
43
+ 2. Activate the plugin through the 'Plugins' menu in WordPress
44
+ 3. Go to the Watermark menu in Settings and set your watermarking options.
45
+ 4. Enable watermark to apply watermark to uploaded images or go to Media Library to apply watermark to previously uploaded images
46
+
47
+ == Frequently Asked Questions ==
48
+
49
+ No questions yet.
50
+
51
+ == Screenshots ==
52
+
53
+ 1. screenshot-1.png
54
+ 2. screenshot-2.png
55
+
56
+ == Changelog ==
57
+
58
+ = 1.7.0 =
59
+ * Tweak: WordPress 5.9 compatibility
60
+ * Tweak: PHP 8.x compatibility
61
+
62
+ = 1.6.6 =
63
+ * Tweak: PHP 7.3 compatibility
64
+
65
+ = 1.6.5 =
66
+ * Fix: Improved support for PHP 7 and above
67
+ * Fix: Backup folders handling of date based organized uploads
68
+
69
+ = 1.6.4 =
70
+ * Fix: Transparent PNG issues with ImageMagic library
71
+
72
+ = 1.6.3.1 =
73
+ * Fix: The plugin directory upload fix.
74
+
75
+ = 1.6.3 =
76
+ * Fix: PNG files watermarking issue
77
+
78
+ = 1.6.2 =
79
+ * New: Option to select watermark offset unit - pixels or percentages
80
+ * Tweak: Added values to slider settings fields
81
+
82
+ = 1.6.1 =
83
+ * Fix: Minor bug with AJAX requests, thanks to [JoryHogeveen](https://github.com/JoryHogeveen)
84
+ * Fix: Prevent watermarking the watermark image, thanks to [JoryHogeveen](https://github.com/JoryHogeveen)
85
+ * Tweak: Code cleanup
86
+
87
+ = 1.6.0 =
88
+ * New: Image backup functionality, thanks to [JoryHogeveen](https://github.com/JoryHogeveen)
89
+ * New: Option to remove watermark (if backup is available)
90
+
91
+ = 1.5.6 =
92
+ * New: PHP image processing library option, if more than one available.
93
+ * Fix: Manual / Media library watermarking not working.
94
+ * Fix: Image sizes not being generated proparly in GD library.
95
+
96
+ = 1.5.5 =
97
+ * Fix: Determine AJAX frontend or backend request
98
+ * Tweak: Remove Polish and Russian translations, in favor of GlotPress
99
+
100
+ = 1.5.4 =
101
+ * Fix: Use of undefined constant DOING_AJAX
102
+
103
+ = 1.5.3 =
104
+ * New: ImageMagic support
105
+
106
+ = 1.5.2 =
107
+ * Tweak: Switch from wp_get_referer() to DOING_AJAX and is_admin().
108
+
109
+ = 1.5.1 =
110
+ * New: Introducing [plugin documentation](https://www.dfactory.eu/docs/image-watermark-plugin/)
111
+ * Tweak: Improved transparent watermark support
112
+
113
+ = 1.5.0 =
114
+ * Tweak: Plugins setting adjusted to WP settings API
115
+ * Tweak: General code cleanup
116
+ * Tweak: Added Media Library bulk watermarking notice
117
+
118
+ = 1.4.1 =
119
+ * New: Hungarian translation, thanks to Meszaros Tamas
120
+
121
+ = 1.4.0 =
122
+ * New: Option to donate this plugin :)
123
+
124
+ = 1.3.3 =
125
+ * New: RUssian translation, thanks to [Sly](http://wpguru.ru)
126
+
127
+ = 1.3.2 =
128
+ * New: Chinese translation, thanks to [xiaoyaole](http://www.luoxiao123.cn/)
129
+
130
+ = 1.3.1 =
131
+ * Fix: Option to disable right click on images not working
132
+
133
+ = 1.3.0 =
134
+ * Tweak: Manual watermarking now works even if selected post types are selected
135
+ * Tweak: UI improvements for WP 3.8
136
+ * Fix: Image protection options not saving properly
137
+
138
+ = 1.2.1 =
139
+ * New: German translation, thanks to Matthias Siebler
140
+
141
+ = 1.2.0 =
142
+ * New: Frontend watermarking option (for front-end upload plugins and custom front-end upload code)
143
+ * New: Introducing iw_watermark_display filter
144
+ * New: Option to delete all plugin data on deactivation
145
+ * Tweak: Rewritten watermark application method
146
+ * Tweak: UI enhancements for settings page
147
+
148
+ = 1.1.4 =
149
+ * New: Arabic translation, thanks to Hassan Hisham
150
+
151
+ = 1.1.3 =
152
+ * New: Introducing API hooks: iw_before_apply_watermark, iw_after_apply_watermark, iw_watermark_options
153
+ * Fix: Wrong watermark watermark path
154
+ * Fix: Final fix (hopefully) for getimagesize() error
155
+
156
+ = 1.1.2 =
157
+ * New: Image quality option
158
+ * New: Image format selection (progressive or baseline)
159
+ * Fix: Error when getimagesize() is not available on some servers
160
+ * Tweak: Files & class naming conventions
161
+
162
+ = 1.1.1 =
163
+ * New: Added option to enable or disable manual watermarking in Media Library
164
+ * Fix: Apply watermark option not visible in Media Library actions
165
+ * Fix: Warning on full size images
166
+
167
+ = 1.1.0 =
168
+ * New: Bulk watermark - Apply watermark in Media Library actions
169
+ * New: Watermark images already uploaded to Media Library
170
+
171
+ = 1.0.3 =
172
+ * Fix: Error during upload of file types other than images (png, jpg)
173
+ * Fix: Limit watermark file types to png, gif, jpg
174
+ * Tweak: Validation for watermark size and transparency values
175
+ * Tweak: Remove unnecessary functions
176
+ * Tweak: Code cleanup
177
+ * Tweak: Added more code comments
178
+ * Tweak: Small css changes
179
+
180
+ = 1.0.2 =
181
+ * New: Add watermark to custom images sizes registered in theme
182
+ * Tweak: Admin notices on settings page if no watermark image selected
183
+ * Tweak: JavaScript enquequing on front-end
184
+ * Tweak: General code cleanup
185
+ * Tweak: Changed label for enabling image protection for logged-in users
186
+
187
+ = 1.0.1 =
188
+ * Fix: Using image ID instead of image URL during image upload
189
+
190
+ = 1.0.0 =
191
+ Initial release
192
+
193
+ == Upgrade Notice ==
194
+
195
+ = 1.7.0 =
196
+ * Tweak: WordPress 5.9 compatibility
197
+ * Tweak: PHP 8.x compatibility