' . esc_html__( 'Settings', 'aaweb-advanced-image-resizer' ) . '';
$backups_link = '' . esc_html__( 'Backups', 'aaweb-advanced-image-resizer' ) . '';
array_unshift( $links, $backups_link );
array_unshift( $links, $settings_link );
return $links;
}
public function defaults() {
return array(
'default_mode' => 'replace',
'jpg_quality' => 92,
'webp_quality' => 90,
'output_format' => 'jpg',
'background_color' => '#ffffff',
'enable_backups' => 1,
'enable_row_action' => 1,
'enable_bulk_action' => 1,
'enable_modal_tool' => 1,
'max_backups' => 10,
'custom_presets' => '',
);
}
public function get_settings() {
$saved = get_option( self::OPTION_KEY, array() );
if ( ! is_array( $saved ) ) {
$saved = array();
}
return wp_parse_args( $saved, $this->defaults() );
}
public function register_settings() {
register_setting(
'aaweb_air_settings_group',
self::OPTION_KEY,
array(
'type' => 'array',
'sanitize_callback' => array( $this, 'sanitize_settings' ),
'default' => $this->defaults(),
)
);
}
public function sanitize_settings( $input ) {
$defaults = $this->defaults();
$input = is_array( $input ) ? $input : array();
$output = $defaults;
$mode = isset( $input['default_mode'] ) ? sanitize_key( wp_unslash( $input['default_mode'] ) ) : $defaults['default_mode'];
$output['default_mode'] = in_array( $mode, array( 'replace', 'create' ), true ) ? $mode : 'replace';
$quality = isset( $input['jpg_quality'] ) ? absint( wp_unslash( $input['jpg_quality'] ) ) : $defaults['jpg_quality'];
$output['jpg_quality'] = min( 100, max( 50, $quality ) );
$webp_quality = isset( $input['webp_quality'] ) ? absint( wp_unslash( $input['webp_quality'] ) ) : $defaults['webp_quality'];
$output['webp_quality'] = min( 100, max( 50, $webp_quality ) );
$output_format = isset( $input['output_format'] ) ? sanitize_key( wp_unslash( $input['output_format'] ) ) : $defaults['output_format'];
$output['output_format'] = in_array( $output_format, array( 'jpg', 'png', 'webp' ), true ) ? $output_format : 'jpg';
$color = isset( $input['background_color'] ) ? sanitize_hex_color( wp_unslash( $input['background_color'] ) ) : $defaults['background_color'];
$output['background_color'] = $color ? $color : '#ffffff';
$output['enable_backups'] = empty( $input['enable_backups'] ) ? 0 : 1;
$output['enable_row_action'] = empty( $input['enable_row_action'] ) ? 0 : 1;
$output['enable_bulk_action'] = empty( $input['enable_bulk_action'] ) ? 0 : 1;
$output['enable_modal_tool'] = empty( $input['enable_modal_tool'] ) ? 0 : 1;
$max_backups = isset( $input['max_backups'] ) ? absint( wp_unslash( $input['max_backups'] ) ) : $defaults['max_backups'];
$output['max_backups'] = min( 50, max( 0, $max_backups ) );
$output['custom_presets'] = isset( $input['custom_presets'] ) ? $this->sanitize_custom_presets_text( wp_unslash( $input['custom_presets'] ) ) : '';
return $output;
}
private function sanitize_custom_presets_text( $value ) {
$value = is_string( $value ) ? $value : '';
$lines = preg_split( '/\r\n|\r|\n/', $value );
$clean = array();
foreach ( $lines as $line ) {
$line = trim( $line );
if ( '' === $line ) {
continue;
}
if ( preg_match( '/^(.+?)\s*\|\s*(\d{2,5})\s*x\s*(\d{2,5})$/i', $line, $m ) ) {
$label = sanitize_text_field( $m[1] );
$w = absint( $m[2] );
$h = absint( $m[3] );
if ( $label && $w >= 10 && $h >= 10 && $w <= 20000 && $h <= 20000 ) {
$clean[] = $label . '|' . $w . 'x' . $h;
}
}
}
return implode( "\n", array_slice( $clean, 0, 40 ) );
}
public function presets() {
$presets = array(
'carousel_square' => array( 'label' => __( 'Carousel Square', 'aaweb-advanced-image-resizer' ), 'w' => 1080, 'h' => 1080 ),
'carousel_landscape' => array( 'label' => __( 'Carousel Landscape', 'aaweb-advanced-image-resizer' ), 'w' => 1080, 'h' => 566 ),
'carousel_portrait' => array( 'label' => __( 'Carousel Portrait', 'aaweb-advanced-image-resizer' ), 'w' => 1080, 'h' => 1350 ),
'shared_link' => array( 'label' => __( 'Shared Link', 'aaweb-advanced-image-resizer' ), 'w' => 1200, 'h' => 630 ),
'facebook_square' => array( 'label' => __( 'Facebook Square', 'aaweb-advanced-image-resizer' ), 'w' => 1200, 'h' => 1200 ),
'facebook_ad' => array( 'label' => __( 'Facebook Ad', 'aaweb-advanced-image-resizer' ), 'w' => 1200, 'h' => 628 ),
'wp_banner' => array( 'label' => __( 'WordPress Banner', 'aaweb-advanced-image-resizer' ), 'w' => 1544, 'h' => 500 ),
'wp_banner_small' => array( 'label' => __( 'WordPress Banner Small', 'aaweb-advanced-image-resizer' ), 'w' => 772, 'h' => 250 ),
'wp_icon_256' => array( 'label' => __( 'WordPress Icon 256', 'aaweb-advanced-image-resizer' ), 'w' => 256, 'h' => 256 ),
'wp_icon_128' => array( 'label' => __( 'WordPress Icon 128', 'aaweb-advanced-image-resizer' ), 'w' => 128, 'h' => 128 ),
);
$settings = $this->get_settings();
$custom = isset( $settings['custom_presets'] ) ? $settings['custom_presets'] : '';
$lines = preg_split( '/\r\n|\r|\n/', $custom );
foreach ( $lines as $index => $line ) {
$line = trim( $line );
if ( preg_match( '/^(.+?)\|([0-9]+)x([0-9]+)$/', $line, $m ) ) {
$key = 'custom_' . $index . '_' . sanitize_key( $m[1] );
$presets[ $key ] = array(
'label' => sanitize_text_field( $m[1] ),
'w' => absint( $m[2] ),
'h' => absint( $m[3] ),
);
}
}
return apply_filters( 'aaweb_air_presets', $presets );
}
private function preset_options_html( $selected = '' ) {
$html = '';
foreach ( $this->presets() as $key => $preset ) {
$label = sprintf( '%s (%dx%d)', $preset['label'], $preset['w'], $preset['h'] );
$html .= sprintf( '', esc_attr( $key ), selected( $selected, $key, false ), esc_html( $label ) );
}
return $html;
}
public function enqueue_admin_assets( $hook ) {
$allowed = array( 'upload.php', 'media.php', 'post.php', 'media_page_aaweb-image-resizer', 'media_page_aaweb-image-backups' );
if ( ! in_array( $hook, $allowed, true ) ) {
return;
}
if ( 'post.php' === $hook ) {
$screen = get_current_screen();
if ( ! $screen || 'attachment' !== $screen->post_type ) {
return;
}
}
$settings = $this->get_settings();
$presets = array();
$formats = array();
foreach ( $this->output_formats() as $key => $format ) {
$formats[ $key ] = $format['label'];
}
foreach ( $this->presets() as $key => $preset ) {
$presets[ $key ] = array(
'w' => (int) $preset['w'],
'h' => (int) $preset['h'],
'label' => sprintf( '%s (%dx%d)', $preset['label'], $preset['w'], $preset['h'] ),
);
}
wp_enqueue_style( 'aaweb-air-admin', AAWEB_AIR_URL . 'assets/admin.css', array(), AAWEB_AIR_VERSION );
wp_enqueue_script( 'aaweb-air-admin', AAWEB_AIR_URL . 'assets/admin.js', array( 'jquery' ), AAWEB_AIR_VERSION, true );
wp_localize_script(
'aaweb-air-admin',
'AAWEBAIR',
array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( self::NONCE_AJAX ),
'bulkNonce' => wp_create_nonce( self::NONCE_BULK ),
'presets' => $presets,
'defaultMode' => $settings['default_mode'],
'outputFormat' => $settings['output_format'],
'formats' => $formats,
'enableModal' => (bool) $settings['enable_modal_tool'],
'enableBulk' => (bool) $settings['enable_bulk_action'],
'confirmReplace' => (bool) $settings['enable_backups'],
'i18n' => array(
'selectSize' => __( 'Select size', 'aaweb-advanced-image-resizer' ),
'sizeBulk' => __( '— Size for Bulk —', 'aaweb-advanced-image-resizer' ),
'mode' => __( '— Mode —', 'aaweb-advanced-image-resizer' ),
'format' => __( '— Format —', 'aaweb-advanced-image-resizer' ),
'outputFormat' => __( 'Output format', 'aaweb-advanced-image-resizer' ),
'replace' => __( 'Replace', 'aaweb-advanced-image-resizer' ),
'create' => __( 'Create New', 'aaweb-advanced-image-resizer' ),
'button' => __( 'Resize', 'aaweb-advanced-image-resizer' ),
'modalButton' => __( 'Resize with AAWEB', 'aaweb-advanced-image-resizer' ),
'missing' => __( 'Please select both size and mode.', 'aaweb-advanced-image-resizer' ),
'working' => __( 'Resizing...', 'aaweb-advanced-image-resizer' ),
'done' => __( 'Done', 'aaweb-advanced-image-resizer' ),
'error' => __( 'Error', 'aaweb-advanced-image-resizer' ),
'confirm' => __( 'Replace will overwrite the current attachment file. Continue?', 'aaweb-advanced-image-resizer' ),
'formatMissing' => __( 'Please select an output format.', 'aaweb-advanced-image-resizer' ),
'backups' => __( 'Backups', 'aaweb-advanced-image-resizer' ),
/* translators: %d: number of backups stored for an attachment. */
'backupCount' => __( 'Backups (%d)', 'aaweb-advanced-image-resizer' ),
'loadBackups' => __( 'Load backups', 'aaweb-advanced-image-resizer' ),
'noBackups' => __( 'No backups found for this image.', 'aaweb-advanced-image-resizer' ),
'missingBackup' => __( 'missing', 'aaweb-advanced-image-resizer' ),
'restore' => __( 'Restore', 'aaweb-advanced-image-resizer' ),
'delete' => __( 'Delete', 'aaweb-advanced-image-resizer' ),
'close' => __( 'Close', 'aaweb-advanced-image-resizer' ),
'restoreConfirm' => __( 'Restore this backup? The current attachment file will be replaced.', 'aaweb-advanced-image-resizer' ),
'deleteConfirm' => __( 'Delete this backup file permanently?', 'aaweb-advanced-image-resizer' ),
'backupRestored' => __( 'Backup restored successfully.', 'aaweb-advanced-image-resizer' ),
'backupDeleted' => __( 'Backup deleted successfully.', 'aaweb-advanced-image-resizer' ),
),
)
);
}
public function settings_page() {
if ( ! current_user_can( 'upload_files' ) ) {
wp_die( esc_html__( 'You do not have permission to access this page.', 'aaweb-advanced-image-resizer' ) );
}
$settings = $this->get_settings();
?>
presets() as $preset ) : ?>
'attachment',
'post_status' => 'inherit',
'post_mime_type' => 'image',
'posts_per_page' => 20,
'paged' => $paged,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Admin-only paginated backup lookup.
'meta_key' => self::BACKUP_META,
'orderby' => 'modified',
'order' => 'DESC',
)
);
?>
have_posts() ) :
$query->the_post();
$attachment_id = get_the_ID();
if ( ! $this->current_user_can_edit_attachment( $attachment_id ) ) {
continue;
}
$backup_items = $this->get_backup_items( $attachment_id );
if ( empty( $backup_items ) ) {
continue;
}
$rendered++;
$attachment_file = get_attached_file( $attachment_id );
$filename = $attachment_file ? basename( $attachment_file ) : '';
$title = get_the_title( $attachment_id );
$title = $title ? $title : $filename;
$thumbnail = wp_get_attachment_image(
$attachment_id,
'thumbnail',
false,
array(
'loading' => 'lazy',
'alt' => '',
)
);
?>
backup_box_html( $attachment_id, count( $backup_items ) );
?>
'aaweb-image-backups',
'paged' => 999999999,
),
admin_url( 'upload.php' )
)
)
);
$pagination = paginate_links(
array(
'base' => $pagination_base,
'format' => '',
'current' => $paged,
'total' => max( 1, (int) $query->max_num_pages ),
'prev_text' => __( 'Previous', 'aaweb-advanced-image-resizer' ),
'next_text' => __( 'Next', 'aaweb-advanced-image-resizer' ),
'type' => 'list',
)
);
if ( $pagination ) {
echo '';
}
?>
get_backup_items( $attachment_id ) ) : absint( $count );
return sprintf(
'',
$attachment_id,
esc_html( $this->backup_count_label( $count ) ),
esc_html__( 'Load backups', 'aaweb-advanced-image-resizer' )
);
}
public function attachment_fields_to_edit( $form_fields, $post ) {
if ( ! $post || 'attachment' !== $post->post_type || 0 !== strpos( (string) $post->post_mime_type, 'image/' ) || ! $this->current_user_can_edit_attachment( $post->ID ) ) {
return $form_fields;
}
$attachment_id = absint( $post->ID );
$backup_count = count( $this->get_backup_items( $attachment_id ) );
$form_fields['aaweb_air_backups'] = array(
'label' => __( 'AAWEB backups', 'aaweb-advanced-image-resizer' ),
'input' => 'html',
'html' => sprintf(
'',
$attachment_id,
esc_html( $this->backup_count_label( $backup_count ) )
),
'helps' => __( 'View, restore or delete backups without opening the image editor.', 'aaweb-advanced-image-resizer' ),
);
return $form_fields;
}
public function media_row_actions( $actions, $post ) {
$settings = $this->get_settings();
if ( ! $post || 'attachment' !== $post->post_type || 0 !== strpos( (string) $post->post_mime_type, 'image/' ) || ! $this->current_user_can_edit_attachment( $post->ID ) ) {
return $actions;
}
if ( ! empty( $settings['enable_row_action'] ) ) {
$allowed_options = array(
'option' => array(
'value' => true,
'selected' => true,
),
);
$preset_options = wp_kses( $this->preset_options_html(), $allowed_options );
$format_options = wp_kses( $this->output_format_options_html( $settings['output_format'] ), $allowed_options );
$actions['aaweb_air_resize'] = sprintf(
'',
$preset_options,
selected( $settings['default_mode'], 'replace', false ),
esc_html__( 'Replace', 'aaweb-advanced-image-resizer' ),
selected( $settings['default_mode'], 'create', false ),
esc_html__( 'Create New', 'aaweb-advanced-image-resizer' ),
$format_options,
absint( $post->ID ),
esc_html__( 'Resize', 'aaweb-advanced-image-resizer' )
);
}
$actions['aaweb_air_backups'] = sprintf(
'',
absint( $post->ID ),
esc_html( $this->backup_count_label( count( $this->get_backup_items( $post->ID ) ) ) )
);
return $actions;
}
private function get_hex_rgb( $hex ) {
$hex = sanitize_hex_color( $hex );
if ( ! $hex ) {
$hex = '#ffffff';
}
$hex = ltrim( $hex, '#' );
return array(
hexdec( substr( $hex, 0, 2 ) ),
hexdec( substr( $hex, 2, 2 ) ),
hexdec( substr( $hex, 4, 2 ) ),
);
}
private function unique_file_path( $dir, $filename ) {
$filename = wp_unique_filename( $dir, sanitize_file_name( $filename ) );
return trailingslashit( $dir ) . $filename;
}
private function output_formats() {
$formats = array(
'jpg' => array(
'label' => __( 'JPG', 'aaweb-advanced-image-resizer' ),
'ext' => 'jpg',
'mime' => 'image/jpeg',
),
'png' => array(
'label' => __( 'PNG', 'aaweb-advanced-image-resizer' ),
'ext' => 'png',
'mime' => 'image/png',
),
'webp' => array(
'label' => __( 'WebP', 'aaweb-advanced-image-resizer' ),
'ext' => 'webp',
'mime' => 'image/webp',
),
);
if ( ! function_exists( 'imagewebp' ) ) {
unset( $formats['webp'] );
}
return $formats;
}
private function output_format_options_html( $selected = '' ) {
$formats = $this->output_formats();
if ( empty( $formats[ $selected ] ) ) {
$selected = 'jpg';
}
$html = '';
foreach ( $formats as $key => $format ) {
$html .= sprintf( '', esc_attr( $key ), selected( $selected, $key, false ), esc_html( $format['label'] ) );
}
return $html;
}
private function save_canvas( $canvas, $file_path, $format ) {
$settings = $this->get_settings();
$format = sanitize_key( $format );
if ( 'png' === $format ) {
return imagepng( $canvas, $file_path, 6 );
}
if ( 'webp' === $format ) {
if ( ! function_exists( 'imagewebp' ) ) {
return false;
}
$quality = min( 100, max( 50, absint( $settings['webp_quality'] ) ) );
return imagewebp( $canvas, $file_path, $quality );
}
$quality = min( 100, max( 50, absint( $settings['jpg_quality'] ) ) );
return imagejpeg( $canvas, $file_path, $quality );
}
private function create_backup( $attachment_id, $file_path ) {
$settings = $this->get_settings();
$file_path = wp_normalize_path( (string) $file_path );
if ( empty( $settings['enable_backups'] ) || ! file_exists( $file_path ) || ! $this->is_upload_file( $file_path ) ) {
return '';
}
$dir = $this->get_backup_dir();
if ( empty( $dir ) || ! wp_mkdir_p( $dir ) || ! $this->is_upload_file( $dir ) ) {
return '';
}
$info = pathinfo( $file_path );
$ext = isset( $info['extension'] ) ? '.' . sanitize_file_name( $info['extension'] ) : '';
$base = isset( $info['filename'] ) ? sanitize_file_name( $info['filename'] ) : 'image';
$backup = $this->unique_file_path( $dir, $attachment_id . '-' . $base . '-aaweb-air-backup-' . gmdate( 'Ymd-His' ) . $ext );
if ( ! $this->is_upload_file( $backup ) ) {
return '';
}
$copied = copy( $file_path, $backup );
if ( ! $copied ) {
return '';
}
$backups = get_post_meta( $attachment_id, self::BACKUP_META, true );
$backups = is_array( $backups ) ? $backups : array();
$backups[] = array(
'file' => $backup,
'original_file' => $file_path,
'time' => current_time( 'mysql' ),
);
$old_backups = array();
$max_backups = isset( $settings['max_backups'] ) ? absint( $settings['max_backups'] ) : 10;
if ( $max_backups > 0 && count( $backups ) > $max_backups ) {
$remove_count = count( $backups ) - $max_backups;
$old_backups = array_splice( $backups, 0, $remove_count );
}
if ( false === update_post_meta( $attachment_id, self::BACKUP_META, $backups ) ) {
wp_delete_file( $backup );
return '';
}
foreach ( $old_backups as $old_backup ) {
if ( ! empty( $old_backup['file'] ) && $this->is_upload_file( $old_backup['file'] ) && file_exists( $old_backup['file'] ) ) {
wp_delete_file( $old_backup['file'] );
}
}
return $backup;
}
private function get_backup_dir() {
$upload_dir = wp_get_upload_dir();
if ( empty( $upload_dir['basedir'] ) ) {
return '';
}
return trailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . 'aaweb-advanced-image-resizer/backups/';
}
private function is_upload_file( $file ) {
$upload_dir = wp_get_upload_dir();
if ( empty( $upload_dir['basedir'] ) ) {
return false;
}
$base_dir = trailingslashit( wp_normalize_path( $upload_dir['basedir'] ) );
$file = wp_normalize_path( (string) $file );
return '' !== $file && 0 === strpos( trailingslashit( $file ), $base_dir );
}
private function get_backup_items( $attachment_id ) {
$backups = get_post_meta( $attachment_id, self::BACKUP_META, true );
$backups = is_array( $backups ) ? $backups : array();
$items = array();
foreach ( $backups as $index => $backup ) {
$file = isset( $backup['file'] ) ? wp_normalize_path( $backup['file'] ) : '';
if ( '' === $file || ! $this->is_upload_file( $file ) ) {
continue;
}
$exists = file_exists( $file );
$items[] = array(
'index' => (int) $index,
'name' => basename( $file ),
'time' => isset( $backup['time'] ) ? sanitize_text_field( $backup['time'] ) : '',
'size' => $exists ? size_format( filesize( $file ) ) : '',
'exists' => $exists,
);
}
return $items;
}
private function get_backup_by_index( $attachment_id, $backup_index ) {
$backups = get_post_meta( $attachment_id, self::BACKUP_META, true );
$backups = is_array( $backups ) ? $backups : array();
if ( ! isset( $backups[ $backup_index ] ) || empty( $backups[ $backup_index ]['file'] ) ) {
return new WP_Error( 'backup_missing', __( 'Backup not found.', 'aaweb-advanced-image-resizer' ) );
}
$file = wp_normalize_path( $backups[ $backup_index ]['file'] );
if ( ! $this->is_upload_file( $file ) || ! file_exists( $file ) ) {
return new WP_Error( 'backup_file_missing', __( 'Backup file is missing.', 'aaweb-advanced-image-resizer' ) );
}
$backups[ $backup_index ]['file'] = $file;
return $backups[ $backup_index ];
}
public function restore_backup( $attachment_id, $backup_index ) {
$attachment_id = absint( $attachment_id );
$backup_index = absint( $backup_index );
$current_file = get_attached_file( $attachment_id );
if ( ! $attachment_id || ! $current_file || ! file_exists( $current_file ) ) {
return new WP_Error( 'file_missing', __( 'Current attachment file not found.', 'aaweb-advanced-image-resizer' ) );
}
$current_file = wp_normalize_path( $current_file );
if ( ! $this->is_upload_file( $current_file ) ) {
return new WP_Error( 'invalid_attachment_path', __( 'Current attachment file is outside the uploads directory.', 'aaweb-advanced-image-resizer' ) );
}
$backup = $this->get_backup_by_index( $attachment_id, $backup_index );
if ( is_wp_error( $backup ) ) {
return $backup;
}
$backup_file = $backup['file'];
$backup_info = wp_check_filetype( $backup_file );
if ( empty( $backup_info['type'] ) || 0 !== strpos( $backup_info['type'], 'image/' ) ) {
return new WP_Error( 'invalid_backup_type', __( 'Invalid backup image type.', 'aaweb-advanced-image-resizer' ) );
}
$current_ext = strtolower( pathinfo( $current_file, PATHINFO_EXTENSION ) );
$backup_ext = strtolower( pathinfo( $backup_file, PATHINFO_EXTENSION ) );
$target_file = $current_file;
if ( $current_ext !== $backup_ext ) {
$target_file = trailingslashit( dirname( $current_file ) ) . sanitize_file_name( pathinfo( $current_file, PATHINFO_FILENAME ) . '.' . $backup_ext );
}
if ( ! $this->is_upload_file( $backup_file ) || ! $this->is_upload_file( $target_file ) ) {
return new WP_Error( 'invalid_restore_path', __( 'Restore paths must be inside the uploads directory.', 'aaweb-advanced-image-resizer' ) );
}
$this->delete_attachment_thumbnails( $attachment_id, $current_file );
if ( ! copy( $backup_file, $target_file ) ) {
return new WP_Error( 'restore_failed', __( 'Failed to restore backup file.', 'aaweb-advanced-image-resizer' ) );
}
if ( wp_normalize_path( $target_file ) !== $current_file && file_exists( $current_file ) ) {
wp_delete_file( $current_file );
}
update_attached_file( $attachment_id, $target_file );
wp_update_post(
array(
'ID' => $attachment_id,
'post_mime_type' => $backup_info['type'],
)
);
require_once ABSPATH . 'wp-admin/includes/image.php';
$meta = wp_generate_attachment_metadata( $attachment_id, $target_file );
wp_update_attachment_metadata( $attachment_id, $meta );
return array( 'message' => __( 'Backup restored successfully.', 'aaweb-advanced-image-resizer' ) );
}
public function delete_backup( $attachment_id, $backup_index ) {
$attachment_id = absint( $attachment_id );
$backup_index = absint( $backup_index );
$backups = get_post_meta( $attachment_id, self::BACKUP_META, true );
$backups = is_array( $backups ) ? $backups : array();
if ( ! isset( $backups[ $backup_index ] ) || empty( $backups[ $backup_index ]['file'] ) ) {
return new WP_Error( 'backup_missing', __( 'Backup not found.', 'aaweb-advanced-image-resizer' ) );
}
$file = wp_normalize_path( $backups[ $backup_index ]['file'] );
if ( $this->is_upload_file( $file ) && file_exists( $file ) ) {
wp_delete_file( $file );
}
unset( $backups[ $backup_index ] );
$backups = array_values( $backups );
if ( empty( $backups ) ) {
delete_post_meta( $attachment_id, self::BACKUP_META );
} else {
update_post_meta( $attachment_id, self::BACKUP_META, $backups );
}
return array( 'message' => __( 'Backup deleted successfully.', 'aaweb-advanced-image-resizer' ) );
}
private function delete_attachment_thumbnails( $attachment_id, $original_file ) {
$meta = wp_get_attachment_metadata( $attachment_id );
if ( ! is_array( $meta ) || empty( $meta['sizes'] ) || empty( $meta['file'] ) ) {
return;
}
$upload_dir = wp_get_upload_dir();
$base_dir = trailingslashit( wp_normalize_path( $upload_dir['basedir'] ) );
$subdir = trailingslashit( dirname( wp_normalize_path( $meta['file'] ) ) );
$original = wp_normalize_path( $original_file );
foreach ( $meta['sizes'] as $size ) {
if ( empty( $size['file'] ) ) {
continue;
}
$thumb = wp_normalize_path( $base_dir . $subdir . $size['file'] );
if ( $thumb !== $original && file_exists( $thumb ) ) {
wp_delete_file( $thumb );
}
}
}
public function resize_one_attachment( $attachment_id, $dimension_key, $mode = 'replace', $output_format = '' ) {
$attachment_id = absint( $attachment_id );
$dimension_key = sanitize_key( $dimension_key );
$mode = in_array( $mode, array( 'replace', 'create' ), true ) ? $mode : 'replace';
$settings = $this->get_settings();
$output_format = $output_format ? sanitize_key( $output_format ) : sanitize_key( $settings['output_format'] );
$formats = $this->output_formats();
if ( empty( $formats[ $output_format ] ) ) {
$output_format = 'jpg';
}
if ( ! function_exists( 'imagecreatetruecolor' ) ) {
return new WP_Error( 'gd_missing', __( 'The GD image library is not available on this server.', 'aaweb-advanced-image-resizer' ) );
}
$output_ext = $formats[ $output_format ]['ext'];
$output_mime = $formats[ $output_format ]['mime'];
$presets = $this->presets();
if ( empty( $presets[ $dimension_key ] ) ) {
return new WP_Error( 'invalid_dimension', __( 'Invalid dimension key.', 'aaweb-advanced-image-resizer' ) );
}
$new_width = absint( $presets[ $dimension_key ]['w'] );
$new_height = absint( $presets[ $dimension_key ]['h'] );
$file_path = get_attached_file( $attachment_id );
if ( ! $file_path || ! file_exists( $file_path ) ) {
return new WP_Error( 'file_missing', __( 'File not found.', 'aaweb-advanced-image-resizer' ) );
}
$file_path = wp_normalize_path( $file_path );
if ( ! $this->is_upload_file( $file_path ) ) {
return new WP_Error( 'invalid_attachment_path', __( 'Attachment file is outside the uploads directory.', 'aaweb-advanced-image-resizer' ) );
}
$image_info = wp_getimagesize( $file_path );
if ( ! $image_info || empty( $image_info['mime'] ) ) {
return new WP_Error( 'invalid_image', __( 'Unsupported or invalid image.', 'aaweb-advanced-image-resizer' ) );
}
$mime_type = $image_info['mime'];
switch ( $mime_type ) {
case 'image/jpeg':
$image = imagecreatefromjpeg( $file_path );
break;
case 'image/png':
$image = imagecreatefrompng( $file_path );
break;
case 'image/gif':
$image = imagecreatefromgif( $file_path );
break;
case 'image/webp':
if ( ! function_exists( 'imagecreatefromwebp' ) ) {
return new WP_Error( 'webp_unsupported', __( 'WebP is not supported on this server.', 'aaweb-advanced-image-resizer' ) );
}
$image = imagecreatefromwebp( $file_path );
break;
default:
return new WP_Error( 'mime_unsupported', __( 'Unsupported image type.', 'aaweb-advanced-image-resizer' ) );
}
if ( ! $image ) {
return new WP_Error( 'open_failed', __( 'Failed to open image.', 'aaweb-advanced-image-resizer' ) );
}
$ow = imagesx( $image );
$oh = imagesy( $image );
if ( $ow < 1 || $oh < 1 ) {
imagedestroy( $image );
return new WP_Error( 'invalid_size', __( 'Invalid image size.', 'aaweb-advanced-image-resizer' ) );
}
if ( $ow / $oh > $new_width / $new_height ) {
$scaled_w = $new_width;
$scaled_h = max( 1, (int) round( $new_width / ( $ow / $oh ) ) );
} else {
$scaled_h = $new_height;
$scaled_w = max( 1, (int) round( $new_height * ( $ow / $oh ) ) );
}
$canvas = imagecreatetruecolor( $new_width, $new_height );
if ( ! $canvas ) {
imagedestroy( $image );
return new WP_Error( 'canvas_failed', __( 'Failed to create the destination image canvas.', 'aaweb-advanced-image-resizer' ) );
}
list( $r, $g, $b ) = $this->get_hex_rgb( $settings['background_color'] );
$bg = imagecolorallocate( $canvas, $r, $g, $b );
imagefill( $canvas, 0, 0, $bg );
imagealphablending( $canvas, true );
$dx = (int) floor( ( $new_width - $scaled_w ) / 2 );
$dy = (int) floor( ( $new_height - $scaled_h ) / 2 );
$resampled = imagecopyresampled( $canvas, $image, $dx, $dy, 0, 0, $scaled_w, $scaled_h, $ow, $oh );
if ( ! $resampled ) {
imagedestroy( $image );
imagedestroy( $canvas );
return new WP_Error( 'resize_failed', __( 'Failed to resize the source image.', 'aaweb-advanced-image-resizer' ) );
}
$upload_dir = wp_upload_dir();
if ( 'replace' === $mode ) {
$new_file_name = pathinfo( $file_path, PATHINFO_FILENAME ) . '.' . $output_ext;
$new_file_path = trailingslashit( dirname( $file_path ) ) . sanitize_file_name( $new_file_name );
if ( ! $this->is_upload_file( $new_file_path ) ) {
imagedestroy( $image );
imagedestroy( $canvas );
return new WP_Error( 'invalid_output_path', __( 'Output file path is outside the uploads directory.', 'aaweb-advanced-image-resizer' ) );
}
if ( ! empty( $settings['enable_backups'] ) && ! $this->create_backup( $attachment_id, $file_path ) ) {
imagedestroy( $image );
imagedestroy( $canvas );
return new WP_Error( 'backup_failed', __( 'The backup could not be created, so the original image was not replaced.', 'aaweb-advanced-image-resizer' ) );
}
$saved = $this->save_canvas( $canvas, $new_file_path, $output_format );
imagedestroy( $image );
imagedestroy( $canvas );
if ( ! $saved ) {
return new WP_Error( 'save_failed', __( 'Failed to save resized image file.', 'aaweb-advanced-image-resizer' ) );
}
$this->delete_attachment_thumbnails( $attachment_id, $file_path );
if ( wp_normalize_path( $file_path ) !== wp_normalize_path( $new_file_path ) && file_exists( $file_path ) ) {
wp_delete_file( $file_path );
}
update_attached_file( $attachment_id, $new_file_path );
wp_update_post( array( 'ID' => $attachment_id, 'post_mime_type' => $output_mime ) );
require_once ABSPATH . 'wp-admin/includes/image.php';
$meta = wp_generate_attachment_metadata( $attachment_id, $new_file_path );
wp_update_attachment_metadata( $attachment_id, $meta );
return array( 'message' => __( 'Image replaced successfully.', 'aaweb-advanced-image-resizer' ) );
}
$original_name = sanitize_title( pathinfo( $file_path, PATHINFO_FILENAME ) );
if ( '' === $original_name ) {
$original_name = 'image';
}
$preset_slug = sanitize_title( str_replace( '_', '-', $dimension_key ) );
if ( '' === $preset_slug ) {
$preset_slug = 'resized';
}
$unique_name = sprintf(
'%1$s-%2$s-%3$dx%4$d.%5$s',
$original_name,
$preset_slug,
$new_width,
$new_height,
$output_ext
);
$new_file_path = $this->unique_file_path( $upload_dir['path'], $unique_name );
if ( ! $this->is_upload_file( $new_file_path ) ) {
imagedestroy( $image );
imagedestroy( $canvas );
return new WP_Error( 'invalid_output_path', __( 'Output file path is outside the uploads directory.', 'aaweb-advanced-image-resizer' ) );
}
$saved = $this->save_canvas( $canvas, $new_file_path, $output_format );
imagedestroy( $image );
imagedestroy( $canvas );
if ( ! $saved ) {
return new WP_Error( 'save_failed', __( 'Failed to save new resized image file.', 'aaweb-advanced-image-resizer' ) );
}
$attachment = array(
'post_mime_type' => $output_mime,
'post_title' => sanitize_text_field( pathinfo( $new_file_path, PATHINFO_FILENAME ) ),
'post_content' => '',
'post_status' => 'inherit',
);
$new_attach_id = wp_insert_attachment( $attachment, $new_file_path );
if ( is_wp_error( $new_attach_id ) || ! $new_attach_id ) {
wp_delete_file( $new_file_path );
return new WP_Error( 'insert_failed', __( 'Failed to create attachment.', 'aaweb-advanced-image-resizer' ) );
}
require_once ABSPATH . 'wp-admin/includes/image.php';
$meta = wp_generate_attachment_metadata( $new_attach_id, $new_file_path );
wp_update_attachment_metadata( $new_attach_id, $meta );
return array(
'message' => __( 'New resized image created successfully.', 'aaweb-advanced-image-resizer' ),
'attachment_id' => $new_attach_id,
);
}
private function current_user_can_edit_attachment( $attachment_id ) {
$attachment_id = absint( $attachment_id );
return $attachment_id && current_user_can( 'upload_files' ) && current_user_can( 'edit_post', $attachment_id );
}
public function ajax_get_backups() {
if ( ! current_user_can( 'upload_files' ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'aaweb-advanced-image-resizer' ) ) );
}
check_ajax_referer( self::NONCE_AJAX, 'nonce' );
$id = isset( $_POST['attachment_id'] ) ? absint( wp_unslash( $_POST['attachment_id'] ) ) : 0;
if ( ! $id ) {
wp_send_json_error( array( 'message' => __( 'Missing attachment ID.', 'aaweb-advanced-image-resizer' ) ) );
}
if ( ! $this->current_user_can_edit_attachment( $id ) ) {
wp_send_json_error( array( 'message' => __( 'You are not allowed to edit this image.', 'aaweb-advanced-image-resizer' ) ) );
}
wp_send_json_success( array( 'backups' => $this->get_backup_items( $id ) ) );
}
public function ajax_restore_backup() {
if ( ! current_user_can( 'upload_files' ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'aaweb-advanced-image-resizer' ) ) );
}
check_ajax_referer( self::NONCE_AJAX, 'nonce' );
$id = isset( $_POST['attachment_id'] ) ? absint( wp_unslash( $_POST['attachment_id'] ) ) : 0;
$index = isset( $_POST['backup_index'] ) ? absint( wp_unslash( $_POST['backup_index'] ) ) : 0;
if ( ! $this->current_user_can_edit_attachment( $id ) ) {
wp_send_json_error( array( 'message' => __( 'You are not allowed to edit this image.', 'aaweb-advanced-image-resizer' ) ) );
}
$res = $this->restore_backup( $id, $index );
if ( is_wp_error( $res ) ) {
wp_send_json_error( array( 'message' => $res->get_error_message() ) );
}
wp_send_json_success( $res );
}
public function ajax_delete_backup() {
if ( ! current_user_can( 'upload_files' ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'aaweb-advanced-image-resizer' ) ) );
}
check_ajax_referer( self::NONCE_AJAX, 'nonce' );
$id = isset( $_POST['attachment_id'] ) ? absint( wp_unslash( $_POST['attachment_id'] ) ) : 0;
$index = isset( $_POST['backup_index'] ) ? absint( wp_unslash( $_POST['backup_index'] ) ) : 0;
if ( ! $this->current_user_can_edit_attachment( $id ) ) {
wp_send_json_error( array( 'message' => __( 'You are not allowed to edit this image.', 'aaweb-advanced-image-resizer' ) ) );
}
$res = $this->delete_backup( $id, $index );
if ( is_wp_error( $res ) ) {
wp_send_json_error( array( 'message' => $res->get_error_message() ) );
}
wp_send_json_success( $res );
}
public function ajax_resize_image() {
if ( ! current_user_can( 'upload_files' ) ) {
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'aaweb-advanced-image-resizer' ) ) );
}
check_ajax_referer( self::NONCE_AJAX, 'nonce' );
$id = isset( $_POST['attachment_id'] ) ? absint( wp_unslash( $_POST['attachment_id'] ) ) : 0;
$dim = isset( $_POST['dimension'] ) ? sanitize_key( wp_unslash( $_POST['dimension'] ) ) : '';
$mode = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : 'replace';
$output_format = isset( $_POST['output_format'] ) ? sanitize_key( wp_unslash( $_POST['output_format'] ) ) : '';
if ( ! $id || ! $dim ) {
wp_send_json_error( array( 'message' => __( 'Missing parameters.', 'aaweb-advanced-image-resizer' ) ) );
}
if ( ! $this->current_user_can_edit_attachment( $id ) ) {
wp_send_json_error( array( 'message' => __( 'You are not allowed to edit this image.', 'aaweb-advanced-image-resizer' ) ) );
}
$res = $this->resize_one_attachment( $id, $dim, $mode, $output_format );
if ( is_wp_error( $res ) ) {
wp_send_json_error( array( 'message' => $res->get_error_message() ) );
}
wp_send_json_success( $res );
}
public function register_bulk_action( $bulk_actions ) {
$settings = $this->get_settings();
if ( ! empty( $settings['enable_bulk_action'] ) ) {
$bulk_actions['aaweb_air_bulk_resize'] = __( 'AAWEB Resize with Padding', 'aaweb-advanced-image-resizer' );
}
return $bulk_actions;
}
public function bulk_controls( $post_type, $which ) {
$settings = $this->get_settings();
if ( 'attachment' !== $post_type || 'top' !== $which || empty( $settings['enable_bulk_action'] ) ) {
return;
}
echo '';
echo '';
echo '';
wp_nonce_field( self::NONCE_BULK, 'aaweb_air_bulk_nonce' );
}
public function handle_bulk_action( $redirect_url, $action, $ids ) {
if ( 'aaweb_air_bulk_resize' !== $action ) {
return $redirect_url;
}
if ( ! current_user_can( 'upload_files' ) ) {
return add_query_arg( 'aaweb_air_bulk_error', rawurlencode( __( 'Permission denied.', 'aaweb-advanced-image-resizer' ) ), $redirect_url );
}
if ( empty( $_REQUEST['aaweb_air_bulk_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['aaweb_air_bulk_nonce'] ) ), self::NONCE_BULK ) ) {
return add_query_arg( 'aaweb_air_bulk_error', rawurlencode( __( 'Invalid nonce.', 'aaweb-advanced-image-resizer' ) ), $redirect_url );
}
$dimension = isset( $_REQUEST['aaweb_air_bulk_dimension'] ) ? sanitize_key( wp_unslash( $_REQUEST['aaweb_air_bulk_dimension'] ) ) : '';
$mode = isset( $_REQUEST['aaweb_air_bulk_mode'] ) ? sanitize_key( wp_unslash( $_REQUEST['aaweb_air_bulk_mode'] ) ) : 'replace';
$output_format = isset( $_REQUEST['aaweb_air_bulk_output_format'] ) ? sanitize_key( wp_unslash( $_REQUEST['aaweb_air_bulk_output_format'] ) ) : '';
$formats = $this->output_formats();
if ( ! $dimension ) {
return add_query_arg( 'aaweb_air_bulk_error', rawurlencode( __( 'Please select a size before applying the bulk action.', 'aaweb-advanced-image-resizer' ) ), $redirect_url );
}
if ( ! in_array( $mode, array( 'replace', 'create' ), true ) ) {
return add_query_arg( 'aaweb_air_bulk_error', rawurlencode( __( 'Please select a valid mode.', 'aaweb-advanced-image-resizer' ) ), $redirect_url );
}
if ( empty( $formats[ $output_format ] ) ) {
return add_query_arg( 'aaweb_air_bulk_error', rawurlencode( __( 'Please select a valid output format.', 'aaweb-advanced-image-resizer' ) ), $redirect_url );
}
$success = 0;
$failed = 0;
foreach ( (array) $ids as $attachment_id ) {
$attachment_id = absint( $attachment_id );
if ( ! $this->current_user_can_edit_attachment( $attachment_id ) ) {
$failed++;
continue;
}
$res = $this->resize_one_attachment( $attachment_id, $dimension, $mode, $output_format );
if ( is_wp_error( $res ) ) {
$failed++;
} else {
$success++;
}
}
return add_query_arg(
array(
'aaweb_air_bulk_done' => 1,
'aaweb_air_bulk_ok' => $success,
'aaweb_air_bulk_failed' => $failed,
),
$redirect_url
);
}
public function admin_notices() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Admin notice reads redirect query args generated by this plugin.
if ( ! empty( $_GET['aaweb_air_bulk_error'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Admin notice reads redirect query args generated by this plugin.
$error = sanitize_text_field( wp_unslash( $_GET['aaweb_air_bulk_error'] ) );
printf( '', esc_html( $error ) );
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Admin notice reads redirect query args generated by this plugin.
if ( empty( $_GET['aaweb_air_bulk_done'] ) ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Admin notice reads redirect query args generated by this plugin.
$ok = isset( $_GET['aaweb_air_bulk_ok'] ) ? absint( $_GET['aaweb_air_bulk_ok'] ) : 0;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Admin notice reads redirect query args generated by this plugin.
$bad = isset( $_GET['aaweb_air_bulk_failed'] ) ? absint( $_GET['aaweb_air_bulk_failed'] ) : 0;
/* translators: 1: number of successfully resized images, 2: number of failed image resize attempts. */
$msg = sprintf( __( 'AAWEB Image Resizer bulk complete: %1$d succeeded, %2$d failed.', 'aaweb-advanced-image-resizer' ), $ok, $bad );
printf( '', esc_html( $msg ) );
}
}
AAWEB_Advanced_Image_Resizer::instance();