PluginProbe
Image Watermark / 2.0.12
Image Watermark v2.0.12
2.0.12 2.0.11 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.4.1 1.5.0 1.5.1 1.5.2 1.5.2.1 1.5.3 1.5.3.1 All 55 releases
image-watermark / image-watermark.php

image-watermark.php in Image Watermark 2.0.12, at image-watermark.php

1,306 lines 46.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Image Watermark
4 Description: Secure and brand your images with automatic watermarks. Apply image or text overlays to new uploads and bulk process existing Media Library images with ease.
5 Version: 2.0.12
6 Author: dFactory
7 Author URI: http://www.dfactory.co/
8 Plugin URI: http://www.dfactory.co/products/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-2026, 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 /**
29 * Image Watermark class.
30 *
31 * @class Image_Watermark
32 * @version 2.0.12
33 */
34 final class Image_Watermark {
35
36 private static $instance;
37 private $extension = false;
38 private $upload_handler;
39 private $watermark_controller;
40 private $diagnostics;
41 private $allowed_mime_types = [
42 'image/webp',
43 'image/jpeg',
44 'image/pjpeg',
45 'image/png'
46 ];
47 private $allowed_fonts = [
48 'Caveat-Regular.ttf' => 'Caveat',
49 'Dosis-Regular.ttf' => 'Dosis',
50 'Lato-Regular.ttf' => 'Lato',
51 'LibreBaskerville-Regular.ttf' => 'Libre Baskerville',
52 'Merriweather-Regular.ttf' => 'Merriweather',
53 'OpenSans-Regular.ttf' => 'Open Sans',
54 'Roboto-Regular.ttf' => 'Roboto',
55 'Ubuntu-Regular.ttf' => 'Ubuntu',
56 ];
57 private $is_watermarked_metakey = 'iw-is-watermarked';
58 public $is_backup_folder_writable = null;
59 public $extensions;
60 public $defaults = [
61 'options' => [
62 'watermark_on' => [],
63 'watermark_apply_on' => 'everywhere',
64 'watermark_cpt_on' => [],
65 'watermark_image' => [
66 'extension' => '',
67 'url' => 0,
68 'type' => 'image',
69 'text_string' => '',
70 'text_font' => 'Lato-Regular.ttf',
71 'text_color' => '#000000',
72 'text_size' => 24,
73 'width' => 80,
74 'plugin_off' => 0,
75 'frontend_active' => false,
76 'manual_watermarking' => 0,
77 'skip_small_images' => 0,
78 'min_image_width' => 0,
79 'min_image_height' => 0,
80 'position' => 'bottom_right',
81 'watermark_size_type' => 2,
82 'offset_unit' => 'pixels',
83 'offset_width' => 0,
84 'offset_height' => 0,
85 'absolute_width' => 0,
86 'absolute_height' => 0,
87 'transparent' => 50,
88 'quality' => 90,
89 'jpeg_format' => 'baseline',
90 'deactivation_delete' => false,
91 'review_notice' => true,
92 'review_delay_date' => 0
93 ],
94 'image_protection' => [
95 'rightclick' => 1,
96 'draganddrop' => 1,
97 'devtools' => 1,
98 'forlogged' => 1,
99 'enable_toast' => 1,
100 'toast_message' => 'This content is protected'
101 ],
102 'backup' => [
103 'backup_image' => true,
104 'preserve_timestamps' => false
105 ]
106 ],
107 'version' => '2.0.12'
108 ];
109 public $options = [];
110
111 /**
112 * Class constructor.
113 *
114 * @return void
115 */
116 public function __construct() {
117 // define plugin constants
118 $this->define_constants();
119
120 // activation hooks
121 register_activation_hook( __FILE__, [ $this, 'activate_watermark' ] );
122 register_deactivation_hook( __FILE__, [ $this, 'deactivate_watermark' ] );
123
124 // settings
125 $options = get_option( 'image_watermark_options', $this->defaults['options'] );
126
127 // Guard against corrupted/non-array option values (PHP 8+ array_merge throws on non-arrays).
128 if ( ! is_array( $options ) ) {
129 $options = [];
130 }
131
132 if ( ! isset( $options['watermark_apply_on'] ) ) {
133 $apply_on = 'everywhere';
134
135 if ( isset( $options['watermark_cpt_on'] ) && is_array( $options['watermark_cpt_on'] ) ) {
136 $cpt_on = $options['watermark_cpt_on'];
137 $is_list = array_values( $cpt_on ) === $cpt_on;
138 $has_everywhere = $is_list ? in_array( 'everywhere', $cpt_on, true ) : array_key_exists( 'everywhere', $cpt_on );
139 $apply_on = $has_everywhere ? 'everywhere' : 'post_types';
140 }
141
142 $options['watermark_apply_on'] = $apply_on;
143 }
144
145 $watermark_image = ( isset( $options['watermark_image'] ) && is_array( $options['watermark_image'] ) ) ? $options['watermark_image'] : [];
146 $image_protection = ( isset( $options['image_protection'] ) && is_array( $options['image_protection'] ) ) ? $options['image_protection'] : [];
147 $backup = ( isset( $options['backup'] ) && is_array( $options['backup'] ) ) ? $options['backup'] : [];
148
149 $this->options = array_merge( $this->defaults['options'], $options );
150 $this->options['watermark_image'] = array_merge( $this->defaults['options']['watermark_image'], $watermark_image );
151 $this->options['image_protection'] = array_merge( $this->defaults['options']['image_protection'], $image_protection );
152 $this->options['backup'] = array_merge( $this->defaults['options']['backup'], $backup );
153
154 include_once( IMAGE_WATERMARK_PATH . 'includes/class-update.php' );
155 include_once( IMAGE_WATERMARK_PATH . 'includes/class-settings-api.php' );
156 include_once( IMAGE_WATERMARK_PATH . 'includes/class-settings.php' );
157 include_once( IMAGE_WATERMARK_PATH . 'includes/class-upload-handler.php' );
158 include_once( IMAGE_WATERMARK_PATH . 'includes/class-actions-controller.php' );
159 include_once( IMAGE_WATERMARK_PATH . 'includes/class-diagnostics.php' );
160
161 new Image_Watermark_Settings( $this );
162
163 $this->upload_handler = new Image_Watermark_Upload_Handler( $this );
164 $this->watermark_controller = new Image_Watermark_Actions_Controller( $this, $this->upload_handler );
165 $this->diagnostics = new Image_Watermark_Diagnostics( $this );
166
167 // actions
168 add_action( 'init', [ $this, 'load_textdomain' ] );
169 add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
170 add_action( 'wp_enqueue_media', [ $this, 'wp_enqueue_media' ] );
171 add_action( 'wp_enqueue_scripts', [ $this, 'wp_enqueue_scripts' ] );
172 add_action( 'load-upload.php', [ $this, 'watermark_bulk_action' ] );
173 add_action( 'admin_init', [ $this, 'update_plugin' ] );
174 add_action( 'admin_init', [ $this, 'check_extensions' ] );
175 add_action( 'admin_init', [ $this, 'check_review_notice' ] );
176 add_action( 'admin_init', [ $this, 'redirect_old_slug' ], 9 );
177 add_action( 'admin_notices', [ $this, 'bulk_admin_notices' ] );
178 add_action( 'delete_attachment', [ $this->upload_handler, 'delete_attachment' ] );
179 add_action( 'wp_ajax_iw_watermark_bulk_action', [ $this->watermark_controller, 'watermark_action_ajax' ] );
180 add_action( 'wp_ajax_iw_diagnose_attachment', [ $this->watermark_controller, 'diagnose_attachment_ajax' ] );
181 add_action( 'wp_ajax_iw_text_preview', [ $this, 'text_preview_ajax' ] );
182 add_action( 'wp_ajax_iw_dismiss_notice', [ $this, 'dismiss_review_notice' ] );
183 add_action( 'attachment_submitbox_misc_actions', [ $this, 'render_attachment_editor_actions' ], 20 );
184
185 // filters
186 add_filter( 'plugin_action_links_' . IMAGE_WATERMARK_BASENAME, [ $this, 'plugin_settings_link' ] );
187 add_filter( 'plugin_row_meta', [ $this, 'plugin_extend_links' ], 10, 2 );
188 add_filter( 'wp_handle_upload', [ $this, 'handle_upload_files' ] );
189 add_filter( 'attachment_fields_to_edit', [ $this, 'attachment_fields_to_edit' ], 10, 2 );
190
191 // define our backup location
192 $upload_dir = wp_upload_dir();
193
194 define( 'IMAGE_WATERMARK_BACKUP_DIR', apply_filters( 'image_watermark_backup_dir', $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'iw-backup' ) );
195
196 // create backup folder and security if enabled
197 if ( $this->options['backup']['backup_image'] ) {
198 if ( is_writable( $upload_dir['basedir'] ) ) {
199 $this->is_backup_folder_writable = true;
200
201 // create backup folder ( if it exists this returns true: https://codex.wordpress.org/Function_Reference/wp_mkdir_p )
202 $backup_folder_created = wp_mkdir_p( IMAGE_WATERMARK_BACKUP_DIR );
203
204 // check if the folder exists and is writable
205 if ( $backup_folder_created && is_writable( IMAGE_WATERMARK_BACKUP_DIR ) ) {
206 // check if the htaccess file exists
207 if ( ! file_exists( IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . '.htaccess' ) ) {
208 // htaccess security
209 file_put_contents( IMAGE_WATERMARK_BACKUP_DIR . DIRECTORY_SEPARATOR . '.htaccess', 'deny from all' );
210 }
211 } else
212 $this->is_backup_folder_writable = false;
213 } else
214 $this->is_backup_folder_writable = false;
215
216 if ( $this->is_backup_folder_writable !== true ) {
217 // disable backup setting
218 $this->options['backup']['backup_image'] = false;
219
220 update_option( 'image_watermark_options', $this->options );
221 }
222
223 add_action( 'admin_notices', [ $this, 'folder_writable_admin_notice' ] );
224 }
225 }
226
227 /**
228 * Disable object cloning.
229 *
230 * @return void
231 */
232 public function __clone() {}
233
234 /**
235 * Disable unserializing of the class.
236 *
237 * @return void
238 */
239 public function __wakeup() {}
240
241 /**
242 * Main plugin instance, insures that only one instance of the plugin exists in memory at one time.
243 *
244 * @return object
245 */
246 public static function instance() {
247 if ( self::$instance === null )
248 self::$instance = new self();
249
250 return self::$instance;
251 }
252
253 /**
254 * Setup plugin constants.
255 *
256 * @return void
257 */
258 private function define_constants() {
259 define( 'IMAGE_WATERMARK_URL', plugins_url( '', __FILE__ ) );
260 define( 'IMAGE_WATERMARK_PATH', plugin_dir_path( __FILE__ ) );
261 define( 'IMAGE_WATERMARK_BASENAME', plugin_basename( __FILE__ ) );
262 define( 'IMAGE_WATERMARK_REL_PATH', dirname( IMAGE_WATERMARK_BASENAME ) );
263 }
264
265 /**
266 * Plugin activation.
267 *
268 * @return void
269 */
270 public function activate_watermark() {
271 // add default options
272 add_option( 'image_watermark_options', $this->defaults['options'], null, false );
273 add_option( 'image_watermark_version', $this->defaults['version'], null, false );
274
275 // set activation date if not exists
276 $activation_date = get_option( 'image_watermark_activation_date' );
277 if ( $activation_date === false ) {
278 add_option( 'image_watermark_activation_date', time(), null, false );
279 }
280 }
281
282 /**
283 * Plugin deactivation.
284 *
285 * @return void
286 */
287 public function deactivate_watermark() {
288 // remove options from database?
289 if ( $this->options['watermark_image']['deactivation_delete'] )
290 delete_option( 'image_watermark_options' );
291 }
292
293 /**
294 * Plugin update, fix for version < 1.5.0.
295 *
296 * @return void
297 */
298 public function update_plugin() {
299 if ( ! current_user_can( 'install_plugins' ) )
300 return;
301
302 $db_version = get_option( 'image_watermark_version' );
303 $db_version = ! ( $db_version ) && ( get_option( 'df_watermark_installed' ) != false ) ? get_option( 'version' ) : $db_version;
304
305 if ( $db_version != false ) {
306 if ( version_compare( $db_version, '1.5.0', '<' ) ) {
307 $options = [];
308
309 $old_new = [
310 'df_watermark_on' => 'watermark_on',
311 'df_watermark_cpt_on' => 'watermark_cpt_on',
312 'df_watermark_image' => 'watermark_image',
313 'df_image_protection' => 'image_protection',
314 'df_watermark_installed' => '',
315 'version' => '',
316 'image_watermark_version' => '',
317 ];
318
319 foreach ( $old_new as $old => $new ) {
320 if ( $new )
321 $options[$new] = get_option( $old );
322
323 delete_option( $old );
324 }
325
326 add_option( 'image_watermark_options', $options, null, false );
327 add_option( 'image_watermark_version', $this->defaults['version'], null, false );
328 }
329 }
330
331 if ( $db_version != false && version_compare( $db_version, '2.0.2', '<' ) ) {
332 $options = get_option( 'image_watermark_options', [] );
333
334 if ( is_array( $options ) && isset( $options['watermark_image'] ) && ! is_array( $options['watermark_image'] ) ) {
335 $watermark_id = (int) $options['watermark_image'];
336 $options['watermark_image'] = array_merge( $this->defaults['options']['watermark_image'], [ 'url' => $watermark_id ] );
337 update_option( 'image_watermark_options', $options );
338 }
339 }
340 }
341
342 /**
343 * Load textdomain.
344 *
345 * @return void
346 */
347 public function load_textdomain() {
348 load_plugin_textdomain( 'image-watermark', false, IMAGE_WATERMARK_REL_PATH . '/languages/' );
349 }
350
351 public function wp_enqueue_media( $page ) {
352 global $pagenow;
353 if ( $pagenow !== 'options-general.php' || ! isset( $_GET['page'] ) || $_GET['page'] !== 'image-watermark' ) {
354 wp_enqueue_style( 'watermark-admin', IMAGE_WATERMARK_URL . '/css/admin.css', [], $this->defaults['version'] );
355 }
356 }
357
358 /**
359 * Enqueue admin scripts and styles.
360 *
361 * @global $pagenow
362 * @return void
363 */
364 public function admin_enqueue_scripts( $page ) {
365 global $pagenow;
366
367 wp_register_style( 'watermark-admin-settings', IMAGE_WATERMARK_URL . '/css/admin-settings.css', [], $this->defaults['version'] );
368 wp_register_style( 'watermark-admin', IMAGE_WATERMARK_URL . '/css/admin.css', [], $this->defaults['version'] );
369
370 $media_script_data = null;
371 if ( $this->options['watermark_image']['manual_watermarking'] == 1 && current_user_can( 'upload_files' ) ) {
372 $media_script_data = [
373 'backupImage' => (bool) $this->options['backup']['backup_image'],
374 'applyWatermark' => __( 'Apply watermark', 'image-watermark' ),
375 'removeWatermark' => __( 'Remove watermark', 'image-watermark' )
376 ];
377 }
378
379 if ( $page === 'settings_page_image-watermark' ) {
380 wp_enqueue_media();
381
382 wp_enqueue_script( 'image-watermark-upload-manager', IMAGE_WATERMARK_URL . '/js/admin-upload.js', [], $this->defaults['version'] );
383
384 // prepare script data
385 $script_data = [
386 'title' => __( 'Select watermark image', 'image-watermark' ),
387 'originalSize' => __( 'Original size', 'image-watermark' ),
388 'noSelectedImg' => __( 'No watermark image has been selected yet.', 'image-watermark' ),
389 'notAllowedImg' => __( 'This image cannot be used as a watermark. Use a JPEG, PNG, WebP, or GIF image.', 'image-watermark' ),
390 'px' => __( 'px', 'image-watermark' ),
391 'frame' => 'select',
392 'button' => [ 'text' => __( 'Add watermark image', 'image-watermark' ) ],
393 'multiple' => false
394 ];
395
396 wp_add_inline_script( 'image-watermark-upload-manager', 'var iwArgsUpload = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
397
398 wp_enqueue_script( 'image-watermark-admin-settings', IMAGE_WATERMARK_URL . '/js/admin-settings.js', [], $this->defaults['version'] );
399
400 // prepare script data
401 $script_data = [
402 'resetToDefaults' => __( 'Are you sure you want to reset all settings to their default values?', 'image-watermark' ),
403 'generatePreview' => __( 'Generate Preview', 'image-watermark' ),
404 'generatingPreview' => __( 'Generating...', 'image-watermark' ),
405 'previewNonce' => wp_create_nonce( 'iw_text_preview' ),
406 'originImageLabel' => __( 'Original watermark image:', 'image-watermark' ),
407 'originImageMissing' => __( 'No watermark image selected.', 'image-watermark' ),
408 'originImageLoading' => __( 'Loading…', 'image-watermark' ),
409 'originTextLabel' => __( 'Original text size:', 'image-watermark' ),
410 'originTextEmpty' => __( 'Enter text to preview.', 'image-watermark' ),
411 'copyReportSuccess' => __( 'Copied!', 'image-watermark' ),
412 'copyReportFailed' => __( 'Copy failed — please select and copy manually.', 'image-watermark' ),
413 ];
414
415 wp_add_inline_script( 'image-watermark-admin-settings', 'var iwArgsSettings = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
416
417 wp_enqueue_style( 'watermark-admin-settings' );
418
419 wp_enqueue_script( 'postbox' );
420 }
421
422 if ( $pagenow === 'upload.php' ) {
423 if ( $media_script_data ) {
424 wp_enqueue_script( 'image-watermark-admin-media', IMAGE_WATERMARK_URL . '/js/admin-media.js', [], $this->defaults['version'], false );
425
426 wp_add_inline_script( 'image-watermark-admin-media', 'var iwArgsMedia = ' . wp_json_encode( $media_script_data ) . ";\n", 'before' );
427 }
428
429 wp_enqueue_style( 'watermark-admin' );
430 }
431
432 // image modal could be loaded in various places
433 if ( $this->options['watermark_image']['manual_watermarking'] == 1 ) {
434 wp_enqueue_script( 'image-watermark-admin-image-actions', IMAGE_WATERMARK_URL . '/js/admin-image-actions.js', [], $this->defaults['version'], true );
435
436 if ( $media_script_data ) {
437 wp_add_inline_script( 'image-watermark-admin-image-actions', 'var iwArgsMedia = ' . wp_json_encode( $media_script_data ) . ";\n", 'before' );
438 }
439
440 // prepare script data
441 $script_data = [
442 'backup_image' => (bool) $this->options['backup']['backup_image'],
443 '_nonce' => wp_create_nonce( 'image-watermark' ),
444 'diagnose_nonce' => wp_create_nonce( 'iw_diagnose_attachment' ),
445 'allowed_mimes' => $this->get_allowed_mime_types(),
446 'apply_label' => __( 'Apply watermark', 'image-watermark' ),
447 'remove_label' => __( 'Remove watermark', 'image-watermark' ),
448 'setting_label' => __( 'Watermark', 'image-watermark' ),
449 'single_running' => __( 'Working…', 'image-watermark' ),
450 'single_applied' => __( 'Watermark applied.', 'image-watermark' ),
451 'single_removed' => __( 'Watermark removed.', 'image-watermark' ),
452 'single_error' => __( 'Action failed.', 'image-watermark' ),
453 'single_skipped' => __( 'Action skipped.', 'image-watermark' ),
454 '__applied_none' => __( 'The watermark could not be applied to the selected files because no valid images (JPEG, PNG, WebP) were selected.', 'image-watermark' ),
455 '__applied_one' => __( 'Watermark was successfully applied to 1 image.', 'image-watermark' ),
456 '__applied_multi' => __( 'Watermark was successfully applied to %s images.', 'image-watermark' ),
457 '__removed_none' => __( 'The watermark could not be removed from the selected files because no valid images (JPEG, PNG, WebP) were selected.', 'image-watermark' ),
458 '__removed_one' => __( 'Watermark was successfully removed from 1 image.', 'image-watermark' ),
459 '__removed_multi' => __( 'Watermark was successfully removed from %s images.', 'image-watermark' ),
460 '__skipped' => __( 'Skipped images', 'image-watermark' ),
461 '__running' => __( 'A bulk action is currently running. Please wait…', 'image-watermark' ),
462 '__dismiss' => __( 'Dismiss this notice.' ), // WordPress default string
463 'diag_checking' => __( 'Checking…', 'image-watermark' ),
464 'diag_ready' => __( 'Ready', 'image-watermark' ),
465 'diag_already' => __( 'Already watermarked', 'image-watermark' ),
466 'diag_blocked' => __( 'Cannot apply', 'image-watermark' ),
467 'diag_no_backup' => __( 'No backup - cannot remove', 'image-watermark' ),
468 'diag_not_applicable' => __( 'Not watermarked', 'image-watermark' ),
469 'diag_details' => __( 'Details', 'image-watermark' ),
470 'diag_hide' => __( 'Hide', 'image-watermark' ),
471 ];
472
473 wp_add_inline_script( 'image-watermark-admin-image-actions', 'var iwArgsImageActions = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
474 }
475
476 if ( $pagenow === 'post.php' && $this->options['watermark_image']['manual_watermarking'] == 1 && current_user_can( 'upload_files' ) ) {
477 $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
478 $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
479
480 if ( $screen && $screen->post_type === 'attachment' && $post_id ) {
481 $mime = get_post_mime_type( $post_id );
482
483 if ( in_array( $mime, $this->get_allowed_mime_types(), true ) ) {
484 wp_enqueue_script( 'image-watermark-admin-classic', IMAGE_WATERMARK_URL . '/js/admin-classic-editor.js', [], $this->defaults['version'], true );
485
486 $script_data = [
487 'postId' => $post_id,
488 'attachmentId' => $post_id,
489 'backupImage' => (bool) $this->options['backup']['backup_image'],
490 'nonce' => wp_create_nonce( 'image-watermark' ),
491 'diagnoseNonce' => wp_create_nonce( 'iw_diagnose_attachment' ),
492 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
493 'strings' => [
494 'apply' => __( 'Apply watermark', 'image-watermark' ),
495 'remove' => __( 'Remove watermark', 'image-watermark' ),
496 'applied' => __( 'Watermark applied.', 'image-watermark' ),
497 'removed' => __( 'Watermark removed.', 'image-watermark' ),
498 'error' => __( 'Action failed.', 'image-watermark' ),
499 'running' => __( 'Working\xe2\x80\xa6', 'image-watermark' ),
500 'diagChecking' => __( 'Checking\xe2\x80\xa6', 'image-watermark' ),
501 'diagReady' => __( 'Ready', 'image-watermark' ),
502 'diagAlready' => __( 'Already watermarked', 'image-watermark' ),
503 'diagBlocked' => __( 'Cannot apply', 'image-watermark' ),
504 'diagNoBackup' => __( 'No backup \xe2\x80\x94 cannot remove', 'image-watermark' ),
505 'diagNotApplicable' => __( 'Not watermarked', 'image-watermark' ),
506 'diagDetails' => __( 'Details', 'image-watermark' ),
507 'diagHide' => __( 'Hide', 'image-watermark' ),
508 ],
509 ];
510
511 wp_add_inline_script( 'image-watermark-admin-classic', 'var iwArgsClassic = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
512 }
513 }
514 }
515 }
516
517 /**
518 * Enqueue frontend script with 'no right click' and 'drag and drop' functions.
519 *
520 * @return void
521 */
522 public function wp_enqueue_scripts() {
523 $right_click = true;
524
525 if ( ( $this->options['image_protection']['forlogged'] == 0 && is_user_logged_in() ) || ( $this->options['image_protection']['draganddrop'] == 0 && $this->options['image_protection']['rightclick'] == 0 && $this->options['image_protection']['devtools'] == 0 ) )
526 $right_click = false;
527
528 if ( apply_filters( 'iw_block_right_click', (bool) $right_click ) === true ) {
529 wp_enqueue_script( 'image-watermark-no-right-click', IMAGE_WATERMARK_URL . '/js/no-right-click.js', [], $this->defaults['version'] );
530
531 // prepare script data
532 $script_data = [
533 'rightclick' => ( $this->options['image_protection']['rightclick'] == 1 ? 'Y' : 'N' ),
534 'draganddrop' => ( $this->options['image_protection']['draganddrop'] == 1 ? 'Y' : 'N' ),
535 'devtools' => ( $this->options['image_protection']['devtools'] == 1 ? 'Y' : 'N' ),
536 'enableToast' => ( $this->options['image_protection']['enable_toast'] == 1 ? 'Y' : 'N' ),
537 'toastMessage' => ! empty( $this->options['image_protection']['toast_message'] ) ? esc_js( $this->options['image_protection']['toast_message'] ) : __( 'This content is protected', 'image-watermark' )
538 ];
539
540 wp_add_inline_script( 'image-watermark-no-right-click', 'var iwArgsNoRightClick = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
541 }
542 }
543
544 /**
545 * Check which extension is available and set it.
546 *
547 * @return void
548 */
549 public function check_extensions() {
550 $ext = null;
551
552 if ( $this->check_imagick() ) {
553 $this->extensions['imagick'] = 'ImageMagick';
554 $ext = 'imagick';
555 }
556
557 if ( $this->check_gd() ) {
558 $this->extensions['gd'] = 'GD Library';
559
560 if ( is_null( $ext ) )
561 $ext = 'gd';
562 }
563
564 if ( isset( $this->options['watermark_image']['extension'] ) ) {
565 if ( $this->options['watermark_image']['extension'] === 'imagick' && isset( $this->extensions['imagick'] ) )
566 $this->extension = 'imagick';
567 elseif ( $this->options['watermark_image']['extension'] === 'gd' && isset( $this->extensions['gd'] ) )
568 $this->extension = 'gd';
569 else
570 $this->extension = $ext;
571 } else
572 $this->extension = $ext;
573 }
574
575 /**
576 * Check and display review notice.
577 *
578 * @return void
579 */
580 public function check_review_notice() {
581 // Only for admins who can install plugins
582 if ( ! current_user_can( 'install_plugins' ) ) {
583 return;
584 }
585
586 // Get activation date
587 $activation_date = get_option( 'image_watermark_activation_date' );
588
589 // If no activation date, set it now (for existing installations)
590 if ( $activation_date === false ) {
591 $activation_date = time();
592 update_option( 'image_watermark_activation_date', $activation_date );
593 }
594
595 // Current time
596 $current_time = time();
597
598 // Check if notice is enabled
599 if ( $this->options['watermark_image']['review_notice'] !== true ) {
600 return;
601 }
602
603 // Initialize delay date if needed
604 if ( (int) $this->options['watermark_image']['review_delay_date'] === 0 ) {
605 // Set delay to 2 weeks from activation, or now if already past
606 if ( $activation_date + 2 * WEEK_IN_SECONDS > $current_time ) {
607 $this->options['watermark_image']['review_delay_date'] = $activation_date + 2 * WEEK_IN_SECONDS;
608 } else {
609 $this->options['watermark_image']['review_delay_date'] = $current_time;
610 }
611
612 update_option( 'image_watermark_options', $this->options );
613 }
614
615 // Check if it's time to show the notice
616 $delay_date = (int) $this->options['watermark_image']['review_delay_date'];
617 if ( $delay_date <= $current_time ) {
618 // Add inline script for notice dismissal
619 add_action( 'admin_print_scripts', [ $this, 'review_notice_inline_js' ], 999 );
620
621 // Display the notice
622 add_action( 'admin_notices', [ $this, 'display_review_notice' ] );
623 }
624 }
625
626 /**
627 * Display review notice.
628 *
629 * @return void
630 */
631 public function display_review_notice() {
632 // Get activation date
633 $activation_date = get_option( 'image_watermark_activation_date', time() );
634
635 // Build notice message
636 $message = sprintf(
637 __( "Hey, you've been using <strong>Image Watermark</strong> for more than %s.", 'image-watermark' ),
638 human_time_diff( $activation_date, time() )
639 );
640
641 $message .= '<br /><br />' . __( 'Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation.', 'image-watermark' ) . ' ' . __( 'Your help is much appreciated. Thank you!', 'image-watermark' );
642 $message .= '<br /><br />';
643
644 // Action links
645 $message .= '<a href="' . esc_url( 'https://wordpress.org/support/plugin/image-watermark/reviews/?filter=5#new-post' ) . '" class="iw-dismissible-notice" target="_blank" rel="noopener">' . __( 'Ok, you deserve it', 'image-watermark' ) . '</a>';
646
647 $message .= '<br /><a href="#" class="iw-dismissible-notice iw-delay-notice" rel="noopener">' . __( 'Nope, maybe later', 'image-watermark' ) . '</a>';
648
649 $message .= '<br /><a href="#" class="iw-dismissible-notice" rel="noopener">' . __( 'I already did', 'image-watermark' ) . '</a>';
650
651 ?>
652 <div class="notice notice-info is-dismissible iw-notice iw-review-notice">
653 <p><?php echo wp_kses_post( $message ); ?></p>
654 </div>
655 <?php
656 }
657
658 /**
659 * Enqueue review notice script.
660 *
661 * @return void
662 */
663 public function review_notice_inline_js() {
664 if ( ! current_user_can( 'install_plugins' ) ) {
665 return;
666 }
667
668 // Enqueue the built JavaScript file
669 wp_enqueue_script(
670 'iw-review-notice',
671 IMAGE_WATERMARK_URL . '/js/admin-notice.js',
672 [],
673 $this->defaults['version'],
674 true
675 );
676
677 // Pass data to JavaScript
678 $script_data = [
679 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
680 'nonce' => wp_create_nonce( 'iw_dismiss_notice' )
681 ];
682
683 wp_add_inline_script( 'iw-review-notice', 'var iwArgsReviewNotice = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
684 }
685
686 /**
687 * Handle review notice dismissal via AJAX.
688 *
689 * @return void
690 */
691 public function dismiss_review_notice() {
692 // Permission check
693 if ( ! current_user_can( 'install_plugins' ) ) {
694 wp_die();
695 }
696
697 // Verify nonce
698 if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], 'iw_dismiss_notice' ) ) {
699 wp_die();
700 }
701
702 // Sanitize action
703 $notice_action = isset( $_REQUEST['notice_action'] ) ? sanitize_text_field( $_REQUEST['notice_action'] ) : 'hide';
704
705 // Update options based on action
706 switch ( $notice_action ) {
707 case 'delay':
708 // Delay notice for 2 weeks
709 $this->options['watermark_image']['review_delay_date'] = time() + 2 * WEEK_IN_SECONDS;
710 break;
711
712 default:
713 // Hide notice permanently
714 $this->options['watermark_image']['review_notice'] = false;
715 $this->options['watermark_image']['review_delay_date'] = 0;
716 break;
717 }
718
719 // Save options
720 update_option( 'image_watermark_options', $this->options );
721
722 // Exit
723 wp_die();
724 }
725
726 /**
727 * Redirect old slug to new slug for backward compatibility.
728 *
729 * @return void
730 */
731 public function redirect_old_slug() {
732 if ( isset( $_GET['page'] ) && $_GET['page'] === 'watermark-options' ) {
733 $url = admin_url( 'options-general.php?page=image-watermark' );
734 if ( isset( $_GET['tab'] ) ) {
735 $url = add_query_arg( 'tab', sanitize_key( $_GET['tab'] ), $url );
736 }
737 wp_safe_redirect( $url );
738 exit;
739 }
740 }
741
742 /**
743 * Apply watermark everywhere or for specific post types.
744 *
745 * @param resource $file
746 * @return resource
747 */
748 public function handle_upload_files( $file ) {
749 return $this->get_upload_handler()->handle_upload_files( $file );
750 }
751
752 /**
753 * Handle manual watermark AJAX requests.
754 *
755 * @return void
756 */
757 public function text_preview_ajax() {
758 check_ajax_referer( 'iw_text_preview', 'nonce' );
759
760 if ( ! current_user_can( 'manage_options' ) ) {
761 wp_die( __( 'You do not have permission to perform this action.', 'image-watermark' ) );
762 }
763
764 // Choose engine for preview (prefer active one).
765 $engine = $this->get_extension();
766
767 if ( ! $engine ) {
768 $this->check_extensions();
769 $engine = $this->get_extension();
770 }
771
772 if ( ! $engine ) {
773 wp_send_json_error( __( 'No image library available to generate a preview.', 'image-watermark' ) );
774 }
775
776 if ( $engine === 'imagick' && ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick', false ) ) ) {
777 wp_send_json_error( __( 'Imagick is selected but not available on the server.', 'image-watermark' ) );
778 }
779
780 if ( $engine === 'gd' && ( ! function_exists( 'imagecreatetruecolor' ) || ! function_exists( 'imagejpeg' ) ) ) {
781 wp_send_json_error( __( 'GD with JPEG support is required to generate a preview.', 'image-watermark' ) );
782 }
783
784 // Get current options or posted options
785 $options = $this->options;
786
787 $allowed_fonts = $this->get_allowed_fonts();
788
789 // Override with posted values for preview
790 if ( isset( $_POST['text_string'] ) ) {
791 $options['watermark_image']['text_string'] = sanitize_text_field( $_POST['text_string'] );
792 }
793 if ( isset( $_POST['text_font'] ) && array_key_exists( $_POST['text_font'], $allowed_fonts ) ) {
794 $options['watermark_image']['text_font'] = $_POST['text_font'];
795 }
796 if ( isset( $_POST['text_color'] ) && preg_match( '/^#[a-f0-9]{6}$/i', $_POST['text_color'] ) ) {
797 $options['watermark_image']['text_color'] = $_POST['text_color'];
798 }
799 if ( isset( $_POST['text_size'] ) ) {
800 $options['watermark_image']['text_size'] = max( 6, min( 400, (int) $_POST['text_size'] ) );
801 }
802 if ( isset( $_POST['position'] ) ) {
803 $options['watermark_image']['position'] = $_POST['position'];
804 }
805 if ( isset( $_POST['transparent'] ) ) {
806 $options['watermark_image']['transparent'] = max( 0, min( 100, (int) $_POST['transparent'] ) );
807 }
808
809 // Create a sample image
810 $sample_image_path = $this->create_sample_image( $engine );
811
812 if ( ! $sample_image_path ) {
813 wp_send_json_error( __( 'Failed to create sample image.', 'image-watermark' ) );
814 }
815
816 // Apply text watermark
817 $this->upload_handler->do_watermark( 0, $sample_image_path, 'full', wp_upload_dir(), [] );
818
819 // Get image data
820 $image_data = file_get_contents( $sample_image_path );
821 $base64 = base64_encode( $image_data );
822
823 // Clean up
824 unlink( $sample_image_path );
825
826 wp_send_json_success( [
827 'image' => 'data:image/jpeg;base64,' . $base64,
828 ] );
829 }
830
831 /**
832 * Create a sample image for preview using the active engine.
833 *
834 * @param string $engine Active engine key ('imagick' or 'gd').
835 * @return string|false Path to sample image or false on failure.
836 */
837 private function create_sample_image( $engine ) {
838 $upload_dir = wp_upload_dir();
839 $base_dir = trailingslashit( $upload_dir['basedir'] );
840 $sample_path = $base_dir . 'iw-sample-preview-' . uniqid( '', true ) . '.jpg';
841
842 if ( $engine === 'imagick' ) {
843 return $this->create_sample_image_imagick( $sample_path );
844 }
845
846 return $this->create_sample_image_gd( $sample_path );
847 }
848
849 /**
850 * Create sample image via GD.
851 *
852 * @param string $sample_path
853 * @return string|false
854 */
855 private function create_sample_image_gd( $sample_path ) {
856 if ( ! function_exists( 'imagecreatetruecolor' ) || ! function_exists( 'imagejpeg' ) ) {
857 return false;
858 }
859
860 $image = imagecreatetruecolor( 400, 300 );
861 $white = imagecolorallocate( $image, 255, 255, 255 );
862 imagefill( $image, 0, 0, $white );
863
864 $gray = imagecolorallocate( $image, 200, 200, 200 );
865 imagerectangle( $image, 50, 50, 350, 250, $gray );
866 imagestring( $image, 5, 150, 120, 'Sample Image', $gray );
867
868 imagejpeg( $image, $sample_path, 90 );
869 imagedestroy( $image );
870
871 return ( is_file( $sample_path ) ? $sample_path : false );
872 }
873
874 /**
875 * Create sample image via Imagick.
876 *
877 * @param string $sample_path
878 * @return string|false
879 */
880 private function create_sample_image_imagick( $sample_path ) {
881 if ( ! class_exists( 'Imagick', false ) || ! class_exists( 'ImagickPixel', false ) || ! class_exists( 'ImagickDraw', false ) ) {
882 return false;
883 }
884
885 try {
886 $image = new Imagick();
887 $image->newImage( 400, 300, new ImagickPixel( 'white' ) );
888 $image->setImageFormat( 'jpeg' );
889 $image->setImageCompressionQuality( 90 );
890
891 $draw = new ImagickDraw();
892 $draw->setStrokeColor( new ImagickPixel( '#c8c8c8' ) );
893 $draw->setFillColor( 'none' );
894 $draw->setStrokeWidth( 1 );
895 $draw->rectangle( 50, 50, 350, 250 );
896
897 $draw_text = new ImagickDraw();
898 $draw_text->setFillColor( new ImagickPixel( '#c8c8c8' ) );
899 $draw_text->setFontSize( 14 );
900
901 $image->annotateImage( $draw_text, 150, 170, 0, 'Sample Image' );
902 $image->drawImage( $draw );
903 $image->writeImage( $sample_path );
904
905 $draw->clear();
906 $draw->destroy();
907 $draw_text->clear();
908 $draw_text->destroy();
909 $image->clear();
910 $image->destroy();
911 } catch ( Exception $e ) {
912 return false;
913 }
914
915 return ( is_file( $sample_path ) ? $sample_path : false );
916 }
917
918 /**
919 * Handle media library bulk watermark actions.
920 *
921 * @return void
922 */
923 public function watermark_bulk_action() {
924 $this->get_watermark_controller()->watermark_bulk_action();
925 }
926
927 /**
928 * Add watermark buttons on attachment image locations.
929 *
930 * @param array $form_fields
931 * @param object $post
932 * @return array
933 */
934 public function attachment_fields_to_edit( $form_fields, $post ) {
935 if ( $this->options['watermark_image']['manual_watermarking'] == 1 && $this->options['backup']['backup_image'] ) {
936 $data = wp_get_attachment_metadata( $post->ID, false );
937
938 // is this really an image?
939 if ( in_array( get_post_mime_type( $post->ID ), $this->allowed_mime_types ) && is_array( $data ) ) {
940 $form_fields['image_watermark'] = [
941 'show_in_edit' => false,
942 'tr' => '
943 <div id="image_watermark_buttons"' . ( get_post_meta( $post->ID, $this->is_watermarked_metakey, true ) ? ' class="watermarked"' : '' ) . ' data-id="' . $post->ID . '" style="display: none;">
944 <label class="setting">
945 <span class="name">' . __( 'Image Watermark', 'image-watermark' ) . '</span>
946 <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>
947 </label>
948 <div class="clear"></div>
949 </div>'
950 ];
951 }
952 }
953
954 return $form_fields;
955 }
956
957 /**
958 * Display admin notices.
959 *
960 * @return void
961 */
962 public function bulk_admin_notices() {
963 global $post_type, $pagenow;
964
965 if ( $pagenow === 'upload.php' ) {
966 if ( ! current_user_can( 'upload_files' ) )
967 return;
968
969 // Display validation error from bulk action
970 if ( isset( $_REQUEST['iw_error'] ) && $post_type === 'attachment' ) {
971 $error_message = sanitize_text_field( wp_unslash( $_REQUEST['iw_error'] ) );
972 echo '<div class="error"><p>' . esc_html( $error_message ) . '</p></div>';
973 $_SERVER['REQUEST_URI'] = esc_url( remove_query_arg( 'iw_error', $_SERVER['REQUEST_URI'] ) );
974 }
975
976 if ( isset( $_REQUEST['watermarked'], $_REQUEST['watermarkremoved'], $_REQUEST['skipped'] ) && $post_type === 'attachment' ) {
977 $watermarked = (int) $_REQUEST['watermarked'];
978 $watermarkremoved = (int) $_REQUEST['watermarkremoved'];
979 $skipped = (int) $_REQUEST['skipped'];
980 $messages = [];
981
982 if ( isset( $_REQUEST['messages'] ) ) {
983 $raw_messages = wp_unslash( $_REQUEST['messages'] );
984 $raw_messages = is_array( $raw_messages ) ? $raw_messages : [ $raw_messages ];
985 $messages = array_filter( array_map( 'sanitize_text_field', $raw_messages ) );
986 }
987
988 if ( $watermarked === 0 )
989 echo '<div class="error"><p>' . __( 'The watermark could not be applied to the selected files because no valid images (JPEG, PNG, WebP) were selected.', 'image-watermark' ) . ($skipped > 0 ? ' ' . __( 'Skipped images', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
990 elseif ( $watermarked > 0 )
991 echo '<div class="updated"><p>' . sprintf( _n( 'Watermark was successfully applied to 1 image.', 'Watermark was successfully applied to %s images.', $watermarked, 'image-watermark' ), number_format_i18n( $watermarked ) ) . ($skipped > 0 ? ' ' . __( 'Skipped images', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
992
993 if ( $watermarkremoved === 0 )
994 echo '<div class="error"><p>' . __( 'The watermark could not be removed from the selected files because no valid images (JPEG, PNG, WebP) were selected.', 'image-watermark' ) . ($skipped > 0 ? ' ' . __( 'Skipped images', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
995 elseif ( $watermarkremoved > 0 )
996 echo '<div class="updated"><p>' . sprintf( _n( 'Watermark was successfully removed from 1 image.', 'Watermark was successfully removed from %s images.', $watermarkremoved, 'image-watermark' ), number_format_i18n( $watermarkremoved ) ) . ($skipped > 0 ? ' ' . __( 'Skipped images', 'image-watermark' ) . ': ' . $skipped . '.' : '') . '</p></div>';
997
998 if ( ! empty( $messages ) ) {
999 echo '<div class="error"><p>' . implode( '<br />', array_map( 'esc_html', $messages ) ) . '</p></div>';
1000 }
1001
1002 $_SERVER['REQUEST_URI'] = esc_url( remove_query_arg( [ 'watermarked', 'watermarkremoved', 'skipped', 'messages' ], $_SERVER['REQUEST_URI'] ) );
1003 }
1004 }
1005 }
1006
1007 /**
1008 * Check whether ImageMagick extension is available.
1009 *
1010 * @return bool
1011 */
1012 public function check_imagick() {
1013 // check Imagick's extension and classes
1014 if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick', false ) || ! class_exists( 'ImagickPixel', false ) || ! class_exists( 'ImagickDraw', false ) )
1015 return false;
1016
1017 // check version
1018 if ( version_compare( phpversion( 'imagick' ), '2.2.0', '<' ) )
1019 return false;
1020
1021 // check for deep requirements within Imagick
1022 if ( ! defined( 'Imagick::COMPRESSION_JPEG' ) || ! defined( 'Imagick::COMPOSITE_DEFAULT' ) || ! defined( 'Imagick::INTERLACE_PLANE' ) || ! defined( 'Imagick::FILTER_CATROM' ) || ! defined( 'Imagick::CHANNEL_ALL' ) || ! defined( 'Imagick::CHANNEL_ALPHA' ) || ! defined( 'Imagick::EVALUATE_MULTIPLY' ) )
1023 return false;
1024
1025 // check methods
1026 if ( ! $this->has_required_methods(
1027 'Imagick',
1028 [
1029 'clear',
1030 'destroy',
1031 'newImage',
1032 'writeImage',
1033 'getImageGeometry',
1034 'setImageFormat',
1035 'setImageCompression',
1036 'setImageCompressionQuality',
1037 'setImageInterlaceScheme',
1038 'getImageAlphaChannel',
1039 'evaluateImage',
1040 'queryFontMetrics',
1041 'resizeImage',
1042 'compositeImage',
1043 'annotateImage',
1044 'drawImage',
1045 ]
1046 ) )
1047 return false;
1048
1049 // require at least one usable whole-image alpha method (setImageAlpha replaces the deprecated setImageOpacity)
1050 if ( ! method_exists( 'Imagick', 'setImageAlpha' ) && ! method_exists( 'Imagick', 'setImageOpacity' ) )
1051 return false;
1052
1053 if ( ! $this->has_required_methods(
1054 'ImagickDraw',
1055 [
1056 'clear',
1057 'destroy',
1058 'setFont',
1059 'setFontSize',
1060 'setFillColor',
1061 'setFillOpacity',
1062 'setStrokeColor',
1063 'setStrokeWidth',
1064 'rectangle',
1065 ]
1066 ) )
1067 return false;
1068
1069 return true;
1070 }
1071
1072 /**
1073 * Check whether a class provides all required methods.
1074 *
1075 * @param string $class_name Class name.
1076 * @param array $methods Required method names.
1077 * @return bool
1078 */
1079 private function has_required_methods( $class_name, $methods ) {
1080 foreach ( $methods as $method ) {
1081 if ( ! method_exists( $class_name, $method ) ) {
1082 return false;
1083 }
1084 }
1085
1086 return true;
1087 }
1088
1089 /**
1090 * Check whether GD extension is available.
1091 *
1092 * @return bool
1093 */
1094 public function check_gd( $args = [] ) {
1095 // check extension
1096 if ( ! extension_loaded( 'gd' ) || ! function_exists( 'gd_info' ) )
1097 return false;
1098
1099 // ensure required formats are supported to avoid fatal errors
1100 $required_functions = [ 'imagecreatefromjpeg', 'imagecreatefrompng', 'imagecreatefromwebp' ];
1101
1102 foreach ( $required_functions as $func ) {
1103 if ( ! function_exists( $func ) ) {
1104 return false;
1105 }
1106 }
1107
1108 return true;
1109 }
1110
1111 /**
1112 * Get active image extension.
1113 *
1114 * @return string|false
1115 */
1116 public function get_extension() {
1117 return $this->extension;
1118 }
1119
1120 /**
1121 * Get allowed mime types.
1122 *
1123 * @return array
1124 */
1125 public function get_allowed_mime_types() {
1126 return $this->allowed_mime_types;
1127 }
1128
1129 /**
1130 * Get allowed fonts list.
1131 *
1132 * @return array
1133 */
1134 public function get_allowed_fonts() {
1135 return apply_filters( 'iw_allowed_fonts', $this->allowed_fonts );
1136 }
1137
1138 /**
1139 * Get meta key for watermark flag.
1140 *
1141 * @return string
1142 */
1143 public function get_watermarked_meta_key() {
1144 return $this->is_watermarked_metakey;
1145 }
1146
1147 /**
1148 * Get upload handler.
1149 *
1150 * @return Image_Watermark_Upload_Handler
1151 */
1152 public function get_upload_handler() {
1153 return $this->upload_handler;
1154 }
1155
1156 /**
1157 * Get watermark controller.
1158 *
1159 * @return Image_Watermark_Actions_Controller
1160 */
1161 public function get_watermark_controller() {
1162 return $this->watermark_controller;
1163 }
1164
1165 /**
1166 * Get diagnostics service.
1167 *
1168 * @return Image_Watermark_Diagnostics
1169 */
1170 public function get_diagnostics() {
1171 return $this->diagnostics;
1172 }
1173
1174 /**
1175 * Apply watermark to selected image sizes.
1176 *
1177 * @param array $data
1178 * @param int|string $attachment_id Attachment ID
1179 * @param string $method
1180 * @return array
1181 */
1182 public function apply_watermark( $data, $attachment_id, $method = '' ) {
1183 return $this->get_upload_handler()->apply_watermark( $data, $attachment_id, $method );
1184 }
1185
1186 /**
1187 * Create admin notice when we can't create the backup folder.
1188 *
1189 * @return void
1190 */
1191 function folder_writable_admin_notice() {
1192 if ( current_user_can( 'manage_options' ) && $this->is_backup_folder_writable !== true ) {
1193 ?>
1194 <div class="notice notice-error is-dismissible">
1195 <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 backups of your images. This feature has been disabled for now.", 'image-watermark' ); ?></p>
1196 </div>
1197 <?php
1198 }
1199 }
1200
1201 /**
1202 * Add link to Settings page.
1203 *
1204 * @param array $links
1205 * @return array
1206 */
1207 public function plugin_settings_link( $links ) {
1208 if ( ! is_admin() || ! current_user_can( 'manage_options' ) )
1209 return $links;
1210
1211 array_unshift( $links, sprintf( '<a href="%s">%s</a>', admin_url( 'options-general.php' ) . '?page=image-watermark', __( 'Settings', 'image-watermark' ) ) );
1212
1213 return $links;
1214 }
1215
1216 /**
1217 * Add link to Support Forum.
1218 *
1219 * @param array $links
1220 * @param string $file
1221 * @return array
1222 */
1223 public function plugin_extend_links( $links, $file ) {
1224 if ( ! current_user_can( 'install_plugins' ) )
1225 return $links;
1226
1227 if ( $file === IMAGE_WATERMARK_BASENAME )
1228 return array_merge( $links, [ sprintf( '<a href="http://www.dfactory.co/support/forum/image-watermark/" target="_blank">%s</a>', __( 'Support', 'image-watermark' ) ) ] );
1229
1230 return $links;
1231 }
1232
1233 /**
1234 * Get font file path.
1235 *
1236 * @param string $font Font filename.
1237 * @return string|null
1238 */
1239 public function get_font_path( $font ) {
1240 if ( file_exists( $font ) && is_file( $font ) ) {
1241 $path = $font;
1242 } else {
1243 $path = IMAGE_WATERMARK_PATH . 'fonts/' . $font;
1244 if ( ! file_exists( $path ) || ! is_file( $path ) ) {
1245 $path = null;
1246 }
1247 }
1248 return apply_filters( 'iw_font_path', $path, $font );
1249 }
1250
1251 /**
1252 * Attachment editor sidebar actions.
1253 *
1254 * @return void
1255 */
1256 public function render_attachment_editor_actions() {
1257 global $post;
1258
1259 if ( ! $post || ! current_user_can( 'upload_files' ) ) {
1260 return;
1261 }
1262
1263 if ( $this->options['watermark_image']['manual_watermarking'] != 1 ) {
1264 return;
1265 }
1266
1267 if ( $post->post_type !== 'attachment' ) {
1268 return;
1269 }
1270
1271 $mime = get_post_mime_type( $post->ID );
1272
1273 if ( ! in_array( $mime, $this->get_allowed_mime_types(), true ) ) {
1274 return;
1275 }
1276
1277 $remove_allowed = (bool) $this->options['backup']['backup_image'];
1278 ?>
1279 <div class="misc-pub-section iw-classic-actions">
1280 <button type="button" class="button-link iw-classic-apply"><?php esc_html_e( 'Apply watermark', 'image-watermark' ); ?></button>
1281 <?php if ( $remove_allowed ) : ?>
1282 | <button type="button" class="button-link iw-classic-remove"><?php esc_html_e( 'Remove watermark', 'image-watermark' ); ?></button>
1283 <?php endif; ?>
1284 <div class="iw-classic-status" aria-live="polite"></div>
1285 </div>
1286 <?php
1287 }
1288 }
1289
1290 /**
1291 * Get instance of main class.
1292 *
1293 * @return object Instance
1294 */
1295 function Image_Watermark() {
1296 static $instance;
1297
1298 // first call to instance() initializes the plugin
1299 if ( $instance === null || ! ( $instance instanceof Image_Watermark ) )
1300 $instance = Image_Watermark::instance();
1301
1302 return $instance;
1303 }
1304
1305 $image_watermark = Image_Watermark();
1306