| 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 |
|