rebase from live enviornment
This commit is contained in:
81
wp/plugins/imagify/classes/Admin/AdminBar.php
Normal file
81
wp/plugins/imagify/classes/Admin/AdminBar.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Imagify\Admin;
|
||||
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
use Imagify\User\User;
|
||||
use Imagify_Views;
|
||||
|
||||
/**
|
||||
* Admin bar handler
|
||||
*/
|
||||
class AdminBar {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Launch the hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
if ( wp_doing_ajax() ) {
|
||||
add_action( 'wp_ajax_imagify_get_admin_bar_profile', array( $this, 'get_admin_bar_profile_callback' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get admin bar profile output.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function get_admin_bar_profile_callback() {
|
||||
imagify_check_nonce( 'imagify-get-admin-bar-profile', 'imagifygetadminbarprofilenonce' );
|
||||
|
||||
if ( ! imagify_get_context( 'wp' )->current_user_can( 'manage' ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
$user = new User();
|
||||
$views = Imagify_Views::get_instance();
|
||||
$unconsumed_quota = $views->get_quota_percent();
|
||||
$text = '';
|
||||
$button_text = '';
|
||||
$upgrade_link = '';
|
||||
|
||||
if ( $user->is_free() ) {
|
||||
$text = esc_html__( 'Upgrade your plan now for more!', 'rocket' ) . '<br>' .
|
||||
esc_html__( 'From $4.99/month only, keep going with image optimization!', 'rocket' );
|
||||
$button_text = esc_html__( 'Upgrade My Plan', 'rocket' );
|
||||
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/?utm_source=plugin&utm_medium=notification';
|
||||
} elseif ( $user->is_growth() ) {
|
||||
$text = esc_html__( 'Switch to Infinite plan for unlimited optimization:', 'rocket' ) . '<br>';
|
||||
|
||||
if ( $user->is_monthly ) {
|
||||
$text .= esc_html__( 'For $9.99/month, optimize as many images as you like!', 'rocket' );
|
||||
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/plan_switch/?label=infinite&payment_plan=1&utm_source=plugin&utm_medium=notification ';
|
||||
} else {
|
||||
$text .= esc_html__( 'For $99.9/year, optimize as many images as you like!', 'rocket' );
|
||||
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/plan_switch/?label=infinite&payment_plan=2&utm_source=plugin&utm_medium=notification ';
|
||||
}
|
||||
|
||||
$button_text = esc_html__( 'Switch To Infinite Plan', 'rocket' );
|
||||
}
|
||||
|
||||
$data = [
|
||||
'quota_icon' => $views->get_quota_icon(),
|
||||
'quota_class' => $views->get_quota_class(),
|
||||
'plan_label' => $user->plan_label,
|
||||
'plan_with_quota' => $user->is_free() || $user->is_growth(),
|
||||
'unconsumed_quota' => $unconsumed_quota,
|
||||
'user_quota' => $user->quota,
|
||||
'next_update' => $user->next_date_update,
|
||||
'text' => $text,
|
||||
'button_text' => $button_text,
|
||||
'upgrade_link' => $upgrade_link,
|
||||
];
|
||||
|
||||
$template = $views->get_template( 'admin/admin-bar-status', $data );
|
||||
|
||||
wp_send_json_success( $template );
|
||||
}
|
||||
}
|
||||
100
wp/plugins/imagify/classes/Auth/Basic.php
Normal file
100
wp/plugins/imagify/classes/Auth/Basic.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace Imagify\Auth;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Class that allows the use of Basic Auth for internal requests.
|
||||
* If this doesn’t work automatically, define the constants IMAGIFY_AUTH_USER and IMAGIFY_AUTH_PASSWORD.
|
||||
*
|
||||
* @since 1.9.5
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Basic {
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class init: launch hooks.
|
||||
*
|
||||
* @since 1.9.5
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'imagify_background_process_url', [ $this, 'get_auth_url' ] );
|
||||
add_filter( 'imagify_async_job_url', [ $this, 'get_auth_url' ] );
|
||||
add_filter( 'imagify_internal_request_url', [ $this, 'get_auth_url' ] );
|
||||
add_filter( 'cron_request', [ $this, 'cron_request_args' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the site uses basic authentication, add the required user and password to the given URL.
|
||||
*
|
||||
* @since 1.9.5
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url An URL.
|
||||
* @return string
|
||||
*/
|
||||
public function get_auth_url( $url ) {
|
||||
if ( ! $url || ! is_string( $url ) ) {
|
||||
// Invalid.
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( preg_match( '%.+?//(.+?):(.+?)@%', $url ) ) {
|
||||
// Credentials already in the URL.
|
||||
return $url;
|
||||
}
|
||||
|
||||
if ( defined( 'IMAGIFY_AUTH_USER' ) && defined( 'IMAGIFY_AUTH_PASSWORD' ) && IMAGIFY_AUTH_USER && IMAGIFY_AUTH_PASSWORD ) {
|
||||
$user = IMAGIFY_AUTH_USER;
|
||||
$pass = IMAGIFY_AUTH_PASSWORD;
|
||||
} else {
|
||||
$auth_type = ! empty( $_SERVER['AUTH_TYPE'] ) ? strtolower( wp_unslash( $_SERVER['AUTH_TYPE'] ) ) : '';
|
||||
|
||||
if ( 'basic' === $auth_type && ! empty( $_SERVER['PHP_AUTH_USER'] ) && ! empty( $_SERVER['PHP_AUTH_PW'] ) ) {
|
||||
$user = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) );
|
||||
$pass = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_PW'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $user ) || empty( $pass ) ) {
|
||||
// No credentials.
|
||||
return $url;
|
||||
}
|
||||
|
||||
return preg_replace( '%^(.+?//)(.+?)$%', '$1' . rawurlencode( $user ) . ':' . rawurlencode( $pass ) . '@$2', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the site uses basic authentication, add the required user and password to the given URL.
|
||||
*
|
||||
* @since 1.9.5
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $args {
|
||||
* An array of cron request URL arguments.
|
||||
*
|
||||
* @type string $url The cron request URL.
|
||||
* @type int $key The 22 digit GMT microtime.
|
||||
* @type array $args {
|
||||
* An array of cron request arguments.
|
||||
*
|
||||
* @type int $timeout The request timeout in seconds. Default .01 seconds.
|
||||
* @type bool $blocking Whether to set blocking for the request. Default false.
|
||||
* @type bool $sslverify Whether SSL should be verified for the request. Default false.
|
||||
* }
|
||||
* }
|
||||
* @return array
|
||||
*/
|
||||
public function cron_request_args( $args ) {
|
||||
if ( ! empty( $args['url'] ) ) {
|
||||
$args['url'] = $this->get_auth_url( $args['url'] );
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
117
wp/plugins/imagify/classes/Bulk/AbstractBulk.php
Normal file
117
wp/plugins/imagify/classes/Bulk/AbstractBulk.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace Imagify\Bulk;
|
||||
|
||||
use Imagify_Filesystem;
|
||||
|
||||
/**
|
||||
* Abstract class to use for bulk.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
abstract class AbstractBulk implements BulkInterface {
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var Imagify_Filesystem
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->filesystem = Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format context data (stats).
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $data {
|
||||
* The data to format.
|
||||
*
|
||||
* @type int $count-optimized Number of media optimized.
|
||||
* @type int $count-errors Number of media having an optimization error.
|
||||
* @type int $optimized-size Optimized filesize.
|
||||
* @type int $original-size Original filesize.
|
||||
* @type string $errors_url URL to the page listing the optimization errors.
|
||||
* }
|
||||
* @return array {
|
||||
* The formated data.
|
||||
*
|
||||
* @type string $count-optimized Number of media optimized.
|
||||
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
|
||||
* @type string $optimized-size Optimized filesize.
|
||||
* @type string $original-size Original filesize.
|
||||
* }
|
||||
*/
|
||||
protected function format_context_data( $data ) {
|
||||
$defaults = [
|
||||
'count-optimized' => '',
|
||||
'count-errors' => '',
|
||||
'optimized-size' => '',
|
||||
'original-size' => '',
|
||||
];
|
||||
|
||||
$data = wp_parse_args( $data, $defaults );
|
||||
|
||||
$data = array_map( function( $item ) {
|
||||
return empty( $item ) ? '' : $item;
|
||||
}, $data );
|
||||
|
||||
if ( ! empty( $data['count-optimized'] ) ) {
|
||||
// translators: %s is a formatted number, dont use %d.
|
||||
$data['count-optimized'] = sprintf( _n( '%s Media File Optimized', '%s Media Files Optimized', $data['count-optimized'], 'imagify' ), '<span>' . number_format_i18n( $data['count-optimized'] ) . '</span>' );
|
||||
|
||||
}
|
||||
|
||||
if ( ! empty( $data['count-errors'] ) ) {
|
||||
/* translators: %s is a formatted number, dont use %d. */
|
||||
$data['count-errors'] = sprintf( _n( '%s Error', '%s Errors', $data['count-errors'], 'imagify' ), '<span>' . number_format_i18n( $data['count-errors'] ) . '</span>' );
|
||||
$data['count-errors'] .= ' <a href="' . esc_url( $data['errors_url'] ) . '">' . __( 'View Errors', 'imagify' ) . '</a>';
|
||||
}
|
||||
|
||||
if ( ! empty( $data['optimized-size'] ) ) {
|
||||
$data['optimized-size'] = '<span class="imagify-cell-label">' . __( 'Optimized Filesize', 'imagify' ) . '</span> ' . imagify_size_format( $data['optimized-size'], 2 );
|
||||
}
|
||||
|
||||
if ( ! empty( $data['original-size'] ) ) {
|
||||
$data['original-size'] = '<span class="imagify-cell-label">' . __( 'Original Filesize', 'imagify' ) . '</span> ' . imagify_size_format( $data['original-size'], 2 );
|
||||
}
|
||||
|
||||
unset( $data['errors_url'] );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to set no limit to the PHP timeout for time intensive processes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function set_no_time_limit() {
|
||||
if (
|
||||
function_exists( 'set_time_limit' )
|
||||
&&
|
||||
false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' )
|
||||
&& ! ini_get( 'safe_mode' ) // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
|
||||
) {
|
||||
@set_time_limit( 0 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if there are optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return int The number of media.
|
||||
*/
|
||||
public function has_optimized_media_without_webp() {
|
||||
return count( $this->get_optimized_media_ids_without_webp()['ids'] );
|
||||
}
|
||||
}
|
||||
578
wp/plugins/imagify/classes/Bulk/Bulk.php
Normal file
578
wp/plugins/imagify/classes/Bulk/Bulk.php
Normal file
@@ -0,0 +1,578 @@
|
||||
<?php
|
||||
namespace Imagify\Bulk;
|
||||
|
||||
use Exception;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Bulk optimization
|
||||
*/
|
||||
class Bulk {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class init: launch hooks.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'imagify_optimize_media', [ $this, 'optimize_media' ], 10, 3 );
|
||||
add_action( 'imagify_convert_webp', [ $this, 'generate_webp_versions' ], 10, 2 );
|
||||
add_action( 'imagify_convert_webp_finished', [ $this, 'clear_webp_transients' ], 10, 2 );
|
||||
add_action( 'wp_ajax_imagify_bulk_optimize', [ $this, 'bulk_optimize_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_missing_webp_generation', [ $this, 'missing_webp_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_get_folder_type_data', [ $this, 'get_folder_type_data_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_bulk_info_seen', [ $this, 'bulk_info_seen_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_bulk_get_stats', [ $this, 'bulk_get_stats_callback' ] );
|
||||
add_action( 'imagify_after_optimize', [ $this, 'check_optimization_status' ], 10, 2 );
|
||||
add_action( 'imagify_deactivation', [ $this, 'delete_transients_data' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete transients data on deactivation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_transients_data() {
|
||||
delete_transient( 'imagify_custom-folders_optimize_running' );
|
||||
delete_transient( 'imagify_wp_optimize_running' );
|
||||
delete_transient( 'imagify_bulk_optimization_complete' );
|
||||
delete_transient( 'imagify_missing_webp_total' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks bulk optimization status after each optimization task
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function check_optimization_status( $process, $item ) {
|
||||
$custom_folders = get_transient( 'imagify_custom-folders_optimize_running' );
|
||||
$library_wp = get_transient( 'imagify_wp_optimize_running' );
|
||||
|
||||
if (
|
||||
! $custom_folders
|
||||
&&
|
||||
! $library_wp
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $process->get_data();
|
||||
$progress = get_transient( 'imagify_bulk_optimization_result' );
|
||||
|
||||
if ( $data->is_optimized() ) {
|
||||
$size_data = $data->get_size_data();
|
||||
|
||||
if ( false === $progress ) {
|
||||
$progress = [
|
||||
'total' => 0,
|
||||
'original_size' => 0,
|
||||
'optimized_size' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$progress['total']++;
|
||||
|
||||
$progress['original_size'] += $size_data['original_size'];
|
||||
$progress['optimized_size'] += $size_data['optimized_size'];
|
||||
|
||||
set_transient( 'imagify_bulk_optimization_result', $progress, DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
$remaining = 0;
|
||||
|
||||
if ( false !== $custom_folders ) {
|
||||
if ( false !== strpos( $item['process_class'], 'CustomFolders' ) ) {
|
||||
$custom_folders['remaining']--;
|
||||
|
||||
set_transient( 'imagify_custom-folders_optimize_running', $custom_folders, DAY_IN_SECONDS );
|
||||
|
||||
$remaining += $custom_folders['remaining'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( false !== $library_wp ) {
|
||||
if ( false !== strpos( $item['process_class'], 'WP' ) ) {
|
||||
$library_wp['remaining']--;
|
||||
|
||||
set_transient( 'imagify_wp_optimize_running', $library_wp, DAY_IN_SECONDS );
|
||||
|
||||
$remaining += $library_wp['remaining'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 >= $remaining ) {
|
||||
delete_transient( 'imagify_custom-folders_optimize_running' );
|
||||
delete_transient( 'imagify_wp_optimize_running' );
|
||||
set_transient( 'imagify_bulk_optimization_complete', 1, DAY_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease optimization running counter for the given context
|
||||
*
|
||||
* @param string $context Context to update.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function decrease_counter( string $context ) {
|
||||
$counter = get_transient( "imagify_{$context}_optimize_running" );
|
||||
|
||||
if ( false === $counter ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$counter['total'] = $counter['total'] - 1;
|
||||
$counter['remaining'] = $counter['remaining'] - 1;
|
||||
|
||||
if (
|
||||
0 === $counter['total']
|
||||
&&
|
||||
0 >= $counter['remaining']
|
||||
) {
|
||||
delete_transient( "imagify_{$context}_optimize_running" );
|
||||
}
|
||||
|
||||
set_transient( "imagify_{$context}_optimize_running", $counter, DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a media with the requested imagify bulk action.
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @param int $media_id Media ID.
|
||||
* @param string $context Current context.
|
||||
* @param int $optimization_level Optimization level.
|
||||
*/
|
||||
public function optimize_media( int $media_id, string $context, int $optimization_level ) {
|
||||
if ( ! $media_id || ! $context || ! is_numeric( $optimization_level ) ) {
|
||||
$this->decrease_counter( $context );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->force_optimize( $media_id, $context, $optimization_level );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the bulk optimization
|
||||
*
|
||||
* @param string $context Current context (WP/Custom folders).
|
||||
* @param int $optimization_level Optimization level.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function run_optimize( string $context, int $optimization_level ) {
|
||||
if ( ! $this->can_optimize() ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'over-quota',
|
||||
];
|
||||
}
|
||||
|
||||
$media_ids = $this->get_bulk_instance( $context )->get_unoptimized_media_ids( $optimization_level );
|
||||
|
||||
if ( empty( $media_ids ) ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'no-images',
|
||||
];
|
||||
}
|
||||
|
||||
foreach ( $media_ids as $media_id ) {
|
||||
try {
|
||||
as_enqueue_async_action(
|
||||
'imagify_optimize_media',
|
||||
[
|
||||
'id' => $media_id,
|
||||
'context' => $context,
|
||||
'level' => $optimization_level,
|
||||
],
|
||||
"imagify-{$context}-optimize-media"
|
||||
);
|
||||
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
// nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'total' => count( $media_ids ),
|
||||
'remaining' => count( $media_ids ),
|
||||
];
|
||||
|
||||
set_transient( "imagify_{$context}_optimize_running", $data, DAY_IN_SECONDS );
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'success',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the WebP generation
|
||||
*
|
||||
* @param array $contexts An array of contexts (WP/Custom folders).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function run_generate_webp( array $contexts ) {
|
||||
if ( ! $this->can_optimize() ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'over-quota',
|
||||
];
|
||||
}
|
||||
|
||||
delete_transient( 'imagify_stat_without_webp' );
|
||||
|
||||
$medias = [];
|
||||
|
||||
foreach ( $contexts as $context ) {
|
||||
$media = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_webp();
|
||||
|
||||
if ( ! $media['ids'] && $media['errors']['no_backup'] ) {
|
||||
// No backup, no WebP.
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'no-backup',
|
||||
];
|
||||
} elseif ( ! $media['ids'] && $media['errors']['no_file_path'] ) {
|
||||
// Error.
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __( 'The path to the selected files could not be retrieved.', 'imagify' ),
|
||||
];
|
||||
}
|
||||
|
||||
$medias[ $context ] = $media['ids'];
|
||||
}
|
||||
|
||||
if ( empty( $medias ) ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'no-images',
|
||||
];
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
|
||||
foreach ( $medias as $context => $media_ids ) {
|
||||
$total += count( $media_ids );
|
||||
|
||||
foreach ( $media_ids as $media_id ) {
|
||||
try {
|
||||
as_enqueue_async_action(
|
||||
'imagify_convert_webp',
|
||||
[
|
||||
'id' => $media_id,
|
||||
'context' => $context,
|
||||
],
|
||||
"imagify-{$context}-convert-webp"
|
||||
);
|
||||
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
// nothing to do.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_transient( 'imagify_missing_webp_total', $total, HOUR_IN_SECONDS );
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Bulk class name depending on a context.
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @param string $context The context name. Default values are 'wp' and 'custom-folders'.
|
||||
* @return string The Bulk class name.
|
||||
*/
|
||||
private function get_bulk_class_name( string $context ): string {
|
||||
switch ( $context ) {
|
||||
case 'wp':
|
||||
$class_name = WP::class;
|
||||
break;
|
||||
|
||||
case 'custom-folders':
|
||||
$class_name = CustomFolders::class;
|
||||
break;
|
||||
|
||||
default:
|
||||
$class_name = Noop::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the name of the class to use for bulk process.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $class_name The class name.
|
||||
* @param string $context The context name.
|
||||
*/
|
||||
$class_name = apply_filters( 'imagify_bulk_class_name', $class_name, $context );
|
||||
|
||||
return '\\' . ltrim( $class_name, '\\' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Bulk instance depending on a context.
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @param string $context The context name. Default values are 'wp' and 'custom-folders'.
|
||||
*
|
||||
* @return BulkInterface The optimization process instance.
|
||||
*/
|
||||
public function get_bulk_instance( string $context ): BulkInterface {
|
||||
$class_name = $this->get_bulk_class_name( $context );
|
||||
return new $class_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize all files from a media, whatever this media’s previous optimization status (will be restored if needed).
|
||||
* This is used by the bulk optimization page.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $media_id The media ID.
|
||||
* @param string $context The context.
|
||||
* @param int $level The optimization level.
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
private function force_optimize( int $media_id, string $context, int $level ) {
|
||||
if ( ! $this->can_optimize() ) {
|
||||
$this->decrease_counter( $context );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$process = imagify_get_optimization_process( $media_id, $context );
|
||||
$data = $process->get_data();
|
||||
|
||||
// Restore before re-optimizing.
|
||||
if ( $data->is_optimized() ) {
|
||||
$result = $process->restore();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$this->decrease_counter( $context );
|
||||
|
||||
// Return an error message.
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $process->optimize( $level );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @param int $media_id Media ID.
|
||||
* @param string $context Current context.
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_webp_versions( int $media_id, string $context ) {
|
||||
if ( ! $this->can_optimize() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return imagify_get_optimization_process( $media_id, $context )->generate_webp_versions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has a valid account and has quota. Die on failure.
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public function can_optimize() {
|
||||
if ( ! \Imagify_Requirements::is_api_key_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( \Imagify_Requirements::is_over_quota() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submitted context.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $method The method used: 'GET' (default), or 'POST'.
|
||||
* @param string $parameter The name of the parameter to look for.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_context( $method = 'GET', $parameter = 'context' ) {
|
||||
$context = 'POST' === $method ? wp_unslash( $_POST[ $parameter ] ) : wp_unslash( $_GET[ $parameter ] ); //phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
|
||||
$context = htmlspecialchars( $context );
|
||||
|
||||
return imagify_sanitize_context( $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submitted optimization level.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 1.9 Added $method and $parameter parameters.
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $method The method used: 'GET' (default), or 'POST'.
|
||||
* @param string $parameter The name of the parameter to look for.
|
||||
* @return int
|
||||
*/
|
||||
public function get_optimization_level( $method = 'GET', $parameter = 'optimization_level' ) {
|
||||
$method = 'POST' === $method ? INPUT_POST : INPUT_GET;
|
||||
$level = filter_input( $method, $parameter );
|
||||
|
||||
if ( ! is_numeric( $level ) || $level < 0 || $level > 2 ) {
|
||||
if ( get_imagify_option( 'lossless' ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return get_imagify_option( 'optimization_level' );
|
||||
}
|
||||
|
||||
return (int) $level;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BULK OPTIMIZATION CALLBACKS ============================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Launch the bulk optimization action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bulk_optimize_callback() {
|
||||
imagify_check_nonce( 'imagify-bulk-optimize' );
|
||||
|
||||
$context = $this->get_context();
|
||||
$level = $this->get_optimization_level();
|
||||
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
$data = $this->run_optimize( $context, $level );
|
||||
|
||||
if ( false === $data['success'] ) {
|
||||
wp_send_json_error( [ 'message' => $data['message'] ] );
|
||||
}
|
||||
|
||||
wp_send_json_success( [ 'total' => $data['message'] ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the missing WebP versions generation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function missing_webp_callback() {
|
||||
imagify_check_nonce( 'imagify-bulk-optimize' );
|
||||
|
||||
$contexts = explode( '_', sanitize_key( wp_unslash( $_GET['context'] ) ) );
|
||||
|
||||
foreach ( $contexts as $context ) {
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->run_generate_webp( $contexts );
|
||||
|
||||
if ( false === $data['success'] ) {
|
||||
wp_send_json_error( [ 'message' => $data['message'] ] );
|
||||
}
|
||||
|
||||
wp_send_json_success( [ 'total' => $data['message'] ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stats data for a specific folder type.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function get_folder_type_data_callback() {
|
||||
imagify_check_nonce( 'imagify-bulk-optimize' );
|
||||
|
||||
$context = $this->get_context();
|
||||
|
||||
if ( ! $context ) {
|
||||
imagify_die( __( 'Invalid request', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
$bulk = $this->get_bulk_instance( $context );
|
||||
|
||||
wp_send_json_success( $bulk->get_context_data() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "bulk info" popup state as "seen".
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function bulk_info_seen_callback() {
|
||||
imagify_check_nonce( 'imagify-bulk-optimize' );
|
||||
|
||||
$context = $this->get_context();
|
||||
|
||||
if ( ! $context ) {
|
||||
imagify_die( __( 'Invalid request', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
set_transient( 'imagify_bulk_optimization_infos', 1, WEEK_IN_SECONDS );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get generic stats to display in the bulk page.
|
||||
*
|
||||
* @since 1.7.1
|
||||
*/
|
||||
public function bulk_get_stats_callback() {
|
||||
imagify_check_nonce( 'imagify-bulk-optimize' );
|
||||
|
||||
$folder_types = filter_input( INPUT_GET, 'types', FILTER_REQUIRE_ARRAY );
|
||||
$folder_types = is_array( $folder_types ) ? array_filter( $folder_types, 'is_string' ) : [];
|
||||
|
||||
if ( ! $folder_types ) {
|
||||
imagify_die( __( 'Invalid request', 'imagify' ) );
|
||||
}
|
||||
|
||||
foreach ( $folder_types as $folder_type_data ) {
|
||||
$context = ! empty( $folder_type_data['context'] ) ? $folder_type_data['context'] : 'noop';
|
||||
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_success( imagify_get_bulk_stats( array_flip( $folder_types ) ) );
|
||||
}
|
||||
}
|
||||
61
wp/plugins/imagify/classes/Bulk/BulkInterface.php
Normal file
61
wp/plugins/imagify/classes/Bulk/BulkInterface.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace Imagify\Bulk;
|
||||
|
||||
/**
|
||||
* Interface to use for bulk.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
interface BulkInterface {
|
||||
/**
|
||||
* Get all unoptimized media ids.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level.
|
||||
* @return array A list of unoptimized media. Array keys are media IDs prefixed with an underscore character, array values are the main file’s URL.
|
||||
*/
|
||||
public function get_unoptimized_media_ids( $optimization_level );
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
* @type array $errors {
|
||||
* @type array $no_file_path A list of media IDs.
|
||||
* @type array $no_backup A list of media IDs.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp();
|
||||
|
||||
/**
|
||||
* Tell if there are optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return int The number of media.
|
||||
*/
|
||||
public function has_optimized_media_without_webp();
|
||||
|
||||
/**
|
||||
* Get the context data.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return array {
|
||||
* The formated data.
|
||||
* The array keys corresponds to the table cell classes: "imagify-cell-{key}".
|
||||
*
|
||||
* @type string $count-optimized Number of media optimized.
|
||||
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
|
||||
* @type string $optimized-size Optimized filesize.
|
||||
* @type string $original-size Original filesize.
|
||||
* }
|
||||
*/
|
||||
public function get_context_data();
|
||||
}
|
||||
178
wp/plugins/imagify/classes/Bulk/CustomFolders.php
Normal file
178
wp/plugins/imagify/classes/Bulk/CustomFolders.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace Imagify\Bulk;
|
||||
|
||||
use Imagify_Custom_Folders;
|
||||
use Imagify_Files_Scan;
|
||||
use Imagify_Files_DB;
|
||||
use Imagify_Folders_DB;
|
||||
use Imagify_DB;
|
||||
use Imagify_Files_Stats;
|
||||
|
||||
/**
|
||||
* Class to use for bulk for custom folders.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class CustomFolders extends AbstractBulk {
|
||||
/**
|
||||
* Context "short name".
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $context = 'custom-folders';
|
||||
|
||||
/**
|
||||
* Get all unoptimized media ids.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level.
|
||||
*
|
||||
* @return array A list of unoptimized media IDs.
|
||||
*/
|
||||
public function get_unoptimized_media_ids( $optimization_level ) {
|
||||
$this->set_no_time_limit();
|
||||
|
||||
/**
|
||||
* Get the folders from DB.
|
||||
*/
|
||||
$folders = Imagify_Custom_Folders::get_folders( [
|
||||
'active' => true,
|
||||
] );
|
||||
|
||||
if ( ! $folders ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires before getting file IDs.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $folders An array of folders data.
|
||||
* @param int $optimization_level The optimization level that will be used for the optimization.
|
||||
*/
|
||||
do_action( 'imagify_bulk_optimize_files_before_get_files', $folders, $optimization_level );
|
||||
|
||||
/**
|
||||
* Get the files from DB, and from the folders.
|
||||
*/
|
||||
$files = Imagify_Custom_Folders::get_files_from_folders( $folders, [
|
||||
'optimization_level' => $optimization_level,
|
||||
] );
|
||||
|
||||
if ( ! $files ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ( $files as $k => $file ) {
|
||||
$files[ $k ] = $file['file_id'];
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
* @type array $errors {
|
||||
* @type array $no_file_path A list of media IDs.
|
||||
* @type array $no_backup A list of media IDs.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
global $wpdb;
|
||||
|
||||
$this->set_no_time_limit();
|
||||
|
||||
$files_table = Imagify_Files_DB::get_instance()->get_table_name();
|
||||
$folders_table = Imagify_Folders_DB::get_instance()->get_table_name();
|
||||
$mime_types = Imagify_DB::get_mime_types( 'image' );
|
||||
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::WEBP_SUFFIX' );
|
||||
$files = $wpdb->get_results( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
SELECT fi.file_id, fi.path
|
||||
FROM $files_table as fi
|
||||
INNER JOIN $folders_table AS fo
|
||||
ON ( fi.folder_id = fo.folder_id )
|
||||
WHERE
|
||||
fi.mime_type IN ( $mime_types )
|
||||
AND ( fi.status = 'success' OR fi.status = 'already_optimized' )
|
||||
AND ( fi.data NOT LIKE %s OR fi.data IS NULL )
|
||||
ORDER BY fi.file_id DESC",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
) );
|
||||
|
||||
$wpdb->flush();
|
||||
unset( $mime_types, $files_table, $folders_table, $webp_suffix );
|
||||
|
||||
$data = [
|
||||
'ids' => [],
|
||||
'errors' => [
|
||||
'no_file_path' => [],
|
||||
'no_backup' => [],
|
||||
],
|
||||
];
|
||||
|
||||
if ( ! $files ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
foreach ( $files as $file ) {
|
||||
$file_id = absint( $file->file_id );
|
||||
|
||||
if ( empty( $file->path ) ) {
|
||||
// Problem.
|
||||
$data['errors']['no_file_path'][] = $file_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_path = Imagify_Files_Scan::remove_placeholder( $file->path );
|
||||
$backup_path = Imagify_Custom_Folders::get_file_backup_path( $file_path );
|
||||
|
||||
if ( ! $this->filesystem->exists( $backup_path ) ) {
|
||||
// No backup, no WebP.
|
||||
$data['errors']['no_backup'][] = $file_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['ids'][] = $file_id;
|
||||
} // End foreach().
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context data.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return array {
|
||||
* The formated data.
|
||||
*
|
||||
* @type string $count-optimized Number of media optimized.
|
||||
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
|
||||
* @type string $optimized-size Optimized filesize.
|
||||
* @type string $original-size Original filesize.
|
||||
* }
|
||||
*/
|
||||
public function get_context_data() {
|
||||
$data = [
|
||||
'count-optimized' => Imagify_Files_Stats::count_optimized_files(),
|
||||
'count-errors' => Imagify_Files_Stats::count_error_files(),
|
||||
'optimized-size' => Imagify_Files_Stats::get_optimized_size(),
|
||||
'original-size' => Imagify_Files_Stats::get_original_size(),
|
||||
'errors_url' => get_imagify_admin_url( 'folder-errors', $this->context ),
|
||||
];
|
||||
|
||||
return $this->format_context_data( $data );
|
||||
}
|
||||
}
|
||||
71
wp/plugins/imagify/classes/Bulk/Noop.php
Normal file
71
wp/plugins/imagify/classes/Bulk/Noop.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace Imagify\Bulk;
|
||||
|
||||
/**
|
||||
* Falback class for bulk.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class Noop extends AbstractBulk {
|
||||
/**
|
||||
* Get all unoptimized media ids.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level.
|
||||
* @return array A list of unoptimized media. Array keys are media IDs prefixed with an underscore character, array values are the main file’s URL.
|
||||
*/
|
||||
public function get_unoptimized_media_ids( $optimization_level ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
* @type array $errors {
|
||||
* @type array $no_file_path A list of media IDs.
|
||||
* @type array $no_backup A list of media IDs.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
return [
|
||||
'ids' => [],
|
||||
'errors' => [
|
||||
'no_file_path' => [],
|
||||
'no_backup' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context data.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return array {
|
||||
* The formated data.
|
||||
*
|
||||
* @type string $count-optimized Number of media optimized.
|
||||
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
|
||||
* @type string $optimized-size Optimized filesize.
|
||||
* @type string $original-size Original filesize.
|
||||
* }
|
||||
*/
|
||||
public function get_context_data() {
|
||||
$data = [
|
||||
'count-optimized' => 0,
|
||||
'count-errors' => 0,
|
||||
'optimized-size' => 0,
|
||||
'original-size' => 0,
|
||||
'errors_url' => get_imagify_admin_url( 'folder-errors', 'noop' ),
|
||||
];
|
||||
|
||||
return $this->format_context_data( $data );
|
||||
}
|
||||
}
|
||||
303
wp/plugins/imagify/classes/Bulk/WP.php
Normal file
303
wp/plugins/imagify/classes/Bulk/WP.php
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
namespace Imagify\Bulk;
|
||||
|
||||
use Imagify_DB;
|
||||
|
||||
/**
|
||||
* Class to use for bulk for WP attachments.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class WP extends AbstractBulk {
|
||||
/**
|
||||
* Context "short name".
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $context = 'wp';
|
||||
|
||||
/**
|
||||
* Get all unoptimized media ids.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level.
|
||||
*
|
||||
* @return array A list of unoptimized media IDs.
|
||||
*/
|
||||
public function get_unoptimized_media_ids( $optimization_level ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->set_no_time_limit();
|
||||
|
||||
$mime_types = Imagify_DB::get_mime_types();
|
||||
$statuses = Imagify_DB::get_post_statuses();
|
||||
$nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
|
||||
$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [
|
||||
'prepared' => true,
|
||||
] );
|
||||
$ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
SELECT DISTINCT p.ID
|
||||
FROM $wpdb->posts AS p
|
||||
$nodata_join
|
||||
LEFT JOIN $wpdb->postmeta AS mt1
|
||||
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
|
||||
LEFT JOIN $wpdb->postmeta AS mt2
|
||||
ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_optimization_level' )
|
||||
WHERE
|
||||
p.post_mime_type IN ( $mime_types )
|
||||
AND (
|
||||
mt1.meta_value = 'error'
|
||||
OR
|
||||
mt2.meta_value != %d
|
||||
OR
|
||||
mt2.post_id IS NULL
|
||||
)
|
||||
AND p.post_type = 'attachment'
|
||||
AND p.post_status IN ( $statuses )
|
||||
$nodata_where
|
||||
ORDER BY
|
||||
CASE mt1.meta_value
|
||||
WHEN 'already_optimized' THEN 2
|
||||
ELSE 1
|
||||
END ASC,
|
||||
p.ID DESC
|
||||
LIMIT 0, %d",
|
||||
$optimization_level,
|
||||
imagify_get_unoptimized_attachment_limit()
|
||||
) );
|
||||
|
||||
$wpdb->flush();
|
||||
unset( $mime_types );
|
||||
$ids = array_filter( array_map( 'absint', $ids ) );
|
||||
|
||||
if ( ! $ids ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$metas = Imagify_DB::get_metas( [
|
||||
// Get attachments filename.
|
||||
'filenames' => '_wp_attached_file',
|
||||
// Get attachments data.
|
||||
'data' => '_imagify_data',
|
||||
// Get attachments optimization level.
|
||||
'optimization_levels' => '_imagify_optimization_level',
|
||||
// Get attachments status.
|
||||
'statuses' => '_imagify_status',
|
||||
], $ids );
|
||||
|
||||
// First run.
|
||||
foreach ( $ids as $i => $id ) {
|
||||
$attachment_status = isset( $metas['statuses'][ $id ] ) ? $metas['statuses'][ $id ] : false;
|
||||
$attachment_optimization_level = isset( $metas['optimization_levels'][ $id ] ) ? $metas['optimization_levels'][ $id ] : false;
|
||||
$attachment_error = '';
|
||||
|
||||
if ( isset( $metas['data'][ $id ]['sizes']['full']['error'] ) ) {
|
||||
$attachment_error = $metas['data'][ $id ]['sizes']['full']['error'];
|
||||
}
|
||||
|
||||
// Don't try to re-optimize if the optimization level is still the same.
|
||||
if ( $optimization_level === $attachment_optimization_level && is_string( $attachment_error ) ) {
|
||||
unset( $ids[ $i ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't try to re-optimize images already compressed.
|
||||
if ( 'already_optimized' === $attachment_status && $attachment_optimization_level >= $optimization_level ) {
|
||||
unset( $ids[ $i ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
$attachment_error = trim( $attachment_error );
|
||||
|
||||
// Don't try to re-optimize images with an empty error message.
|
||||
if ( 'error' === $attachment_status && empty( $attachment_error ) ) {
|
||||
unset( $ids[ $i ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ids ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_values( $ids );
|
||||
|
||||
/**
|
||||
* Fires before testing for file existence.
|
||||
*
|
||||
* @since 1.6.7
|
||||
*
|
||||
* @param array $ids An array of attachment IDs.
|
||||
* @param array $metas An array of the data fetched from the database.
|
||||
* @param int $optimization_level The optimization level that will be used for the optimization.
|
||||
*/
|
||||
do_action( 'imagify_bulk_optimize_before_file_existence_tests', $ids, $metas, $optimization_level );
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ( $ids as $i => $id ) {
|
||||
if ( empty( $metas['filenames'][ $id ] ) ) {
|
||||
// Problem.
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_path = get_imagify_attached_file( $metas['filenames'][ $id ] );
|
||||
|
||||
if ( ! $file_path || ! $this->filesystem->exists( $file_path ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attachment_backup_path = get_imagify_attachment_backup_path( $file_path );
|
||||
$attachment_status = isset( $metas['statuses'][ $id ] ) ? $metas['statuses'][ $id ] : false;
|
||||
$attachment_optimization_level = isset( $metas['optimization_levels'][ $id ] ) ? $metas['optimization_levels'][ $id ] : false;
|
||||
|
||||
// Don't try to re-optimize if there is no backup file.
|
||||
if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! $this->filesystem->exists( $attachment_backup_path ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[] = $id;
|
||||
} // End foreach().
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
* @type array $errors {
|
||||
* @type array $no_file_path A list of media IDs.
|
||||
* @type array $no_backup A list of media IDs.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
global $wpdb;
|
||||
|
||||
$this->set_no_time_limit();
|
||||
|
||||
$mime_types = Imagify_DB::get_mime_types( 'image' );
|
||||
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
|
||||
$statuses = Imagify_DB::get_post_statuses();
|
||||
$nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
|
||||
$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [
|
||||
'prepared' => true,
|
||||
] );
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'wp' ) . '::WEBP_SUFFIX' );
|
||||
$ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
SELECT p.ID
|
||||
FROM $wpdb->posts AS p
|
||||
$nodata_join
|
||||
LEFT JOIN $wpdb->postmeta AS mt1
|
||||
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
|
||||
LEFT JOIN $wpdb->postmeta AS mt2
|
||||
ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_data' )
|
||||
WHERE
|
||||
p.post_mime_type IN ( $mime_types )
|
||||
AND ( mt1.meta_value = 'success' OR mt1.meta_value = 'already_optimized' )
|
||||
AND mt2.meta_value NOT LIKE %s
|
||||
AND p.post_type = 'attachment'
|
||||
AND p.post_status IN ( $statuses )
|
||||
$nodata_where
|
||||
ORDER BY p.ID DESC
|
||||
LIMIT 0, %d",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%',
|
||||
imagify_get_unoptimized_attachment_limit()
|
||||
) );
|
||||
|
||||
$wpdb->flush();
|
||||
unset( $mime_types, $statuses, $webp_suffix );
|
||||
|
||||
$ids = array_filter( array_map( 'absint', $ids ) );
|
||||
$data = [
|
||||
'ids' => [],
|
||||
'errors' => [
|
||||
'no_file_path' => [],
|
||||
'no_backup' => [],
|
||||
],
|
||||
];
|
||||
|
||||
if ( ! $ids ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$metas = Imagify_DB::get_metas( [
|
||||
// Get attachments filename.
|
||||
'filenames' => '_wp_attached_file',
|
||||
], $ids );
|
||||
|
||||
/**
|
||||
* Fires before testing for file existence.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $ids An array of attachment IDs.
|
||||
* @param array $metas An array of the data fetched from the database.
|
||||
* @param string $context The context.
|
||||
*/
|
||||
do_action( 'imagify_bulk_generate_webp_before_file_existence_tests', $ids, $metas, 'wp' );
|
||||
|
||||
foreach ( $ids as $i => $id ) {
|
||||
if ( empty( $metas['filenames'][ $id ] ) ) {
|
||||
// Problem. Should not happen, thanks to the wpdb query.
|
||||
$data['errors']['no_file_path'][] = $id;
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_path = get_imagify_attached_file( $metas['filenames'][ $id ] );
|
||||
|
||||
if ( ! $file_path ) {
|
||||
// Main file not found.
|
||||
$data['errors']['no_file_path'][] = $id;
|
||||
continue;
|
||||
}
|
||||
|
||||
$backup_path = get_imagify_attachment_backup_path( $file_path );
|
||||
|
||||
if ( ! $this->filesystem->exists( $backup_path ) ) {
|
||||
// No backup, no WebP.
|
||||
$data['errors']['no_backup'][] = $id;
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['ids'][] = $id;
|
||||
} // End foreach().
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context data.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return array {
|
||||
* The formated data.
|
||||
*
|
||||
* @type string $count-optimized Number of media optimized.
|
||||
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
|
||||
* @type string $optimized-size Optimized filesize.
|
||||
* @type string $original-size Original filesize.
|
||||
* }
|
||||
*/
|
||||
public function get_context_data() {
|
||||
$total_saving_data = imagify_count_saving_data();
|
||||
$data = [
|
||||
'count-optimized' => imagify_count_optimized_attachments(),
|
||||
'count-errors' => imagify_count_error_attachments(),
|
||||
'optimized-size' => $total_saving_data['optimized_size'],
|
||||
'original-size' => $total_saving_data['original_size'],
|
||||
'errors_url' => get_imagify_admin_url( 'folder-errors', $this->context ),
|
||||
];
|
||||
|
||||
return $this->format_context_data( $data );
|
||||
}
|
||||
}
|
||||
96
wp/plugins/imagify/classes/CDN/PushCDNInterface.php
Normal file
96
wp/plugins/imagify/classes/CDN/PushCDNInterface.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
namespace Imagify\CDN;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Interface to use for Push CDNs.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface PushCDNInterface {
|
||||
|
||||
/**
|
||||
* Tell if the CDN is ready (not necessarily reachable).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready();
|
||||
|
||||
/**
|
||||
* Tell if the media is on the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function media_is_on_cdn();
|
||||
|
||||
/**
|
||||
* Get files from the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $file_paths A list of file paths.
|
||||
* @return bool|\WP_Error True on success. A \WP_error object on failure.
|
||||
*/
|
||||
public function get_files_from_cdn( $file_paths );
|
||||
|
||||
/**
|
||||
* Remove files from the CDN.
|
||||
* Don't use this to empty a folder.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $file_paths A list of file paths. Those paths are not necessary absolute, and can be also file names.
|
||||
* @return bool|\WP_Error True on success. A \WP_error object on failure.
|
||||
*/
|
||||
public function remove_files_from_cdn( $file_paths );
|
||||
|
||||
/**
|
||||
* Send all files from a media to the CDN.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $is_new_upload Tell if the current media is a new upload. If not, it means it's a media being regenerated, restored, etc.
|
||||
* @return bool|\WP_Error True/False if sent or not. A \WP_error object on failure.
|
||||
*/
|
||||
public function send_to_cdn( $is_new_upload );
|
||||
|
||||
/**
|
||||
* Get a file URL.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_name Name of the file. Leave empty for the full size file.
|
||||
* @return string URL to the file.
|
||||
*/
|
||||
public function get_file_url( $file_name = false );
|
||||
|
||||
/**
|
||||
* Get a file path.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_name Name of the file. Leave empty for the full size file. Use 'original' to get the path to the original file.
|
||||
* @return string Path to the file.
|
||||
*/
|
||||
public function get_file_path( $file_name = false );
|
||||
}
|
||||
30
wp/plugins/imagify/classes/CLI/AbstractCommand.php
Normal file
30
wp/plugins/imagify/classes/CLI/AbstractCommand.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\CLI;
|
||||
|
||||
/**
|
||||
* Abstrat class for CLI Command
|
||||
*/
|
||||
abstract class AbstractCommand implements CommandInterface {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
final public function get_name(): string {
|
||||
return sprintf( 'imagify %s', $this->get_command_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "imagify" command name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_command_name(): string;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_synopsis(): array {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
66
wp/plugins/imagify/classes/CLI/BulkOptimizeCommand.php
Normal file
66
wp/plugins/imagify/classes/CLI/BulkOptimizeCommand.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\CLI;
|
||||
|
||||
use Imagify\Bulk\Bulk;
|
||||
|
||||
/**
|
||||
* Command class for the bulk optimization
|
||||
*/
|
||||
class BulkOptimizeCommand extends AbstractCommand {
|
||||
/**
|
||||
* Executes the command.
|
||||
*
|
||||
* @param array $arguments Positional argument.
|
||||
* @param array $options Optional arguments.
|
||||
*/
|
||||
public function __invoke( $arguments, $options ) {
|
||||
$level = 2;
|
||||
|
||||
if ( isset( $options['lossless'] ) ) {
|
||||
$level = 0;
|
||||
}
|
||||
|
||||
foreach ( $arguments as $context ) {
|
||||
Bulk::get_instance()->run_optimize( $context, $level );
|
||||
}
|
||||
|
||||
\WP_CLI::log( 'Imagify bulk optimization triggered.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function get_command_name(): string {
|
||||
return 'bulk-optimize';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_description(): string {
|
||||
return 'Run the bulk optimization';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_synopsis(): array {
|
||||
return [
|
||||
[
|
||||
'type' => 'positional',
|
||||
'name' => 'contexts',
|
||||
'description' => 'The context(s) to run the bulk optimization for. Possible values are wp and custom-folders.',
|
||||
'optional' => false,
|
||||
'repeating' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'flag',
|
||||
'name' => 'lossless',
|
||||
'description' => 'Use lossless compression.',
|
||||
'optional' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
35
wp/plugins/imagify/classes/CLI/CommandInterface.php
Normal file
35
wp/plugins/imagify/classes/CLI/CommandInterface.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\CLI;
|
||||
|
||||
interface CommandInterface {
|
||||
/**
|
||||
* Get the command name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name(): string;
|
||||
|
||||
/**
|
||||
* Executes the command.
|
||||
*
|
||||
* @param array $arguments Positional argument.
|
||||
* @param array $options Optional arguments.
|
||||
*/
|
||||
public function __invoke( $arguments, $options );
|
||||
|
||||
/**
|
||||
* Get the positional and associative arguments a command accepts.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_synopsis(): array;
|
||||
|
||||
/**
|
||||
* Get the command description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description(): string;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\CLI;
|
||||
|
||||
use Imagify\Bulk\Bulk;
|
||||
|
||||
/**
|
||||
* Command class for the missing WebP generation
|
||||
*/
|
||||
class GenerateMissingWebpCommand extends AbstractCommand {
|
||||
/**
|
||||
* Executes the command.
|
||||
*
|
||||
* @param array $arguments Positional argument.
|
||||
* @param array $options Optional arguments.
|
||||
*/
|
||||
public function __invoke( $arguments, $options ) {
|
||||
Bulk::get_instance()->run_generate_webp( $arguments );
|
||||
|
||||
\WP_CLI::log( 'Imagify missing WebP generation triggered.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function get_command_name(): string {
|
||||
return 'generate-missing-webp';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_description(): string {
|
||||
return 'Run the generation of the missing WebP versions';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_synopsis(): array {
|
||||
return [
|
||||
[
|
||||
'type' => 'positional',
|
||||
'name' => 'contexts',
|
||||
'description' => 'The context(s) to run the missing WebP generation for. Possible values are wp and custom-folders.',
|
||||
'optional' => false,
|
||||
'repeating' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
262
wp/plugins/imagify/classes/Context/AbstractContext.php
Normal file
262
wp/plugins/imagify/classes/Context/AbstractContext.php
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
namespace Imagify\Context;
|
||||
|
||||
/**
|
||||
* Abstract used for contexts.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class AbstractContext implements ContextInterface {
|
||||
|
||||
/**
|
||||
* Context "short name".
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Tell if the media/context is network-wide.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_network_wide = false;
|
||||
|
||||
/**
|
||||
* Type of files this context allows.
|
||||
*
|
||||
* @var string Possible values are:
|
||||
* - 'all' to allow all types.
|
||||
* - 'image' to allow only images.
|
||||
* - 'not-image' to allow only pdf files.
|
||||
* @since 1.9
|
||||
* @see imagify_get_mime_types()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $allowed_mime_types = 'all';
|
||||
|
||||
/**
|
||||
* The thumbnail sizes for this context, except the full size.
|
||||
*
|
||||
* @var array {
|
||||
* Data for the currently registered thumbnail sizes.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* }
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $thumbnail_sizes;
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed to backup in this context.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $can_backup;
|
||||
|
||||
/**
|
||||
* Get the context "short name".
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the context is network-wide.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_network_wide() {
|
||||
return $this->is_network_wide;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of files this context allows.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see imagify_get_mime_types()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string Possible values are:
|
||||
* - 'all' to allow all types.
|
||||
* - 'image' to allow only images.
|
||||
* - 'not-image' to allow only pdf files.
|
||||
*/
|
||||
public function get_allowed_mime_types() {
|
||||
return $this->allowed_mime_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thumbnail sizes for this context, except the full size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* Data for the currently registered thumbnail sizes.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* }
|
||||
*/
|
||||
public function get_thumbnail_sizes() {
|
||||
return $this->thumbnail_sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed resize in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_resize() {
|
||||
return $this->get_resizing_threshold() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed to backup in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_backup() {
|
||||
return $this->can_backup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function current_user_can( $describer, $media_id = null ) {
|
||||
return $this->user_can( null, $describer, $media_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|\WP_User $user_id A user ID or \WP_User object. Fallback to the current user ID.
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function user_can( $user_id, $describer, $media_id = null ) {
|
||||
$current_user_id = get_current_user_id();
|
||||
|
||||
if ( ! $user_id ) {
|
||||
$user = $current_user_id;
|
||||
$user_id = $current_user_id;
|
||||
} elseif ( $user_id instanceof \WP_User ) {
|
||||
$user = $user_id;
|
||||
$user_id = (int) $user->ID;
|
||||
} elseif ( is_numeric( $user_id ) ) {
|
||||
$user = (int) $user_id;
|
||||
$user_id = $user;
|
||||
} else {
|
||||
$user_id = 0;
|
||||
}
|
||||
|
||||
if ( ! $user_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$media_id = $media_id ? (int) $media_id : null;
|
||||
$capacity = $this->get_capacity( $describer );
|
||||
|
||||
if ( $user_id === $current_user_id ) {
|
||||
$user_can = current_user_can( $capacity, $media_id );
|
||||
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.6.11
|
||||
* @since 1.9 Added the context name as parameter.
|
||||
*
|
||||
* @param bool $user_can Tell if the current user is allowed to operate Imagify in this context.
|
||||
* @param string $capacity The user capacity.
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @param string $context The context name.
|
||||
*/
|
||||
$user_can = (bool) apply_filters( 'imagify_current_user_can', $user_can, $capacity, $describer, $media_id, $this->get_name() );
|
||||
} else {
|
||||
$user_can = user_can( $user, $capacity, $media_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the given user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $user_can Tell if the given user is allowed to operate Imagify in this context.
|
||||
* @param int $user_id The user ID.
|
||||
* @param string $capacity The user capacity.
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @param string $context The context name.
|
||||
*/
|
||||
return (bool) apply_filters( 'imagify_user_can', $user_can, $user_id, $capacity, $describer, $media_id, $this->get_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a user capacity used to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $capacity The user capacity.
|
||||
* @param string $describer Capacity describer. Possible values are like 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize'.
|
||||
* @return string
|
||||
*/
|
||||
protected function filter_capacity( $capacity, $describer ) {
|
||||
/**
|
||||
* Filter a user capacity used to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.0
|
||||
* @since 1.6.5 Added $force_mono parameter.
|
||||
* @since 1.6.11 Replaced $force_mono by $describer.
|
||||
* @since 1.9 Added the context name as parameter.
|
||||
*
|
||||
* @param string $capacity The user capacity.
|
||||
* @param string $describer Capacity describer. Possible values are like 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize'.
|
||||
* @param string $context The context name.
|
||||
*/
|
||||
return (string) apply_filters( 'imagify_capacity', $capacity, $describer, $this->get_name() );
|
||||
}
|
||||
}
|
||||
141
wp/plugins/imagify/classes/Context/ContextInterface.php
Normal file
141
wp/plugins/imagify/classes/Context/ContextInterface.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
namespace Imagify\Context;
|
||||
|
||||
/**
|
||||
* Interface to use for contexts.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface ContextInterface {
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance();
|
||||
|
||||
/**
|
||||
* Get the context "short name".
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name();
|
||||
|
||||
/**
|
||||
* Tell if the context is network-wide.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_network_wide();
|
||||
|
||||
/**
|
||||
* Get the type of files this context allows.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see imagify_get_mime_types()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string Possible values are:
|
||||
* - 'all' to allow all types.
|
||||
* - 'image' to allow only images.
|
||||
* - 'not-image' to allow only pdf files.
|
||||
*/
|
||||
public function get_allowed_mime_types();
|
||||
|
||||
/**
|
||||
* Get the thumbnail sizes for this context, except the full size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* Data for the currently registered thumbnail sizes.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* }
|
||||
*/
|
||||
public function get_thumbnail_sizes();
|
||||
|
||||
/**
|
||||
* Get images max width for this context. This is used when resizing.
|
||||
* 0 means to not resize.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_resizing_threshold();
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed resize in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_resize();
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed to backup in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_backup();
|
||||
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function current_user_can( $describer, $media_id = null );
|
||||
|
||||
/**
|
||||
* Tell if a user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $user_id A user ID.
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function user_can( $user_id, $describer, $media_id = null );
|
||||
|
||||
/**
|
||||
* Get user capacity to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9 The describer 'auto-optimize' is not used anymore.
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. Possible values are like 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize'.
|
||||
* @return string
|
||||
*/
|
||||
public function get_capacity( $describer );
|
||||
}
|
||||
102
wp/plugins/imagify/classes/Context/CustomFolders.php
Normal file
102
wp/plugins/imagify/classes/Context/CustomFolders.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
namespace Imagify\Context;
|
||||
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Context class used for the custom folders.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class CustomFolders extends AbstractContext {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Context "short name".
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $context = 'custom-folders';
|
||||
|
||||
/**
|
||||
* The thumbnail sizes for this context, except the full size.
|
||||
*
|
||||
* @var array {
|
||||
* Data for the currently registered thumbnail sizes.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* }
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $thumbnail_sizes = [];
|
||||
|
||||
/**
|
||||
* Get images max width for this context. This is used when resizing.
|
||||
* 0 means to not resize.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_resizing_threshold() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed to backup in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_backup() {
|
||||
if ( isset( $this->can_backup ) ) {
|
||||
return $this->can_backup;
|
||||
}
|
||||
|
||||
$this->can_backup = get_imagify_option( 'backup' );
|
||||
|
||||
return $this->can_backup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user capacity to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. Possible values are like 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize'.
|
||||
* @return string
|
||||
*/
|
||||
public function get_capacity( $describer ) {
|
||||
switch ( $describer ) {
|
||||
case 'manage':
|
||||
$capacity = imagify_is_active_for_network() ? 'manage_network_options' : 'manage_options';
|
||||
break;
|
||||
|
||||
case 'bulk-optimize':
|
||||
case 'optimize':
|
||||
case 'restore':
|
||||
case 'manual-optimize':
|
||||
case 'manual-restore':
|
||||
case 'auto-optimize':
|
||||
$capacity = is_multisite() ? 'manage_network_options' : 'manage_options';
|
||||
break;
|
||||
|
||||
default:
|
||||
$capacity = $describer;
|
||||
}
|
||||
|
||||
return $this->filter_capacity( $capacity, $describer );
|
||||
}
|
||||
}
|
||||
153
wp/plugins/imagify/classes/Context/Noop.php
Normal file
153
wp/plugins/imagify/classes/Context/Noop.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
namespace Imagify\Context;
|
||||
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Fallback class for contexts.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Noop implements ContextInterface {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Get the context "short name".
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'noop';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the context is network-wide.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_network_wide() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of files this context allows.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see imagify_get_mime_types()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string Possible values are:
|
||||
* - 'all' to allow all types.
|
||||
* - 'image' to allow only images.
|
||||
* - 'not-image' to allow only pdf files.
|
||||
*/
|
||||
public function get_allowed_mime_types() {
|
||||
return 'all';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thumbnail sizes for this context, except the full size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* Data for the currently registered thumbnail sizes.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* }
|
||||
*/
|
||||
public function get_thumbnail_sizes() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get images max width for this context. This is used when resizing.
|
||||
* 0 means to not resize.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_resizing_threshold() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed resize in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_resize() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed to backup in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_backup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function current_user_can( $describer, $media_id = null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $user_id A user ID.
|
||||
* @param string $describer Capacity describer. See $this->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @param int $media_id A media ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function user_can( $user_id, $describer, $media_id = null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user capacity to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. Possible values are like 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize'.
|
||||
* @return string
|
||||
*/
|
||||
public function get_capacity( $describer ) {
|
||||
return 'noop';
|
||||
}
|
||||
}
|
||||
146
wp/plugins/imagify/classes/Context/WP.php
Normal file
146
wp/plugins/imagify/classes/Context/WP.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
namespace Imagify\Context;
|
||||
|
||||
/**
|
||||
* Context class used for the WP media library.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class WP extends AbstractContext {
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Context "short name".
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $context = 'wp';
|
||||
|
||||
/**
|
||||
* Images max width for this context. This is used when resizing.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.9.8
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $resizing_threshold;
|
||||
|
||||
/**
|
||||
* Get the thumbnail sizes for this context, except the full size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* Data for the currently registered thumbnail sizes.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* }
|
||||
*/
|
||||
public function get_thumbnail_sizes() {
|
||||
if ( isset( $this->thumbnail_sizes ) ) {
|
||||
return $this->thumbnail_sizes;
|
||||
}
|
||||
|
||||
$this->thumbnail_sizes = get_imagify_thumbnail_sizes();
|
||||
|
||||
return $this->thumbnail_sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get images max width for this context. This is used when resizing.
|
||||
* 0 means to not resize.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_resizing_threshold() {
|
||||
if ( isset( $this->resizing_threshold ) ) {
|
||||
return $this->resizing_threshold;
|
||||
}
|
||||
|
||||
if ( ! get_imagify_option( 'resize_larger' ) ) {
|
||||
$this->resizing_threshold = 0;
|
||||
} else {
|
||||
$this->resizing_threshold = max( 0, get_imagify_option( 'resize_larger_w' ) );
|
||||
}
|
||||
|
||||
return $this->resizing_threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the optimization process is allowed to backup in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_backup() {
|
||||
if ( isset( $this->can_backup ) ) {
|
||||
return $this->can_backup;
|
||||
}
|
||||
|
||||
$this->can_backup = get_imagify_option( 'backup' );
|
||||
|
||||
return $this->can_backup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user capacity to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. Possible values are like 'manage', 'bulk-optimize', 'manual-optimize', 'auto-optimize'.
|
||||
* @return string
|
||||
*/
|
||||
public function get_capacity( $describer ) {
|
||||
static $edit_attachment_cap;
|
||||
|
||||
switch ( $describer ) {
|
||||
case 'manage':
|
||||
$capacity = imagify_is_active_for_network() ? 'manage_network_options' : 'manage_options';
|
||||
break;
|
||||
|
||||
case 'bulk-optimize':
|
||||
$capacity = 'manage_options';
|
||||
break;
|
||||
|
||||
case 'optimize':
|
||||
case 'restore':
|
||||
// This is a generic capacity: don't use it unless you have no other choices!
|
||||
if ( ! isset( $edit_attachment_cap ) ) {
|
||||
$edit_attachment_cap = get_post_type_object( 'attachment' );
|
||||
$edit_attachment_cap = $edit_attachment_cap ? $edit_attachment_cap->cap->edit_posts : 'edit_posts';
|
||||
}
|
||||
|
||||
$capacity = $edit_attachment_cap;
|
||||
break;
|
||||
|
||||
case 'manual-optimize':
|
||||
case 'manual-restore':
|
||||
// Must be used with an Attachment ID.
|
||||
$capacity = 'edit_post';
|
||||
break;
|
||||
|
||||
case 'auto-optimize':
|
||||
$capacity = 'upload_files';
|
||||
break;
|
||||
|
||||
default:
|
||||
$capacity = $describer;
|
||||
}
|
||||
|
||||
return $this->filter_capacity( $capacity, $describer );
|
||||
}
|
||||
}
|
||||
84
wp/plugins/imagify/classes/DB/DBInterface.php
Normal file
84
wp/plugins/imagify/classes/DB/DBInterface.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace Imagify\DB;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Interface to interact with the database.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface DBInterface {
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return DBInterface Main instance.
|
||||
*/
|
||||
public static function get_instance();
|
||||
|
||||
/**
|
||||
* Retrieve a row by the primary key.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $row_id A primary key.
|
||||
* @return array
|
||||
*/
|
||||
public function get( $row_id );
|
||||
|
||||
/**
|
||||
* Update a row.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $row_id A primary key.
|
||||
* @param array $data New data.
|
||||
* @param string $where A column name.
|
||||
* @return bool
|
||||
*/
|
||||
public function update( $row_id, $data = [], $where = '' );
|
||||
|
||||
/**
|
||||
* Delete a row identified by the primary key.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $row_id A primary key.
|
||||
* @return bool
|
||||
*/
|
||||
public function delete( $row_id );
|
||||
|
||||
/**
|
||||
* Default column values.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_column_defaults();
|
||||
|
||||
/**
|
||||
* Get the primary column name.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_primary_key();
|
||||
}
|
||||
371
wp/plugins/imagify/classes/Imagifybeat/Actions.php
Normal file
371
wp/plugins/imagify/classes/Imagifybeat/Actions.php
Normal file
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
namespace Imagify\Imagifybeat;
|
||||
|
||||
use Imagify_Requirements;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
use Imagify\Bulk\Bulk;
|
||||
|
||||
/**
|
||||
* Imagifybeat actions.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
class Actions {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* The list of action IDs.
|
||||
* Keys are related to method names, values are Imagifybeat IDs.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
private $imagifybeat_ids = [
|
||||
'requirements' => 'imagify_requirements',
|
||||
'bulk_optimization_stats' => 'imagify_bulk_optimization_stats',
|
||||
'bulk_optimization_status' => 'imagify_bulk_optimization_status',
|
||||
'options_optimization_status' => 'imagify_options_optimization_status',
|
||||
'library_optimization_status' => 'imagify_library_optimization_status',
|
||||
'custom_folders_optimization_status' => 'imagify_custom_folders_optimization_status',
|
||||
];
|
||||
|
||||
/**
|
||||
* Class init: launch hooks.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*/
|
||||
public function init() {
|
||||
foreach ( $this->imagifybeat_ids as $action => $imagifybeat_id ) {
|
||||
add_filter( 'imagifybeat_received', [ $this, 'add_' . $action . '_to_response' ], 10, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** IMAGIFYBEAT CALLBACKS =================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Add requirements to Imagifybeat data.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @return array
|
||||
*/
|
||||
public function add_requirements_to_response( $response, $data ) {
|
||||
$imagifybeat_id = $this->get_imagifybeat_id_for_callback( __FUNCTION__ );
|
||||
|
||||
if ( ! $imagifybeat_id || empty( $data[ $imagifybeat_id ] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response[ $imagifybeat_id ] = [
|
||||
'curl_missing' => ! Imagify_Requirements::supports_curl(),
|
||||
'editor_missing' => ! Imagify_Requirements::supports_image_editor(),
|
||||
'external_http_blocked' => Imagify_Requirements::is_imagify_blocked(),
|
||||
'api_down' => ! Imagify_Requirements::is_api_up(),
|
||||
'key_is_valid' => Imagify_Requirements::is_api_key_valid(),
|
||||
'is_over_quota' => Imagify_Requirements::is_over_quota(),
|
||||
];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bulk stats to Imagifybeat data.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @return array
|
||||
*/
|
||||
public function add_bulk_optimization_stats_to_response( $response, $data ) {
|
||||
$imagifybeat_id = $this->get_imagifybeat_id_for_callback( __FUNCTION__ );
|
||||
|
||||
if ( ! $imagifybeat_id || empty( $data[ $imagifybeat_id ] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$folder_types = array_flip( array_filter( $data[ $imagifybeat_id ] ) );
|
||||
|
||||
$response[ $imagifybeat_id ] = imagify_get_bulk_stats(
|
||||
$folder_types,
|
||||
[
|
||||
'fullset' => true,
|
||||
]
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for media where status has changed, compared to what Imagifybeat sends.
|
||||
* This is used in the bulk optimization page.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @return array
|
||||
*/
|
||||
public function add_bulk_optimization_status_to_response( $response, $data ) {
|
||||
$imagifybeat_id = $this->get_imagifybeat_id_for_callback( __FUNCTION__ );
|
||||
|
||||
if ( ! $imagifybeat_id || empty( $data[ $imagifybeat_id ] ) || ! is_array( $data[ $imagifybeat_id ] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ( ! isset( $data[ $imagifybeat_id ] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$bulk = Bulk::get_instance();
|
||||
$groups_data = [];
|
||||
$types = [];
|
||||
$total = 0;
|
||||
$remaining = 0;
|
||||
$percentage = 0;
|
||||
|
||||
foreach ( $data[ $imagifybeat_id ] as $group ) {
|
||||
$types[ $group['groupID'] . '|' . $group['context'] ] = true;
|
||||
|
||||
$transient = get_transient( "imagify_{$group['context']}_optimize_running" );
|
||||
|
||||
if ( false !== $transient ) {
|
||||
$total += $transient['total'];
|
||||
$remaining += $transient['remaining'];
|
||||
}
|
||||
|
||||
$groups_data[ $group['context'] ] = $bulk->get_bulk_instance( $group['context'] )->get_context_data();
|
||||
}
|
||||
|
||||
if ( 0 !== $total ) {
|
||||
$percentage = ( $total - $remaining ) / $total * 100;
|
||||
}
|
||||
|
||||
$response[ $imagifybeat_id ] = [
|
||||
'groups_data' => $groups_data,
|
||||
'remaining' => $remaining,
|
||||
'percentage' => round( $percentage ),
|
||||
'result' => get_transient( 'imagify_bulk_optimization_result' ),
|
||||
];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for media where status has changed, compared to what Imagifybeat sends.
|
||||
* This is used in the settings page.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @return array
|
||||
*/
|
||||
public function add_options_optimization_status_to_response( $response, $data ) {
|
||||
$imagifybeat_id = $this->get_imagifybeat_id_for_callback( __FUNCTION__ );
|
||||
|
||||
if ( ! $imagifybeat_id || empty( $data[ $imagifybeat_id ] ) || ! is_array( $data[ $imagifybeat_id ] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$remaining = 0;
|
||||
$total = get_transient( 'imagify_missing_webp_total' );
|
||||
|
||||
if ( false === $total ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$bulk = Bulk::get_instance();
|
||||
|
||||
foreach ( $data[ $imagifybeat_id ] as $context ) {
|
||||
$media = $bulk->get_bulk_instance( $context )->get_optimized_media_ids_without_webp();
|
||||
$remaining += count( $media['ids'] );
|
||||
}
|
||||
|
||||
$response[ $imagifybeat_id ] = [
|
||||
'remaining' => $remaining,
|
||||
'total' => (int) $total,
|
||||
];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for media where status has changed, compared to what Imagifybeat sends.
|
||||
* This is used in the WP Media Library.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @return array
|
||||
*/
|
||||
public function add_library_optimization_status_to_response( $response, $data ) {
|
||||
$imagifybeat_id = $this->get_imagifybeat_id_for_callback( __FUNCTION__ );
|
||||
|
||||
if ( ! $imagifybeat_id || empty( $data[ $imagifybeat_id ] ) || ! is_array( $data[ $imagifybeat_id ] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response[ $imagifybeat_id ] = $this->get_modified_optimization_statuses( $data[ $imagifybeat_id ] );
|
||||
|
||||
if ( ! $response[ $imagifybeat_id ] ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Sanitize received data and grab some other info.
|
||||
foreach ( $response[ $imagifybeat_id ] as $context_id => $media_atts ) {
|
||||
$process = imagify_get_optimization_process( $media_atts['media_id'], $media_atts['context'] );
|
||||
|
||||
$response[ $imagifybeat_id ][ $context_id ] = get_imagify_media_column_content( $process, false );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for media where status has changed, compared to what Imagifybeat sends.
|
||||
* This is used in the custom folders list (the "Other Media" page).
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @return array
|
||||
*/
|
||||
public function add_custom_folders_optimization_status_to_response( $response, $data ) {
|
||||
$imagifybeat_id = $this->get_imagifybeat_id_for_callback( __FUNCTION__ );
|
||||
|
||||
if ( ! $imagifybeat_id || empty( $data[ $imagifybeat_id ] ) || ! is_array( $data[ $imagifybeat_id ] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response[ $imagifybeat_id ] = $this->get_modified_optimization_statuses( $data[ $imagifybeat_id ] );
|
||||
|
||||
if ( ! $response[ $imagifybeat_id ] ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$admin_ajax_post = \Imagify_Admin_Ajax_Post::get_instance();
|
||||
$list_table = new \Imagify_Files_List_Table( [
|
||||
'screen' => 'imagify-files',
|
||||
] );
|
||||
|
||||
// Sanitize received data and grab some other info.
|
||||
foreach ( $response[ $imagifybeat_id ] as $context_id => $media_atts ) {
|
||||
$process = imagify_get_optimization_process( $media_atts['media_id'], $media_atts['context'] );
|
||||
|
||||
$response[ $imagifybeat_id ][ $context_id ] = $admin_ajax_post->get_media_columns( $process, $list_table );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Look for media where status has changed, compared to what Imagifybeat sends.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $data The data received.
|
||||
* @return array
|
||||
*/
|
||||
private function get_modified_optimization_statuses( $data ) {
|
||||
if ( ! $data ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$output = [];
|
||||
|
||||
// Sanitize received data and grab some other info.
|
||||
foreach ( $data as $context => $media_statuses ) {
|
||||
if ( ! $context || ! $media_statuses || ! is_array( $media_statuses ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize the IDs: IDs come as strings, prefixed with an undescore character (to prevent JavaScript from screwing everything).
|
||||
$media_ids = array_keys( $media_statuses );
|
||||
$media_ids = array_map( function( $media_id ) {
|
||||
return (int) substr( $media_id, 1 );
|
||||
}, $media_ids );
|
||||
$media_ids = array_filter( $media_ids );
|
||||
|
||||
if ( ! $media_ids ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize the context.
|
||||
$context_instance = imagify_get_context( $context );
|
||||
$context = $context_instance->get_name();
|
||||
$process_class_name = imagify_get_optimization_process_class_name( $context );
|
||||
$transient_name = sprintf( $process_class_name::LOCK_NAME, $context, '%' );
|
||||
$is_network_wide = $context_instance->is_network_wide();
|
||||
|
||||
\Imagify_DB::cache_process_locks( $context, $media_ids );
|
||||
|
||||
// Now that everything is cached for this context, we can get the transients without hitting the DB.
|
||||
foreach ( $media_ids as $id ) {
|
||||
$is_locked = (bool) $media_statuses[ '_' . $id ];
|
||||
$option_name = str_replace( '%', $id, $transient_name );
|
||||
|
||||
if ( $is_network_wide ) {
|
||||
$in_db = (bool) get_site_transient( $option_name );
|
||||
} else {
|
||||
$in_db = (bool) get_transient( $option_name );
|
||||
}
|
||||
|
||||
if ( $is_locked === $in_db ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output[ $context . '_' . $id ] = [
|
||||
'media_id' => $id,
|
||||
'context' => $context,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an Imagifybeat ID, given an action.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param string $action An action corresponding to the ID we want.
|
||||
* @return string|bool The ID. False on failure.
|
||||
*/
|
||||
public function get_imagifybeat_id( $action ) {
|
||||
if ( ! empty( $this->imagifybeat_ids[ $action ] ) ) {
|
||||
return $this->imagifybeat_ids[ $action ];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an Imagifybeat ID, given a callback name.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param string $callback A method’s name.
|
||||
* @return string|bool The ID. False on failure.
|
||||
*/
|
||||
private function get_imagifybeat_id_for_callback( $callback ) {
|
||||
if ( preg_match( '@^add_(?<id>.+)_to_response$@', $callback, $matches ) ) {
|
||||
return $this->get_imagifybeat_id( $matches['id'] );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
172
wp/plugins/imagify/classes/Imagifybeat/Core.php
Normal file
172
wp/plugins/imagify/classes/Imagifybeat/Core.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
namespace Imagify\Imagifybeat;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Imagifybeat core.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Core {
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class init: launch hooks.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'wp_ajax_imagifybeat', [ $this, 'core_handler' ], 1 );
|
||||
add_filter( 'imagifybeat_refresh_nonces', [ $this, 'refresh_imagifybeat_nonces' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax handler for the Imagifybeat API.
|
||||
*
|
||||
* Runs when the user is logged in.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function core_handler() {
|
||||
if ( empty( $_POST['_nonce'] ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$response = [];
|
||||
$nonce_state = wp_verify_nonce( wp_unslash( $_POST['_nonce'] ), 'imagifybeat-nonce' );
|
||||
|
||||
// Screen_id is the same as $current_screen->id and the JS global 'pagenow'.
|
||||
if ( ! empty( $_POST['screen_id'] ) ) {
|
||||
$screen_id = sanitize_key( $_POST['screen_id'] );
|
||||
} else {
|
||||
$screen_id = 'front';
|
||||
}
|
||||
|
||||
if ( ! empty( $_POST['data'] ) ) {
|
||||
$data = wp_unslash( (array) $_POST['data'] );
|
||||
}
|
||||
|
||||
if ( 1 !== $nonce_state ) {
|
||||
/**
|
||||
* Filters the nonces to send.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @param string $screen_id The screen id.
|
||||
*/
|
||||
$response = apply_filters( 'imagifybeat_refresh_nonces', $response, $data, $screen_id );
|
||||
|
||||
if ( false === $nonce_state ) {
|
||||
// User is logged in but nonces have expired.
|
||||
$response['nonces_expired'] = true;
|
||||
wp_send_json( $response );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $data ) ) {
|
||||
/**
|
||||
* Filters the Imagifybeat response received.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param array $data The $_POST data sent.
|
||||
* @param string $screen_id The screen id.
|
||||
*/
|
||||
$response = apply_filters( 'imagifybeat_received', $response, $data, $screen_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Imagifybeat response sent.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param string $screen_id The screen id.
|
||||
*/
|
||||
$response = apply_filters( 'imagifybeat_send', $response, $screen_id );
|
||||
|
||||
/**
|
||||
* Fires when Imagifybeat ticks in logged-in environments.
|
||||
*
|
||||
* Allows the transport to be easily replaced with long-polling.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @param string $screen_id The screen id.
|
||||
*/
|
||||
do_action( 'imagifybeat_tick', $response, $screen_id );
|
||||
|
||||
// Send the current time according to the server.
|
||||
$response['server_time'] = time();
|
||||
|
||||
wp_send_json( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the latest Imagifybeat nonce to the Imagifybeat response.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $response The Imagifybeat response.
|
||||
* @return array The Imagifybeat response.
|
||||
*/
|
||||
public function refresh_imagifybeat_nonces( $response ) {
|
||||
// Refresh the Imagifybeat nonce.
|
||||
$response['imagifybeat_nonce'] = wp_create_nonce( 'imagifybeat-nonce' );
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Imagifybeat settings.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_settings() {
|
||||
global $pagenow;
|
||||
|
||||
$settings = [];
|
||||
|
||||
if ( ! is_admin() ) {
|
||||
$settings['ajaxurl'] = admin_url( 'admin-ajax.php', 'relative' );
|
||||
}
|
||||
|
||||
if ( is_user_logged_in() ) {
|
||||
$settings['nonce'] = wp_create_nonce( 'imagifybeat-nonce' );
|
||||
}
|
||||
|
||||
if ( 'customize.php' === $pagenow ) {
|
||||
$settings['screenId'] = 'customize';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Imagifybeat settings.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $settings Imagifybeat settings array.
|
||||
*/
|
||||
return (array) apply_filters( 'imagifybeat_settings', $settings );
|
||||
}
|
||||
}
|
||||
373
wp/plugins/imagify/classes/Job/MediaOptimization.php
Normal file
373
wp/plugins/imagify/classes/Job/MediaOptimization.php
Normal file
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
namespace Imagify\Job;
|
||||
|
||||
use Imagify\Optimization\Process\ProcessInterface;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Job class for media optimization.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class MediaOptimization extends \Imagify_Abstract_Background_Process {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Background process: the action to perform.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $action = 'optimize_media';
|
||||
|
||||
/**
|
||||
* The optimization process instance.
|
||||
*
|
||||
* @var ProcessInterface
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $optimization_process;
|
||||
|
||||
/**
|
||||
* Handle job logic.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $item {
|
||||
* The data to use for this job.
|
||||
*
|
||||
* @type string $task The task to perform. Optional: set it only if you know what you’re doing.
|
||||
* @type int $id The media ID.
|
||||
* @type array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
|
||||
* @type array $sizes_done Used internally to store the media sizes that have been processed.
|
||||
* @type int $optimization_level The optimization level. Null for the level set in the settings.
|
||||
* @type string $process_class The name of the process class. The class must implement ProcessInterface.
|
||||
* @type array $data {
|
||||
* Can be used to pass any data. Keep it short, don’t forget it will be stored in the database.
|
||||
* It should contain the following though:
|
||||
*
|
||||
* @type string $hook_suffix Suffix used to trigger hooks before and after optimization. Should be always provided.
|
||||
* @type bool $delete_backup True to delete the backup file after the optimization process. This is used when a temporary backup of the original file has been created, but backup option is disabled. Default is false.
|
||||
* }
|
||||
* }
|
||||
* @return array|bool The modified item to put back in the queue. False to remove the item from the queue.
|
||||
*/
|
||||
protected function task( $item ) {
|
||||
$item = $this->validate_item( $item );
|
||||
|
||||
if ( ! $item ) {
|
||||
// Not valid.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Launch the task.
|
||||
$method = 'task_' . $item['task'];
|
||||
$item = $this->$method( $item );
|
||||
|
||||
if ( $item['task'] ) {
|
||||
// Next task.
|
||||
return $item;
|
||||
}
|
||||
|
||||
// End of the queue.
|
||||
$this->optimization_process->unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger hooks before the optimization job.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $item See $this->task().
|
||||
* @return array The item.
|
||||
*/
|
||||
private function task_before( $item ) {
|
||||
if ( ! empty( $item['error'] ) && is_wp_error( $item['error'] ) ) {
|
||||
$wp_error = $item['error'];
|
||||
} else {
|
||||
$wp_error = new WP_Error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires before optimizing a media.
|
||||
* Any number of files can be optimized, not necessarily all of the media files.
|
||||
* If you want to return a WP_Error, use the existing $wp_error object.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array|WP_Error $data New data to pass along the item. A WP_Error object to stop the process.
|
||||
* @param WP_Error $wp_error Add errors to this object and return it to stop the process.
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed. See $this->task().
|
||||
*/
|
||||
$data = apply_filters( 'imagify_before_optimize', [], $wp_error, $this->optimization_process, $item );
|
||||
|
||||
if ( is_wp_error( $data ) ) {
|
||||
$wp_error = $data;
|
||||
} elseif ( $data && is_array( $data ) ) {
|
||||
$item['data'] = array_merge( $data, $item['data'] );
|
||||
}
|
||||
|
||||
if ( $wp_error->get_error_codes() ) {
|
||||
// Don't optimize if there is an error.
|
||||
$item['task'] = 'after';
|
||||
$item['error'] = $wp_error;
|
||||
return $item;
|
||||
}
|
||||
|
||||
if ( empty( $item['data']['hook_suffix'] ) ) {
|
||||
// Next task.
|
||||
$item['task'] = 'optimize';
|
||||
return $item;
|
||||
}
|
||||
|
||||
$hook_suffix = $item['data']['hook_suffix'];
|
||||
|
||||
/**
|
||||
* Fires before optimizing a media.
|
||||
* Any number of files can be optimized, not necessarily all of the media files.
|
||||
* If you want to return a WP_Error, use the existing $wp_error object.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array|WP_Error $data New data to pass along the item. A WP_Error object to stop the process.
|
||||
* @param WP_Error $wp_error Add errors to this object and return it to stop the process.
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed. See $this->task().
|
||||
*/
|
||||
$data = apply_filters( "imagify_before_{$hook_suffix}", [], $wp_error, $this->optimization_process, $item );
|
||||
|
||||
if ( is_wp_error( $data ) ) {
|
||||
$wp_error = $data;
|
||||
} elseif ( $data && is_array( $data ) ) {
|
||||
$item['data'] = array_merge( $data, $item['data'] );
|
||||
}
|
||||
|
||||
if ( $wp_error->get_error_codes() ) {
|
||||
// Don't optimize if there is an error.
|
||||
$item['task'] = 'after';
|
||||
$item['error'] = $wp_error;
|
||||
return $item;
|
||||
}
|
||||
|
||||
// Next task.
|
||||
$item['task'] = 'optimize';
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the optimization job.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $item See $this->task().
|
||||
* @return array The item.
|
||||
*/
|
||||
private function task_optimize( $item ) {
|
||||
// Determine which size we're going to optimize. The 'full' size must be optimized before any other.
|
||||
if ( in_array( 'full', $item['sizes'], true ) ) {
|
||||
$current_size = 'full';
|
||||
$item['sizes'] = array_diff( $item['sizes'], [ 'full' ] );
|
||||
} else {
|
||||
$current_size = array_shift( $item['sizes'] );
|
||||
}
|
||||
|
||||
$item['sizes_done'][] = $current_size;
|
||||
|
||||
// Optimize the file.
|
||||
$data = $this->optimization_process->optimize_size( $current_size, $item['optimization_level'] );
|
||||
|
||||
if ( 'full' === $current_size ) {
|
||||
if ( is_wp_error( $data ) ) {
|
||||
// Don't go further if there is an error.
|
||||
$item['sizes'] = [];
|
||||
$item['error'] = $data;
|
||||
|
||||
} elseif ( 'already_optimized' === $data['status'] ) {
|
||||
// Status is "already_optimized", try to create WebP versions only.
|
||||
$item['sizes'] = array_filter( $item['sizes'], [ $this->optimization_process, 'is_size_webp' ] );
|
||||
|
||||
} elseif ( 'success' !== $data['status'] ) {
|
||||
// Don't go further if the full size has not the "success" status.
|
||||
$item['sizes'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $item['sizes'] ) {
|
||||
// No more files to optimize.
|
||||
$item['task'] = 'after';
|
||||
}
|
||||
|
||||
// Optimize the next file or go to the next task.
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger hooks after the optimization job.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $item See $this->task().
|
||||
* @return array The item.
|
||||
*/
|
||||
private function task_after( $item ) {
|
||||
if ( ! empty( $item['data']['delete_backup'] ) ) {
|
||||
$this->optimization_process->delete_backup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after optimizing a media.
|
||||
* Any number of files can be optimized, not necessarily all of the media files.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed. See $this->task().
|
||||
*/
|
||||
do_action( 'imagify_after_optimize', $this->optimization_process, $item );
|
||||
|
||||
if ( empty( $item['data']['hook_suffix'] ) ) {
|
||||
$item['task'] = false;
|
||||
return $item;
|
||||
}
|
||||
|
||||
$hook_suffix = $item['data']['hook_suffix'];
|
||||
|
||||
/**
|
||||
* Fires after optimizing a media.
|
||||
* Any number of files can be optimized, not necessarily all of the media files.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed. See $this->task().
|
||||
*/
|
||||
do_action( "imagify_after_{$hook_suffix}", $this->optimization_process, $item );
|
||||
|
||||
$item['task'] = false;
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an item.
|
||||
* On success, the property $this->optimization_process is set.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $item See $this->task().
|
||||
* @return array|bool The item. False if invalid.
|
||||
*/
|
||||
protected function validate_item( $item ) {
|
||||
$this->optimization_process = null;
|
||||
|
||||
$default = [
|
||||
'task' => '',
|
||||
'id' => 0,
|
||||
'sizes' => [],
|
||||
'sizes_done' => [],
|
||||
'optimization_level' => null,
|
||||
'process_class' => '',
|
||||
'data' => [],
|
||||
];
|
||||
|
||||
$item = imagify_merge_intersect( $item, $default );
|
||||
|
||||
// Validate some types first.
|
||||
if ( ! is_array( $item['sizes'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isset( $item['error'] ) && ! is_wp_error( $item['error'] ) ) {
|
||||
unset( $item['error'] );
|
||||
}
|
||||
|
||||
if ( isset( $item['data']['hook_suffix'] ) && ! is_string( $item['data']['hook_suffix'] ) ) {
|
||||
unset( $item['data']['hook_suffix'] );
|
||||
}
|
||||
|
||||
$item['id'] = (int) $item['id'];
|
||||
$item['optimization_level'] = $this->sanitize_optimization_level( $item['optimization_level'] );
|
||||
|
||||
if ( ! $item['id'] || ! $item['process_class'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process.
|
||||
$item['process_class'] = '\\' . ltrim( $item['process_class'], '\\' );
|
||||
|
||||
if ( ! class_exists( $item['process_class'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$process = $this->get_process( $item );
|
||||
|
||||
if ( ! $process ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->optimization_process = $process;
|
||||
|
||||
// Validate the current task.
|
||||
if ( empty( $item['task'] ) ) {
|
||||
$item['task'] = 'before';
|
||||
}
|
||||
|
||||
if ( ! $item['task'] || ! method_exists( $this, 'task_' . $item['task'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $item['sizes'] && 'after' !== $item['task'] ) {
|
||||
// Allow to have no sizes, but only after the optimize task is complete.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isset( $item['sizes_done'] ) || ! is_array( $item['sizes_done'] ) ) {
|
||||
$item['sizes_done'] = [];
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the process instance.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $item See $this->task().
|
||||
* @return ProcessInterface|bool The instance object on success. False on failure.
|
||||
*/
|
||||
protected function get_process( $item ) {
|
||||
$process_class = $item['process_class'];
|
||||
$process = new $process_class( $item['id'] );
|
||||
|
||||
if ( ! $process instanceof ProcessInterface || ! $process->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $process;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize and validate an optimization level.
|
||||
* If not provided (false, null), fallback to the level set in the plugin's settings.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param mixed $optimization_level The optimization level.
|
||||
* @return int
|
||||
*/
|
||||
protected function sanitize_optimization_level( $optimization_level ) {
|
||||
if ( ! is_numeric( $optimization_level ) ) {
|
||||
if ( get_imagify_option( 'lossless' ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return get_imagify_option( 'optimization_level' );
|
||||
}
|
||||
|
||||
return \Imagify_Options::get_instance()->sanitize_and_validate( 'optimization_level', $optimization_level );
|
||||
}
|
||||
}
|
||||
552
wp/plugins/imagify/classes/Media/AbstractMedia.php
Normal file
552
wp/plugins/imagify/classes/Media/AbstractMedia.php
Normal file
@@ -0,0 +1,552 @@
|
||||
<?php
|
||||
namespace Imagify\Media;
|
||||
|
||||
use Imagify\CDN\PushCDNInterface;
|
||||
use Imagify\Context\ContextInterface;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Abstract used for "media groups" (aka attachments).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class AbstractMedia implements MediaInterface {
|
||||
|
||||
/**
|
||||
* The media ID.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Context (where the media "comes from").
|
||||
*
|
||||
* @var ContextInterface
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* CDN to use.
|
||||
*
|
||||
* @var PushCDNInterface
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $cdn;
|
||||
|
||||
/**
|
||||
* Tell if the media/context is network-wide.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_network_wide = false;
|
||||
|
||||
/**
|
||||
* Tell if the file is an image.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @see $this->is_image()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_image;
|
||||
|
||||
/**
|
||||
* Tell if the file is a pdf.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @see $this->is_pdf()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_pdf;
|
||||
|
||||
/**
|
||||
* Store the file mime type + file extension (if the file is supported).
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @see $this->get_file_type()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $file_type;
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var object Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $id The media ID.
|
||||
*/
|
||||
public function __construct( $id ) {
|
||||
$this->id = (int) $id;
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media ID.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
return $this->get_id() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media context name.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_context() {
|
||||
return $this->get_context_instance()->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media context instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return ContextInterface
|
||||
*/
|
||||
public function get_context_instance() {
|
||||
if ( $this->context ) {
|
||||
if ( is_string( $this->context ) ) {
|
||||
$this->context = imagify_get_context( $this->context );
|
||||
}
|
||||
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
$class_name = get_class( $this );
|
||||
$class_name = '\\' . trim( $class_name, '\\' );
|
||||
$class_name = str_replace( '\\Media\\', '\\Context\\', $class_name );
|
||||
$this->context = new $class_name();
|
||||
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CDN instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|PushCDNInterface A PushCDNInterface instance. False if no CDN is used.
|
||||
*/
|
||||
public function get_cdn() {
|
||||
if ( isset( $this->cdn ) ) {
|
||||
return $this->cdn;
|
||||
}
|
||||
|
||||
if ( ! $this->is_valid() ) {
|
||||
$this->cdn = false;
|
||||
return $this->cdn;
|
||||
}
|
||||
|
||||
$media_id = $this->get_id();
|
||||
$context = $this->get_context_instance();
|
||||
|
||||
/**
|
||||
* The CDN to use for this media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool|PushCDNInterface $cdn A PushCDNInterface instance. False if no CDN is used.
|
||||
* @param int $media_id The media ID.
|
||||
* @param ContextInterface $context The context object.
|
||||
*/
|
||||
$this->cdn = apply_filters( 'imagify_cdn', false, $media_id, $context );
|
||||
|
||||
if ( ! $this->cdn || ! $this->cdn instanceof PushCDNInterface ) {
|
||||
$this->cdn = false;
|
||||
return $this->cdn;
|
||||
}
|
||||
|
||||
return $this->cdn;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ORIGINAL FILE =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the original media's path if the file exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_original_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$original_path = $this->get_raw_original_path();
|
||||
|
||||
if ( ! $original_path || ! $this->filesystem->exists( $original_path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $original_path;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FULL SIZE FILE ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the path to the media’s full size file if the file exists.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_fullsize_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$original_path = $this->get_raw_fullsize_path();
|
||||
|
||||
if ( ! $original_path || ! $this->filesystem->exists( $original_path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $original_path;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the backup file path if the file exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_backup_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$backup_path = $this->get_raw_backup_path();
|
||||
|
||||
if ( ! $backup_path || ! $this->filesystem->exists( $backup_path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $backup_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the media has a backup of the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media has a backup.
|
||||
*/
|
||||
public function has_backup() {
|
||||
return (bool) $this->get_backup_path();
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MEDIA DATA ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the current media type is supported.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_supported() {
|
||||
return (bool) $this->get_mime_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media refers to an image, based on file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool Returns false in case it's an image but not in a supported format (bmp for example).
|
||||
*/
|
||||
public function is_image() {
|
||||
if ( isset( $this->is_image ) ) {
|
||||
return $this->is_image;
|
||||
}
|
||||
|
||||
$this->is_image = strpos( (string) $this->get_mime_type(), 'image/' ) === 0;
|
||||
|
||||
return $this->is_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media refers to a pdf, based on file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_pdf() {
|
||||
if ( isset( $this->is_pdf ) ) {
|
||||
return $this->is_pdf;
|
||||
}
|
||||
|
||||
$this->is_pdf = 'application/pdf' === $this->get_mime_type();
|
||||
|
||||
return $this->is_pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original file extension (if supported by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_extension() {
|
||||
return $this->get_file_type()->ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original file mime type (if supported by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_mime_type() {
|
||||
return $this->get_file_type()->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file mime type + file extension (if the file is supported by Imagify).
|
||||
* This test is ran against the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_allowed_mime_types() {
|
||||
return imagify_get_mime_types( $this->get_context_instance()->get_allowed_mime_types() );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the media is an image, update the dimensions in the database with the current file dimensions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True on success. False on failure.
|
||||
*/
|
||||
public function update_dimensions() {
|
||||
if ( ! $this->is_image() ) {
|
||||
// The media is not a supported image.
|
||||
return false;
|
||||
}
|
||||
|
||||
$dimensions = $this->filesystem->get_image_size( $this->get_raw_fullsize_path() );
|
||||
|
||||
if ( ! $dimensions ) {
|
||||
// Could not get the new dimensions.
|
||||
return false;
|
||||
}
|
||||
|
||||
$context = $this->get_context();
|
||||
|
||||
/**
|
||||
* Triggered before updating an image width and height into its metadata.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see Imagify_Filesystem->get_image_size()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $media_id The media ID.
|
||||
* @param array $dimensions {
|
||||
* An array with, among other data:
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* }
|
||||
*/
|
||||
do_action( "imagify_before_update_{$context}_media_data_dimensions", $this->get_id(), $dimensions );
|
||||
|
||||
$this->update_media_data_dimensions( $dimensions );
|
||||
|
||||
/**
|
||||
* Triggered after updating an image width and height into its metadata.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see Imagify_Filesystem->get_image_size()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $media_id The media ID.
|
||||
* @param array $dimensions {
|
||||
* An array with, among other data:
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* }
|
||||
*/
|
||||
do_action( "imagify_after_update_{$context}_media_data_dimensions", $this->get_id(), $dimensions );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the media data dimensions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $dimensions {
|
||||
* An array containing width and height.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* }
|
||||
*/
|
||||
abstract protected function update_media_data_dimensions( $dimensions );
|
||||
|
||||
/**
|
||||
* Get the file mime type + file extension (if the file is supported by Imagify).
|
||||
* This test is ran against the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @see wp_check_filetype()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function get_file_type() {
|
||||
if ( isset( $this->file_type ) ) {
|
||||
return $this->file_type;
|
||||
}
|
||||
|
||||
$this->file_type = (object) [
|
||||
'ext' => '',
|
||||
'type' => '',
|
||||
];
|
||||
|
||||
if ( ! $this->is_valid() ) {
|
||||
return $this->file_type;
|
||||
}
|
||||
|
||||
$path = $this->get_raw_fullsize_path();
|
||||
|
||||
if ( ! $path ) {
|
||||
return $this->file_type;
|
||||
}
|
||||
|
||||
$this->file_type = (object) wp_check_filetype( $path, $this->get_allowed_mime_types() );
|
||||
|
||||
return $this->file_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the result of $this->get_media_files().
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @see $this->get_media_files()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $files An array with the size names as keys ('full' is used for the full size file), and arrays of data as values.
|
||||
* @return array
|
||||
*/
|
||||
protected function filter_media_files( $files ) {
|
||||
/**
|
||||
* Filter the media files.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $files An array with the size names as keys ('full' is used for the full size file), and arrays of data as values.
|
||||
* @param MediaInterface $media This instance.
|
||||
*/
|
||||
return (array) apply_filters( 'imagify_media_files', $files, $this );
|
||||
}
|
||||
}
|
||||
358
wp/plugins/imagify/classes/Media/CustomFolders.php
Normal file
358
wp/plugins/imagify/classes/Media/CustomFolders.php
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
namespace Imagify\Media;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Media class for the custom folders.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class CustomFolders extends AbstractMedia {
|
||||
use \Imagify\Traits\MediaRowTrait;
|
||||
use \Imagify\Deprecated\Traits\Media\CustomFoldersDeprecatedTrait;
|
||||
|
||||
/**
|
||||
* Context (where the media "comes from").
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $context = 'custom-folders';
|
||||
|
||||
/**
|
||||
* The attachment SQL DB class.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $db_class_name = 'Imagify_Files_DB';
|
||||
|
||||
/**
|
||||
* Tell if the media/context is network-wide.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_network_wide = true;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|array|object $id The file ID. It can also be an array or object representing the file data.
|
||||
*/
|
||||
public function __construct( $id ) {
|
||||
if ( ! static::constructor_accepts( $id ) ) {
|
||||
$this->invalidate_row();
|
||||
parent::__construct( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_numeric( $id ) ) {
|
||||
$this->id = (int) $id;
|
||||
$this->get_row();
|
||||
} else {
|
||||
$prim_key = $this->get_row_db_instance()->get_primary_key();
|
||||
$this->row = (array) $id;
|
||||
$this->id = $this->row[ $prim_key ];
|
||||
}
|
||||
|
||||
parent::__construct( $this->id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id ) {
|
||||
return $id && ( is_numeric( $id ) || is_array( $id ) || is_object( $id ) );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ORIGINAL FILE =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the original media's path.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_original_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->get_cdn() ) {
|
||||
return $this->get_cdn()->get_file_path( 'original' );
|
||||
}
|
||||
|
||||
$row = $this->get_row();
|
||||
|
||||
if ( ! $row || empty( $row['path'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \Imagify_Files_Scan::remove_placeholder( $row['path'] );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FULL SIZE FILE ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the URL of the media’s full size file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_fullsize_url() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->get_cdn() ) {
|
||||
return $this->get_cdn()->get_file_url();
|
||||
}
|
||||
|
||||
$row = $this->get_row();
|
||||
|
||||
if ( ! $row || empty( $row['path'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \Imagify_Files_Scan::remove_placeholder( $row['path'], 'url' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the media’s full size file, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_fullsize_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->get_cdn() ) {
|
||||
return $this->get_cdn()->get_file_path();
|
||||
}
|
||||
|
||||
$row = $this->get_row();
|
||||
|
||||
if ( ! $row || empty( $row['path'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \Imagify_Files_Scan::remove_placeholder( $row['path'] );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the backup URL, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_backup_url() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return site_url( $this->filesystem->make_path_relative( $this->get_raw_backup_path() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup file path, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_backup_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \Imagify_Custom_Folders::get_file_backup_path( $this->get_raw_original_path() );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** THUMBNAILS ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create the media thumbnails.
|
||||
* And since this context does not support thumbnails...
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_thumbnails() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MEDIA DATA ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the current media has the required data (the data containing the file paths and thumbnails).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_required_media_data() {
|
||||
return $this->is_valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the files of this media, including the full size file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* An array with the size names as keys ('full' is used for the full size file), and arrays of data as values:
|
||||
*
|
||||
* @type string $size The size name.
|
||||
* @type string $path Absolute path to the file.
|
||||
* @type int $width The file width.
|
||||
* @type int $height The file height.
|
||||
* @type string $mime-type The file mime type.
|
||||
* @type bool $disabled True if the size is disabled in the plugin’s settings.
|
||||
* }
|
||||
*/
|
||||
public function get_media_files() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fullsize_path = $this->get_raw_fullsize_path();
|
||||
|
||||
if ( ! $fullsize_path ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dimensions = $this->get_dimensions();
|
||||
$sizes = [
|
||||
'full' => [
|
||||
'size' => 'full',
|
||||
'path' => $fullsize_path,
|
||||
'width' => $dimensions['width'],
|
||||
'height' => $dimensions['height'],
|
||||
'mime-type' => $this->get_mime_type(),
|
||||
'disabled' => false,
|
||||
],
|
||||
];
|
||||
|
||||
return $this->filter_media_files( $sizes );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the media is an image, get its width and height.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_dimensions() {
|
||||
if ( ! $this->is_image() ) {
|
||||
return [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$row = $this->get_row();
|
||||
|
||||
return [
|
||||
'width' => ! empty( $row['width'] ) ? $row['width'] : 0,
|
||||
'height' => ! empty( $row['height'] ) ? $row['height'] : 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the media data dimensions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $dimensions {
|
||||
* An array containing width and height.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* }
|
||||
*/
|
||||
protected function update_media_data_dimensions( $dimensions ) {
|
||||
$row = $this->get_row();
|
||||
|
||||
if ( ! is_array( $row ) ) {
|
||||
$row = [];
|
||||
}
|
||||
|
||||
if ( isset( $row['width'], $row['height'] ) && $row['width'] === $dimensions['width'] && $row['height'] === $dimensions['height'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$row['width'] = $dimensions['width'];
|
||||
$row['height'] = $dimensions['height'];
|
||||
|
||||
$this->update_row( $row );
|
||||
}
|
||||
}
|
||||
337
wp/plugins/imagify/classes/Media/MediaInterface.php
Normal file
337
wp/plugins/imagify/classes/Media/MediaInterface.php
Normal file
@@ -0,0 +1,337 @@
|
||||
<?php
|
||||
namespace Imagify\Media;
|
||||
|
||||
use Imagify\CDN\PushCDNInterface;
|
||||
use Imagify\Context\ContextInterface;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Interface to use for "media groups" (aka attachments).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface MediaInterface {
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id );
|
||||
|
||||
/**
|
||||
* Get the media ID.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_id();
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid();
|
||||
|
||||
/**
|
||||
* Get the media context name.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_context();
|
||||
|
||||
/**
|
||||
* Get the media context instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return ContextInterface
|
||||
*/
|
||||
public function get_context_instance();
|
||||
|
||||
/**
|
||||
* Get the CDN instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|PushCDNInterface A PushCDNInterface instance. False if no CDN is used.
|
||||
*/
|
||||
public function get_cdn();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ORIGINAL FILE =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the original file path, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_original_path();
|
||||
|
||||
/**
|
||||
* Get the original media's path if the file exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_original_path();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FULL SIZE FILE ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the URL of the media’s full size file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_fullsize_url();
|
||||
|
||||
/**
|
||||
* Get the path to the media’s full size file, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_fullsize_path();
|
||||
|
||||
/**
|
||||
* Get the path to the media’s full size file if the file exists.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_fullsize_path();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the backup URL, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_backup_url();
|
||||
|
||||
/**
|
||||
* Get the backup file path, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_backup_path();
|
||||
|
||||
/**
|
||||
* Get the backup file path if the file exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_backup_path();
|
||||
|
||||
/**
|
||||
* Check if the media has a backup of the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media has a backup.
|
||||
*/
|
||||
public function has_backup();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** THUMBNAILS ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create the media thumbnails.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_thumbnails();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MEDIA DATA ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the current media type is supported.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_supported();
|
||||
|
||||
/**
|
||||
* Tell if the current media refers to an image, based on file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool Returns false in case it's an image but not in a supported format (bmp for example).
|
||||
*/
|
||||
public function is_image();
|
||||
|
||||
/**
|
||||
* Tell if the current media refers to a pdf, based on file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_pdf();
|
||||
|
||||
/**
|
||||
* Get the original file extension (if supported by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_extension();
|
||||
|
||||
/**
|
||||
* Get the original file mime type (if supported by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_mime_type();
|
||||
|
||||
/**
|
||||
* Get the file mime type + file extension (if the file is supported by Imagify).
|
||||
* This test is ran against the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_allowed_mime_types();
|
||||
|
||||
/**
|
||||
* Tell if the current media has the required data (the data containing the file paths and thumbnails).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_required_media_data();
|
||||
|
||||
/**
|
||||
* Get the list of the files of this media, including the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* An array with the size names as keys ('full' is used for the original file), and arrays of data as values:
|
||||
*
|
||||
* @type string $path Absolute path to the file.
|
||||
* @type int $width The file width.
|
||||
* @type int $height The file height.
|
||||
* @type string $mime-type The file mime type.
|
||||
* }
|
||||
*/
|
||||
public function get_media_files();
|
||||
|
||||
/**
|
||||
* If the media is an image, get its width and height.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_dimensions();
|
||||
|
||||
/**
|
||||
* If the media is an image, update the dimensions in the database with the current file dimensions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True on success. False on failure.
|
||||
*/
|
||||
public function update_dimensions();
|
||||
}
|
||||
390
wp/plugins/imagify/classes/Media/Noop.php
Normal file
390
wp/plugins/imagify/classes/Media/Noop.php
Normal file
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
namespace Imagify\Media;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Fallback class for "media groups" (aka attachments).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Noop implements MediaInterface {
|
||||
use \Imagify\Deprecated\Traits\Media\NoopDeprecatedTrait;
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media ID.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_id() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media context name.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_context() {
|
||||
return 'noop';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media context instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return ContextInterface
|
||||
*/
|
||||
public function get_context_instance() {
|
||||
return \Imagify\Context\Noop::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CDN instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|PushCDNInterface A PushCDNInterface instance. False if no CDN is used.
|
||||
*/
|
||||
public function get_cdn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ORIGINAL FILE =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the original file path, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_original_path() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original media's path if the file exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_original_path() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FULL SIZE FILE ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the URL of the media’s full size file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_fullsize_url() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the media’s full size file, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_fullsize_path() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the media’s full size file if the file exists.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_fullsize_path() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the backup URL, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_backup_url() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup file path, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_backup_path() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup file path if the file exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False if it doesn't exist.
|
||||
*/
|
||||
public function get_backup_path() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the media has a backup of the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media has a backup.
|
||||
*/
|
||||
public function has_backup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** THUMBNAILS ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create the media thumbnails.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_thumbnails() {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MEDIA DATA ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the current media type is supported.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media refers to an image, based on file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool Returns false in case it's an image but not in a supported format (bmp for example).
|
||||
*/
|
||||
public function is_image() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media refers to a pdf, based on file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_pdf() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original file extension (if supported by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_extension() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original file mime type (if supported by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_mime_type() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file mime type + file extension (if the file is supported by Imagify).
|
||||
* This test is ran against the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_allowed_mime_types() {
|
||||
return imagify_get_mime_types( 'all' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media has the required data (the data containing the file paths and thumbnails).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_required_media_data() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the files of this media, including the original file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* An array with the size names as keys ('full' is used for the original file), and arrays of data as values:
|
||||
*
|
||||
* @type string $path Absolute path to the file.
|
||||
* @type int $width The file width.
|
||||
* @type int $height The file height.
|
||||
* @type string $mime-type The file mime type.
|
||||
* }
|
||||
*/
|
||||
public function get_media_files() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the media is an image, get its width and height.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_dimensions() {
|
||||
return [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the media is an image, update the dimensions in the database with the current file dimensions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True on success. False on failure.
|
||||
*/
|
||||
public function update_dimensions() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
430
wp/plugins/imagify/classes/Media/WP.php
Normal file
430
wp/plugins/imagify/classes/Media/WP.php
Normal file
@@ -0,0 +1,430 @@
|
||||
<?php
|
||||
namespace Imagify\Media;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Media class for the medias in the WP library.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class WP extends AbstractMedia {
|
||||
use \Imagify\Deprecated\Traits\Media\WPDeprecatedTrait;
|
||||
|
||||
/**
|
||||
* Tell if we’re playing in WP 5.3’s garden.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9.8
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_wp53;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int|\WP_Post $id The attachment ID, or \WP_Post object.
|
||||
*/
|
||||
public function __construct( $id ) {
|
||||
if ( ! static::constructor_accepts( $id ) ) {
|
||||
parent::__construct( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_numeric( $id ) ) {
|
||||
$id = get_post( (int) $id );
|
||||
}
|
||||
|
||||
if ( ! $id || 'attachment' !== $id->post_type ) {
|
||||
parent::__construct( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
parent::__construct( $id->ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id ) {
|
||||
return $id && ( is_numeric( $id ) || $id instanceof \WP_Post );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ORIGINAL FILE =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the original file path, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_original_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->get_cdn() ) {
|
||||
return $this->get_cdn()->get_file_path( 'original' );
|
||||
}
|
||||
|
||||
if ( $this->is_wp_53() ) {
|
||||
// `wp_get_original_image_path()` may return false.
|
||||
$path = wp_get_original_image_path( $this->id );
|
||||
} else {
|
||||
$path = false;
|
||||
}
|
||||
|
||||
if ( ! $path ) {
|
||||
$path = get_attached_file( $this->id );
|
||||
}
|
||||
|
||||
return $path ? $path : false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** FULL SIZE FILE ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the URL of the media’s full size file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_fullsize_url() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->get_cdn() ) {
|
||||
return $this->get_cdn()->get_file_url();
|
||||
}
|
||||
|
||||
$url = wp_get_attachment_url( $this->id );
|
||||
|
||||
return $url ? $url : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the media’s full size file, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_fullsize_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->get_cdn() ) {
|
||||
return $this->get_cdn()->get_file_path();
|
||||
}
|
||||
|
||||
$path = get_attached_file( $this->id );
|
||||
|
||||
return $path ? $path : false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the backup URL, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file URL. False on failure.
|
||||
*/
|
||||
public function get_backup_url() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get_imagify_attachment_url( $this->get_raw_backup_path() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup file path, even if the file doesn't exist.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_raw_backup_path() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get_imagify_attachment_backup_path( $this->get_raw_original_path() );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** THUMBNAILS ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Create the media thumbnails.
|
||||
* With WP 5.3+, this will also generate a new full size file if the original file is wider or taller than a defined threshold.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_thumbnails() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/image.php';
|
||||
}
|
||||
|
||||
// Store the path to the current full size file before generating the thumbnails.
|
||||
$old_full_size_path = $this->get_raw_fullsize_path();
|
||||
$metadata = wp_generate_attachment_metadata( $this->get_id(), $this->get_raw_original_path() );
|
||||
|
||||
if ( empty( $metadata['file'] ) ) {
|
||||
// Σ(゚Д゚).
|
||||
update_post_meta( $this->get_id(), '_wp_attachment_metadata', $metadata );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't change the full size file name.
|
||||
* WP 5.3+ will rename the full size file if the resizing threshold has changed (not the same as the one used to generate it previously).
|
||||
* This will force WP to keep the previous file name.
|
||||
*/
|
||||
$old_full_size_file_name = $this->filesystem->file_name( $old_full_size_path );
|
||||
$new_full_size_file_name = $this->filesystem->file_name( $metadata['file'] );
|
||||
|
||||
if ( $new_full_size_file_name !== $old_full_size_file_name ) {
|
||||
$new_full_size_path = $this->filesystem->dir_path( $old_full_size_path ) . $new_full_size_file_name;
|
||||
|
||||
$moved = $this->filesystem->move( $new_full_size_path, $old_full_size_path, true );
|
||||
|
||||
if ( $moved ) {
|
||||
$metadata['file'] = $this->filesystem->dir_path( $metadata['file'] ) . $old_full_size_file_name;
|
||||
update_post_meta( $this->get_id(), '_wp_attached_file', $metadata['file'] );
|
||||
}
|
||||
}
|
||||
|
||||
update_post_meta( $this->get_id(), '_wp_attachment_metadata', $metadata );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MEDIA DATA ============================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the current media has the required data (the data containing the file paths and thumbnails).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_required_media_data() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = get_post_meta( $this->id, '_wp_attached_file', true );
|
||||
|
||||
if ( ! $file || preg_match( '@://@', $file ) || preg_match( '@^.:\\\@', $file ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) wp_get_attachment_metadata( $this->id, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the files of this media, including the full size file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* An array with the size names as keys ('full' is used for the full size file), and arrays of data as values:
|
||||
*
|
||||
* @type string $size The size name.
|
||||
* @type string $path Absolute path to the file.
|
||||
* @type int $width The file width.
|
||||
* @type int $height The file height.
|
||||
* @type string $mime-type The file mime type.
|
||||
* @type bool $disabled True if the size is disabled in the plugin’s settings.
|
||||
* }
|
||||
*/
|
||||
public function get_media_files() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fullsize_path = $this->get_raw_fullsize_path();
|
||||
|
||||
if ( ! $fullsize_path ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dimensions = $this->get_dimensions();
|
||||
$all_sizes = [
|
||||
'full' => [
|
||||
'size' => 'full',
|
||||
'path' => $fullsize_path,
|
||||
'width' => $dimensions['width'],
|
||||
'height' => $dimensions['height'],
|
||||
'mime-type' => $this->get_mime_type(),
|
||||
'disabled' => false,
|
||||
],
|
||||
];
|
||||
|
||||
if ( $this->is_image() ) {
|
||||
$sizes = wp_get_attachment_metadata( $this->id, true );
|
||||
$sizes = ! empty( $sizes['sizes'] ) && is_array( $sizes['sizes'] ) ? $sizes['sizes'] : [];
|
||||
$sizes = array_intersect_key( $sizes, $this->get_context_instance()->get_thumbnail_sizes() );
|
||||
} else {
|
||||
$sizes = [];
|
||||
}
|
||||
|
||||
if ( ! $sizes ) {
|
||||
return $all_sizes;
|
||||
}
|
||||
|
||||
$dir_path = $this->filesystem->dir_path( $fullsize_path );
|
||||
$disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
|
||||
$is_active_for_network = imagify_is_active_for_network();
|
||||
|
||||
foreach ( $sizes as $size => $size_data ) {
|
||||
$all_sizes[ $size ] = [
|
||||
'size' => $size,
|
||||
'path' => $dir_path . $size_data['file'],
|
||||
'width' => $size_data['width'],
|
||||
'height' => $size_data['height'],
|
||||
'mime-type' => $size_data['mime-type'],
|
||||
'disabled' => ! $is_active_for_network && isset( $disallowed_sizes[ $size ] ),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->filter_media_files( $all_sizes );
|
||||
}
|
||||
|
||||
/**
|
||||
* If the media is an image, get its width and height.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_dimensions() {
|
||||
if ( ! $this->is_image() ) {
|
||||
return [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$values = wp_get_attachment_image_src( $this->id, 'full' );
|
||||
|
||||
return [
|
||||
'width' => $values[1],
|
||||
'height' => $values[2],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the media data dimensions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $dimensions {
|
||||
* An array containing width and height.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* }
|
||||
*/
|
||||
protected function update_media_data_dimensions( $dimensions ) {
|
||||
$metadata = wp_get_attachment_metadata( $this->id );
|
||||
|
||||
if ( ! is_array( $metadata ) ) {
|
||||
$row = [];
|
||||
}
|
||||
|
||||
if ( isset( $metadata['width'], $metadata['height'] ) && $metadata['width'] === $dimensions['width'] && $metadata['height'] === $dimensions['height'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$metadata['width'] = $dimensions['width'];
|
||||
$metadata['height'] = $dimensions['height'];
|
||||
|
||||
update_post_meta( $this->get_id(), '_wp_attachment_metadata', $metadata );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** INTERNAL TOOLS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if we’re playing in WP 5.3’s garden.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_wp_53() {
|
||||
if ( isset( $this->is_wp53 ) ) {
|
||||
return $this->is_wp53;
|
||||
}
|
||||
|
||||
$this->is_wp53 = function_exists( 'wp_get_original_image_path' );
|
||||
|
||||
return $this->is_wp53;
|
||||
}
|
||||
}
|
||||
909
wp/plugins/imagify/classes/Notices/Notices.php
Normal file
909
wp/plugins/imagify/classes/Notices/Notices.php
Normal file
@@ -0,0 +1,909 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Notices;
|
||||
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
use Imagify\User\User;
|
||||
|
||||
/**
|
||||
* Class that handles the admin notices.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
class Notices {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '1.0.1';
|
||||
|
||||
/**
|
||||
* Name of the transient storing temporary notices.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TEMPORARY_NOTICES_TRANSIENT_NAME = 'imagify_temporary_notices';
|
||||
|
||||
/**
|
||||
* Name of the user meta that stores the dismissed notice IDs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DISMISS_META_NAME = '_imagify_ignore_notices';
|
||||
|
||||
/**
|
||||
* Action used in the nonce to dismiss a notice.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DISMISS_NONCE_ACTION = 'imagify-dismiss-notice';
|
||||
|
||||
/**
|
||||
* Action used in the nonce to deactivate a plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DEACTIVATE_PLUGIN_NONCE_ACTION = 'imagify-deactivate-plugin';
|
||||
|
||||
/**
|
||||
* List of notice IDs.
|
||||
* They correspond to method names and IDs stored in the "dismissed" transient.
|
||||
* Only use "-" character, not "_".
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $notice_ids = [
|
||||
// This warning is displayed when the API key is empty. Dismissible.
|
||||
'welcome-steps',
|
||||
// This warning is displayed when the API key is wrong. Dismissible.
|
||||
'wrong-api-key',
|
||||
// This warning is displayed if some plugins are active. NOT dismissible.
|
||||
'plugins-to-deactivate',
|
||||
// This notice is displayed when external HTTP requests are blocked via the WP_HTTP_BLOCK_EXTERNAL constant. Dismissible.
|
||||
'http-block-external',
|
||||
// This warning is displayed when the grid view is active on the library. Dismissible.
|
||||
'grid-view',
|
||||
// This warning is displayed if the backup folder is not writable. NOT dismissible.
|
||||
'backup-folder-not-writable',
|
||||
// This notice is displayed to rate the plugin after 100 optimizations & 7 days after the first installation. Dismissible.
|
||||
'rating',
|
||||
// Add a message about WP Rocket on the "Bulk Optimization" screen. Dismissible.
|
||||
'wp-rocket',
|
||||
'bulk-optimization-complete',
|
||||
'bulk-optimization-running',
|
||||
'upsell-banner',
|
||||
'upsell-admin-bar',
|
||||
];
|
||||
|
||||
/**
|
||||
* List of user capabilities to use for each notice.
|
||||
* Default value 'manage' is not listed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $capabilities = [
|
||||
'grid-view' => 'optimize',
|
||||
'backup-folder-not-writable' => 'bulk-optimize',
|
||||
'rating' => 'bulk-optimize',
|
||||
'wp-rocket' => 'bulk-optimize',
|
||||
'bulk-optimization-complete' => 'bulk-optimize',
|
||||
'bulk-optimization-running' => 'bulk-optimize',
|
||||
];
|
||||
|
||||
/**
|
||||
* List of plugins that conflict with Imagify.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $conflicting_plugins = [
|
||||
'wp-smush' => 'wp-smushit/wp-smush.php', // WP Smush.
|
||||
'wp-smush-pro' => 'wp-smush-pro/wp-smush.php', // WP Smush Pro.
|
||||
'kraken' => 'kraken-image-optimizer/kraken.php', // Kraken.io.
|
||||
'tinypng' => 'tiny-compress-images/tiny-compress-images.php', // TinyPNG.
|
||||
'shortpixel' => 'shortpixel-image-optimiser/wp-shortpixel.php', // Shortpixel.
|
||||
'ewww' => 'ewww-image-optimizer/ewww-image-optimizer.php', // EWWW Image Optimizer.
|
||||
'ewww-cloud' => 'ewww-image-optimizer-cloud/ewww-image-optimizer-cloud.php', // EWWW Image Optimizer Cloud.
|
||||
'imagerecycle' => 'imagerecycle-pdf-image-compression/wp-image-recycle.php', // ImageRecycle.
|
||||
];
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function __construct() {}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** INIT ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Launch the hooks.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
public function init() {
|
||||
// For generic purpose.
|
||||
add_action( 'all_admin_notices', [ $this, 'render_notices' ] );
|
||||
add_action( 'wp_ajax_imagify_dismiss_notice', [ $this, 'admin_post_dismiss_notice' ] );
|
||||
add_action( 'admin_post_imagify_dismiss_notice', [ $this, 'admin_post_dismiss_notice' ] );
|
||||
// For specific notices.
|
||||
add_action( 'imagify_dismiss_notice', [ $this, 'clear_scheduled_rating' ] );
|
||||
add_action( 'admin_post_imagify_deactivate_plugin', [ $this, 'deactivate_plugin' ] );
|
||||
add_action( 'imagify_not_almost_over_quota_anymore', [ $this, 'renew_almost_over_quota_notice' ] );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Maybe display some notices.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*/
|
||||
public function render_notices() {
|
||||
foreach ( $this->get_notice_ids() as $notice_id ) {
|
||||
// Get the name of the method that will tell if this notice should be displayed.
|
||||
$callback = 'display_' . str_replace( '-', '_', $notice_id );
|
||||
|
||||
if ( ! method_exists( $this, $callback ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = call_user_func( [ $this, $callback ] );
|
||||
|
||||
if ( $data ) {
|
||||
// The notice must be displayed: render the view.
|
||||
\Imagify_Views::get_instance()->print_template( 'notice-' . $notice_id, $data );
|
||||
}
|
||||
}
|
||||
|
||||
// Temporary notices.
|
||||
$this->render_temporary_notices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a dismissed notice.
|
||||
*
|
||||
* @since 1.6.10
|
||||
* @see _do_admin_post_imagify_dismiss_notice()
|
||||
*/
|
||||
public function admin_post_dismiss_notice() {
|
||||
imagify_check_nonce( self::DISMISS_NONCE_ACTION );
|
||||
|
||||
$notice = ! empty( $_GET['notice'] ) ? esc_html( wp_unslash( $_GET['notice'] ) ) : false;
|
||||
$notices = $this->get_notice_ids();
|
||||
$notices = array_flip( $notices );
|
||||
|
||||
if ( ! $notice || ! isset( $notices[ $notice ] ) || ! $this->user_can( $notice ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
self::dismiss_notice( $notice );
|
||||
|
||||
/**
|
||||
* Fires when a notice is dismissed.
|
||||
*
|
||||
* @since 1.4.2
|
||||
*
|
||||
* @param int $notice The notice slug
|
||||
*/
|
||||
do_action( 'imagify_dismiss_notice', $notice );
|
||||
|
||||
imagify_maybe_redirect();
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the rating cron when the notice is dismissed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
* @see _imagify_clear_scheduled_rating()
|
||||
*
|
||||
* @param string $notice The notice name.
|
||||
*/
|
||||
public function clear_scheduled_rating( $notice ) {
|
||||
if ( 'rating' === $notice ) {
|
||||
set_site_transient( 'do_imagify_rating_cron', 'no' );
|
||||
\Imagify_Cron_Rating::get_instance()->unschedule_event();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a plugin which can be in conflict with Imagify.
|
||||
*
|
||||
* @since 1.6.10
|
||||
* @see _imagify_deactivate_plugin()
|
||||
*/
|
||||
public function deactivate_plugin() {
|
||||
imagify_check_nonce( self::DEACTIVATE_PLUGIN_NONCE_ACTION );
|
||||
|
||||
if ( empty( $_GET['plugin'] ) || ! $this->user_can( 'plugins-to-deactivate' ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
$plugin = esc_html( wp_unslash( $_GET['plugin'] ) );
|
||||
$plugins = $this->get_conflicting_plugins();
|
||||
$plugins = array_flip( $plugins );
|
||||
|
||||
if ( empty( $plugins[ $plugin ] ) ) {
|
||||
imagify_die();
|
||||
}
|
||||
|
||||
deactivate_plugins( $plugin );
|
||||
|
||||
imagify_maybe_redirect();
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renew the "almost-over-quota" notice when the consumed quota percent decreases back below 80%.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function renew_almost_over_quota_notice() {
|
||||
global $wpdb;
|
||||
|
||||
$results = $wpdb->get_results( $wpdb->prepare( "SELECT umeta_id, user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value LIKE %s", self::DISMISS_META_NAME, '%upsell%' ) );
|
||||
|
||||
if ( ! $results ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent multiple queries to the DB by caching user metas.
|
||||
$not_cached = [];
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
if ( ! wp_cache_get( $result->umeta_id, 'user_meta' ) ) {
|
||||
$not_cached[] = $result->umeta_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $not_cached ) {
|
||||
update_meta_cache( 'user', $not_cached );
|
||||
}
|
||||
|
||||
// Renew the notice for all users.
|
||||
foreach ( $results as $result ) {
|
||||
self::renew_notice( 'upsell-banner', $result->user_id );
|
||||
self::renew_notice( 'upsell-admin-bar', $result->user_id );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** NOTICES ================================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the 'welcome-steps' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function display_welcome_steps() {
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = false;
|
||||
|
||||
if ( ! $this->user_can( 'welcome-steps' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( imagify_is_screen( 'imagify-settings' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( self::notice_is_dismissed( 'welcome-steps' ) || get_imagify_option( 'api_key' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = true;
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the 'wrong-api-key' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function display_wrong_api_key() {
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = false;
|
||||
|
||||
if ( ! $this->user_can( 'wrong-api-key' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( ! imagify_is_screen( 'bulk' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( self::notice_is_dismissed( 'wrong-api-key' ) || ! get_imagify_option( 'api_key' ) || \Imagify_Requirements::is_api_key_valid() ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = true;
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the 'plugins-to-deactivate' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return array An array of plugins to deactivate.
|
||||
*/
|
||||
public function display_plugins_to_deactivate() {
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( ! $this->user_can( 'plugins-to-deactivate' ) ) {
|
||||
$display = false;
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = $this->get_conflicting_plugins();
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the 'plugins-to-deactivate' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function display_http_block_external() {
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = false;
|
||||
|
||||
if ( ! $this->user_can( 'http-block-external' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( imagify_is_screen( 'imagify-settings' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( self::notice_is_dismissed( 'http-block-external' ) || ! \Imagify_Requirements::is_imagify_blocked() ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = true;
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the 'grid-view' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function display_grid_view() {
|
||||
global $wp_version;
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = false;
|
||||
|
||||
if ( ! $this->user_can( 'grid-view' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( ! imagify_is_screen( 'library' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$media_library_mode = get_user_option( 'media_library_mode', get_current_user_id() );
|
||||
|
||||
if ( 'list' === $media_library_mode || self::notice_is_dismissed( 'grid-view' ) || version_compare( $wp_version, '4.0' ) < 0 ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
// Don't display the notice if the API key isn't valid.
|
||||
if ( ! \Imagify_Requirements::is_api_key_valid() ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = true;
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the 'backup-folder-not-writable' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function display_backup_folder_not_writable() {
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = false;
|
||||
|
||||
if ( ! $this->user_can( 'backup-folder-not-writable' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
// Every places where images can be optimized, automatically or not (+ the settings page).
|
||||
if ( ! imagify_is_screen( 'imagify-settings' ) && ! imagify_is_screen( 'library' ) && ! imagify_is_screen( 'upload' ) && ! imagify_is_screen( 'bulk' ) && ! imagify_is_screen( 'media-modal' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( ! get_imagify_option( 'backup' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( \Imagify_Requirements::attachments_backup_dir_is_writable() ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = true;
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the 'rating' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
public function display_rating() {
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = false;
|
||||
|
||||
if ( ! $this->user_can( 'rating' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( ! imagify_is_screen( 'bulk' ) && ! imagify_is_screen( 'library' ) && ! imagify_is_screen( 'upload' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( self::notice_is_dismissed( 'rating' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$user_images_count = (int) get_site_transient( 'imagify_user_images_count' );
|
||||
|
||||
if ( ! $user_images_count || get_site_transient( 'imagify_seen_rating_notice' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = $user_images_count;
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the 'wp-rocket' notice should be displayed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function display_wp_rocket() {
|
||||
static $display;
|
||||
|
||||
if ( isset( $display ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = false;
|
||||
|
||||
if ( ! $this->user_can( 'wp-rocket' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
if ( ! imagify_is_screen( 'bulk' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$plugins = get_plugins();
|
||||
|
||||
if ( isset( $plugins['wp-rocket/wp-rocket.php'] ) || self::notice_is_dismissed( 'wp-rocket' ) ) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
$display = true;
|
||||
return $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the bulk optimization complete notice should be displayed
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display_bulk_optimization_complete(): array {
|
||||
if ( ! $this->user_can( 'bulk-optimization-complete' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( imagify_is_screen( 'bulk' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( self::notice_is_dismissed( 'bulk-optimization-complete' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( false === get_transient( 'imagify_bulk_optimization_complete' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = get_transient( 'imagify_bulk_optimization_result' );
|
||||
|
||||
if ( empty( $data ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$global_gain = $data['original_size'] - $data['optimized_size'];
|
||||
|
||||
$data['original_size'] = imagify_size_format( $data['original_size'], 2 );
|
||||
$data['optimized_size'] = imagify_size_format( $global_gain, 2 );
|
||||
$data['bulk_page_url'] = admin_url( 'upload.php?page=imagify-bulk-optimization' );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the bulk optimization running notice should be displayed
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display_bulk_optimization_running(): array {
|
||||
if ( ! $this->user_can( 'bulk-optimization-running' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( imagify_is_screen( 'bulk' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( self::notice_is_dismissed( 'bulk-optimization-running' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$custom_folders = get_transient( 'imagify_custom-folders_optimize_running' );
|
||||
$library_wp = get_transient( 'imagify_wp_optimize_running' );
|
||||
|
||||
if (
|
||||
! $custom_folders
|
||||
&&
|
||||
! $library_wp
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
$data['bulk_page_url'] = admin_url( 'upload.php?page=imagify-bulk-optimization' );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TEMPORARY NOTICES ======================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Maybe display some notices.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
protected function render_temporary_notices() {
|
||||
if ( is_network_admin() ) {
|
||||
$notices = $this->get_network_temporary_notices();
|
||||
} else {
|
||||
$notices = $this->get_site_temporary_notices();
|
||||
}
|
||||
|
||||
if ( ! $notices ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$views = \Imagify_Views::get_instance();
|
||||
|
||||
foreach ( $notices as $i => $notice_data ) {
|
||||
$notices[ $i ]['type'] = ! empty( $notice_data['type'] ) ? $notice_data['type'] : 'error';
|
||||
}
|
||||
|
||||
$views->print_template( 'notice-temporary', $notices );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get temporary notices for the network.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_network_temporary_notices() {
|
||||
$notices = get_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
|
||||
|
||||
if ( false === $notices ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
delete_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
|
||||
|
||||
return $notices && is_array( $notices ) ? $notices : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary notice for the network.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array|object|string $notice_data Some data, with the message to display.
|
||||
*/
|
||||
public function add_network_temporary_notice( $notice_data ) {
|
||||
$notices = get_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
|
||||
$notices = is_array( $notices ) ? $notices : [];
|
||||
|
||||
if ( is_wp_error( $notice_data ) ) {
|
||||
$notice_data = $notice_data->get_error_messages();
|
||||
$notice_data = implode( '<br/>', $notice_data );
|
||||
}
|
||||
|
||||
if ( is_string( $notice_data ) ) {
|
||||
$notice_data = [
|
||||
'message' => $notice_data,
|
||||
];
|
||||
} elseif ( is_object( $notice_data ) ) {
|
||||
$notice_data = (array) $notice_data;
|
||||
}
|
||||
|
||||
if ( ! is_array( $notice_data ) || empty( $notice_data['message'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notices[] = $notice_data;
|
||||
|
||||
set_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME, $notices, 30 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get temporary notices for the current site.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_site_temporary_notices() {
|
||||
$notices = get_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
|
||||
|
||||
if ( false === $notices ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
delete_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
|
||||
|
||||
return $notices && is_array( $notices ) ? $notices : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary notice for the current site.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array|string $notice_data Some data, with the message to display.
|
||||
*/
|
||||
public function add_site_temporary_notice( $notice_data ) {
|
||||
$notices = get_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
|
||||
$notices = is_array( $notices ) ? $notices : [];
|
||||
|
||||
if ( is_string( $notice_data ) ) {
|
||||
$notice_data = [
|
||||
'message' => $notice_data,
|
||||
];
|
||||
} elseif ( is_object( $notice_data ) ) {
|
||||
$notice_data = (array) $notice_data;
|
||||
}
|
||||
|
||||
if ( ! is_array( $notice_data ) || empty( $notice_data['message'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notices[] = $notice_data;
|
||||
|
||||
set_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME, $notices, 30 );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PUBLIC TOOLS ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Renew a dismissed Imagify notice.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $notice A notice ID.
|
||||
* @param int $user_id A user ID.
|
||||
*/
|
||||
public static function renew_notice( $notice, $user_id = 0 ) {
|
||||
$user_id = $user_id ? (int) $user_id : get_current_user_id();
|
||||
$notices = get_user_meta( $user_id, self::DISMISS_META_NAME, true );
|
||||
$notices = $notices && is_array( $notices ) ? array_flip( $notices ) : [];
|
||||
|
||||
if ( ! isset( $notices[ $notice ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset( $notices[ $notice ] );
|
||||
$notices = array_flip( $notices );
|
||||
$notices = array_filter( $notices );
|
||||
$notices = array_values( $notices );
|
||||
|
||||
update_user_meta( $user_id, self::DISMISS_META_NAME, $notices );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss an Imagify notice.
|
||||
*
|
||||
* @since 1.6.10
|
||||
* @see imagify_dismiss_notice()
|
||||
*
|
||||
* @param string $notice A notice ID.
|
||||
* @param int $user_id A user ID.
|
||||
*/
|
||||
public static function dismiss_notice( $notice, $user_id = 0 ) {
|
||||
$user_id = $user_id ? (int) $user_id : get_current_user_id();
|
||||
$notices = get_user_meta( $user_id, self::DISMISS_META_NAME, true );
|
||||
$notices = $notices && is_array( $notices ) ? array_flip( $notices ) : [];
|
||||
|
||||
if ( isset( $notices[ $notice ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notices = array_flip( $notices );
|
||||
$notices[] = $notice;
|
||||
$notices = array_filter( $notices );
|
||||
$notices = array_values( $notices );
|
||||
|
||||
update_user_meta( $user_id, self::DISMISS_META_NAME, $notices );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if an Imagify notice is dismissed.
|
||||
*
|
||||
* @since 1.6.10
|
||||
* @see imagify_notice_is_dismissed()
|
||||
*
|
||||
* @param string $notice A notice ID.
|
||||
* @param int $user_id A user ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function notice_is_dismissed( $notice, $user_id = 0 ) {
|
||||
$user_id = $user_id ? (int) $user_id : get_current_user_id();
|
||||
$notices = get_user_meta( $user_id, self::DISMISS_META_NAME, true );
|
||||
$notices = $notices && is_array( $notices ) ? array_flip( $notices ) : [];
|
||||
|
||||
return isset( $notices[ $notice ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if one or more notices will be displayed later in the page.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_notices() {
|
||||
foreach ( self::$notice_ids as $notice_id ) {
|
||||
$callback = 'display_' . str_replace( '-', '_', $notice_id );
|
||||
|
||||
if ( method_exists( $this, $callback ) && call_user_func( [ $this, $callback ] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** INTERNAL TOOLS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get all notice IDs.
|
||||
*
|
||||
* @since 1.6.10
|
||||
* @since 1.10 Cast return value to array.
|
||||
*
|
||||
* @return array The filtered notice ids.
|
||||
*/
|
||||
protected function get_notice_ids() {
|
||||
/**
|
||||
* Filter the notices Imagify can display.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param array $notice_ids An array of notice "IDs".
|
||||
*/
|
||||
return (array) apply_filters( 'imagify_notices', self::$notice_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current user can see the notices.
|
||||
* Notice IDs that are not listed in self::$capabilities are assumed as 'manage'.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @param string $notice_id A notice ID.
|
||||
* @return bool
|
||||
*/
|
||||
protected function user_can( $notice_id ) {
|
||||
$capability = isset( self::$capabilities[ $notice_id ] ) ? self::$capabilities[ $notice_id ] : 'manage';
|
||||
|
||||
return imagify_get_context( 'wp' )->current_user_can( $capability );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of plugins that can conflict with Imagify.
|
||||
*
|
||||
* @since 1.6.10
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_conflicting_plugins() {
|
||||
/**
|
||||
* Filter the recommended plugins to deactivate to prevent conflicts.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @param string $plugins List of recommended plugins to deactivate.
|
||||
*/
|
||||
$plugins = apply_filters( 'imagify_plugins_to_deactivate', self::$conflicting_plugins );
|
||||
|
||||
return array_filter( $plugins, 'is_plugin_active' );
|
||||
}
|
||||
}
|
||||
452
wp/plugins/imagify/classes/Optimization/Data/AbstractData.php
Normal file
452
wp/plugins/imagify/classes/Optimization/Data/AbstractData.php
Normal file
@@ -0,0 +1,452 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Data;
|
||||
|
||||
use Imagify\Media\MediaInterface;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Abstract class used to handle the optimization data of "media groups" (aka attachments).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class AbstractData implements DataInterface {
|
||||
|
||||
/**
|
||||
* Optimization data structure.
|
||||
* This is the format returned when we "get" optimization data from the DB.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @see $this->get_optimization_data()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $default_optimization_data = [
|
||||
'status' => '',
|
||||
'message' => '',
|
||||
'level' => false,
|
||||
'sizes' => [],
|
||||
'stats' => [
|
||||
'original_size' => 0,
|
||||
'optimized_size' => 0,
|
||||
'percent' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The media object.
|
||||
*
|
||||
* @var MediaInterface
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $media;
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var object Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @see self::constructor_accepts()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id An ID, or whatever type the constructor accepts.
|
||||
*/
|
||||
public function __construct( $id ) {
|
||||
// Set the Media instance.
|
||||
if ( $id instanceof MediaInterface ) {
|
||||
$this->media = $id;
|
||||
} elseif ( static::constructor_accepts( $id ) ) {
|
||||
$media_class = str_replace( '\\Optimization\\Data\\', '\\Media\\', get_called_class() );
|
||||
$media_class = '\\' . ltrim( $media_class, '\\' );
|
||||
$this->media = new $media_class( $id );
|
||||
} else {
|
||||
$this->media = false;
|
||||
}
|
||||
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id ) {
|
||||
if ( $id instanceof MediaInterface ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$media_class = str_replace( '\\Optimization\\Data\\', '\\Media\\', get_called_class() );
|
||||
$media_class = '\\' . ltrim( $media_class, '\\' );
|
||||
|
||||
return $media_class::constructor_accepts( $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return MediaInterface|false
|
||||
*/
|
||||
public function get_media() {
|
||||
return $this->media;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
return $this->get_media() && $this->get_media()->is_valid();
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION DATA ======================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_optimized() {
|
||||
return 'success' === $this->get_optimization_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (NOT by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_already_optimized() {
|
||||
return 'already_optimized' === $this->get_optimization_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_error() {
|
||||
return 'error' === $this->get_optimization_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media's optimization level.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int|false The optimization level. False if not optimized.
|
||||
*/
|
||||
public function get_optimization_level() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $this->get_optimization_data();
|
||||
return $data['level'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media's optimization status (success or error).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string The optimization status. An empty string if there is none.
|
||||
*/
|
||||
public function get_optimization_status() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$data = $this->get_optimization_data();
|
||||
return $data['status'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of optimized sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int Number of optimized sizes.
|
||||
*/
|
||||
public function get_optimized_sizes_count() {
|
||||
$data = $this->get_optimization_data();
|
||||
$count = 0;
|
||||
|
||||
if ( ! $data['sizes'] ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$context_sizes = $this->get_media()->get_media_files();
|
||||
$data['sizes'] = array_intersect_key( $data['sizes'], $context_sizes );
|
||||
|
||||
if ( ! $data['sizes'] ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach ( $data['sizes'] as $size ) {
|
||||
if ( ! empty( $size['success'] ) ) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original media's size (weight).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $human_format True to display the image human format size (1Mb).
|
||||
* @param int $decimals Precision of number of decimal places.
|
||||
* @return string|int
|
||||
*/
|
||||
public function get_original_size( $human_format = true, $decimals = 2 ) {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return $human_format ? imagify_size_format( 0, $decimals ) : 0;
|
||||
}
|
||||
|
||||
$size = $this->get_optimization_data();
|
||||
$size = ! empty( $size['sizes']['full']['original_size'] ) ? $size['sizes']['full']['original_size'] : 0;
|
||||
|
||||
// If nothing in the database, try to get the info from the file.
|
||||
if ( ! $size ) {
|
||||
// Check for the backup file first.
|
||||
$filepath = $this->get_media()->get_backup_path();
|
||||
|
||||
if ( ! $filepath ) {
|
||||
// Try the original file then.
|
||||
$filepath = $this->get_media()->get_original_path();
|
||||
}
|
||||
|
||||
$size = $filepath ? $this->filesystem->size( $filepath ) : 0;
|
||||
}
|
||||
|
||||
if ( $human_format ) {
|
||||
return imagify_size_format( (int) $size, $decimals );
|
||||
}
|
||||
|
||||
return (int) $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file size of the full size file.
|
||||
* If the WebP size is available, it is used.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $human_format True to display the image human format size (1Mb).
|
||||
* @param int $decimals Precision of number of decimal places.
|
||||
* @param bool $use_webp Use the WebP size if available.
|
||||
* @return string|int
|
||||
*/
|
||||
public function get_optimized_size( $human_format = true, $decimals = 2, $use_webp = true ) {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return $human_format ? imagify_size_format( 0, $decimals ) : 0;
|
||||
}
|
||||
|
||||
$data = $this->get_optimization_data();
|
||||
$media = $this->get_media();
|
||||
|
||||
if ( $use_webp ) {
|
||||
$process_class_name = imagify_get_optimization_process_class_name( $media->get_context() );
|
||||
$webp_size_name = 'full' . constant( $process_class_name . '::WEBP_SUFFIX' );
|
||||
}
|
||||
|
||||
if ( $use_webp && ! empty( $data['sizes'][ $webp_size_name ]['optimized_size'] ) ) {
|
||||
$size = (int) $data['sizes'][ $webp_size_name ]['optimized_size'];
|
||||
} elseif ( ! empty( $data['sizes']['full']['optimized_size'] ) ) {
|
||||
$size = (int) $data['sizes']['full']['optimized_size'];
|
||||
} else {
|
||||
$size = 0;
|
||||
}
|
||||
|
||||
if ( $size ) {
|
||||
return $human_format ? imagify_size_format( $size, $decimals ) : $size;
|
||||
}
|
||||
|
||||
// If nothing in the database, try to get the info from the file.
|
||||
$filepath = false;
|
||||
|
||||
if ( $use_webp && ! empty( $data['sizes'][ $webp_size_name ]['success'] ) ) {
|
||||
// Try with the WebP file first.
|
||||
$filepath = $media->get_raw_fullsize_path();
|
||||
$filepath = $filepath ? imagify_path_to_webp( $filepath ) : false;
|
||||
|
||||
if ( ! $filepath || ! $this->filesystem->exists( $filepath ) ) {
|
||||
$filepath = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $filepath ) {
|
||||
// No WebP? The full size then.
|
||||
$filepath = $media->get_fullsize_path();
|
||||
}
|
||||
|
||||
if ( ! $filepath ) {
|
||||
return $human_format ? imagify_size_format( 0, $decimals ) : 0;
|
||||
}
|
||||
|
||||
$size = (int) $this->filesystem->size( $filepath );
|
||||
|
||||
return $human_format ? imagify_size_format( $size, $decimals ) : $size;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION STATS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get one or all statistics of a specific size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The thumbnail slug.
|
||||
* @param string $key The specific data slug.
|
||||
* @return array|string
|
||||
*/
|
||||
public function get_size_data( $size = 'full', $key = '' ) {
|
||||
$data = $this->get_optimization_data();
|
||||
|
||||
if ( ! isset( $data['sizes'][ $size ] ) ) {
|
||||
return $key ? '' : [];
|
||||
}
|
||||
|
||||
if ( ! $key ) {
|
||||
return $data['sizes'][ $size ];
|
||||
}
|
||||
|
||||
if ( ! isset( $data['sizes'][ $size ][ $key ] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $data['sizes'][ $size ][ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall statistics data or a specific one.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $key The specific data slug.
|
||||
* @return array|string
|
||||
*/
|
||||
public function get_stats_data( $key = '' ) {
|
||||
$data = $this->get_optimization_data();
|
||||
$stats = '';
|
||||
|
||||
if ( empty( $data['stats'] ) ) {
|
||||
return $key ? '' : [];
|
||||
}
|
||||
|
||||
if ( ! isset( $data['stats'][ $key ] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $data['stats'][ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optimized/original saving of the original image in percent.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return float A 2-decimals float.
|
||||
*/
|
||||
public function get_saving_percent() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return round( (float) 0, 2 );
|
||||
}
|
||||
|
||||
$process_class_name = imagify_get_optimization_process_class_name( $this->get_media()->get_context() );
|
||||
$webp_size_name = 'full' . constant( $process_class_name . '::WEBP_SUFFIX' );
|
||||
|
||||
$percent = $this->get_size_data( $webp_size_name, 'percent' );
|
||||
|
||||
if ( ! $percent ) {
|
||||
$percent = $this->get_size_data( 'full', 'percent' );
|
||||
}
|
||||
|
||||
$percent = $percent ? $percent : 0;
|
||||
|
||||
return round( (float) $percent, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall optimized/original saving (original image + all thumbnails) in percent.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return float A 2-decimals float.
|
||||
*/
|
||||
public function get_overall_saving_percent() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return round( (float) 0, 2 );
|
||||
}
|
||||
|
||||
$percent = $this->get_stats_data( 'percent' );
|
||||
|
||||
return round( (float) $percent, 2 );
|
||||
}
|
||||
}
|
||||
320
wp/plugins/imagify/classes/Optimization/Data/CustomFolders.php
Normal file
320
wp/plugins/imagify/classes/Optimization/Data/CustomFolders.php
Normal file
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Data;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Optimization data class for the custom folders.
|
||||
* This class constructor accepts:
|
||||
* - A media ID (int).
|
||||
* - An array of data coming from the files DB table /!\
|
||||
* - An object of data coming from the files DB table /!\
|
||||
* - A \Imagify\Media\MediaInterface object.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see Imagify\Media\CustomFolders
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class CustomFolders extends AbstractData {
|
||||
use \Imagify\Traits\MediaRowTrait;
|
||||
|
||||
/**
|
||||
* The attachment SQL DB class.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $db_class_name = 'Imagify_Files_DB';
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id An ID, or whatever type the "Media" class constructor accepts.
|
||||
*/
|
||||
public function __construct( $id ) {
|
||||
parent::__construct( $id );
|
||||
|
||||
if ( ! $this->is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is required by MediaRowTrait.
|
||||
$this->id = $this->get_media()->get_id();
|
||||
|
||||
// In this context, the media data and the optimization data are stored in the same DB table, so, no need to request twice the DB.
|
||||
$this->row = $this->get_media()->get_row();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the whole media optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array The data. See parent method for details.
|
||||
*/
|
||||
public function get_optimization_data() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return $this->default_optimization_data;
|
||||
}
|
||||
|
||||
$row = array_merge( $this->get_row_db_instance()->get_column_defaults(), $this->get_row() );
|
||||
$data = $this->default_optimization_data;
|
||||
|
||||
$data['status'] = $row['status'];
|
||||
$data['level'] = $row['optimization_level'];
|
||||
$data['level'] = is_numeric( $data['level'] ) ? (int) $data['level'] : false;
|
||||
|
||||
if ( 'success' === $row['status'] ) {
|
||||
/**
|
||||
* Success.
|
||||
*/
|
||||
$data['sizes']['full'] = [
|
||||
'success' => true,
|
||||
'original_size' => $row['original_size'],
|
||||
'optimized_size' => $row['optimized_size'],
|
||||
'percent' => $row['percent'],
|
||||
];
|
||||
} elseif ( ! empty( $row['status'] ) ) {
|
||||
/**
|
||||
* Error.
|
||||
*/
|
||||
$data['sizes']['full'] = [
|
||||
'success' => false,
|
||||
'error' => $row['error'],
|
||||
];
|
||||
}
|
||||
|
||||
if ( ! empty( $row['data']['sizes'] ) && is_array( $row['data']['sizes'] ) ) {
|
||||
unset( $row['data']['sizes']['full'] );
|
||||
$data['sizes'] = array_merge( $data['sizes'], $row['data']['sizes'] );
|
||||
$data['sizes'] = array_filter( $data['sizes'], 'is_array' );
|
||||
}
|
||||
|
||||
if ( empty( $data['sizes'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
foreach ( $data['sizes'] as $size_data ) {
|
||||
// Cast.
|
||||
if ( isset( $size_data['original_size'] ) ) {
|
||||
$size_data['original_size'] = (int) $size_data['original_size'];
|
||||
}
|
||||
if ( isset( $size_data['optimized_size'] ) ) {
|
||||
$size_data['optimized_size'] = (int) $size_data['optimized_size'];
|
||||
}
|
||||
if ( isset( $size_data['percent'] ) ) {
|
||||
$size_data['percent'] = round( $size_data['percent'], 2 );
|
||||
}
|
||||
// Stats.
|
||||
if ( ! empty( $size_data['original_size'] ) && ! empty( $size_data['optimized_size'] ) ) {
|
||||
$data['stats']['original_size'] += $size_data['original_size'];
|
||||
$data['stats']['optimized_size'] += $size_data['optimized_size'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( $data['stats']['original_size'] && $data['stats']['optimized_size'] ) {
|
||||
$data['stats']['percent'] = $data['stats']['original_size'] - $data['stats']['optimized_size'];
|
||||
$data['stats']['percent'] = round( $data['stats']['percent'] / $data['stats']['original_size'] * 100, 2 );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the optimization data, level, and status for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param array $data The optimization data. See parent method for details.
|
||||
*/
|
||||
public function update_size_optimization_data( $size, array $data ) {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$old_data = array_merge( $this->get_reset_data(), $this->get_row() );
|
||||
|
||||
if ( 'full' === $size ) {
|
||||
/**
|
||||
* Original file.
|
||||
*/
|
||||
$old_data['optimization_level'] = $data['level'];
|
||||
$old_data['status'] = $data['status'];
|
||||
$old_data['modified'] = 0;
|
||||
|
||||
$file_path = $this->get_media()->get_fullsize_path();
|
||||
|
||||
if ( $file_path ) {
|
||||
$old_data['hash'] = md5_file( $file_path );
|
||||
}
|
||||
|
||||
if ( key_exists( 'message', $data ) ) {
|
||||
$old_data['message'] = $data['message'];
|
||||
}
|
||||
|
||||
if ( ! $data['success'] ) {
|
||||
/**
|
||||
* Error.
|
||||
*/
|
||||
$old_data['error'] = $data['error'];
|
||||
} else {
|
||||
/**
|
||||
* Success.
|
||||
*/
|
||||
$old_data['original_size'] = $data['original_size'];
|
||||
$old_data['optimized_size'] = $data['optimized_size'];
|
||||
$old_data['percent'] = $data['original_size'] - $data['optimized_size'];
|
||||
$old_data['percent'] = round( ( $old_data['percent'] / $data['original_size'] ) * 100, 2 );
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* WebP version or any other size.
|
||||
*/
|
||||
$old_data['data'] = ! empty( $old_data['data'] ) && is_array( $old_data['data'] ) ? $old_data['data'] : [];
|
||||
$old_data['data']['sizes'] = ! empty( $old_data['data']['sizes'] ) && is_array( $old_data['data']['sizes'] ) ? $old_data['data']['sizes'] : [];
|
||||
|
||||
if ( ! $data['success'] ) {
|
||||
/**
|
||||
* Error.
|
||||
*/
|
||||
$old_data['data']['sizes'][ $size ] = [
|
||||
'success' => false,
|
||||
'error' => $data['error'],
|
||||
];
|
||||
} else {
|
||||
/**
|
||||
* Success.
|
||||
*/
|
||||
$old_data['data']['sizes'][ $size ] = [
|
||||
'success' => true,
|
||||
'original_size' => $data['original_size'],
|
||||
'optimized_size' => $data['optimized_size'],
|
||||
'percent' => round( ( ( $data['original_size'] - $data['optimized_size'] ) / $data['original_size'] ) * 100, 2 ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $old_data['data']['sizes'] ) && ( ! $old_data['data']['sizes'] || ! is_array( $old_data['data']['sizes'] ) ) ) {
|
||||
unset( $old_data['data']['sizes'] );
|
||||
}
|
||||
|
||||
$this->update_row( $old_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the media optimization data, level, and status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_optimization_data() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->update_row( $this->get_reset_data() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the optimization data for the given sizes.
|
||||
* If all sizes are removed, all optimization data is deleted.
|
||||
* Status and level are not modified nor removed if the "full" size is removed. This leaves the media in a Schrödinger state.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $sizes A list of sizes to remove.
|
||||
*/
|
||||
public function delete_sizes_optimization_data( array $sizes ) {
|
||||
if ( ! $sizes || ! $this->is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = array_merge( $this->get_reset_data(), $this->get_row() );
|
||||
|
||||
$data['data']['sizes'] = ! empty( $data['data']['sizes'] ) && is_array( $data['data']['sizes'] ) ? $data['data']['sizes'] : [];
|
||||
|
||||
if ( ! $data['data']['sizes'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$remaining_sizes_data = array_diff_key( $data['data']['sizes'], array_flip( $sizes ) );
|
||||
|
||||
if ( ! $remaining_sizes_data ) {
|
||||
// All sizes have been removed: delete everything.
|
||||
$this->delete_optimization_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count( $remaining_sizes_data ) === count( $data['data']['sizes'] ) ) {
|
||||
// Nothing has been removed.
|
||||
return;
|
||||
}
|
||||
|
||||
$data['data']['sizes'] = $remaining_sizes_data;
|
||||
|
||||
$this->update_row( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default values used to reset optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* The default values related to the optimization.
|
||||
*
|
||||
* @type string $hash The file hash.
|
||||
* @type int $modified 0 to tell that the file has not been modified
|
||||
* @type int $optimized_size File size after optimization.
|
||||
* @type int $percent Saving optimized/original in percent.
|
||||
* @type int $optimization_level The optimization level.
|
||||
* @type string $status The status: success, already_optimized, error.
|
||||
* @type string $error An error message.
|
||||
* }
|
||||
*/
|
||||
protected function get_reset_data() {
|
||||
static $column_defaults;
|
||||
|
||||
if ( ! isset( $column_defaults ) ) {
|
||||
$column_defaults = $this->get_row_db_instance()->get_column_defaults();
|
||||
|
||||
// All DB columns that have `null` as default value, are Imagify data.
|
||||
foreach ( $column_defaults as $column_name => $value ) {
|
||||
if ( 'hash' === $column_name || 'modified' === $column_name || 'data' === $column_name ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $value ) ) {
|
||||
unset( $column_defaults[ $column_name ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$imagify_columns = $column_defaults;
|
||||
|
||||
// Also set the new file hash.
|
||||
$file_path = $this->get_media()->get_fullsize_path();
|
||||
|
||||
if ( $file_path ) {
|
||||
$imagify_columns['hash'] = md5_file( $file_path );
|
||||
}
|
||||
|
||||
return $imagify_columns;
|
||||
}
|
||||
}
|
||||
275
wp/plugins/imagify/classes/Optimization/Data/DataInterface.php
Normal file
275
wp/plugins/imagify/classes/Optimization/Data/DataInterface.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Data;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Interface to use to handle the optimization data of "media groups" (aka attachments).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface DataInterface {
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id );
|
||||
|
||||
/**
|
||||
* Get the media instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return MediaInterface|false
|
||||
*/
|
||||
public function get_media();
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION DATA ======================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_optimized();
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (NOT by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_already_optimized();
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_error();
|
||||
|
||||
/**
|
||||
* Get the whole media optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array {
|
||||
* The data.
|
||||
*
|
||||
* @type string $status The optimization status of the whole media: 'success', 'already_optimized', or 'error'.
|
||||
* It is the same as the main file’s status.
|
||||
* @type int|bool $level The optimization level (0=normal, 1=aggressive, 2=ultra). False if not set.
|
||||
* @type array $sizes {
|
||||
* A list of size data, keyed by size name, and containing:
|
||||
*
|
||||
* @type bool $success Whether the optimization has been successful.
|
||||
* If a success:
|
||||
* @type int $original_size The file size before optimization.
|
||||
* @type int $optimized_size The file size after optimization.
|
||||
* @type int $percent Saving in percent.
|
||||
* If an error or 'already_optimized':
|
||||
* @type string $error An error message.
|
||||
* }
|
||||
* @type array $stats {
|
||||
* @type int $original_size Overall size before optimization.
|
||||
* @type int $optimized_size Overall size after optimization.
|
||||
* @type int $percent Overall saving in percent.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimization_data();
|
||||
|
||||
/**
|
||||
* Update the optimization data, level, and status for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param array $data {
|
||||
* The optimization data.
|
||||
*
|
||||
* @type int $level The optimization level.
|
||||
* @type string $status The status: 'success', 'already_optimized', 'error'.
|
||||
* @type bool $success True if successfully optimized. False on error or if already optimized.
|
||||
* @type string $error An error message.
|
||||
* @type int $original_size The weight of the file, before optimization.
|
||||
* @type int $optimized_size The weight of the file, after optimization.
|
||||
* }
|
||||
*/
|
||||
public function update_size_optimization_data( $size, array $data );
|
||||
|
||||
/**
|
||||
* Delete the media optimization data, level, and status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_optimization_data();
|
||||
|
||||
/**
|
||||
* Delete the optimization data for the given sizes.
|
||||
* If all sizes are removed, all optimization data is deleted.
|
||||
* Status and level are not modified nor removed if the "full" size is removed. This leaves the media in a Schrödinger state.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $sizes A list of sizes to remove.
|
||||
*/
|
||||
public function delete_sizes_optimization_data( array $sizes );
|
||||
|
||||
/**
|
||||
* Get the media's optimization level.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int|bool The optimization level. False if not optimized.
|
||||
*/
|
||||
public function get_optimization_level();
|
||||
|
||||
/**
|
||||
* Get the media's optimization status (success or error).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string The optimization status. An empty string if there is none.
|
||||
*/
|
||||
public function get_optimization_status();
|
||||
|
||||
/**
|
||||
* Count number of optimized sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int Number of optimized sizes.
|
||||
*/
|
||||
public function get_optimized_sizes_count();
|
||||
|
||||
/**
|
||||
* Get the original media's size (weight).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $human_format True to display the image human format size (1Mb).
|
||||
* @param int $decimals Precision of number of decimal places.
|
||||
* @return string|int
|
||||
*/
|
||||
public function get_original_size( $human_format = true, $decimals = 2 );
|
||||
|
||||
/**
|
||||
* Get the file size of the full size file.
|
||||
* If the WebP size is available, it is used.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $human_format True to display the image human format size (1Mb).
|
||||
* @param int $decimals Precision of number of decimal places.
|
||||
* @param bool $use_webp Use the WebP size if available.
|
||||
* @return string|int
|
||||
*/
|
||||
public function get_optimized_size( $human_format = true, $decimals = 2, $use_webp = true );
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION STATS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get one or all statistics of a specific size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The thumbnail slug.
|
||||
* @param string $key The specific data slug.
|
||||
* @return array|string
|
||||
*/
|
||||
public function get_size_data( $size = 'full', $key = '' );
|
||||
|
||||
/**
|
||||
* Get the overall statistics data or a specific one.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $key The specific data slug.
|
||||
* @return array|string
|
||||
*/
|
||||
public function get_stats_data( $key = '' );
|
||||
|
||||
/**
|
||||
* Get the optimized/original saving of the original image in percent.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return float A 2-decimals float.
|
||||
*/
|
||||
public function get_saving_percent();
|
||||
|
||||
/**
|
||||
* Get the overall optimized/original saving (original image + all thumbnails) in percent.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return float A 2-decimals float.
|
||||
*/
|
||||
public function get_overall_saving_percent();
|
||||
}
|
||||
285
wp/plugins/imagify/classes/Optimization/Data/Noop.php
Normal file
285
wp/plugins/imagify/classes/Optimization/Data/Noop.php
Normal file
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Data;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Fallback class optimization data of "media groups" (aka attachments).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Noop implements DataInterface {
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return MediaInterface|false
|
||||
*/
|
||||
public function get_media() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION DATA ======================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_optimized() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (NOT by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_already_optimized() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the main file is optimized (by Imagify).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True if the media is optimized.
|
||||
*/
|
||||
public function is_error() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the whole media optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array The data. See parent method for details.
|
||||
*/
|
||||
public function get_optimization_data() {
|
||||
return [
|
||||
'status' => '',
|
||||
'level' => false,
|
||||
'sizes' => [],
|
||||
'stats' => [
|
||||
'original_size' => 0,
|
||||
'optimized_size' => 0,
|
||||
'percent' => 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the optimization data, level, and status for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param array $data The optimization data. See parent method for details.
|
||||
*/
|
||||
public function update_size_optimization_data( $size, array $data ) {}
|
||||
|
||||
/**
|
||||
* Delete the media optimization data, level, and status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_optimization_data() {}
|
||||
|
||||
/**
|
||||
* Delete the optimization data for the given sizes.
|
||||
* If all sizes are removed, all optimization data is deleted.
|
||||
* Status and level are not modified nor removed if the "full" size is removed. This leaves the media in a Schrödinger state.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $sizes A list of sizes to remove.
|
||||
*/
|
||||
public function delete_sizes_optimization_data( array $sizes ) {}
|
||||
|
||||
/**
|
||||
* Get the media's optimization level.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int|bool The optimization level. False if not optimized.
|
||||
*/
|
||||
public function get_optimization_level() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media's optimization status (success or error).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string The optimization status. An empty string if there is none.
|
||||
*/
|
||||
public function get_optimization_status() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of optimized sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return int Number of optimized sizes.
|
||||
*/
|
||||
public function get_optimized_sizes_count() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original media's size (weight).
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $human_format True to display the image human format size (1Mb).
|
||||
* @param int $decimals Precision of number of decimal places.
|
||||
* @return string|int
|
||||
*/
|
||||
public function get_original_size( $human_format = true, $decimals = 2 ) {
|
||||
return $human_format ? imagify_size_format( 0, $decimals ) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file size of the full size file.
|
||||
* If the WebP size is available, it is used.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $human_format True to display the image human format size (1Mb).
|
||||
* @param int $decimals Precision of number of decimal places.
|
||||
* @param bool $use_webp Use the WebP size if available.
|
||||
* @return string|int
|
||||
*/
|
||||
public function get_optimized_size( $human_format = true, $decimals = 2, $use_webp = true ) {
|
||||
return $human_format ? imagify_size_format( 0, $decimals ) : 0;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION STATS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get one or all statistics of a specific size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The thumbnail slug.
|
||||
* @param string $key The specific data slug.
|
||||
* @return array|string
|
||||
*/
|
||||
public function get_size_data( $size = 'full', $key = '' ) {
|
||||
return $key ? '' : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall statistics data or a specific one.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $key The specific data slug.
|
||||
* @return array|string
|
||||
*/
|
||||
public function get_stats_data( $key = '' ) {
|
||||
return $key ? '' : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optimized/original saving of the original image in percent.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return float A 2-decimals float.
|
||||
*/
|
||||
public function get_saving_percent() {
|
||||
return round( (float) 0, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall optimized/original saving (original image + all thumbnails) in percent.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return float A 2-decimals float.
|
||||
*/
|
||||
public function get_overall_saving_percent() {
|
||||
return round( (float) 0, 2 );
|
||||
}
|
||||
}
|
||||
207
wp/plugins/imagify/classes/Optimization/Data/WP.php
Normal file
207
wp/plugins/imagify/classes/Optimization/Data/WP.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Data;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Optimization data class for the medias in the WP library.
|
||||
* This class constructor accepts:
|
||||
* - A post ID (int).
|
||||
* - A \WP_Post object.
|
||||
* - A \Imagify\Media\MediaInterface object.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class WP extends AbstractData {
|
||||
|
||||
/**
|
||||
* Get the whole media optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array The data. See parent method for details.
|
||||
*/
|
||||
public function get_optimization_data() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return $this->default_optimization_data;
|
||||
}
|
||||
|
||||
$id = $this->get_media()->get_id();
|
||||
|
||||
$data = get_post_meta( $id, '_imagify_data', true );
|
||||
$data = is_array( $data ) ? $data : [];
|
||||
|
||||
if ( isset( $data['sizes'] ) && ! is_array( $data['sizes'] ) ) {
|
||||
$data['sizes'] = [];
|
||||
}
|
||||
|
||||
if ( isset( $data['stats'] ) && ! is_array( $data['stats'] ) ) {
|
||||
$data['stats'] = [];
|
||||
}
|
||||
|
||||
$data = array_merge( $this->default_optimization_data, $data );
|
||||
|
||||
$data['status'] = get_post_meta( $id, '_imagify_status', true );
|
||||
$data['status'] = is_string( $data['status'] ) ? $data['status'] : '';
|
||||
|
||||
$data['level'] = get_post_meta( $id, '_imagify_optimization_level', true );
|
||||
$data['level'] = is_numeric( $data['level'] ) ? (int) $data['level'] : false;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the optimization data, level, and status for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param array $data The optimization data. See parent method for details.
|
||||
*/
|
||||
public function update_size_optimization_data( $size, array $data ) {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $this->get_media()->get_id();
|
||||
|
||||
if ( 'full' === $size ) {
|
||||
// Optimization level.
|
||||
update_post_meta( $id, '_imagify_optimization_level', $data['level'] );
|
||||
// Optimization status.
|
||||
update_post_meta( $id, '_imagify_status', $data['status'] );
|
||||
}
|
||||
|
||||
// Size data and stats.
|
||||
$old_data = get_post_meta( $id, '_imagify_data', true );
|
||||
$old_data = is_array( $old_data ) ? $old_data : [];
|
||||
|
||||
if ( ! isset( $old_data['sizes'] ) || ! is_array( $old_data['sizes'] ) ) {
|
||||
$old_data['sizes'] = [];
|
||||
}
|
||||
|
||||
if ( ! isset( $old_data['stats'] ) || ! is_array( $old_data['stats'] ) ) {
|
||||
$old_data['stats'] = [];
|
||||
}
|
||||
|
||||
$old_data['stats'] = array_merge( [
|
||||
'original_size' => 0,
|
||||
'optimized_size' => 0,
|
||||
'percent' => 0,
|
||||
'message' => '',
|
||||
], $old_data['stats'] );
|
||||
|
||||
if ( key_exists( 'message', $data ) ) {
|
||||
$old_data['message'] = $data['message'];
|
||||
}
|
||||
|
||||
if ( ! $data['success'] ) {
|
||||
/**
|
||||
* Error.
|
||||
*/
|
||||
$old_data['sizes'][ $size ] = [
|
||||
'success' => false,
|
||||
'error' => $data['error'],
|
||||
];
|
||||
} else {
|
||||
/**
|
||||
* Success.
|
||||
*/
|
||||
$old_data['sizes'][ $size ] = [
|
||||
'success' => true,
|
||||
'original_size' => $data['original_size'],
|
||||
'optimized_size' => $data['optimized_size'],
|
||||
'percent' => round( ( ( $data['original_size'] - $data['optimized_size'] ) / $data['original_size'] ) * 100, 2 ),
|
||||
];
|
||||
|
||||
$old_data['stats']['original_size'] += $data['original_size'];
|
||||
$old_data['stats']['optimized_size'] += $data['optimized_size'];
|
||||
$old_data['stats']['percent'] = round( ( ( $old_data['stats']['original_size'] - $old_data['stats']['optimized_size'] ) / $old_data['stats']['original_size'] ) * 100, 2 );
|
||||
}
|
||||
|
||||
update_post_meta( $id, '_imagify_data', $old_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the media optimization data, level, and status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_optimization_data() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $this->get_media()->get_id();
|
||||
|
||||
delete_post_meta( $id, '_imagify_data' );
|
||||
delete_post_meta( $id, '_imagify_status' );
|
||||
delete_post_meta( $id, '_imagify_optimization_level' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the optimization data for the given sizes.
|
||||
* If all sizes are removed, all optimization data is deleted.
|
||||
* Status and level are not modified nor removed if the "full" size is removed. This leaves the media in a Schrödinger state.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $sizes A list of sizes to remove.
|
||||
*/
|
||||
public function delete_sizes_optimization_data( array $sizes ) {
|
||||
if ( ! $sizes || ! $this->is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$media_id = $this->get_media()->get_id();
|
||||
$data = get_post_meta( $media_id, '_imagify_data', true );
|
||||
|
||||
if ( empty( $data['sizes'] ) || ! is_array( $data['sizes'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$remaining_sizes_data = array_diff_key( $data['sizes'], array_flip( $sizes ) );
|
||||
|
||||
if ( ! $remaining_sizes_data ) {
|
||||
// All sizes have been removed: delete everything.
|
||||
$this->delete_optimization_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count( $remaining_sizes_data ) === count( $data['sizes'] ) ) {
|
||||
// Nothing has been removed.
|
||||
return;
|
||||
}
|
||||
|
||||
$data['sizes'] = $remaining_sizes_data;
|
||||
|
||||
// Update stats.
|
||||
$data['stats'] = [
|
||||
'original_size' => 0,
|
||||
'optimized_size' => 0,
|
||||
'percent' => 0,
|
||||
];
|
||||
|
||||
foreach ( $data['sizes'] as $size_data ) {
|
||||
if ( empty( $size_data['success'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['stats']['original_size'] += $size_data['original_size'];
|
||||
$data['stats']['optimized_size'] += $size_data['optimized_size'];
|
||||
}
|
||||
|
||||
$data['stats']['percent'] = round( ( ( $data['stats']['original_size'] - $data['stats']['optimized_size'] ) / $data['stats']['original_size'] ) * 100, 2 );
|
||||
|
||||
update_post_meta( $media_id, '_imagify_data', $data );
|
||||
}
|
||||
}
|
||||
861
wp/plugins/imagify/classes/Optimization/File.php
Normal file
861
wp/plugins/imagify/classes/Optimization/File.php
Normal file
@@ -0,0 +1,861 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization;
|
||||
|
||||
use Imagify_Requirements;
|
||||
|
||||
/**
|
||||
* A generic optimization class focussed on the file itself.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class File {
|
||||
|
||||
/**
|
||||
* Absolute path to the file.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Tell if the file is an image.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.9
|
||||
* @see $this->is_image()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $is_image;
|
||||
|
||||
/**
|
||||
* Store the file mime type + file extension (if the file is supported).
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9
|
||||
* @see $this->get_file_type()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $file_type;
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var \Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* The editor instance used to resize the file.
|
||||
*
|
||||
* @var \WP_Image_Editor_Imagick|\WP_Image_Editor_GD|WP_Error.
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $editor;
|
||||
|
||||
/**
|
||||
* Used to cache the plugin’s options.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path Absolute path to the file.
|
||||
*/
|
||||
public function __construct( $file_path ) {
|
||||
$this->path = $file_path;
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
return (bool) $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file can be processed.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function can_be_processed() {
|
||||
if ( ! $this->path ) {
|
||||
return new \WP_Error( 'empty_path', __( 'File path is empty.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $this->filesystem->errors->errors ) ) {
|
||||
return new \WP_Error( 'filesystem_error', __( 'Filesystem error.', 'imagify' ), $this->filesystem->errors );
|
||||
}
|
||||
|
||||
if ( ! $this->filesystem->exists( $this->path ) ) {
|
||||
return new \WP_Error(
|
||||
'not_exists',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to exist.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $this->filesystem->is_file( $this->path ) ) {
|
||||
return new \WP_Error(
|
||||
'not_a_file',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
__( 'This does not seem to be a file: %s.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $this->filesystem->is_writable( $this->path ) ) {
|
||||
return new \WP_Error(
|
||||
'not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to be writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$parent_folder = $this->filesystem->dir_path( $this->path );
|
||||
|
||||
if ( ! $this->filesystem->is_writable( $parent_folder ) ) {
|
||||
return new \WP_Error(
|
||||
'folder_not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The folder %s does not seem to be writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $parent_folder ) ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** EDITION ================================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Resize (and rotate) an image if it is bigger than the maximum width provided.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @author Remy Perona
|
||||
*
|
||||
* @param array $dimensions {
|
||||
* Array of image dimensions.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* }
|
||||
* @param int $max_width Maximum width to resize to.
|
||||
* @return string|WP_Error Path the the resized image. A WP_Error object on failure.
|
||||
*/
|
||||
public function resize( $dimensions = [], $max_width = 0 ) {
|
||||
$can_be_processed = $this->can_be_processed();
|
||||
|
||||
if ( is_wp_error( $can_be_processed ) ) {
|
||||
return $can_be_processed;
|
||||
}
|
||||
|
||||
if ( ! $max_width ) {
|
||||
return new \WP_Error(
|
||||
'no_resizing_threshold',
|
||||
__( 'No threshold provided for resizing.', 'imagify' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $this->is_image() ) {
|
||||
return new \WP_Error(
|
||||
'not_an_image',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$editor = $this->get_editor();
|
||||
|
||||
if ( is_wp_error( $editor ) ) {
|
||||
return $editor;
|
||||
}
|
||||
|
||||
// Try to correct the auto-rotation if the info is available.
|
||||
if ( $this->filesystem->can_get_exif() && 'image/jpeg' === $this->get_mime_type() ) {
|
||||
$exif = $this->filesystem->get_image_exif( $this->path );
|
||||
$orientation = isset( $exif['Orientation'] ) ? (int) $exif['Orientation'] : 1;
|
||||
|
||||
switch ( $orientation ) {
|
||||
case 2:
|
||||
// Flip horizontally.
|
||||
$editor->flip( true, false );
|
||||
break;
|
||||
case 3:
|
||||
// Rotate 180 degrees or flip horizontally and vertically.
|
||||
// Flipping seems faster/uses less resources.
|
||||
$editor->flip( true, true );
|
||||
break;
|
||||
case 4:
|
||||
// Flip vertically.
|
||||
$editor->flip( false, true );
|
||||
break;
|
||||
case 5:
|
||||
// Rotate 90 degrees counter-clockwise and flip vertically.
|
||||
$result = $editor->rotate( 90 );
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
$editor->flip( false, true );
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
// Rotate 90 degrees clockwise (270 counter-clockwise).
|
||||
$editor->rotate( 270 );
|
||||
break;
|
||||
case 7:
|
||||
// Rotate 90 degrees counter-clockwise and flip horizontally.
|
||||
$result = $editor->rotate( 90 );
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
$editor->flip( true, false );
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
// Rotate 90 degrees counter-clockwise.
|
||||
$editor->rotate( 90 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $dimensions ) {
|
||||
$dimensions = $this->get_dimensions();
|
||||
}
|
||||
|
||||
// Prevent removal of the exif data when resizing (only works with Imagick).
|
||||
add_filter( 'image_strip_meta', '__return_false', 789 );
|
||||
|
||||
// Resize.
|
||||
$new_sizes = wp_constrain_dimensions( $dimensions['width'], $dimensions['height'], $max_width );
|
||||
$resized = $editor->resize( $new_sizes[0], $new_sizes[1], false );
|
||||
|
||||
// Remove the filter when we're done to prevent any conflict.
|
||||
remove_filter( 'image_strip_meta', '__return_false', 789 );
|
||||
|
||||
if ( is_wp_error( $resized ) ) {
|
||||
return $resized;
|
||||
}
|
||||
|
||||
$resized_image_path = $editor->generate_filename( 'imagifyresized' );
|
||||
$resized_image_saved = $editor->save( $resized_image_path );
|
||||
|
||||
if ( is_wp_error( $resized_image_saved ) ) {
|
||||
return $resized_image_saved;
|
||||
}
|
||||
|
||||
return $resized_image_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a thumbnail.
|
||||
* Warning: If the destination file already exists, it will be overwritten.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $destination {
|
||||
* The thumbnail data.
|
||||
*
|
||||
* @type string $path Path to the destination file.
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type bool $adjust_filename True to adjust the file name like what `$editor->multi_resize()` returns, like WP default behavior (default). False to prevent it, and use the file name from $path instead.
|
||||
* }
|
||||
* @return bool|array|WP_Error {
|
||||
* A WP_Error object on error. True if the file exists.
|
||||
* An array of thumbnail data if the file has just been created:
|
||||
*
|
||||
* @type string $file File name.
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type string $mime-type The mime type.
|
||||
* }
|
||||
*/
|
||||
public function create_thumbnail( $destination ) {
|
||||
$can_be_processed = $this->can_be_processed();
|
||||
|
||||
if ( is_wp_error( $can_be_processed ) ) {
|
||||
return $can_be_processed;
|
||||
}
|
||||
|
||||
if ( ! $this->is_image() ) {
|
||||
return new \WP_Error(
|
||||
'not_an_image',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$editor = $this->get_editor();
|
||||
|
||||
if ( is_wp_error( $editor ) ) {
|
||||
return $editor;
|
||||
}
|
||||
|
||||
// Create the file.
|
||||
$result = $editor->multi_resize( [ $destination ] );
|
||||
|
||||
if ( ! $result ) {
|
||||
return new \WP_Error( 'image_resize_error', __( 'The thumbnail could not be created.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$result = reset( $result );
|
||||
|
||||
$filename = $result['file'];
|
||||
$source_thumb_path = $this->filesystem->dir_path( $this->path ) . $filename;
|
||||
|
||||
if ( ! isset( $destination['adjust_filename'] ) || $destination['adjust_filename'] ) {
|
||||
// The file name can change from what we expected (1px wider, etc), let's use the resulting data to move the file to the right place.
|
||||
$destination_thumb_path = $this->filesystem->dir_path( $destination['path'] ) . $filename;
|
||||
} else {
|
||||
// Respect what is set in $path.
|
||||
$destination_thumb_path = $destination['path'];
|
||||
$result['file'] = $this->filesystem->file_name( $destination['path'] );
|
||||
}
|
||||
|
||||
if ( $source_thumb_path === $destination_thumb_path ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$moved = $this->filesystem->move( $source_thumb_path, $destination_thumb_path, true );
|
||||
|
||||
if ( ! $moved ) {
|
||||
return new \WP_Error( 'move_error', __( 'The file could not be moved to its final destination.', 'imagify' ) );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup a file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.8 Added $backup_source argument.
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $backup_path The backup path.
|
||||
* @param string $backup_source Path to the file to backup. This is useful in WP 5.3+ when we want to optimize the full size: in that case we need to backup the original file.
|
||||
* @return bool|WP_Error True on success. False if the backup option is disabled. A WP_Error object on failure.
|
||||
*/
|
||||
public function backup( $backup_path = null, $backup_source = null ) {
|
||||
$can_be_processed = $this->can_be_processed();
|
||||
|
||||
if ( is_wp_error( $can_be_processed ) ) {
|
||||
return $can_be_processed;
|
||||
}
|
||||
|
||||
// Make sure the backups directory has no errors.
|
||||
if ( ! $backup_path ) {
|
||||
return new \WP_Error( 'wp_upload_error', __( 'Error while retrieving the backups directory path.', 'imagify' ) );
|
||||
}
|
||||
|
||||
// Create sub-directories.
|
||||
$created = $this->filesystem->make_dir( $this->filesystem->dir_path( $backup_path ) );
|
||||
|
||||
if ( ! $created ) {
|
||||
return new \WP_Error( 'backup_dir_not_writable', __( 'The backup directory is not writable.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$path = $backup_source && $this->filesystem->exists( $backup_source ) ? $backup_source : $this->path;
|
||||
|
||||
/**
|
||||
* Allow to overwrite the backup file if it already exists.
|
||||
*
|
||||
* @since 1.6.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $overwrite Whether to overwrite the backup file.
|
||||
* @param string $path The file path.
|
||||
* @param string $backup_path The backup path.
|
||||
*/
|
||||
$overwrite = apply_filters( 'imagify_backup_overwrite_backup', false, $path, $backup_path );
|
||||
|
||||
// Copy the file.
|
||||
$this->filesystem->copy( $path, $backup_path, $overwrite, FS_CHMOD_FILE );
|
||||
|
||||
// Make sure the backup copy exists.
|
||||
if ( ! $this->filesystem->exists( $backup_path ) ) {
|
||||
return new \WP_Error( 'backup_doesnt_exist', __( 'The file could not be saved.', 'imagify' ), array(
|
||||
'file_path' => $this->filesystem->make_path_relative( $path ),
|
||||
'backup_path' => $this->filesystem->make_path_relative( $backup_path ),
|
||||
) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize a file with Imagify.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional. An array of arguments.
|
||||
*
|
||||
* @type bool $backup False to prevent backup. True to follow the user's setting. A backup can't be forced.
|
||||
* @type string $backup_path If a backup must be done, this is the path to use. Default is the backup path used for the WP Media Library.
|
||||
* @type int $optimization_level The optimization level (2=ultra, 1=aggressive, 0=normal).
|
||||
* @type string $convert Set to 'webp' to convert the image to WebP.
|
||||
* @type string $context The context.
|
||||
* @type int $original_size The file size, sent to the API.
|
||||
* }
|
||||
* @return \sdtClass|\WP_Error Optimized image data. A \WP_Error object on error.
|
||||
*/
|
||||
public function optimize( $args = [] ) {
|
||||
$args = array_merge( [
|
||||
'backup' => true,
|
||||
'backup_path' => null,
|
||||
'backup_source' => null,
|
||||
'optimization_level' => 0,
|
||||
'convert' => '',
|
||||
'context' => 'wp',
|
||||
'original_size' => 0,
|
||||
], $args );
|
||||
|
||||
$can_be_processed = $this->can_be_processed();
|
||||
|
||||
if ( is_wp_error( $can_be_processed ) ) {
|
||||
return $can_be_processed;
|
||||
}
|
||||
|
||||
// Check if external HTTP requests are blocked.
|
||||
if ( Imagify_Requirements::is_imagify_blocked() ) {
|
||||
return new \WP_Error( 'http_block_external', __( 'External HTTP requests are blocked.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires before a media file optimization.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $path Absolute path to the media file.
|
||||
* @param array $args Arguments passed to the method.
|
||||
*/
|
||||
do_action( 'imagify_before_optimize_file', $this->path, $args );
|
||||
|
||||
/**
|
||||
* Fires before to optimize the Image with Imagify.
|
||||
*
|
||||
* @since 1.0
|
||||
* @deprecated
|
||||
*
|
||||
* @param string $path Absolute path to the image file.
|
||||
* @param bool $backup True if a backup will be make.
|
||||
*/
|
||||
do_action_deprecated( 'before_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
|
||||
|
||||
if ( $args['backup'] ) {
|
||||
$backup_result = $this->backup( $args['backup_path'], $args['backup_source'] );
|
||||
|
||||
if ( is_wp_error( $backup_result ) ) {
|
||||
// Stop the process if we can't backup the file.
|
||||
return $backup_result;
|
||||
}
|
||||
}
|
||||
|
||||
// Send file for optimization and fetch the response.
|
||||
$data = [
|
||||
'normal' => 0 === $args['optimization_level'],
|
||||
'aggressive' => 1 === $args['optimization_level'],
|
||||
'ultra' => 2 === $args['optimization_level'],
|
||||
'keep_exif' => true,
|
||||
'original_size' => $args['original_size'],
|
||||
'context' => $args['context'],
|
||||
];
|
||||
|
||||
if ( $args['convert'] ) {
|
||||
$data['convert'] = $args['convert'];
|
||||
}
|
||||
|
||||
$response = upload_imagify_image( [
|
||||
'image' => $this->path,
|
||||
'data' => wp_json_encode( $data ),
|
||||
] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return new \WP_Error( 'api_error', $response->get_error_message() );
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'download_url' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
}
|
||||
|
||||
$temp_file = download_url( $response->image );
|
||||
|
||||
if ( is_wp_error( $temp_file ) ) {
|
||||
return new \WP_Error( 'temp_file_not_found', $temp_file->get_error_message() );
|
||||
}
|
||||
|
||||
if ( property_exists( $response, 'message' ) ) {
|
||||
$args['convert'] = '';
|
||||
}
|
||||
|
||||
if ( 'webp' === $args['convert'] ) {
|
||||
$destination_path = $this->get_path_to_webp();
|
||||
$this->path = $destination_path;
|
||||
$this->file_type = null;
|
||||
$this->editor = null;
|
||||
} else {
|
||||
$destination_path = $this->path;
|
||||
}
|
||||
|
||||
$moved = $this->filesystem->move( $temp_file, $destination_path, true );
|
||||
|
||||
if ( ! $moved ) {
|
||||
return new \WP_Error( 'move_error', __( 'The file could not be moved to its final destination.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after to optimize the Image with Imagify.
|
||||
*
|
||||
* @since 1.0
|
||||
* @deprecated
|
||||
*
|
||||
* @param string $path Absolute path to the image file.
|
||||
* @param bool $backup True if a backup has been made.
|
||||
*/
|
||||
do_action_deprecated( 'after_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
|
||||
|
||||
/**
|
||||
* Fires after a media file optimization.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $path Absolute path to the media file.
|
||||
* @param array $args Arguments passed to the method.
|
||||
*/
|
||||
do_action( 'imagify_after_optimize_file', $this->path, $args );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** IMAGE EDITOR (GD/IMAGEMAGICK) =========================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get an image editor instance (WP_Image_Editor_Imagick, WP_Image_Editor_GD).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return WP_Image_Editor_Imagick|WP_Image_Editor_GD|WP_Error
|
||||
*/
|
||||
protected function get_editor() {
|
||||
if ( isset( $this->editor ) ) {
|
||||
return $this->editor;
|
||||
}
|
||||
|
||||
$this->editor = wp_get_image_editor( $this->path, [
|
||||
'methods' => $this->get_editor_methods(),
|
||||
] );
|
||||
|
||||
if ( ! is_wp_error( $this->editor ) ) {
|
||||
return $this->editor;
|
||||
}
|
||||
|
||||
$this->editor = new \WP_Error(
|
||||
'image_editor',
|
||||
sprintf(
|
||||
/* translators: %1$s is an error message, %2$s is a "More info?" link. */
|
||||
__( 'No php extensions are available to edit images on the server. ImageMagick or GD is required. The internal error is: %1$s. %2$s', 'imagify' ),
|
||||
$this->editor->get_error_message(),
|
||||
'<a href="' . esc_url( imagify_get_external_url( 'documentation-imagick-gd' ) ) . '" target="_blank">' . __( 'More info?', 'imagify' ) . '</a>'
|
||||
)
|
||||
);
|
||||
|
||||
return $this->editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image editor methods we will use.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_editor_methods() {
|
||||
static $methods;
|
||||
|
||||
if ( isset( $methods ) ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
$methods = [
|
||||
'resize',
|
||||
'multi_resize',
|
||||
'generate_filename',
|
||||
'save',
|
||||
];
|
||||
|
||||
if ( $this->filesystem->can_get_exif() ) {
|
||||
$methods[] = 'rotate';
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** VARIOUS TOOLS =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Check if a file exceeds the weight limit (> 5mo).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_exceeded() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$size = $this->filesystem->size( $this->path );
|
||||
|
||||
return $size > IMAGIFY_MAX_BYTES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current file is supported for a given context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see imagify_get_mime_types()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $allowed_mime_types A list of allowed mime types.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_supported( $allowed_mime_types ) {
|
||||
return in_array( $this->get_mime_type(), $allowed_mime_types, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file is an image.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_image() {
|
||||
if ( isset( $this->is_image ) ) {
|
||||
return $this->is_image;
|
||||
}
|
||||
|
||||
$this->is_image = strpos( $this->get_mime_type(), 'image/' ) === 0;
|
||||
|
||||
return $this->is_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file is a pdf.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_pdf() {
|
||||
return 'application/pdf' === $this->get_mime_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file mime type.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_mime_type() {
|
||||
return $this->get_file_type()->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_extension() {
|
||||
return $this->get_file_type()->ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path() {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the file extension by WebP.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The file path on success. False if not an image or on failure.
|
||||
*/
|
||||
public function get_path_to_webp() {
|
||||
if ( ! $this->is_image() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->is_webp() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return imagify_path_to_webp( $this->path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file is a WebP image.
|
||||
* Rejects "path/to/.webp" files.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_webp() {
|
||||
return preg_match( '@(?!^|/|\\\)\.webp$@i', $this->path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file mime type + file extension.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see wp_check_filetype()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object {
|
||||
* @type string $ext The file extension.
|
||||
* @type string $type The mime type.
|
||||
* }
|
||||
*/
|
||||
protected function get_file_type() {
|
||||
if ( isset( $this->file_type ) ) {
|
||||
return $this->file_type;
|
||||
}
|
||||
|
||||
$this->file_type = (object) [
|
||||
'ext' => '',
|
||||
'type' => '',
|
||||
];
|
||||
|
||||
if ( ! $this->is_valid() ) {
|
||||
return $this->file_type;
|
||||
}
|
||||
|
||||
$this->file_type = (object) wp_check_filetype( $this->path );
|
||||
|
||||
return $this->file_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the media is an image, get its width and height.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_dimensions() {
|
||||
if ( ! $this->is_image() ) {
|
||||
return [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$values = $this->filesystem->get_image_size( $this->path );
|
||||
|
||||
if ( empty( $values ) ) {
|
||||
return [
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'width' => $values['width'],
|
||||
'height' => $values['height'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a plugin’s option.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $option_name The option nme.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_option( $option_name ) {
|
||||
if ( isset( $this->options[ $option_name ] ) ) {
|
||||
return $this->options[ $option_name ];
|
||||
}
|
||||
|
||||
$this->options[ $option_name ] = get_imagify_option( $option_name );
|
||||
|
||||
return $this->options[ $option_name ];
|
||||
}
|
||||
}
|
||||
1883
wp/plugins/imagify/classes/Optimization/Process/AbstractProcess.php
Normal file
1883
wp/plugins/imagify/classes/Optimization/Process/AbstractProcess.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Process;
|
||||
|
||||
use Imagify\Optimization\File;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Optimization class for the custom folders.
|
||||
* This class constructor accepts:
|
||||
* - A post ID (int).
|
||||
* - An array of data coming from the files DB table /!\
|
||||
* - An object of data coming from the files DB table /!\
|
||||
* - A \Imagify\Media\MediaInterface object.
|
||||
* - A \Imagify\Media\DataInterface object.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see Imagify\Media\CustomFolders
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class CustomFolders extends AbstractProcess {
|
||||
|
||||
/**
|
||||
* Restore the thumbnails.
|
||||
* This context has no thumbnails.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
protected function restore_thumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MISSING THUMBNAILS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the sizes for this media that have not get through optimization.
|
||||
* Since this context has no thumbnails, this will always return an empty array, unless an error is triggered.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array|WP_Error A WP_Error object on failure. An empty array on success: this context has no thumbnails.
|
||||
* The tests are kept for consistency.
|
||||
*/
|
||||
public function get_missing_sizes() {
|
||||
// The media must have been optimized once and have a backup.
|
||||
if ( ! $this->is_valid() ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$media = $this->get_media();
|
||||
|
||||
if ( ! $media->is_supported() ) {
|
||||
return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$data = $this->get_data();
|
||||
|
||||
if ( ! $data->is_optimized() ) {
|
||||
return new \WP_Error( 'media_not_optimized', __( 'This media is not optimized yet.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $media->has_backup() ) {
|
||||
return new \WP_Error( 'no_backup', __( 'This file has no backup file.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $media->is_image() ) {
|
||||
return new \WP_Error( 'media_not_an_image', __( 'This media is not an image.', 'imagify' ) );
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize missing thumbnail sizes.
|
||||
* Since this context has no thumbnails, this will always return a \WP_Error object.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_missing_thumbnails() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $this->get_media()->is_supported() ) {
|
||||
return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) );
|
||||
}
|
||||
|
||||
return new \WP_Error( 'no_sizes', __( 'No thumbnails seem to be missing.', 'imagify' ) );
|
||||
}
|
||||
}
|
||||
413
wp/plugins/imagify/classes/Optimization/Process/Noop.php
Normal file
413
wp/plugins/imagify/classes/Optimization/Process/Noop.php
Normal file
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Process;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Fallback class to optimize medias.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Noop implements ProcessInterface {
|
||||
|
||||
/**
|
||||
* The suffix used in the thumbnail size name.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const WEBP_SUFFIX = '@imagify-webp';
|
||||
|
||||
/**
|
||||
* The suffix used in file name to create a temporary copy of the full size.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const TMP_SUFFIX = '@imagify-tmp';
|
||||
|
||||
/**
|
||||
* Used for the name of the transient telling if a media is locked.
|
||||
* %1$s is the context, %2$s is the media ID.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const LOCK_NAME = 'imagify_%1$s_%2$s_process_locked';
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return DataInterface|false
|
||||
*/
|
||||
public function get_data() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the media instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return MediaInterface|false
|
||||
*/
|
||||
public function get_media() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the File instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return File|false
|
||||
*/
|
||||
public function get_file() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @return bool
|
||||
*/
|
||||
public function current_user_can( $describer ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Optimize a media files by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize( $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-optimize a media files with a different level.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function reoptimize( $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize several file sizes by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_sizes( $sizes, $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize one file with Imagify directly.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The media size.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array|WP_Error The optimization data. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_size( $size, $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the media files from the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function restore() {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MISSING THUMBNAILS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the sizes for this media that have not get through optimization.
|
||||
* No sizes are returned if the file is not optimized, has no backup, or is not an image.
|
||||
* The 'full' size os never returned.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* A WP_Error object on failure.
|
||||
* An array of data for the thumbnail sizes on success.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* @type string $file The name the thumbnail "should" have.
|
||||
* }
|
||||
*/
|
||||
public function get_missing_sizes() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize missing thumbnail sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_missing_thumbnails() {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Delete the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_backup() {}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** RESIZE FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Maybe resize an image.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param File $file A File instance.
|
||||
* @return array|WP_Error A \WP_Error instance on failure, an array on success as follow: {
|
||||
* @type bool $resized True when the image has been resized.
|
||||
* @type bool $backuped True when the image has been backuped.
|
||||
* @type int $file_size The file size in bytes.
|
||||
* }
|
||||
*/
|
||||
public function maybe_resize( $size, $file ) {
|
||||
return [
|
||||
'resized' => false,
|
||||
'backuped' => false,
|
||||
'file_size' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** WEBP ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_webp_versions() {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the WebP images.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_webp_files() {}
|
||||
|
||||
/**
|
||||
* Tell if a thumbnail size is an "Imagify WebP" size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size_name The size name.
|
||||
* @return string|bool The unsuffixed name of the size if WebP. False if not WebP.
|
||||
*/
|
||||
public function is_size_webp( $size_name ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PROCESS STATUS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if a process is running for this media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the running status to "running" for a period of time.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function lock() {}
|
||||
|
||||
/**
|
||||
* Delete the running status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function unlock() {}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** DATA ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if a size already has optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @return bool
|
||||
*/
|
||||
public function size_has_optimization_data( $size ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the optimization data for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param object $response The API response.
|
||||
* @param string $size The size name.
|
||||
* @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array {
|
||||
* The optimization data.
|
||||
*
|
||||
* @type string $size The size name.
|
||||
* @type int $level The optimization level.
|
||||
* @type string $status The status: 'success', 'already_optimized', 'error'.
|
||||
* @type bool $success True if successfully optimized. False on error or if already optimized.
|
||||
* @type string $error An error message.
|
||||
* @type int $original_size The weight of the file, before optimization.
|
||||
* @type int $optimized_size The weight of the file, once optimized.
|
||||
* }
|
||||
*/
|
||||
public function update_size_optimization_data( $response, $size, $level ) {
|
||||
return [
|
||||
'size' => 'noop',
|
||||
'level' => false,
|
||||
'status' => '',
|
||||
'success' => false,
|
||||
'error' => '',
|
||||
'original_size' => 0,
|
||||
'optimized_size' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Process;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Interface to use to optimize medias.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface ProcessInterface {
|
||||
|
||||
/**
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id );
|
||||
|
||||
/**
|
||||
* Get the data instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return DataInterface|false
|
||||
*/
|
||||
public function get_data();
|
||||
|
||||
/**
|
||||
* Get the media instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return MediaInterface|false
|
||||
*/
|
||||
public function get_media();
|
||||
|
||||
/**
|
||||
* Get the File instance of the original file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return File|false
|
||||
*/
|
||||
public function get_original_file();
|
||||
|
||||
/**
|
||||
* Get the File instance of the full size file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return File|false
|
||||
*/
|
||||
public function get_fullsize_file();
|
||||
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid();
|
||||
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @return bool
|
||||
*/
|
||||
public function current_user_can( $describer );
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Optimize a media files by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize( $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Re-optimize a media files with a different level.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function reoptimize( $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Optimize several file sizes by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_sizes( $sizes, $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Optimize one file with Imagify directly.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The media size.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array|WP_Error The optimization data. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_size( $size, $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Restore the media files from the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function restore();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MISSING THUMBNAILS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the sizes for this media that have not get through optimization.
|
||||
* No sizes are returned if the file is not optimized, has no backup, or is not an image.
|
||||
* The 'full' size os never returned.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* A WP_Error object on failure.
|
||||
* An array of data for the thumbnail sizes on success.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* @type string $file The name the thumbnail "should" have.
|
||||
* }
|
||||
*/
|
||||
public function get_missing_sizes();
|
||||
|
||||
/**
|
||||
* Optimize missing thumbnail sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_missing_thumbnails();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Delete the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_backup();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** RESIZE FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Maybe resize an image.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param File $file A File instance.
|
||||
* @return array|WP_Error A \WP_Error instance on failure, an array on success as follow: {
|
||||
* @type bool $resized True when the image has been resized.
|
||||
* @type bool $backuped True when the image has been backuped.
|
||||
* @type int $file_size The file size in bytes.
|
||||
* }
|
||||
*/
|
||||
public function maybe_resize( $size, $file );
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** WEBP ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_webp_versions();
|
||||
|
||||
/**
|
||||
* Delete the WebP images.
|
||||
* This doesn't delete the related optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.6 Return WP_Error or true.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $keep_full Set to true to keep the full size.
|
||||
* @return bool|\WP_Error True on success. A \WP_Error object on failure.
|
||||
*/
|
||||
public function delete_webp_files( $keep_full = false );
|
||||
|
||||
/**
|
||||
* Tell if a thumbnail size is an "Imagify WebP" size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size_name The size name.
|
||||
* @return string|bool The unsuffixed name of the size if WebP. False if not WebP.
|
||||
*/
|
||||
public function is_size_webp( $size_name );
|
||||
|
||||
/**
|
||||
* Tell if the media has all WebP versions.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_full_webp();
|
||||
|
||||
/**
|
||||
* Tell if the media has WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_webp();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PROCESS STATUS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if a process is running for this media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked();
|
||||
|
||||
/**
|
||||
* Set the running status to "running" for a period of time.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function lock();
|
||||
|
||||
/**
|
||||
* Delete the running status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function unlock();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** DATA ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if a size already has optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @return bool
|
||||
*/
|
||||
public function size_has_optimization_data( $size );
|
||||
|
||||
/**
|
||||
* Update the optimization data for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param object $response The API response.
|
||||
* @param string $size The size name.
|
||||
* @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array {
|
||||
* The optimization data.
|
||||
*
|
||||
* @type string $size The size name.
|
||||
* @type int $level The optimization level.
|
||||
* @type string $status The status: 'success', 'already_optimized', 'error'.
|
||||
* @type bool $success True if successfully optimized. False on error or if already optimized.
|
||||
* @type string $error An error message.
|
||||
* @type int $original_size The weight of the file, before optimization.
|
||||
* @type int $optimized_size The weight of the file, once optimized.
|
||||
* }
|
||||
*/
|
||||
public function update_size_optimization_data( $response, $size, $level );
|
||||
}
|
||||
255
wp/plugins/imagify/classes/Optimization/Process/WP.php
Normal file
255
wp/plugins/imagify/classes/Optimization/Process/WP.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Process;
|
||||
|
||||
use Imagify\Optimization\File;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Optimization class for the attachments in the WP library.
|
||||
* This class constructor accepts:
|
||||
* - A post ID (int).
|
||||
* - A \WP_Post object.
|
||||
* - A \Imagify\Media\MediaInterface object.
|
||||
* - A \Imagify\Media\DataInterface object.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class WP extends AbstractProcess {
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MISSING THUMBNAILS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the sizes for this media that have not get through optimization.
|
||||
* No sizes are returned if the file is not optimized, has no backup, or is not an image.
|
||||
* The 'full' size os never returned.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* A WP_Error object on failure.
|
||||
* An array of data for the thumbnail sizes on success.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* @type string $file The name the thumbnail "should" have.
|
||||
* }
|
||||
*/
|
||||
public function get_missing_sizes() {
|
||||
// The media must have been optimized once and have a backup.
|
||||
if ( ! $this->is_valid() ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$media = $this->get_media();
|
||||
|
||||
if ( ! $media->is_supported() ) {
|
||||
return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$data = $this->get_data();
|
||||
|
||||
if ( ! $data->is_optimized() ) {
|
||||
return new \WP_Error( 'media_not_optimized', __( 'This media is not optimized yet.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $media->has_backup() ) {
|
||||
return new \WP_Error( 'no_backup', __( 'This file has no backup file.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $media->is_image() ) {
|
||||
return new \WP_Error( 'media_not_an_image', __( 'This media is not an image.', 'imagify' ) );
|
||||
}
|
||||
|
||||
// Compare registered sizes and optimized sizes.
|
||||
$context_sizes = $media->get_context_instance()->get_thumbnail_sizes();
|
||||
$optimized_sizes = $data->get_optimization_data();
|
||||
$missing_sizes = array_diff_key( $context_sizes, $optimized_sizes['sizes'] );
|
||||
|
||||
if ( ! $missing_sizes ) {
|
||||
// We have everything we need.
|
||||
return [];
|
||||
}
|
||||
|
||||
$media_sizes = $media->get_media_files();
|
||||
$full_size = $media_sizes['full'];
|
||||
|
||||
if ( ! $full_size['path'] || ! $full_size['width'] || ! $full_size['height'] ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$file_name = $this->filesystem->path_info( $full_size['path'] );
|
||||
$file_name = $file_name['file_base'] . '-{%suffix%}.' . $file_name['extension'];
|
||||
|
||||
// Test if the missing sizes are needed.
|
||||
foreach ( $missing_sizes as $size_name => $size_data ) {
|
||||
if ( $full_size['width'] === $size_data['width'] && $full_size['height'] === $size_data['height'] ) {
|
||||
// Same dimensions as the full size.
|
||||
unset( $missing_sizes[ $size_name ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $media_sizes[ $size_name ]['disabled'] ) ) {
|
||||
// This size must not be optimized.
|
||||
unset( $missing_sizes[ $size_name ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
$resize_result = image_resize_dimensions( $full_size['width'], $full_size['height'], $size_data['width'], $size_data['height'], $size_data['crop'] );
|
||||
|
||||
if ( ! $resize_result ) {
|
||||
// This thumbnail is not needed, it is smaller than this size.
|
||||
unset( $missing_sizes[ $size_name ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Provide what should be the file name.
|
||||
list( , , , , $new_width, $new_height ) = $resize_result;
|
||||
$missing_sizes[ $size_name ]['file'] = str_replace( '{%suffix%}', "{$new_width}x{$new_height}", $file_name );
|
||||
}
|
||||
|
||||
return $missing_sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize missing thumbnail sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_missing_thumbnails() {
|
||||
if ( ! $this->is_valid() ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( ! $this->get_media()->is_supported() ) {
|
||||
return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$missing_sizes = $this->get_missing_sizes();
|
||||
|
||||
if ( ! $missing_sizes ) {
|
||||
return new \WP_Error( 'no_sizes', __( 'No thumbnails seem to be missing.', 'imagify' ) );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $missing_sizes ) ) {
|
||||
return $missing_sizes;
|
||||
}
|
||||
|
||||
if ( $this->is_locked() ) {
|
||||
return new \WP_Error( 'media_locked', __( 'This media is already being processed.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$this->lock();
|
||||
|
||||
// Create the missing thumbnails.
|
||||
$sizes = $this->create_missing_thumbnails( $missing_sizes );
|
||||
|
||||
if ( ! $sizes ) {
|
||||
$this->unlock();
|
||||
return new \WP_Error( 'thumbnail_creation_failed', __( 'The thumbnails failed to be created.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$optimization_level = $this->get_data()->get_optimization_level();
|
||||
|
||||
if ( false === $optimization_level ) {
|
||||
$this->unlock();
|
||||
return new \WP_Error( 'optimization_level_not_set', __( 'The optimization level of this media seems to have disappear from the database. You should restore this media and then launch a new optimization.', 'imagify' ) );
|
||||
}
|
||||
|
||||
$args = [
|
||||
'hook_suffix' => 'optimize_missing_thumbnails',
|
||||
'locked' => true,
|
||||
];
|
||||
|
||||
// Optimize.
|
||||
return $this->optimize_sizes( array_keys( $sizes ), $optimization_level, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all missing thumbnails if they don't exist and update the attachment metadata.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @see $this->get_missing_sizes()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $missing_sizes array {
|
||||
* An array of data for the thumbnail sizes on success.
|
||||
* Size names are used as array keys.
|
||||
*
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type bool $crop True to crop, false to resize.
|
||||
* @type string $name The size name.
|
||||
* @type string $file The name the thumbnail "should" have.
|
||||
* }
|
||||
* @return array {
|
||||
* An array of thumbnail data (those without errors):
|
||||
*
|
||||
* @type string $file File name.
|
||||
* @type int $width The image width.
|
||||
* @type int $height The image height.
|
||||
* @type string $mime-type The mime type.
|
||||
* }
|
||||
*/
|
||||
protected function create_missing_thumbnails( $missing_sizes ) {
|
||||
if ( ! $missing_sizes ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$media = $this->get_media();
|
||||
$media_id = $media->get_id();
|
||||
$metadata = wp_get_attachment_metadata( $media_id );
|
||||
$metadata['sizes'] = ! empty( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ? $metadata['sizes'] : [];
|
||||
|
||||
$destination_dir = $this->filesystem->dir_path( $media->get_raw_fullsize_path() );
|
||||
$backup_file = new File( $media->get_backup_path() );
|
||||
$without_errors = [];
|
||||
$has_new_data = false;
|
||||
|
||||
// Create the missing thumbnails.
|
||||
foreach ( $missing_sizes as $size_name => $thumbnail_data ) {
|
||||
// The path to the destination file.
|
||||
$thumbnail_data['path'] = $destination_dir . $thumbnail_data['file'];
|
||||
|
||||
if ( ! $this->filesystem->exists( $thumbnail_data['path'] ) ) {
|
||||
$result = $backup_file->create_thumbnail( $thumbnail_data );
|
||||
|
||||
if ( is_array( $result ) ) {
|
||||
// New file.
|
||||
$metadata['sizes'][ $size_name ] = $result;
|
||||
$has_new_data = true;
|
||||
}
|
||||
} else {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $metadata['sizes'][ $size_name ] ) && ! is_wp_error( $result ) ) {
|
||||
// Not an error.
|
||||
$without_errors[ $size_name ] = $metadata['sizes'][ $size_name ];
|
||||
}
|
||||
}
|
||||
|
||||
// Save the new data into the attachment metadata.
|
||||
if ( $has_new_data ) {
|
||||
/**
|
||||
* Here we don't use wp_update_attachment_metadata() to prevent triggering unwanted hooks.
|
||||
*/
|
||||
update_post_meta( $media_id, '_wp_attachment_metadata', $metadata );
|
||||
}
|
||||
|
||||
return $without_errors;
|
||||
}
|
||||
}
|
||||
174
wp/plugins/imagify/classes/Plugin.php
Normal file
174
wp/plugins/imagify/classes/Plugin.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify;
|
||||
|
||||
use Imagify\Bulk\Bulk;
|
||||
use Imagify\CLI\BulkOptimizeCommand;
|
||||
use Imagify\CLI\GenerateMissingWebpCommand;
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\Admin\AdminBar;
|
||||
|
||||
/**
|
||||
* Main plugin class.
|
||||
*/
|
||||
class Plugin {
|
||||
/**
|
||||
* Absolute path to the plugin (with trailing slash).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $plugin_path;
|
||||
|
||||
/**
|
||||
* Instantiate the class.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $plugin_args {
|
||||
* An array of arguments.
|
||||
*
|
||||
* @type string $plugin_path Absolute path to the plugin (with trailing slash).
|
||||
* }
|
||||
*/
|
||||
public function __construct( $plugin_args ) {
|
||||
$this->plugin_path = $plugin_args['plugin_path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin init.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function init() {
|
||||
$this->include_files();
|
||||
|
||||
class_alias( '\\Imagify\\Traits\\InstanceGetterTrait', '\\Imagify\\Traits\\FakeSingletonTrait' );
|
||||
|
||||
\Imagify_Auto_Optimization::get_instance()->init();
|
||||
\Imagify_Options::get_instance()->init();
|
||||
\Imagify_Data::get_instance()->init();
|
||||
\Imagify_Folders_DB::get_instance()->init();
|
||||
\Imagify_Files_DB::get_instance()->init();
|
||||
\Imagify_Cron_Library_Size::get_instance()->init();
|
||||
\Imagify_Cron_Rating::get_instance()->init();
|
||||
\Imagify_Cron_Sync_Files::get_instance()->init();
|
||||
\Imagify\Auth\Basic::get_instance()->init();
|
||||
\Imagify\Job\MediaOptimization::get_instance()->init();
|
||||
\Imagify\Stats\OptimizedMediaWithoutWebp::get_instance()->init();
|
||||
Bulk::get_instance()->init();
|
||||
AdminBar::get_instance()->init();
|
||||
|
||||
if ( is_admin() ) {
|
||||
Notices::get_instance()->init();
|
||||
\Imagify_Admin_Ajax_Post::get_instance()->init();
|
||||
\Imagify_Settings::get_instance()->init();
|
||||
\Imagify_Views::get_instance()->init();
|
||||
\Imagify\Imagifybeat\Core::get_instance()->init();
|
||||
\Imagify\Imagifybeat\Actions::get_instance()->init();
|
||||
}
|
||||
|
||||
if ( ! wp_doing_ajax() ) {
|
||||
\Imagify_Assets::get_instance()->init();
|
||||
}
|
||||
|
||||
\Imagify\Webp\Display::get_instance()->init();
|
||||
|
||||
add_action( 'init', [ $this, 'maybe_activate' ] );
|
||||
|
||||
// Load plugin translations.
|
||||
imagify_load_translations();
|
||||
|
||||
imagify_add_command( new BulkOptimizeCommand() );
|
||||
imagify_add_command( new GenerateMissingWebpCommand() );
|
||||
|
||||
/**
|
||||
* Fires when Imagify is fully loaded.
|
||||
*
|
||||
* @since 1.0
|
||||
* @since 1.9 Added the class instance as parameter.
|
||||
*
|
||||
* @param \Imagify_Plugin $plugin Instance of this class.
|
||||
*/
|
||||
do_action( 'imagify_loaded', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Include plugin files.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function include_files() {
|
||||
$instance_getter_path = $this->plugin_path . 'classes/Traits/InstanceGetterTrait.php';
|
||||
|
||||
if ( file_exists( $instance_getter_path . '.suspected' ) && ! file_exists( $instance_getter_path ) ) {
|
||||
// Trolling greedy antiviruses.
|
||||
require_once $instance_getter_path . '.suspected';
|
||||
}
|
||||
|
||||
$inc_path = $this->plugin_path . 'inc/';
|
||||
|
||||
require_once $inc_path . '/Dependencies/ActionScheduler/action-scheduler.php';
|
||||
require_once $inc_path . 'deprecated/deprecated.php';
|
||||
require_once $inc_path . 'deprecated/3rd-party.php';
|
||||
require_once $inc_path . 'functions/common.php';
|
||||
require_once $inc_path . 'functions/options.php';
|
||||
require_once $inc_path . 'functions/formatting.php';
|
||||
require_once $inc_path . 'functions/admin.php';
|
||||
require_once $inc_path . 'functions/api.php';
|
||||
require_once $inc_path . 'functions/media.php';
|
||||
require_once $inc_path . 'functions/attachments.php';
|
||||
require_once $inc_path . 'functions/process.php';
|
||||
require_once $inc_path . 'functions/admin-ui.php';
|
||||
require_once $inc_path . 'functions/admin-stats.php';
|
||||
require_once $inc_path . 'functions/i18n.php';
|
||||
require_once $inc_path . 'functions/partners.php';
|
||||
require_once $inc_path . 'common/attachments.php';
|
||||
require_once $inc_path . 'common/admin-bar.php';
|
||||
require_once $inc_path . 'common/partners.php';
|
||||
require_once $inc_path . '3rd-party/3rd-party.php';
|
||||
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $inc_path . 'admin/upgrader.php';
|
||||
require_once $inc_path . 'admin/upload.php';
|
||||
require_once $inc_path . 'admin/media.php';
|
||||
require_once $inc_path . 'admin/meta-boxes.php';
|
||||
require_once $inc_path . 'admin/custom-folders.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a hook on plugin activation after the plugin is loaded.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see imagify_set_activation()
|
||||
*/
|
||||
public function maybe_activate() {
|
||||
if ( imagify_is_active_for_network() ) {
|
||||
$user_id = get_site_transient( 'imagify_activation' );
|
||||
} else {
|
||||
$user_id = get_transient( 'imagify_activation' );
|
||||
}
|
||||
|
||||
if ( ! is_numeric( $user_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( imagify_is_active_for_network() ) {
|
||||
delete_site_transient( 'imagify_activation' );
|
||||
} else {
|
||||
delete_transient( 'imagify_activation' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Imagify activation.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $user_id ID of the user activating the plugin.
|
||||
*/
|
||||
do_action( 'imagify_activation', (int) $user_id );
|
||||
}
|
||||
}
|
||||
183
wp/plugins/imagify/classes/Stats/OptimizedMediaWithoutWebp.php
Normal file
183
wp/plugins/imagify/classes/Stats/OptimizedMediaWithoutWebp.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
namespace Imagify\Stats;
|
||||
|
||||
use Imagify\Bulk\Bulk;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class to get and cache the number of optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Name of the transient storing the cached result.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NAME = 'imagify_stat_without_webp';
|
||||
|
||||
/**
|
||||
* Launch hooks.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'imagify_after_optimize', [ $this, 'maybe_clear_cache_after_optimization' ], 10, 2 );
|
||||
add_action( 'imagify_after_restore_media', [ $this, 'maybe_clear_cache_after_restoration' ], 10, 4 );
|
||||
add_action( 'imagify_delete_media', [ $this, 'maybe_clear_cache_on_deletion' ] );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** GET/CACHE THE STAT ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the number of optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_stat() {
|
||||
$bulk = Bulk::get_instance();
|
||||
$stat = 0;
|
||||
|
||||
// Sum the counts of each context.
|
||||
foreach ( imagify_get_context_names() as $context ) {
|
||||
$stat += $bulk->get_bulk_instance( $context )->has_optimized_media_without_webp();
|
||||
}
|
||||
|
||||
return $stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and cache the number of optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_cached_stat() {
|
||||
$contexts = implode( '|', imagify_get_context_names() );
|
||||
$stat = get_transient( static::NAME );
|
||||
|
||||
if ( isset( $stat['stat'], $stat['contexts'] ) && $stat['contexts'] === $contexts ) {
|
||||
// The number is stored and the contexts are the same.
|
||||
return (int) $stat['stat'];
|
||||
}
|
||||
|
||||
$stat = [
|
||||
'contexts' => $contexts,
|
||||
'stat' => $this->get_stat(),
|
||||
];
|
||||
|
||||
set_transient( static::NAME, $stat, 2 * DAY_IN_SECONDS );
|
||||
|
||||
return $stat['stat'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the stat cache.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function clear_cache() {
|
||||
delete_transient( static::NAME );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Clear cache after optimizing a media.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed.
|
||||
*/
|
||||
public function maybe_clear_cache_after_optimization( $process, $item ) {
|
||||
if ( ! $process->get_media()->is_image() || false === get_transient( static::NAME ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sizes = $process->get_data()->get_optimization_data();
|
||||
$sizes = isset( $sizes['sizes'] ) ? (array) $sizes['sizes'] : [];
|
||||
$new_sizes = array_flip( $item['sizes_done'] );
|
||||
$new_sizes = array_intersect_key( $sizes, $new_sizes );
|
||||
$size_name = 'full' . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( ! isset( $new_sizes['full'] ) && ! empty( $new_sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* We just successfully generated the WebP version of the full size.
|
||||
* The full size was not optimized at the same time, that means it was optimized previously.
|
||||
* Meaning: we just added a WebP version to a media that was previously optimized, so there is one less optimized media without WebP.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $new_sizes['full']['success'] ) && empty( $new_sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* We now have a new optimized media without WebP.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache after restoring a media.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param bool|WP_Error $response The result of the operation: true on success, a WP_Error object on failure.
|
||||
* @param array $files The list of files, before restoring them.
|
||||
* @param array $data The optimization data, before deleting it.
|
||||
*/
|
||||
public function maybe_clear_cache_after_restoration( $process, $response, $files, $data ) {
|
||||
if ( ! $process->get_media()->is_image() || false === get_transient( static::NAME ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sizes = isset( $data['sizes'] ) ? (array) $data['sizes'] : [];
|
||||
$size_name = 'full' . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( ! empty( $sizes['full']['success'] ) && empty( $sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* This media had no WebP versions.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache on media deletion.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param ProcessInterface $process An optimization process.
|
||||
*/
|
||||
public function maybe_clear_cache_on_deletion( $process ) {
|
||||
if ( false === get_transient( static::NAME ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $process->get_data()->get_optimization_data();
|
||||
$sizes = isset( $data['sizes'] ) ? (array) $data['sizes'] : [];
|
||||
$size_name = 'full' . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( ! empty( $sizes['full']['success'] ) && empty( $sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* This media had no WebP versions.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
}
|
||||
}
|
||||
}
|
||||
44
wp/plugins/imagify/classes/Stats/StatInterface.php
Normal file
44
wp/plugins/imagify/classes/Stats/StatInterface.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace Imagify\Stats;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Interface to use to get and cache a stat.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface StatInterface {
|
||||
|
||||
/**
|
||||
* Get the stat value.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_stat();
|
||||
|
||||
/**
|
||||
* Get and cache the stat value.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_cached_stat();
|
||||
|
||||
/**
|
||||
* Clear the stat cache.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function clear_cache();
|
||||
}
|
||||
43
wp/plugins/imagify/classes/Traits/InstanceGetterTrait.php
Normal file
43
wp/plugins/imagify/classes/Traits/InstanceGetterTrait.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace Imagify\Traits;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Trait that simulates a singleton pattern.
|
||||
* The idea is more to ease the instance retrieval than to prevent multiple instances.
|
||||
* This is temporary, until we get a DI container.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.4 Renamed into InstanceGetterTrait.
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
trait InstanceGetterTrait {
|
||||
|
||||
/**
|
||||
* The "not-so-single" instance of the class.
|
||||
*
|
||||
* @var object
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* Get the main Instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( static::$instance ) ) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
}
|
||||
139
wp/plugins/imagify/classes/Traits/MediaRowTrait.php
Normal file
139
wp/plugins/imagify/classes/Traits/MediaRowTrait.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
namespace Imagify\Traits;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Trait to use to connect medias and database.
|
||||
* It also cache the results.
|
||||
* Classes using that trait must define a protected property $db_class_name (string) containing the media SQL DB class name.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
trait MediaRowTrait {
|
||||
|
||||
/**
|
||||
* The media SQL data row.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $row;
|
||||
|
||||
/**
|
||||
* The media ID.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Get the row.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_row() {
|
||||
if ( isset( $this->row ) ) {
|
||||
return $this->row;
|
||||
}
|
||||
|
||||
if ( ! $this->db_class_name || $this->id <= 0 ) {
|
||||
return $this->invalidate_row();
|
||||
}
|
||||
|
||||
$this->row = $this->get_row_db_instance()->get( $this->id );
|
||||
|
||||
if ( ! $this->row ) {
|
||||
return $this->invalidate_row();
|
||||
}
|
||||
|
||||
return $this->row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the row.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $data The data to update.
|
||||
*/
|
||||
public function update_row( $data ) {
|
||||
if ( ! $this->db_class_name || $this->id <= 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->get_row_db_instance()->update( $this->id, $data );
|
||||
|
||||
$this->reset_row_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the row.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function delete_row() {
|
||||
if ( ! $this->db_class_name || $this->id <= 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->get_row_db_instance()->delete( $this->id );
|
||||
|
||||
$this->invalidate_row();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand to get the DB table instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return \Imagify\DB\DBInterface The DB table instance.
|
||||
*/
|
||||
public function get_row_db_instance() {
|
||||
return call_user_func( [ $this->db_class_name, 'get_instance' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the row, by setting it to an empty array.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array The row.
|
||||
*/
|
||||
public function invalidate_row() {
|
||||
$this->row = [];
|
||||
return $this->row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the row cache.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return null The row.
|
||||
*/
|
||||
public function reset_row_cache() {
|
||||
$this->row = null;
|
||||
return $this->row;
|
||||
}
|
||||
}
|
||||
292
wp/plugins/imagify/classes/User/User.php
Normal file
292
wp/plugins/imagify/classes/User/User.php
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
namespace Imagify\User;
|
||||
|
||||
use Date;
|
||||
use Imagify_Data;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Imagify User class.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class User {
|
||||
/**
|
||||
* The Imagify user ID.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The user email.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* The plan ID.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $plan_id;
|
||||
|
||||
/**
|
||||
* The plan label.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $plan_label;
|
||||
|
||||
/**
|
||||
* The total quota.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $quota;
|
||||
|
||||
/**
|
||||
* The total extra quota (Imagify Pack).
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $extra_quota;
|
||||
|
||||
/**
|
||||
* The extra quota consumed.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $extra_quota_consumed;
|
||||
|
||||
/**
|
||||
* The current month consumed quota.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $consumed_current_month_quota;
|
||||
|
||||
/**
|
||||
* The next month date to credit the account.
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @var Date
|
||||
*/
|
||||
public $next_date_update;
|
||||
|
||||
/**
|
||||
* If the account is activate or not.
|
||||
*
|
||||
* @since 1.0.1
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $is_active;
|
||||
|
||||
/**
|
||||
* If the account is monthly or yearly.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $is_monthly;
|
||||
|
||||
/**
|
||||
* Store a \WP_Error object if the request to fetch the user data failed.
|
||||
* False overwise.
|
||||
*
|
||||
* @var bool|WP_Error
|
||||
* @since 1.9.9
|
||||
*/
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
$user = get_imagify_user();
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
$this->error = $user;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->id = $user->id;
|
||||
$this->email = $user->email;
|
||||
$this->plan_id = (int) $user->plan_id;
|
||||
$this->plan_label = ucfirst( $user->plan_label );
|
||||
$this->quota = $user->quota;
|
||||
$this->extra_quota = $user->extra_quota;
|
||||
$this->extra_quota_consumed = $user->extra_quota_consumed;
|
||||
$this->consumed_current_month_quota = $user->consumed_current_month_quota;
|
||||
$this->next_date_update = $user->next_date_update;
|
||||
$this->is_active = $user->is_active;
|
||||
$this->is_monthly = $user->is_monthly;
|
||||
$this->error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the possible error returned when fetching user data.
|
||||
*
|
||||
* @return bool|WP_Error A \WP_Error object if the request to fetch the user data failed. False overwise.
|
||||
* @since 1.9.9
|
||||
*/
|
||||
public function get_error() {
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Percentage of consumed quota, including extra quota.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return float|int
|
||||
*/
|
||||
public function get_percent_consumed_quota() {
|
||||
static $done = false;
|
||||
|
||||
if ( $this->get_error() ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$quota = $this->quota;
|
||||
$consumed_quota = $this->consumed_current_month_quota;
|
||||
|
||||
if ( imagify_round_half_five( $this->extra_quota_consumed ) < $this->extra_quota ) {
|
||||
$quota += $this->extra_quota;
|
||||
$consumed_quota += $this->extra_quota_consumed;
|
||||
}
|
||||
|
||||
if ( ! $quota || ! $consumed_quota ) {
|
||||
$percent = 0;
|
||||
} else {
|
||||
$percent = 100 * $consumed_quota / $quota;
|
||||
$percent = round( $percent, 1 );
|
||||
$percent = min( max( 0, $percent ), 100 );
|
||||
}
|
||||
|
||||
$percent = (float) $percent;
|
||||
|
||||
if ( $done ) {
|
||||
return $percent;
|
||||
}
|
||||
|
||||
$previous_percent = Imagify_Data::get_instance()->get( 'previous_quota_percent' );
|
||||
|
||||
// Percent is not 100% anymore.
|
||||
if ( 100.0 === (float) $previous_percent && $percent < 100 ) {
|
||||
/**
|
||||
* Triggered when the consumed quota percent decreases below 100%.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param float|int $percent The current percentage of consumed quota.
|
||||
*/
|
||||
do_action( 'imagify_not_over_quota_anymore', $percent );
|
||||
}
|
||||
|
||||
// Percent is not >= 80% anymore.
|
||||
if ( ( (float) $previous_percent >= 80.0 && $percent < 80 ) ) {
|
||||
/**
|
||||
* Triggered when the consumed quota percent decreases below 80%.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param float|int $percent The current percentage of consumed quota.
|
||||
* @param float|int $previous_percent The previous percentage of consumed quota.
|
||||
*/
|
||||
do_action( 'imagify_not_almost_over_quota_anymore', $percent, $previous_percent );
|
||||
}
|
||||
|
||||
if ( (float) $previous_percent !== (float) $percent ) {
|
||||
Imagify_Data::get_instance()->set( 'previous_quota_percent', $percent );
|
||||
}
|
||||
|
||||
$done = true;
|
||||
|
||||
return $percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count percent of unconsumed quota.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return float|int
|
||||
*/
|
||||
public function get_percent_unconsumed_quota() {
|
||||
return 100 - $this->get_percent_consumed_quota();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has a free account.
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_free() {
|
||||
return 1 === $this->plan_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is a growth account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_growth() {
|
||||
return ( 16 === $this->plan_id || 18 === $this->plan_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is an infinite account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_infinite() {
|
||||
return ( 15 === $this->plan_id || 17 === $this->plan_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has consumed all his/her quota.
|
||||
*
|
||||
* @since 1.1.1
|
||||
* @since 1.9.9 Return false if the request to fetch the user data failed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_over_quota() {
|
||||
if ( $this->get_error() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
$this->is_free()
|
||||
&&
|
||||
floatval( 100 ) === round( $this->get_percent_consumed_quota() )
|
||||
);
|
||||
}
|
||||
}
|
||||
38
wp/plugins/imagify/classes/Webp/Apache.php
Normal file
38
wp/plugins/imagify/classes/Webp/Apache.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace Imagify\Webp;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Add and remove contents to the .htaccess file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Apache extends \Imagify\WriteFile\AbstractApacheDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: webp file type';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
return trim( '
|
||||
<IfModule mod_mime.c>
|
||||
AddType image/webp .webp
|
||||
</IfModule>' );
|
||||
}
|
||||
}
|
||||
239
wp/plugins/imagify/classes/Webp/Display.php
Normal file
239
wp/plugins/imagify/classes/Webp/Display.php
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
namespace Imagify\Webp;
|
||||
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class Display {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Server conf object.
|
||||
*
|
||||
* @var \Imagify\WriteFile\WriteFileInterface
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $server_conf;
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'imagify_settings_on_save', [ $this, 'maybe_add_rewrite_rules' ] );
|
||||
add_action( 'imagify_settings_webp_info', [ $this, 'maybe_add_webp_info' ] );
|
||||
add_action( 'imagify_activation', [ $this, 'activate' ] );
|
||||
add_action( 'imagify_deactivation', [ $this, 'deactivate' ] );
|
||||
|
||||
Picture\Display::get_instance()->init();
|
||||
RewriteRules\Display::get_instance()->init();
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* If display WebP images, add the WebP type to the .htaccess/etc file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_add_rewrite_rules( $values ) {
|
||||
$old_value = (bool) get_imagify_option( 'display_webp' );
|
||||
// See \Imagify_Options->validate_values_on_update() for why we use 'convert_to_webp' here.
|
||||
$new_value = ! empty( $values['display_webp'] ) && ! empty( $values['convert_to_webp'] );
|
||||
|
||||
if ( $old_value === $new_value ) {
|
||||
// No changes.
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( $new_value ) {
|
||||
// Add the WebP file type.
|
||||
$result = $this->get_server_conf()->add();
|
||||
} else {
|
||||
// Remove the WebP file type.
|
||||
$result = $this->get_server_conf()->remove();
|
||||
}
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
// Display an error message.
|
||||
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
|
||||
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
|
||||
} else {
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the conf file is not writable, add a warning.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function maybe_add_webp_info() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$writable = $conf->is_file_writable();
|
||||
|
||||
if ( ! is_wp_error( $writable ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rules = $conf->get_new_contents();
|
||||
|
||||
if ( ! $rules ) {
|
||||
// Uh?
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<br/>';
|
||||
|
||||
printf(
|
||||
/* translators: %s is a file name. */
|
||||
esc_html__( 'Imagify does not seem to be able to edit or create a %s file, you will have to add the following lines manually to it:', 'imagify' ),
|
||||
'<code>' . $this->get_file_path( true ) . '</code>'
|
||||
);
|
||||
|
||||
echo '<pre class="code">' . esc_html( $rules ) . '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add rules on plugin activation.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function activate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( is_wp_error( $conf->is_file_writable() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove rules on plugin deactivation.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function deactivate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file_path = $conf->get_file_path();
|
||||
$filesystem = \Imagify_Filesystem::get_instance();
|
||||
|
||||
if ( ! $filesystem->exists( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $filesystem->is_writable( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->remove();
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the path to the directory conf file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param bool $relative True to get a path relative to the site’s root.
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_file_path( $relative = false ) {
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_path = $this->get_server_conf()->get_file_path();
|
||||
|
||||
if ( $relative ) {
|
||||
return \Imagify_Filesystem::get_instance()->make_path_relative( $file_path );
|
||||
}
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebP display method by validating the given value.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
* @return string 'picture' or 'rewrite'.
|
||||
*/
|
||||
public function get_display_webp_method( $values ) {
|
||||
$options = \Imagify_Options::get_instance();
|
||||
$default = $options->get_default_values();
|
||||
$default = $default['display_webp_method'];
|
||||
$method = ! empty( $values['display_webp_method'] ) ? $values['display_webp_method'] : '';
|
||||
|
||||
return $options->sanitize_and_validate( 'display_webp_method', $method, $default );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server conf instance.
|
||||
* Note: nothing needed for nginx.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return \Imagify\WriteFile\WriteFileInterface
|
||||
*/
|
||||
protected function get_server_conf() {
|
||||
global $is_apache, $is_iis7;
|
||||
|
||||
if ( isset( $this->server_conf ) ) {
|
||||
return $this->server_conf;
|
||||
}
|
||||
|
||||
if ( $is_apache ) {
|
||||
$this->server_conf = new Apache();
|
||||
} elseif ( $is_iis7 ) {
|
||||
$this->server_conf = new IIS();
|
||||
} else {
|
||||
$this->server_conf = false;
|
||||
}
|
||||
|
||||
return $this->server_conf;
|
||||
}
|
||||
}
|
||||
39
wp/plugins/imagify/classes/Webp/IIS.php
Normal file
39
wp/plugins/imagify/classes/Webp/IIS.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace Imagify\Webp;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Add and remove contents to the web.config file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class IIS extends \Imagify\WriteFile\AbstractIISDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: webp file type';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
return trim( '
|
||||
<!-- @parent /configuration/system.webServer -->
|
||||
<staticContent name="' . esc_attr( static::TAG_NAME ) . ' 1">
|
||||
<mimeMap fileExtension=".webp" mimeType="image/webp" />
|
||||
</staticContent>' );
|
||||
}
|
||||
}
|
||||
800
wp/plugins/imagify/classes/Webp/Picture/Display.php
Normal file
800
wp/plugins/imagify/classes/Webp/Picture/Display.php
Normal file
@@ -0,0 +1,800 @@
|
||||
<?php
|
||||
namespace Imagify\Webp\Picture;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Display WebP images on the site with <picture> tags.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Display {
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const OPTION_VALUE = 'picture';
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var \Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'template_redirect', [ $this, 'start_content_process' ], -1000 );
|
||||
add_filter( 'imagify_process_webp_content', [ $this, 'process_content' ] );
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ADD <PICTURE> TAGS TO THE PAGE ========================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Start buffering the page content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function start_content_process() {
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_webp_method' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the replacement of <img> tags into <picture> tags.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $allow True to allow the use of <picture> tags (default). False to prevent their use.
|
||||
*/
|
||||
$allow = apply_filters( 'imagify_allow_picture_tags_for_webp', true );
|
||||
|
||||
if ( ! $allow ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start( [ $this, 'maybe_process_buffer' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe process the page content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $buffer The buffer content.
|
||||
* @return string
|
||||
*/
|
||||
public function maybe_process_buffer( $buffer ) {
|
||||
if ( ! $this->is_html( $buffer ) ) {
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
if ( strlen( $buffer ) <= 255 ) {
|
||||
// Buffer length must be > 255 (IE does not read pages under 255 c).
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
$buffer = $this->process_content( $buffer );
|
||||
|
||||
/**
|
||||
* Filter the page content after Imagify.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $buffer The page content.
|
||||
*/
|
||||
$buffer = (string) apply_filters( 'imagify_buffer', $buffer );
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @return string
|
||||
*/
|
||||
public function process_content( $content ) {
|
||||
$html_no_picture_tags = $this->remove_picture_tags( $content );
|
||||
$images = $this->get_images( $html_no_picture_tags );
|
||||
|
||||
if ( ! $images ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ( $images as $image ) {
|
||||
$tag = $this->build_picture_tag( $image );
|
||||
$content = str_replace( $image['tag'], $tag, $content );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove pre-existing <picture> tags.
|
||||
*
|
||||
* We shouldn't replace images already nested inside picture tags
|
||||
* that are already in the page.
|
||||
*
|
||||
* @since 1.10.0
|
||||
*
|
||||
* @param string $html Content of the page.
|
||||
*
|
||||
* @return string HTML content without pre-existing <picture> tags.
|
||||
*/
|
||||
private function remove_picture_tags( $html ) {
|
||||
$replace = preg_replace( '#<picture[^>]*>.*?<\/picture\s*>#mis', '', $html );
|
||||
|
||||
if ( null === $replace ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
return $replace;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BUILD HTML TAGS AND ATTRIBUTES ========================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Build the <picture> tag to insert.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $image An array of data.
|
||||
* @return string A <picture> tag.
|
||||
*/
|
||||
protected function build_picture_tag( $image ) {
|
||||
$to_remove = [
|
||||
'alt' => '',
|
||||
'height' => '',
|
||||
'width' => '',
|
||||
'data-lazy-src' => '',
|
||||
'data-src' => '',
|
||||
'src' => '',
|
||||
'data-lazy-srcset' => '',
|
||||
'data-srcset' => '',
|
||||
'srcset' => '',
|
||||
'data-lazy-sizes' => '',
|
||||
'data-sizes' => '',
|
||||
'sizes' => '',
|
||||
];
|
||||
|
||||
$attributes = array_diff_key( $image['attributes'], $to_remove );
|
||||
|
||||
/**
|
||||
* Filter the attributes to be added to the <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $attributes A list of attributes to be added to the <picture> tag.
|
||||
* @param array $data Data built from the originale <img> tag. See $this->process_image().
|
||||
*/
|
||||
$attributes = apply_filters( 'imagify_picture_attributes', $attributes, $image );
|
||||
|
||||
/**
|
||||
* Remove Gutenberg specific attributes from picture tag, leave them on img tag.
|
||||
* Optional: $attributes['class'] = 'imagify-webp-cover-wrapper'; for website admin styling ease.
|
||||
*/
|
||||
if ( ! empty( $image['attributes']['class'] ) && strpos( $image['attributes']['class'], 'wp-block-cover__image-background' ) !== false ) {
|
||||
unset( $attributes['style'] );
|
||||
unset( $attributes['class'] );
|
||||
unset( $attributes['data-object-fit'] );
|
||||
unset( $attributes['data-object-position'] );
|
||||
}
|
||||
|
||||
$output = '<picture' . $this->build_attributes( $attributes ) . ">\n";
|
||||
/**
|
||||
* Allow to add more <source> tags to the <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $more_source_tags Additional <source> tags.
|
||||
* @param array $data Data built from the originale <img> tag. See $this->process_image().
|
||||
*/
|
||||
$output .= apply_filters( 'imagify_additional_source_tags', '', $image );
|
||||
$output .= $this->build_source_tag( $image );
|
||||
$output .= $this->build_img_tag( $image );
|
||||
$output .= "</picture>\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the <source> tag to insert in the <picture>.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $image An array of data.
|
||||
* @return string A <source> tag.
|
||||
*/
|
||||
protected function build_source_tag( $image ) {
|
||||
$srcset_source = ! empty( $image['srcset_attribute'] ) ? $image['srcset_attribute'] : $image['src_attribute'] . 'set';
|
||||
$attributes = [
|
||||
'type' => 'image/webp',
|
||||
$srcset_source => [],
|
||||
];
|
||||
|
||||
if ( ! empty( $image['srcset'] ) ) {
|
||||
foreach ( $image['srcset'] as $srcset ) {
|
||||
if ( empty( $srcset['webp_url'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[ $srcset_source ][] = $srcset['webp_url'] . ' ' . $srcset['descriptor'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $attributes[ $srcset_source ] ) ) {
|
||||
$attributes[ $srcset_source ][] = $image['src']['webp_url'];
|
||||
}
|
||||
|
||||
$attributes[ $srcset_source ] = implode( ', ', $attributes[ $srcset_source ] );
|
||||
|
||||
foreach ( [ 'data-lazy-srcset', 'data-srcset', 'srcset' ] as $srcset_attr ) {
|
||||
if ( ! empty( $image['attributes'][ $srcset_attr ] ) && $srcset_attr !== $srcset_source ) {
|
||||
$attributes[ $srcset_attr ] = $image['attributes'][ $srcset_attr ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'srcset' !== $srcset_source && empty( $attributes['srcset'] ) && ! empty( $image['attributes']['src'] ) ) {
|
||||
// Lazyload: the "src" attr should contain a placeholder (a data image or a blank.gif ).
|
||||
$attributes['srcset'] = $image['attributes']['src'];
|
||||
}
|
||||
|
||||
foreach ( [ 'data-lazy-sizes', 'data-sizes', 'sizes' ] as $sizes_attr ) {
|
||||
if ( ! empty( $image['attributes'][ $sizes_attr ] ) ) {
|
||||
$attributes[ $sizes_attr ] = $image['attributes'][ $sizes_attr ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the attributes to be added to the <source> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $attributes A list of attributes to be added to the <source> tag.
|
||||
* @param array $data Data built from the original <img> tag. See $this->process_image().
|
||||
*/
|
||||
$attributes = apply_filters( 'imagify_picture_source_attributes', $attributes, $image );
|
||||
|
||||
return '<source' . $this->build_attributes( $attributes ) . "/>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the <img> tag to insert in the <picture>.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $image An array of data.
|
||||
* @return string A <img> tag.
|
||||
*/
|
||||
protected function build_img_tag( $image ) {
|
||||
/**
|
||||
* Gutenberg fix.
|
||||
* Check for the 'wp-block-cover__image-background' class on the original image, and leave that class and style attributes if found.
|
||||
*/
|
||||
if ( ! empty( $image['attributes']['class'] ) && strpos( $image['attributes']['class'], 'wp-block-cover__image-background' ) !== false ) {
|
||||
$to_remove = [
|
||||
'id' => '',
|
||||
'title' => '',
|
||||
];
|
||||
|
||||
$attributes = array_diff_key( $image['attributes'], $to_remove );
|
||||
} else {
|
||||
$to_remove = [
|
||||
'class' => '',
|
||||
'id' => '',
|
||||
'style' => '',
|
||||
'title' => '',
|
||||
];
|
||||
|
||||
$attributes = array_diff_key( $image['attributes'], $to_remove );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the attributes to be added to the <img> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $attributes A list of attributes to be added to the <img> tag.
|
||||
* @param array $data Data built from the originale <img> tag. See $this->process_image().
|
||||
*/
|
||||
$attributes = apply_filters( 'imagify_picture_img_attributes', $attributes, $image );
|
||||
|
||||
return '<img' . $this->build_attributes( $attributes ) . "/>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HTML attributes from an array.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $attributes A list of attribute pairs.
|
||||
* @return string HTML attributes.
|
||||
*/
|
||||
protected function build_attributes( $attributes ) {
|
||||
if ( ! $attributes || ! is_array( $attributes ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$out = '';
|
||||
|
||||
foreach ( $attributes as $attribute => $value ) {
|
||||
$out .= ' ' . $attribute . '="' . esc_attr( $value ) . '"';
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** VARIOUS TOOLS =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get a list of images in a content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_images( $content ) {
|
||||
// Remove comments.
|
||||
$content = preg_replace( '/<!--(.*)-->/Uis', '', $content );
|
||||
|
||||
if ( ! preg_match_all( '/<img\s.*>/isU', $content, $matches ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$images = array_map( [ $this, 'process_image' ], $matches[0] );
|
||||
$images = array_filter( $images );
|
||||
|
||||
/**
|
||||
* Filter the images to display with a <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $images A list of arrays.
|
||||
* @param string $content The page content.
|
||||
*/
|
||||
$images = apply_filters( 'imagify_webp_picture_images_to_display', $images, $content );
|
||||
|
||||
if ( ! $images || ! is_array( $images ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ( $images as $i => $image ) {
|
||||
if ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) {
|
||||
unset( $images[ $i ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
unset( $images[ $i ]['src']['webp_path'], $images[ $i ]['src']['webp_exists'] );
|
||||
|
||||
if ( empty( $image['srcset'] ) || ! is_array( $image['srcset'] ) ) {
|
||||
unset( $images[ $i ]['srcset'] );
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $image['srcset'] as $j => $srcset ) {
|
||||
if ( ! is_array( $srcset ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( empty( $srcset['webp_exists'] ) || empty( $srcset['webp_url'] ) ) {
|
||||
unset( $images[ $i ]['srcset'][ $j ]['webp_url'] );
|
||||
}
|
||||
|
||||
unset( $images[ $i ]['srcset'][ $j ]['webp_path'], $images[ $i ]['srcset'][ $j ]['webp_exists'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an image tag and get an array containing some data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $image An image html tag.
|
||||
* @return array|false {
|
||||
* An array of data if the image has a WebP version. False otherwise.
|
||||
*
|
||||
* @type string $tag The image tag.
|
||||
* @type array $attributes The image attributes (minus src and srcset).
|
||||
* @type array $src {
|
||||
* @type string $url URL to the original image.
|
||||
* @type string $webp_url URL to the WebP version.
|
||||
* }
|
||||
* @type array $srcset {
|
||||
* An array or arrays. Not set if not applicable.
|
||||
*
|
||||
* @type string $url URL to the original image.
|
||||
* @type string $webp_url URL to the WebP version. Not set if not applicable.
|
||||
* @type string $descriptor A src descriptor.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
protected function process_image( $image ) {
|
||||
static $extensions;
|
||||
|
||||
$atts_pattern = '/(?<name>[^\s"\']+)\s*=\s*(["\'])\s*(?<value>.*?)\s*\2/s';
|
||||
|
||||
if ( ! preg_match_all( $atts_pattern, $image, $tmp_attributes, PREG_SET_ORDER ) ) {
|
||||
// No attributes?
|
||||
return false;
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
|
||||
foreach ( $tmp_attributes as $attribute ) {
|
||||
$attributes[ $attribute['name'] ] = $attribute['value'];
|
||||
}
|
||||
|
||||
if ( ! empty( $attributes['class'] ) && strpos( $attributes['class'], 'imagify-no-webp' ) !== false ) {
|
||||
// Has the 'imagify-no-webp' class.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deal with the src attribute.
|
||||
$src_source = false;
|
||||
|
||||
foreach ( [ 'data-lazy-src', 'data-src', 'src' ] as $src_attr ) {
|
||||
if ( ! empty( $attributes[ $src_attr ] ) ) {
|
||||
$src_source = $src_attr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $src_source ) {
|
||||
// No src attribute.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isset( $extensions ) ) {
|
||||
$extensions = imagify_get_mime_types( 'image' );
|
||||
$extensions = array_keys( $extensions );
|
||||
$extensions = implode( '|', $extensions );
|
||||
}
|
||||
|
||||
if ( ! preg_match( '@^(?<src>(?:(?:https?:)?//|/).+\.(?<extension>' . $extensions . '))(?<query>\?.*)?$@i', $attributes[ $src_source ], $src ) ) {
|
||||
// Not a supported image format.
|
||||
return false;
|
||||
}
|
||||
|
||||
$webp_url = imagify_path_to_webp( $src['src'] );
|
||||
$webp_path = $this->url_to_path( $webp_url );
|
||||
$webp_url .= ! empty( $src['query'] ) ? $src['query'] : '';
|
||||
|
||||
$data = [
|
||||
'tag' => $image,
|
||||
'attributes' => $attributes,
|
||||
'src_attribute' => $src_source,
|
||||
'src' => [
|
||||
'url' => $attributes[ $src_source ],
|
||||
'webp_url' => $webp_url,
|
||||
'webp_path' => $webp_path,
|
||||
'webp_exists' => $webp_path && $this->filesystem->exists( $webp_path ),
|
||||
],
|
||||
'srcset_attribute' => false,
|
||||
'srcset' => [],
|
||||
];
|
||||
|
||||
// Deal with the srcset attribute.
|
||||
$srcset_source = false;
|
||||
|
||||
foreach ( [ 'data-lazy-srcset', 'data-srcset', 'srcset' ] as $srcset_attr ) {
|
||||
if ( ! empty( $attributes[ $srcset_attr ] ) ) {
|
||||
$srcset_source = $srcset_attr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $srcset_source ) {
|
||||
$data['srcset_attribute'] = $srcset_source;
|
||||
|
||||
$srcset = explode( ',', $attributes[ $srcset_source ] );
|
||||
|
||||
foreach ( $srcset as $srcs ) {
|
||||
$srcs = preg_split( '/\s+/', trim( $srcs ) );
|
||||
|
||||
if ( count( $srcs ) > 2 ) {
|
||||
// Not a good idea to have space characters in file name.
|
||||
$descriptor = array_pop( $srcs );
|
||||
$srcs = [ implode( ' ', $srcs ), $descriptor ];
|
||||
}
|
||||
|
||||
if ( empty( $srcs[1] ) ) {
|
||||
$srcs[1] = '1x';
|
||||
}
|
||||
|
||||
if ( ! preg_match( '@^(?<src>(?:https?:)?//.+\.(?<extension>' . $extensions . '))(?<query>\?.*)?$@i', $srcs[0], $src ) ) {
|
||||
// Not a supported image format.
|
||||
$data['srcset'][] = [
|
||||
'url' => $srcs[0],
|
||||
'descriptor' => $srcs[1],
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$webp_url = imagify_path_to_webp( $src['src'] );
|
||||
$webp_path = $this->url_to_path( $webp_url );
|
||||
$webp_url .= ! empty( $src['query'] ) ? $src['query'] : '';
|
||||
|
||||
$data['srcset'][] = [
|
||||
'url' => $srcs[0],
|
||||
'descriptor' => $srcs[1],
|
||||
'webp_url' => $webp_url,
|
||||
'webp_path' => $webp_path,
|
||||
'webp_exists' => $webp_path && $this->filesystem->exists( $webp_path ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a processed image tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $data An array of data for this image.
|
||||
* @param string $image An image html tag.
|
||||
*/
|
||||
$data = apply_filters( 'imagify_webp_picture_process_image', $data, $image );
|
||||
|
||||
if ( ! $data || ! is_array( $data ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isset( $data['tag'], $data['attributes'], $data['src_attribute'], $data['src'], $data['srcset_attribute'], $data['srcset'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a content is HTML.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_html( $content ) {
|
||||
return preg_match( '/<\/html>/i', $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a file URL to an absolute path.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url A file URL.
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
protected function url_to_path( $url ) {
|
||||
static $uploads_url;
|
||||
static $uploads_dir;
|
||||
static $root_url;
|
||||
static $root_dir;
|
||||
static $cdn_url;
|
||||
static $domain_url;
|
||||
|
||||
/**
|
||||
* $url, $uploads_url, $root_url, and $cdn_url are passed through `set_url_scheme()` only to make sure `stripos()` doesn't fail over a stupid http/https difference.
|
||||
*/
|
||||
if ( ! isset( $uploads_url ) ) {
|
||||
$uploads_url = set_url_scheme( $this->filesystem->get_upload_baseurl() );
|
||||
$uploads_dir = $this->filesystem->get_upload_basedir( true );
|
||||
$root_url = set_url_scheme( $this->filesystem->get_site_root_url() );
|
||||
$root_dir = $this->filesystem->get_site_root();
|
||||
$cdn_url = $this->get_cdn_source();
|
||||
$cdn_url = $cdn_url['url'] ? set_url_scheme( $cdn_url['url'] ) : false;
|
||||
$domain_url = wp_parse_url( $root_url );
|
||||
|
||||
if ( ! empty( $domain_url['scheme'] ) && ! empty( $domain_url['host'] ) ) {
|
||||
$domain_url = $domain_url['scheme'] . '://' . $domain_url['host'] . '/';
|
||||
} else {
|
||||
$domain_url = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the right URL format.
|
||||
if ( $domain_url && strpos( $url, '/' ) === 0 ) {
|
||||
// URL like `/path/to/image.jpg.webp`.
|
||||
$url = $domain_url . ltrim( $url, '/' );
|
||||
}
|
||||
|
||||
$url = set_url_scheme( $url );
|
||||
|
||||
if ( $cdn_url && $domain_url && stripos( $url, $cdn_url ) === 0 ) {
|
||||
// CDN.
|
||||
$url = str_ireplace( $cdn_url, $domain_url, $url );
|
||||
}
|
||||
|
||||
// Return the path.
|
||||
if ( stripos( $url, $uploads_url ) === 0 ) {
|
||||
return str_ireplace( $uploads_url, $uploads_dir, $url );
|
||||
}
|
||||
|
||||
if ( stripos( $url, $root_url ) === 0 ) {
|
||||
return str_ireplace( $root_url, $root_dir, $url );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CDN "source".
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $option_url An URL to use instead of the one stored in the option. It is used only if no constant/filter.
|
||||
* @return array {
|
||||
* @type string $source Where does it come from? Possible values are 'constant', 'filter', or 'option'.
|
||||
* @type string $name Who? Can be a constant name, a plugin name, or an empty string.
|
||||
* @type string $url The CDN URL, with a trailing slash. An empty string if no URL is set.
|
||||
* }
|
||||
*/
|
||||
public function get_cdn_source( $option_url = '' ) {
|
||||
if ( defined( 'IMAGIFY_CDN_URL' ) && IMAGIFY_CDN_URL && is_string( IMAGIFY_CDN_URL ) ) {
|
||||
// Use a constant.
|
||||
$source = [
|
||||
'source' => 'constant',
|
||||
'name' => 'IMAGIFY_CDN_URL',
|
||||
'url' => IMAGIFY_CDN_URL,
|
||||
];
|
||||
} else {
|
||||
// Maybe use a filter.
|
||||
$filter_source = [
|
||||
'name' => null,
|
||||
'url' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* Provide a custom CDN source.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $filter_source {
|
||||
* @type $name string The name of which provides the URL (plugin name, etc).
|
||||
* @type $url string The CDN URL.
|
||||
* }
|
||||
*/
|
||||
$filter_source = apply_filters( 'imagify_cdn_source', $filter_source );
|
||||
|
||||
if ( ! empty( $filter_source['url'] ) ) {
|
||||
$source = [
|
||||
'source' => 'filter',
|
||||
'name' => ! empty( $filter_source['name'] ) ? $filter_source['name'] : '',
|
||||
'url' => $filter_source['url'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// No constant, no filter: use the option.
|
||||
$source = [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => $option_url && is_string( $option_url ) ? $option_url : get_imagify_option( 'cdn_url' ),
|
||||
];
|
||||
}
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// Nothing set.
|
||||
return [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$source['url'] = $this->sanitize_cdn_url( $source['url'] );
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// Not an URL.
|
||||
return [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => '',
|
||||
];
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the CDN URL value.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url The URL to sanitize.
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_cdn_url( $url ) {
|
||||
$url = sanitize_text_field( $url );
|
||||
|
||||
if ( ! $url || ! preg_match( '@^https?://.+\.[^.]+@i', $url ) ) {
|
||||
// Not an URL.
|
||||
return '';
|
||||
}
|
||||
|
||||
return trailingslashit( $url );
|
||||
}
|
||||
}
|
||||
62
wp/plugins/imagify/classes/Webp/RewriteRules/Apache.php
Normal file
62
wp/plugins/imagify/classes/Webp/RewriteRules/Apache.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the .htaccess file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Apache extends \Imagify\WriteFile\AbstractApacheDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for webp';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @source https://github.com/vincentorback/WebP-images-with-htaccess
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
return trim( '
|
||||
<IfModule mod_setenvif.c>
|
||||
# Vary: Accept for all the requests to jpeg, png, and gif.
|
||||
SetEnvIf Request_URI "\.(' . $extensions . ')$" REQUEST_image
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase ' . $home_root . '
|
||||
|
||||
# Check if browser supports WebP images.
|
||||
RewriteCond %{HTTP_ACCEPT} image/webp
|
||||
|
||||
# Check if WebP replacement image exists.
|
||||
RewriteCond %{REQUEST_FILENAME}.webp -f
|
||||
|
||||
# Serve WebP image instead.
|
||||
RewriteRule (.+)\.(' . $extensions . ')$ $1.$2.webp [T=image/webp,NC]
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
Header append Vary Accept env=REQUEST_image
|
||||
</IfModule>' );
|
||||
}
|
||||
}
|
||||
261
wp/plugins/imagify/classes/Webp/RewriteRules/Display.php
Normal file
261
wp/plugins/imagify/classes/Webp/RewriteRules/Display.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
use Imagify\WriteFile\AbstractWriteDirConfFile;
|
||||
|
||||
/**
|
||||
* Display WebP images on the site with rewrite rules.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class Display {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Configuration file writer.
|
||||
*
|
||||
* @var AbstractWriteDirConfFile
|
||||
*/
|
||||
protected $server_conf;
|
||||
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const OPTION_VALUE = 'rewrite';
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'imagify_settings_on_save', [ $this, 'maybe_add_rewrite_rules' ] );
|
||||
add_action( 'imagify_settings_webp_info', [ $this, 'maybe_add_webp_info' ] );
|
||||
add_action( 'imagify_activation', [ $this, 'activate' ] );
|
||||
add_action( 'imagify_deactivation', [ $this, 'deactivate' ] );
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* If display WebP images via rewrite rules, add the rules to the .htaccess/etc file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_add_rewrite_rules( $values ) {
|
||||
global $is_apache, $is_iis7, $is_nginx;
|
||||
|
||||
// Display WebP?
|
||||
$was_enabled = (bool) get_imagify_option( 'display_webp' );
|
||||
// See \Imagify_Options->validate_values_on_update() for why we use 'convert_to_webp' here.
|
||||
$is_enabled = ! empty( $values['display_webp'] ) && ! empty( $values['convert_to_webp'] );
|
||||
|
||||
// Which method?
|
||||
$old_value = get_imagify_option( 'display_webp_method' );
|
||||
$new_value = ! empty( $values['display_webp_method'] ) ? $values['display_webp_method'] : '';
|
||||
|
||||
// Decide when to add or remove rules.
|
||||
$is_rewrite = self::OPTION_VALUE === $new_value;
|
||||
$was_rewrite = self::OPTION_VALUE === $old_value;
|
||||
$add_or_remove = false;
|
||||
|
||||
if ( $is_enabled && $is_rewrite && ( ! $was_enabled || ! $was_rewrite ) ) {
|
||||
// Display WebP & use rewrite method, but only if one of the values changed: add rules.
|
||||
$add_or_remove = 'add';
|
||||
} elseif ( $was_enabled && $was_rewrite && ( ! $is_enabled || ! $is_rewrite ) ) {
|
||||
// Was displaying WebP & was using rewrite method, but only if one of the values changed: remove rules.
|
||||
$add_or_remove = 'remove';
|
||||
} else {
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( $is_apache ) {
|
||||
$rules = new Apache();
|
||||
} elseif ( $is_iis7 ) {
|
||||
$rules = new IIS();
|
||||
} elseif ( $is_nginx ) {
|
||||
$rules = new Nginx();
|
||||
} else {
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( 'add' === $add_or_remove ) {
|
||||
// Add the rewrite rules.
|
||||
$result = $rules->add();
|
||||
} else {
|
||||
// Remove the rewrite rules.
|
||||
$result = $rules->remove();
|
||||
}
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
// Display an error message.
|
||||
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
|
||||
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
|
||||
} else {
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the conf file is not writable, add a warning.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function maybe_add_webp_info() {
|
||||
global $is_nginx;
|
||||
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$writable = $conf->is_file_writable();
|
||||
|
||||
if ( is_wp_error( $writable ) ) {
|
||||
$rules = $conf->get_new_contents();
|
||||
|
||||
if ( ! $rules ) {
|
||||
// Uh?
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
/* translators: %s is a file name. */
|
||||
esc_html__( 'If you choose to use rewrite rules, you will have to add the following lines manually to the %s file:', 'imagify' ),
|
||||
'<code>' . $this->get_file_path( true ) . '</code>'
|
||||
);
|
||||
|
||||
echo '<pre class="code">' . esc_html( $rules ) . '</pre>';
|
||||
} elseif ( $is_nginx ) {
|
||||
printf(
|
||||
/* translators: %s is a file name. */
|
||||
esc_html__( 'If you choose to use rewrite rules, the file %s will be created and must be included into the server’s configuration file (then restart the server).', 'imagify' ),
|
||||
'<code>' . $this->get_file_path( true ) . '</code>'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add rules on plugin activation.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function activate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_webp_method' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( is_wp_error( $conf->is_file_writable() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove rules on plugin deactivation.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function deactivate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_webp_method' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file_path = $conf->get_file_path();
|
||||
$filesystem = \Imagify_Filesystem::get_instance();
|
||||
|
||||
if ( ! $filesystem->exists( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $filesystem->is_writable( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->remove();
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the path to the directory conf file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param bool $relative True to get a path relative to the site’s root.
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_file_path( $relative = false ) {
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_path = $this->get_server_conf()->get_file_path();
|
||||
|
||||
if ( $relative ) {
|
||||
return \Imagify_Filesystem::get_instance()->make_path_relative( $file_path );
|
||||
}
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server conf instance.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return \Imagify\WriteFile\WriteFileInterface
|
||||
*/
|
||||
protected function get_server_conf() {
|
||||
global $is_apache, $is_iis7, $is_nginx;
|
||||
|
||||
if ( isset( $this->server_conf ) ) {
|
||||
return $this->server_conf;
|
||||
}
|
||||
|
||||
if ( $is_apache ) {
|
||||
$this->server_conf = new Apache();
|
||||
} elseif ( $is_iis7 ) {
|
||||
$this->server_conf = new IIS();
|
||||
} elseif ( $is_nginx ) {
|
||||
$this->server_conf = new Nginx();
|
||||
} else {
|
||||
$this->server_conf = false;
|
||||
}
|
||||
|
||||
return $this->server_conf;
|
||||
}
|
||||
}
|
||||
63
wp/plugins/imagify/classes/Webp/RewriteRules/IIS.php
Normal file
63
wp/plugins/imagify/classes/Webp/RewriteRules/IIS.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the web.config file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class IIS extends \Imagify\WriteFile\AbstractIISDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for webp';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @source https://github.com/igrigorik/webp-detect/blob/master/iis.config
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
return trim( '
|
||||
<!-- @parent /configuration/system.webServer/rewrite/rules -->
|
||||
<rule name="' . esc_attr( static::TAG_NAME ) . ' 2">
|
||||
<match url="^(' . $home_root . '.+)\.(' . $extensions . ')$" ignoreCase="true" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{HTTP_ACCEPT}" pattern="image/webp" ignoreCase="false" />
|
||||
<add input="{DOCUMENT_ROOT}/{R:1}{R:2}.webp" matchType="IsFile" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="{R:1}{R:2}.webp" logRewrittenUrl="true" />
|
||||
<serverVariables>
|
||||
<set name="ACCEPTS_WEBP" value="true" />
|
||||
</serverVariables>
|
||||
</rule>
|
||||
|
||||
<!-- @parent /configuration/system.webServer/rewrite/outboundRules -->
|
||||
<rule preCondition="IsWebp" name="' . esc_attr( static::TAG_NAME ) . ' 3">
|
||||
<match serverVariable="RESPONSE_Vary" pattern=".*" />
|
||||
<action type="Rewrite" value="Accept"/>
|
||||
</rule>
|
||||
<preConditions name="' . esc_attr( static::TAG_NAME ) . ' 4">
|
||||
<preCondition name="IsWebp">
|
||||
<add input="{ACCEPTS_WEBP}" pattern="true" ignoreCase="false" />
|
||||
</preCondition>
|
||||
</preConditions>' );
|
||||
}
|
||||
}
|
||||
52
wp/plugins/imagify/classes/Webp/RewriteRules/Nginx.php
Normal file
52
wp/plugins/imagify/classes/Webp/RewriteRules/Nginx.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the imagify.conf file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Nginx extends \Imagify\WriteFile\AbstractNginxDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for webp';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
return trim( '
|
||||
location ~* ^(' . $home_root . '.+)\.(' . $extensions . ')$ {
|
||||
add_header Vary Accept;
|
||||
|
||||
if ($http_accept ~* "webp"){
|
||||
set $imwebp A;
|
||||
}
|
||||
if (-f $request_filename.webp) {
|
||||
set $imwebp "${imwebp}B";
|
||||
}
|
||||
if ($imwebp = AB) {
|
||||
rewrite ^(.*) $1.webp;
|
||||
}
|
||||
}' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace Imagify\WriteFile;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Abstract class used to add and remove contents to the .htaccess file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class AbstractApacheDirConfFile extends AbstractWriteDirConfFile {
|
||||
|
||||
/**
|
||||
* Insert new contents into the directory conf file.
|
||||
* Replaces existing marked info. Creates file if none exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $new_contents Contents to insert.
|
||||
* @return bool|\WP_Error True on write success, a \WP_Error object on failure.
|
||||
*/
|
||||
protected function insert_contents( $new_contents ) {
|
||||
$contents = $this->get_file_contents();
|
||||
|
||||
if ( is_wp_error( $contents ) ) {
|
||||
return $contents;
|
||||
}
|
||||
|
||||
$start_marker = '# BEGIN ' . static::TAG_NAME;
|
||||
$end_marker = '# END ' . static::TAG_NAME;
|
||||
|
||||
// Remove previous rules.
|
||||
$contents = preg_replace( '/\s*?' . preg_quote( $start_marker, '/' ) . '.*' . preg_quote( $end_marker, '/' ) . '\s*?/isU', "\n\n", $contents );
|
||||
$contents = trim( $contents );
|
||||
|
||||
if ( $new_contents ) {
|
||||
$contents = $new_contents . "\n\n" . $contents;
|
||||
}
|
||||
|
||||
return $this->put_file_contents( $contents );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_contents() {
|
||||
$contents = parent::get_new_contents();
|
||||
|
||||
if ( ! $contents ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '# BEGIN ' . static::TAG_NAME . "\n" . $contents . "\n# END " . static::TAG_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unfiltered path to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_file_path() {
|
||||
return $this->filesystem->get_site_root() . '.htaccess';
|
||||
}
|
||||
}
|
||||
283
wp/plugins/imagify/classes/WriteFile/AbstractIISDirConfFile.php
Normal file
283
wp/plugins/imagify/classes/WriteFile/AbstractIISDirConfFile.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
namespace Imagify\WriteFile;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Abstract class used to add and remove contents to the web.config file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class AbstractIISDirConfFile extends AbstractWriteDirConfFile {
|
||||
|
||||
/**
|
||||
* Insert new contents into the directory conf file.
|
||||
* Replaces existing marked info. Creates file if none exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $new_contents Contents to insert.
|
||||
* @return bool|\WP_Error True on write success, a \WP_Error object on failure.
|
||||
*/
|
||||
protected function insert_contents( $new_contents ) {
|
||||
$doc = $this->get_file_contents();
|
||||
|
||||
if ( is_wp_error( $doc ) ) {
|
||||
return $doc;
|
||||
}
|
||||
|
||||
$marker = static::TAG_NAME;
|
||||
$xpath = new \DOMXPath( $doc );
|
||||
|
||||
// Remove previous rules.
|
||||
$old_nodes = $xpath->query( ".//*[starts-with(@name,'$marker')]" );
|
||||
|
||||
if ( $old_nodes->length > 0 ) {
|
||||
foreach ( $old_nodes as $old_node ) {
|
||||
$old_node->parentNode->removeChild( $old_node );
|
||||
}
|
||||
}
|
||||
|
||||
// No new contents? Stop here.
|
||||
if ( ! $new_contents ) {
|
||||
return $this->put_file_contents( $doc );
|
||||
}
|
||||
|
||||
$new_contents = preg_split( '/<!--\s+@parent\s+(.+?)\s+-->/', $new_contents, -1, PREG_SPLIT_DELIM_CAPTURE );
|
||||
unset( $new_contents[0] );
|
||||
$new_contents = array_chunk( $new_contents, 2 );
|
||||
|
||||
foreach ( $new_contents as $i => $new_content ) {
|
||||
$path = rtrim( $new_content[0], '/' );
|
||||
$new_content = trim( $new_content[1] );
|
||||
|
||||
if ( '' === $new_content ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fragment = $doc->createDocumentFragment();
|
||||
$fragment->appendXML( $new_content );
|
||||
|
||||
$this->get_node( $doc, $xpath, $path, $fragment );
|
||||
}
|
||||
|
||||
return $this->put_file_contents( $doc );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unfiltered path to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_file_path() {
|
||||
return $this->filesystem->get_site_root() . 'web.config';
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OTHER TOOLS ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the file is writable.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|\WP_Error True if writable. A \WP_Error object if not.
|
||||
*/
|
||||
public function is_file_writable() {
|
||||
$file_path = $this->get_file_path();
|
||||
$file_name = $this->filesystem->make_path_relative( $file_path );
|
||||
|
||||
if ( $this->is_conf_edition_disabled() ) {
|
||||
return new \WP_Error(
|
||||
'edition_disabled',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'Edition of the %s file is disabled.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! class_exists( '\DOMDocument' ) ) {
|
||||
return new \WP_Error(
|
||||
'not_domdocument',
|
||||
sprintf(
|
||||
/* translators: 1 is a php class name, 2 is a file name. */
|
||||
__( 'The class %1$s is not present on your server, a %2$s file cannot be created nor edited.', 'imagify' ),
|
||||
'<code>DOMDocument</code>',
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $this->filesystem->exists( $file_path ) ) {
|
||||
$dir_path = $this->filesystem->dir_path( $file_path );
|
||||
|
||||
$this->filesystem->make_dir( $dir_path );
|
||||
|
||||
if ( ! $this->filesystem->is_writable( $dir_path ) ) {
|
||||
return new \WP_Error(
|
||||
'parent_not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( '%s’s parent folder is not writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( ! $this->filesystem->exists( $file_path ) ) {
|
||||
$result = $this->filesystem->put_contents( $file_path, '<configuration/>' );
|
||||
|
||||
if ( ! $result ) {
|
||||
return new \WP_Error(
|
||||
'not_created',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'The %s file could not be created.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} elseif ( ! $this->filesystem->is_writable( $file_path ) ) {
|
||||
return new \WP_Error(
|
||||
'not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'The %s file is not writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file contents.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return \DOMDocument|\WP_Error A \DOMDocument object on success, a \WP_Error object on failure.
|
||||
*/
|
||||
protected function get_file_contents() {
|
||||
$writable = $this->is_file_writable();
|
||||
|
||||
if ( is_wp_error( $writable ) ) {
|
||||
return $writable;
|
||||
}
|
||||
|
||||
$file_path = $this->get_file_path();
|
||||
$doc = new \DOMDocument();
|
||||
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
if ( false === $doc->load( $file_path ) ) {
|
||||
$file_path = $this->get_file_path();
|
||||
$file_name = $this->filesystem->make_path_relative( $file_path );
|
||||
|
||||
return new \WP_Error(
|
||||
'not_read',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'The %s file could not be read.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put new contents into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param \DOMDocument $contents A \DOMDocument object.
|
||||
* @return bool|\WP_Error True on success, a \WP_Error object on failure.
|
||||
*/
|
||||
protected function put_file_contents( $contents ) {
|
||||
$contents->encoding = 'UTF-8';
|
||||
$contents->formatOutput = true;
|
||||
|
||||
saveDomDocument( $contents, $this->get_file_path() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a DOMNode node.
|
||||
* If it does not exist it is created recursively.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param \DOMDocument $doc A \DOMDocument element.
|
||||
* @param \DOMXPath $xpath A \DOMXPath element.
|
||||
* @param string $path Path to the desired node.
|
||||
* @param \DOMNode $child A \DOMNode to be prepended.
|
||||
* @return \DOMNode The \DOMNode node.
|
||||
*/
|
||||
protected function get_node( $doc, $xpath, $path, $child ) {
|
||||
$nodelist = $xpath->query( $path );
|
||||
|
||||
if ( $nodelist->length > 0 ) {
|
||||
return $this->prepend_node( $nodelist->item( 0 ), $child );
|
||||
}
|
||||
|
||||
$path = explode( '/', $path );
|
||||
$node = array_pop( $path );
|
||||
$path = implode( '/', $path );
|
||||
|
||||
$final_node = $doc->createElement( $node );
|
||||
|
||||
if ( $child ) {
|
||||
$final_node->appendChild( $child );
|
||||
}
|
||||
|
||||
return $this->get_node( $doc, $xpath, $path, $final_node );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepend a DOMNode node.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param \DOMNode $container_node The \DOMNode that will contain the new node.
|
||||
* @param \DOMNode $new_node The \DOMNode to be prepended.
|
||||
* @return \DOMNode The \DOMNode containing the new node.
|
||||
*/
|
||||
protected function prepend_node( $container_node, $new_node ) {
|
||||
if ( ! $new_node ) {
|
||||
return $container_node;
|
||||
}
|
||||
|
||||
if ( $container_node->hasChildNodes() ) {
|
||||
$container_node->insertBefore( $new_node, $container_node->firstChild );
|
||||
} else {
|
||||
$container_node->appendChild( $new_node );
|
||||
}
|
||||
|
||||
return $container_node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace Imagify\WriteFile;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Abstract class used to add and remove contents to imagify.conf file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class AbstractNginxDirConfFile extends AbstractWriteDirConfFile {
|
||||
|
||||
/**
|
||||
* Insert new contents into the directory conf file.
|
||||
* Replaces existing marked info. Creates file if none exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $new_contents Contents to insert.
|
||||
* @return bool|\WP_Error True on write success, a \WP_Error object on failure.
|
||||
*/
|
||||
protected function insert_contents( $new_contents ) {
|
||||
$contents = $this->get_file_contents();
|
||||
|
||||
if ( is_wp_error( $contents ) ) {
|
||||
return $contents;
|
||||
}
|
||||
|
||||
$start_marker = '# BEGIN ' . static::TAG_NAME;
|
||||
$end_marker = '# END ' . static::TAG_NAME;
|
||||
|
||||
// Remove previous rules.
|
||||
$contents = preg_replace( '/\s*?' . preg_quote( $start_marker, '/' ) . '.*' . preg_quote( $end_marker, '/' ) . '\s*?/isU', "\n\n", $contents );
|
||||
$contents = trim( $contents );
|
||||
|
||||
if ( $new_contents ) {
|
||||
$contents = $new_contents . "\n\n" . $contents;
|
||||
}
|
||||
|
||||
return $this->put_file_contents( $contents );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_contents() {
|
||||
$contents = parent::get_new_contents();
|
||||
|
||||
if ( ! $contents ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '# BEGIN ' . static::TAG_NAME . "\n" . $contents . "\n# END " . static::TAG_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unfiltered path to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_file_path() {
|
||||
return $this->filesystem->get_site_root() . 'conf/imagify.conf';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
<?php
|
||||
namespace Imagify\WriteFile;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Abstract class used to add and remove contents to a directory conf file (.htaccess, etc).
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class AbstractWriteDirConfFile implements WriteFileInterface {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
const TAG_NAME = 'Imagify ###';
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var \Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new contents to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|\WP_Error True on success. A \WP_Error object on error.
|
||||
*/
|
||||
public function add() {
|
||||
$result = $this->insert_contents( $this->get_new_contents() );
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
return true;
|
||||
}
|
||||
$file_path = $this->get_file_path();
|
||||
$file_name = $this->filesystem->make_path_relative( $file_path );
|
||||
|
||||
if ( 'edition_disabled' === $result->get_error_code() ) {
|
||||
return new \WP_Error(
|
||||
'edition_disabled',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'Imagify did not add contents to the %s file, as its edition is disabled.', 'imagify' ),
|
||||
$file_name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new \WP_Error(
|
||||
'add_contents_failure',
|
||||
sprintf(
|
||||
/* translators: 1 is a file name, 2 is an error message. */
|
||||
__( 'Imagify could not insert contents into the %1$s file: %2$s', 'imagify' ),
|
||||
$file_name,
|
||||
$result->get_error_message()
|
||||
),
|
||||
[ 'code' => $result->get_error_code() ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the related contents from the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|\WP_Error True on success. A \WP_Error object on error.
|
||||
*/
|
||||
public function remove() {
|
||||
$result = $this->insert_contents( '' );
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$file_name = $this->filesystem->make_path_relative( $file_path );
|
||||
|
||||
if ( 'edition_disabled' === $result->get_error_code() ) {
|
||||
return new \WP_Error(
|
||||
'edition_disabled',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'Imagify did not remove the contents from the %s file, as its edition is disabled.', 'imagify' ),
|
||||
$file_name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new \WP_Error(
|
||||
'add_contents_failure',
|
||||
sprintf(
|
||||
/* translators: 1 is a file name, 2 is an error message. */
|
||||
__( 'Imagify could not remove contents from the %1$s file: %2$s', 'imagify' ),
|
||||
$file_name,
|
||||
$result->get_error_message()
|
||||
),
|
||||
[ 'code' => $result->get_error_code() ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_file_path() {
|
||||
$file_path = $this->get_raw_file_path();
|
||||
|
||||
/**
|
||||
* Filter the path to the directory conf file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $file_path Path to the file.
|
||||
*/
|
||||
$new_file_path = apply_filters( 'imagify_dir_conf_path', $file_path );
|
||||
|
||||
if ( $new_file_path && is_string( $new_file_path ) ) {
|
||||
return $new_file_path;
|
||||
}
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file is writable.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|\WP_Error True if writable. A \WP_Error object if not.
|
||||
*/
|
||||
public function is_file_writable() {
|
||||
$file_path = $this->get_file_path();
|
||||
$file_name = $this->filesystem->make_path_relative( $file_path );
|
||||
|
||||
if ( $this->is_conf_edition_disabled() ) {
|
||||
return new \WP_Error(
|
||||
'edition_disabled',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'Edition of the %s file is disabled.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! $this->filesystem->exists( $file_path ) ) {
|
||||
$dir_path = $this->filesystem->dir_path( $file_path );
|
||||
|
||||
$this->filesystem->make_dir( $dir_path );
|
||||
|
||||
if ( ! $this->filesystem->is_writable( $dir_path ) ) {
|
||||
return new \WP_Error(
|
||||
'parent_not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( '%s’s parent folder is not writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( ! $this->filesystem->touch( $file_path ) ) {
|
||||
return new \WP_Error(
|
||||
'not_created',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'The %s file could not be created.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
} elseif ( ! $this->filesystem->is_writable( $file_path ) ) {
|
||||
return new \WP_Error(
|
||||
'not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'The %s file is not writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_contents() {
|
||||
$contents = $this->get_raw_new_contents();
|
||||
|
||||
/**
|
||||
* Filter the contents to add to the directory conf file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $contents The contents.
|
||||
*/
|
||||
$new_contents = apply_filters( 'imagify_dir_conf_contents', $contents );
|
||||
|
||||
if ( $new_contents && is_string( $new_contents ) ) {
|
||||
return $new_contents;
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** ABSTRACT METHODS ======================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Insert new contents into the directory conf file.
|
||||
* Replaces existing marked info. Creates file if none exists.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $new_contents Contents to insert.
|
||||
* @return bool|\WP_Error True on write success, a \WP_Error object on failure.
|
||||
*/
|
||||
abstract protected function insert_contents( $new_contents );
|
||||
|
||||
/**
|
||||
* Get the unfiltered path to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_raw_file_path();
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_raw_new_contents();
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OTHER TOOLS ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the file contents.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return mixed|\WP_Error The file contents on success, a \WP_Error object on failure.
|
||||
*/
|
||||
protected function get_file_contents() {
|
||||
$writable = $this->is_file_writable();
|
||||
|
||||
if ( is_wp_error( $writable ) ) {
|
||||
return $writable;
|
||||
}
|
||||
|
||||
$file_path = $this->get_file_path();
|
||||
|
||||
if ( ! $this->filesystem->exists( $file_path ) ) {
|
||||
// This should not happen.
|
||||
return '';
|
||||
}
|
||||
|
||||
$contents = $this->filesystem->get_contents( $file_path );
|
||||
|
||||
if ( false === $contents ) {
|
||||
return new \WP_Error(
|
||||
'not_read',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'The %s file could not be read.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put new contents into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $contents New contents to add to the file.
|
||||
* @return bool|\WP_Error True on success, a \WP_Error object on failure.
|
||||
*/
|
||||
protected function put_file_contents( $contents ) {
|
||||
$file_path = $this->get_file_path();
|
||||
$result = $this->filesystem->put_contents( $file_path, $contents );
|
||||
|
||||
if ( $result ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$file_name = $this->filesystem->make_path_relative( $file_path );
|
||||
|
||||
return new \WP_Error(
|
||||
'edition_failed',
|
||||
sprintf(
|
||||
/* translators: %s is a file name. */
|
||||
__( 'Could not write into the %s file.', 'imagify' ),
|
||||
'<code>' . esc_html( $file_name ) . '</code>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if edition of the directory conf file is disabled.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool True to disable, false otherwise.
|
||||
*/
|
||||
protected function is_conf_edition_disabled() {
|
||||
/**
|
||||
* Disable directory conf edition.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $disable True to disable, false otherwise.
|
||||
*/
|
||||
return (bool) apply_filters( 'imagify_disable_dir_conf_edition', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a regex pattern to be used to match the supported file extensions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_extensions_pattern() {
|
||||
$extensions = imagify_get_mime_types( 'image' );
|
||||
$extensions = array_keys( $extensions );
|
||||
|
||||
return implode( '|', $extensions );
|
||||
}
|
||||
}
|
||||
68
wp/plugins/imagify/classes/WriteFile/WriteFileInterface.php
Normal file
68
wp/plugins/imagify/classes/WriteFile/WriteFileInterface.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
namespace Imagify\WriteFile;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
/**
|
||||
* Interface to add and remove contents to a file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
interface WriteFileInterface {
|
||||
|
||||
/**
|
||||
* Add new contents to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|\WP_Error True on success. A \WP_Error object on error.
|
||||
*/
|
||||
public function add();
|
||||
|
||||
/**
|
||||
* Remove the related contents from the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|\WP_Error True on success. A \WP_Error object on error.
|
||||
*/
|
||||
public function remove();
|
||||
|
||||
/**
|
||||
* Get the path to the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_file_path();
|
||||
|
||||
/**
|
||||
* Tell if the file is writable.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool|\WP_Error True if writable. A \WP_Error object if not.
|
||||
*/
|
||||
public function is_file_writable();
|
||||
|
||||
/**
|
||||
* Get new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_contents();
|
||||
}
|
||||
Reference in New Issue
Block a user